Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2021 Yubico AB. All rights reserved. |
3 | | * Use of this source code is governed by a BSD-style |
4 | | * license that can be found in the LICENSE file. |
5 | | */ |
6 | | |
7 | | /* |
8 | | * Trusted Platform Module (TPM) 2.0 attestation support. Documentation |
9 | | * references are relative to revision 01.38 of the TPM 2.0 specification. |
10 | | */ |
11 | | |
12 | | #include <openssl/sha.h> |
13 | | |
14 | | #include "packed.h" |
15 | | #include "fido.h" |
16 | | |
17 | | /* Part 1, 4.89: TPM_GENERATED_VALUE */ |
18 | 17 | #define TPM_MAGIC 0xff544347 |
19 | | |
20 | | /* Part 2, 6.3: TPM_ALG_ID */ |
21 | 8 | #define TPM_ALG_RSA 0x0001 |
22 | 46 | #define TPM_ALG_SHA256 0x000b |
23 | 73 | #define TPM_ALG_NULL 0x0010 |
24 | 19 | #define TPM_ALG_ECC 0x0023 |
25 | | |
26 | | /* Part 2, 6.4: TPM_ECC_CURVE */ |
27 | 19 | #define TPM_ECC_P256 0x0003 |
28 | | |
29 | | /* Part 2, 6.9: TPM_ST_ATTEST_CERTIFY */ |
30 | 17 | #define TPM_ST_CERTIFY 0x8017 |
31 | | |
32 | | /* Part 2, 8.3: TPMA_OBJECT */ |
33 | 27 | #define TPMA_RESERVED 0xfff8f309 /* reserved bits; must be zero */ |
34 | 27 | #define TPMA_FIXED 0x00000002 /* object has fixed hierarchy */ |
35 | 27 | #define TPMA_CLEAR 0x00000004 /* object persists */ |
36 | 27 | #define TPMA_FIXED_P 0x00000010 /* object has fixed parent */ |
37 | 27 | #define TPMA_SENSITIVE 0x00000020 /* data originates within tpm */ |
38 | 27 | #define TPMA_SIGN 0x00040000 /* object may sign */ |
39 | | |
40 | | /* Part 2, 10.4.2: TPM2B_DIGEST */ |
41 | | PACKED_TYPE(tpm_sha256_digest_t, |
42 | | struct tpm_sha256_digest { |
43 | | uint16_t size; /* sizeof(body) */ |
44 | | uint8_t body[32]; |
45 | | }) |
46 | | |
47 | | /* Part 2, 10.4.3: TPM2B_DATA */ |
48 | | PACKED_TYPE(tpm_sha1_data_t, |
49 | | struct tpm_sha1_data { |
50 | | uint16_t size; /* sizeof(body */ |
51 | | uint8_t body[20]; |
52 | | }) |
53 | | |
54 | | /* Part 2, 10.5.3: TPM2B_NAME */ |
55 | | PACKED_TYPE(tpm_sha256_name_t, |
56 | | struct tpm_sha256_name { |
57 | | uint16_t size; /* sizeof(alg) + sizeof(body) */ |
58 | | uint16_t alg; /* TPM_ALG_SHA256 */ |
59 | | uint8_t body[32]; |
60 | | }) |
61 | | |
62 | | /* Part 2, 10.11.1: TPMS_CLOCK_INFO */ |
63 | | PACKED_TYPE(tpm_clock_info_t, |
64 | | struct tpm_clock_info { |
65 | | uint64_t timestamp_ms; |
66 | | uint32_t reset_count; /* obfuscated by tpm */ |
67 | | uint32_t restart_count; /* obfuscated by tpm */ |
68 | | uint8_t safe; /* 1 if timestamp_ms is current */ |
69 | | }) |
70 | | |
71 | | /* Part 2, 10.12.8 TPMS_ATTEST */ |
72 | | PACKED_TYPE(tpm_sha1_attest_t, |
73 | | struct tpm_sha1_attest { |
74 | | uint32_t magic; /* TPM_MAGIC */ |
75 | | uint16_t type; /* TPM_ST_ATTEST_CERTIFY */ |
76 | | tpm_sha256_name_t signer; /* full tpm path of signing key */ |
77 | | tpm_sha1_data_t data; /* signed sha1 */ |
78 | | tpm_clock_info_t clock; |
79 | | uint64_t fwversion; /* obfuscated by tpm */ |
80 | | tpm_sha256_name_t name; /* sha256 of tpm_rs256_pubarea_t */ |
81 | | tpm_sha256_name_t qual_name; /* full tpm path of attested key */ |
82 | | }) |
83 | | |
84 | | /* Part 2, 11.2.4.5: TPM2B_PUBLIC_KEY_RSA */ |
85 | | PACKED_TYPE(tpm_rs256_key_t, |
86 | | struct tpm_rs256_key { |
87 | | uint16_t size; /* sizeof(body) */ |
88 | | uint8_t body[256]; |
89 | | }) |
90 | | |
91 | | /* Part 2, 11.2.5.1: TPM2B_ECC_PARAMETER */ |
92 | | PACKED_TYPE(tpm_es256_coord_t, |
93 | | struct tpm_es256_coord { |
94 | | uint16_t size; /* sizeof(body) */ |
95 | | uint8_t body[32]; |
96 | | }) |
97 | | |
98 | | /* Part 2, 11.2.5.2: TPMS_ECC_POINT */ |
99 | | PACKED_TYPE(tpm_es256_point_t, |
100 | | struct tpm_es256_point { |
101 | | tpm_es256_coord_t x; |
102 | | tpm_es256_coord_t y; |
103 | | }) |
104 | | |
105 | | /* Part 2, 12.2.3.5: TPMS_RSA_PARMS */ |
106 | | PACKED_TYPE(tpm_rs256_param_t, |
107 | | struct tpm_rs256_param { |
108 | | uint16_t symmetric; /* TPM_ALG_NULL */ |
109 | | uint16_t scheme; /* TPM_ALG_NULL */ |
110 | | uint16_t keybits; /* 2048 */ |
111 | | uint32_t exponent; /* zero (meaning 2^16 + 1) */ |
112 | | }) |
113 | | |
114 | | /* Part 2, 12.2.3.6: TPMS_ECC_PARMS */ |
115 | | PACKED_TYPE(tpm_es256_param_t, |
116 | | struct tpm_es256_param { |
117 | | uint16_t symmetric; /* TPM_ALG_NULL */ |
118 | | uint16_t scheme; /* TPM_ALG_NULL */ |
119 | | uint16_t curve_id; /* TPM_ECC_P256 */ |
120 | | uint16_t kdf; /* TPM_ALG_NULL */ |
121 | | }) |
122 | | |
123 | | /* Part 2, 12.2.4: TPMT_PUBLIC */ |
124 | | PACKED_TYPE(tpm_rs256_pubarea_t, |
125 | | struct tpm_rs256_pubarea { |
126 | | uint16_t alg; /* TPM_ALG_RSA */ |
127 | | uint16_t hash; /* TPM_ALG_SHA256 */ |
128 | | uint32_t attr; |
129 | | tpm_sha256_digest_t policy; /* must be present? */ |
130 | | tpm_rs256_param_t param; |
131 | | tpm_rs256_key_t key; |
132 | | }) |
133 | | |
134 | | /* Part 2, 12.2.4: TPMT_PUBLIC */ |
135 | | PACKED_TYPE(tpm_es256_pubarea_t, |
136 | | struct tpm_es256_pubarea { |
137 | | uint16_t alg; /* TPM_ALG_ECC */ |
138 | | uint16_t hash; /* TPM_ALG_SHA256 */ |
139 | | uint32_t attr; |
140 | | tpm_sha256_digest_t policy; /* must be present? */ |
141 | | tpm_es256_param_t param; |
142 | | tpm_es256_point_t point; |
143 | | }) |
144 | | |
145 | | static int |
146 | | get_signed_sha1(tpm_sha1_data_t *dgst, const fido_blob_t *authdata, |
147 | | const fido_blob_t *clientdata) |
148 | 25 | { |
149 | 25 | const EVP_MD *md = NULL; |
150 | 25 | EVP_MD_CTX *ctx = NULL; |
151 | 25 | int ok = -1; |
152 | | |
153 | 25 | if ((dgst->size = sizeof(dgst->body)) != SHA_DIGEST_LENGTH || |
154 | 25 | (md = EVP_sha1()) == NULL || |
155 | 25 | (ctx = EVP_MD_CTX_new()) == NULL || |
156 | 25 | EVP_DigestInit_ex(ctx, md, NULL) != 1 || |
157 | 25 | EVP_DigestUpdate(ctx, authdata->ptr, authdata->len) != 1 || |
158 | 25 | EVP_DigestUpdate(ctx, clientdata->ptr, clientdata->len) != 1 || |
159 | 25 | EVP_DigestFinal_ex(ctx, dgst->body, NULL) != 1) { |
160 | 6 | fido_log_debug("%s: sha1", __func__); |
161 | 6 | goto fail; |
162 | 6 | } |
163 | | |
164 | 19 | ok = 0; |
165 | 25 | fail: |
166 | 25 | EVP_MD_CTX_free(ctx); |
167 | | |
168 | 25 | return (ok); |
169 | 19 | } |
170 | | |
171 | | static int |
172 | | get_signed_name(tpm_sha256_name_t *name, const fido_blob_t *pubarea) |
173 | 19 | { |
174 | 19 | name->alg = TPM_ALG_SHA256; |
175 | 19 | name->size = sizeof(name->alg) + sizeof(name->body); |
176 | 19 | if (sizeof(name->body) != SHA256_DIGEST_LENGTH || |
177 | 19 | SHA256(pubarea->ptr, pubarea->len, name->body) != name->body) { |
178 | 1 | fido_log_debug("%s: sha256", __func__); |
179 | 1 | return -1; |
180 | 1 | } |
181 | | |
182 | 18 | return 0; |
183 | 19 | } |
184 | | |
185 | | static void |
186 | | bswap_rs256_pubarea(tpm_rs256_pubarea_t *x) |
187 | 8 | { |
188 | 8 | x->alg = htobe16(x->alg); |
189 | 8 | x->hash = htobe16(x->hash); |
190 | 8 | x->attr = htobe32(x->attr); |
191 | 8 | x->policy.size = htobe16(x->policy.size); |
192 | 8 | x->param.symmetric = htobe16(x->param.symmetric); |
193 | 8 | x->param.scheme = htobe16(x->param.scheme); |
194 | 8 | x->param.keybits = htobe16(x->param.keybits); |
195 | 8 | x->key.size = htobe16(x->key.size); |
196 | 8 | } |
197 | | |
198 | | static void |
199 | | bswap_es256_pubarea(tpm_es256_pubarea_t *x) |
200 | 19 | { |
201 | 19 | x->alg = htobe16(x->alg); |
202 | 19 | x->hash = htobe16(x->hash); |
203 | 19 | x->attr = htobe32(x->attr); |
204 | 19 | x->policy.size = htobe16(x->policy.size); |
205 | 19 | x->param.symmetric = htobe16(x->param.symmetric); |
206 | 19 | x->param.scheme = htobe16(x->param.scheme); |
207 | 19 | x->param.curve_id = htobe16(x->param.curve_id); |
208 | 19 | x->param.kdf = htobe16(x->param.kdf); |
209 | 19 | x->point.x.size = htobe16(x->point.x.size); |
210 | 19 | x->point.y.size = htobe16(x->point.y.size); |
211 | 19 | } |
212 | | |
213 | | static void |
214 | | bswap_sha1_certinfo(tpm_sha1_attest_t *x) |
215 | 17 | { |
216 | 17 | x->magic = htobe32(x->magic); |
217 | 17 | x->type = htobe16(x->type); |
218 | 17 | x->signer.size = htobe16(x->signer.size); |
219 | 17 | x->data.size = htobe16(x->data.size); |
220 | 17 | x->name.alg = htobe16(x->name.alg); |
221 | 17 | x->name.size = htobe16(x->name.size); |
222 | 17 | } |
223 | | |
224 | | static int |
225 | | check_rs256_pubarea(const fido_blob_t *buf, const rs256_pk_t *pk) |
226 | 9 | { |
227 | 9 | const tpm_rs256_pubarea_t *actual; |
228 | 9 | tpm_rs256_pubarea_t expected; |
229 | 9 | int ok; |
230 | | |
231 | 9 | if (buf->len != sizeof(*actual)) { |
232 | 1 | fido_log_debug("%s: buf->len=%zu", __func__, buf->len); |
233 | 1 | return -1; |
234 | 1 | } |
235 | 8 | actual = (const void *)buf->ptr; |
236 | | |
237 | 8 | memset(&expected, 0, sizeof(expected)); |
238 | 8 | expected.alg = TPM_ALG_RSA; |
239 | 8 | expected.hash = TPM_ALG_SHA256; |
240 | 8 | expected.attr = be32toh(actual->attr); |
241 | 8 | expected.attr &= ~(TPMA_RESERVED|TPMA_CLEAR); |
242 | 8 | expected.attr |= (TPMA_FIXED|TPMA_FIXED_P|TPMA_SENSITIVE|TPMA_SIGN); |
243 | 8 | expected.policy = actual->policy; |
244 | 8 | expected.policy.size = sizeof(expected.policy.body); |
245 | 8 | expected.param.symmetric = TPM_ALG_NULL; |
246 | 8 | expected.param.scheme = TPM_ALG_NULL; |
247 | 8 | expected.param.keybits = 2048; |
248 | 8 | expected.param.exponent = 0; /* meaning 2^16+1 */ |
249 | 8 | expected.key.size = sizeof(expected.key.body); |
250 | 8 | memcpy(&expected.key.body, &pk->n, sizeof(expected.key.body)); |
251 | 8 | bswap_rs256_pubarea(&expected); |
252 | | |
253 | 8 | ok = timingsafe_bcmp(&expected, actual, sizeof(expected)); |
254 | 8 | explicit_bzero(&expected, sizeof(expected)); |
255 | | |
256 | 8 | return ok != 0 ? -1 : 0; |
257 | 9 | } |
258 | | |
259 | | static int |
260 | | check_es256_pubarea(const fido_blob_t *buf, const es256_pk_t *pk) |
261 | 20 | { |
262 | 20 | const tpm_es256_pubarea_t *actual; |
263 | 20 | tpm_es256_pubarea_t expected; |
264 | 20 | int ok; |
265 | | |
266 | 20 | if (buf->len != sizeof(*actual)) { |
267 | 1 | fido_log_debug("%s: buf->len=%zu", __func__, buf->len); |
268 | 1 | return -1; |
269 | 1 | } |
270 | 19 | actual = (const void *)buf->ptr; |
271 | | |
272 | 19 | memset(&expected, 0, sizeof(expected)); |
273 | 19 | expected.alg = TPM_ALG_ECC; |
274 | 19 | expected.hash = TPM_ALG_SHA256; |
275 | 19 | expected.attr = be32toh(actual->attr); |
276 | 19 | expected.attr &= ~(TPMA_RESERVED|TPMA_CLEAR); |
277 | 19 | expected.attr |= (TPMA_FIXED|TPMA_FIXED_P|TPMA_SENSITIVE|TPMA_SIGN); |
278 | 19 | expected.policy = actual->policy; |
279 | 19 | expected.policy.size = sizeof(expected.policy.body); |
280 | 19 | expected.param.symmetric = TPM_ALG_NULL; |
281 | 19 | expected.param.scheme = TPM_ALG_NULL; /* TCG Alg. Registry, 5.2.4 */ |
282 | 19 | expected.param.curve_id = TPM_ECC_P256; |
283 | 19 | expected.param.kdf = TPM_ALG_NULL; |
284 | 19 | expected.point.x.size = sizeof(expected.point.x.body); |
285 | 19 | expected.point.y.size = sizeof(expected.point.y.body); |
286 | 19 | memcpy(&expected.point.x.body, &pk->x, sizeof(expected.point.x.body)); |
287 | 19 | memcpy(&expected.point.y.body, &pk->y, sizeof(expected.point.y.body)); |
288 | 19 | bswap_es256_pubarea(&expected); |
289 | | |
290 | 19 | ok = timingsafe_bcmp(&expected, actual, sizeof(expected)); |
291 | 19 | explicit_bzero(&expected, sizeof(expected)); |
292 | | |
293 | 19 | return ok != 0 ? -1 : 0; |
294 | 20 | } |
295 | | |
296 | | static int |
297 | | check_sha1_certinfo(const fido_blob_t *buf, const fido_blob_t *clientdata_hash, |
298 | | const fido_blob_t *authdata_raw, const fido_blob_t *pubarea) |
299 | 25 | { |
300 | 25 | const tpm_sha1_attest_t *actual; |
301 | 25 | tpm_sha1_attest_t expected; |
302 | 25 | tpm_sha1_data_t signed_data; |
303 | 25 | tpm_sha256_name_t signed_name; |
304 | 25 | int ok = -1; |
305 | | |
306 | 25 | memset(&signed_data, 0, sizeof(signed_data)); |
307 | 25 | memset(&signed_name, 0, sizeof(signed_name)); |
308 | | |
309 | 25 | if (get_signed_sha1(&signed_data, authdata_raw, clientdata_hash) < 0 || |
310 | 25 | get_signed_name(&signed_name, pubarea) < 0) { |
311 | 7 | fido_log_debug("%s: get_signed_sha1/name", __func__); |
312 | 7 | goto fail; |
313 | 7 | } |
314 | 18 | if (buf->len != sizeof(*actual)) { |
315 | 1 | fido_log_debug("%s: buf->len=%zu", __func__, buf->len); |
316 | 1 | goto fail; |
317 | 1 | } |
318 | 17 | actual = (const void *)buf->ptr; |
319 | | |
320 | 17 | memset(&expected, 0, sizeof(expected)); |
321 | 17 | expected.magic = TPM_MAGIC; |
322 | 17 | expected.type = TPM_ST_CERTIFY; |
323 | 17 | expected.signer = actual->signer; |
324 | 17 | expected.signer.size = sizeof(expected.signer.alg) + |
325 | 17 | sizeof(expected.signer.body); |
326 | 17 | expected.data = signed_data; |
327 | 17 | expected.clock = actual->clock; |
328 | 17 | expected.clock.safe = 1; |
329 | 17 | expected.fwversion = actual->fwversion; |
330 | 17 | expected.name = signed_name; |
331 | 17 | expected.qual_name = actual->qual_name; |
332 | 17 | bswap_sha1_certinfo(&expected); |
333 | | |
334 | 17 | ok = timingsafe_bcmp(&expected, actual, sizeof(expected)); |
335 | 25 | fail: |
336 | 25 | explicit_bzero(&expected, sizeof(expected)); |
337 | 25 | explicit_bzero(&signed_data, sizeof(signed_data)); |
338 | 25 | explicit_bzero(&signed_name, sizeof(signed_name)); |
339 | | |
340 | 25 | return ok != 0 ? -1 : 0; |
341 | 17 | } |
342 | | |
343 | | int |
344 | | fido_get_signed_hash_tpm(fido_blob_t *dgst, const fido_blob_t *clientdata_hash, |
345 | | const fido_blob_t *authdata_raw, const fido_attstmt_t *attstmt, |
346 | | const fido_attcred_t *attcred) |
347 | 31 | { |
348 | 31 | const fido_blob_t *pubarea = &attstmt->pubarea; |
349 | 31 | const fido_blob_t *certinfo = &attstmt->certinfo; |
350 | | |
351 | 31 | if (attstmt->alg != COSE_RS1) { |
352 | 1 | fido_log_debug("%s: unsupported alg %d", __func__, |
353 | 1 | attstmt->alg); |
354 | 1 | return -1; |
355 | 1 | } |
356 | | |
357 | 30 | switch (attcred->type) { |
358 | 20 | case COSE_ES256: |
359 | 20 | if (check_es256_pubarea(pubarea, &attcred->pubkey.es256) < 0) { |
360 | 2 | fido_log_debug("%s: check_es256_pubarea", __func__); |
361 | 2 | return -1; |
362 | 2 | } |
363 | 18 | break; |
364 | 18 | case COSE_RS256: |
365 | 9 | if (check_rs256_pubarea(pubarea, &attcred->pubkey.rs256) < 0) { |
366 | 2 | fido_log_debug("%s: check_rs256_pubarea", __func__); |
367 | 2 | return -1; |
368 | 2 | } |
369 | 7 | break; |
370 | 7 | default: |
371 | 1 | fido_log_debug("%s: unsupported type %d", __func__, |
372 | 1 | attcred->type); |
373 | 1 | return -1; |
374 | 30 | } |
375 | | |
376 | 25 | if (check_sha1_certinfo(certinfo, clientdata_hash, authdata_raw, |
377 | 25 | pubarea) < 0) { |
378 | 12 | fido_log_debug("%s: check_sha1_certinfo", __func__); |
379 | 12 | return -1; |
380 | 12 | } |
381 | | |
382 | 13 | if (dgst->len < SHA_DIGEST_LENGTH || |
383 | 13 | SHA1(certinfo->ptr, certinfo->len, dgst->ptr) != dgst->ptr) { |
384 | 1 | fido_log_debug("%s: sha1", __func__); |
385 | 1 | return -1; |
386 | 1 | } |
387 | 12 | dgst->len = SHA_DIGEST_LENGTH; |
388 | | |
389 | 12 | return 0; |
390 | 13 | } |