]>
Commit | Line | Data |
---|---|---|
8249e6af AS |
1 | /* |
2 | * Copyright (C) 2021 Andreas Steffen, strongSec GmbH | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify it | |
5 | * under the terms of the GNU General Public License as published by the | |
6 | * Free Software Foundation; either version 2 of the License, or (at your | |
7 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, but | |
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
11 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
12 | * for more details. | |
13 | */ | |
14 | ||
15 | #ifdef TSS_TSS2_V2 | |
16 | ||
17 | #include "tpm_tss_tss2_session.h" | |
0b76ca13 | 18 | #include "tpm_tss_tss2_names.h" |
8249e6af AS |
19 | |
20 | #define LABEL "TPM 2.0 - " | |
21 | ||
22 | typedef struct private_tpm_tss_tss2_session_t private_tpm_tss_tss2_session_t; | |
23 | ||
24 | /** | |
25 | * Private data of an tpm_tss_tss2_session_t object. | |
26 | */ | |
27 | struct private_tpm_tss_tss2_session_t { | |
28 | ||
29 | /** | |
30 | * Public tpm_tss_tss2_session_t interface. | |
31 | */ | |
32 | tpm_tss_tss2_session_t public; | |
33 | ||
34 | /** | |
35 | * Session handle for protected communication with TPM 2.0 | |
36 | */ | |
37 | uint32_t session_handle; | |
38 | ||
39 | /** | |
40 | * Session key for protected communication with TPM 2.0 | |
41 | */ | |
42 | chunk_t session_key; | |
43 | ||
44 | /** | |
45 | * Hash algorithm to be used for protected communication with TPM 2.0 | |
46 | */ | |
47 | TPM2_ALG_ID hash_alg; | |
48 | ||
49 | /** | |
50 | * nonceCaller used for protected communication with TPM 2.0 | |
51 | */ | |
52 | TPM2B_NONCE nonceCaller; | |
53 | ||
54 | /** | |
55 | * nonceTPM used for protected communication with TPM 2.0 | |
56 | */ | |
57 | TPM2B_NONCE nonceTPM; | |
58 | ||
59 | /** | |
0b76ca13 | 60 | * AES-CFB key size in bytes |
8249e6af | 61 | */ |
0b76ca13 | 62 | size_t aes_key_len; |
8249e6af AS |
63 | |
64 | /** | |
65 | * SYS context | |
66 | */ | |
67 | TSS2_SYS_CONTEXT *sys_context; | |
68 | ||
69 | }; | |
70 | ||
71 | /** | |
72 | * Two functions shared with tpm_tss_tss2_v2.c | |
73 | */ | |
74 | ||
75 | hash_algorithm_t hash_alg_from_tpm_alg_id(TPM2_ALG_ID alg); | |
76 | ||
77 | size_t hash_len_from_tpm_alg_id(TPM2_ALG_ID alg); | |
78 | ||
79 | ||
80 | /** | |
81 | * Convert TPM2_ALG_ID to PRF algorithm | |
82 | */ | |
83 | pseudo_random_function_t prf_alg_from_tpm_alg_id(TPM2_ALG_ID alg) | |
84 | { | |
85 | switch (alg) | |
86 | { | |
87 | case TPM2_ALG_SHA1: | |
88 | return PRF_HMAC_SHA1; | |
89 | case TPM2_ALG_SHA256: | |
90 | return PRF_HMAC_SHA2_256; | |
91 | case TPM2_ALG_SHA384: | |
92 | return PRF_HMAC_SHA2_384; | |
93 | case TPM2_ALG_SHA512: | |
94 | return PRF_HMAC_SHA2_512; | |
95 | default: | |
96 | return PRF_UNDEFINED; | |
97 | } | |
98 | } | |
99 | ||
100 | static bool generate_nonce(size_t size, TPM2B_NONCE *nonce) | |
101 | { | |
102 | nonce_gen_t *nonce_gen; | |
103 | bool success; | |
104 | ||
105 | nonce_gen = lib->crypto->create_nonce_gen(lib->crypto); | |
106 | if (!nonce_gen) | |
107 | { | |
108 | DBG1(DBG_PTS, "no nonce generator available"); | |
109 | return FALSE; | |
110 | } | |
111 | nonce->size = size; | |
112 | success = nonce_gen->get_nonce(nonce_gen, nonce->size, nonce->buffer); | |
113 | nonce_gen->destroy(nonce_gen); | |
114 | ||
115 | if (!success) | |
116 | { | |
117 | DBG1(DBG_PTS, "generation of nonce failed"); | |
118 | return FALSE; | |
119 | } | |
120 | ||
121 | return TRUE; | |
122 | } | |
123 | ||
124 | METHOD(tpm_tss_tss2_session_t, set_cmd_auths, bool, | |
125 | private_tpm_tss_tss2_session_t *this) | |
126 | { | |
127 | size_t hash_len, param_size, cp_size; | |
128 | const uint8_t *param_buffer, *cp_buffer; | |
129 | uint8_t cc_buffer[4]; | |
130 | hash_algorithm_t hash_algorithm; | |
131 | hasher_t *hasher; | |
132 | pseudo_random_function_t prf_alg; | |
133 | prf_t *prf; | |
134 | chunk_t data, cp_hash, cp_hmac, nonce_caller, nonce_tpm, session_attributes; | |
135 | bool success; | |
136 | uint32_t rval; | |
137 | ||
138 | TSS2L_SYS_AUTH_COMMAND cmd; | |
139 | TPM2B_DIGEST cpHash; | |
140 | ||
141 | cmd.count = 1; | |
142 | cmd.auths[0].sessionHandle = this->session_handle; | |
143 | cmd.auths[0].sessionAttributes = TPMA_SESSION_CONTINUESESSION | | |
144 | TPMA_SESSION_ENCRYPT; | |
145 | session_attributes = chunk_create(&cmd.auths[0].sessionAttributes, 1); | |
146 | ||
147 | hash_len = hash_len_from_tpm_alg_id(this->hash_alg); | |
148 | ||
149 | if (!generate_nonce(hash_len, &this->nonceCaller)) | |
150 | { | |
151 | return FALSE; | |
152 | } | |
153 | cmd.auths[0].nonce.size = this->nonceCaller.size; | |
154 | memcpy(cmd.auths[0].nonce.buffer, this->nonceCaller.buffer, | |
155 | this->nonceCaller.size); | |
156 | ||
157 | rval = Tss2_Sys_GetEncryptParam(this->sys_context, ¶m_size, | |
158 | ¶m_buffer); | |
159 | if (rval == TSS2_SYS_RC_NO_ENCRYPT_PARAM) | |
160 | { | |
161 | DBG2(DBG_PTS, LABEL "parameter encryption not possible"); | |
162 | return FALSE; | |
163 | } | |
164 | ||
165 | rval = Tss2_Sys_GetCommandCode(this->sys_context, cc_buffer); | |
166 | if (rval != TSS2_RC_SUCCESS) | |
167 | { | |
168 | DBG1(DBG_PTS, LABEL "Tss2_Sys_GetCommandCode failed: 0x%06x", rval); | |
169 | return FALSE; | |
170 | } | |
171 | ||
172 | rval = Tss2_Sys_GetCpBuffer(this->sys_context, &cp_size, &cp_buffer); | |
173 | if (rval != TSS2_RC_SUCCESS) | |
174 | { | |
175 | DBG1(DBG_PTS, LABEL "Tss2_GetCpBuffer failed: 0x%06x", rval); | |
176 | return FALSE; | |
177 | } | |
178 | ||
179 | /* compute cpHash */ | |
180 | hash_algorithm = hash_alg_from_tpm_alg_id(this->hash_alg); | |
181 | hasher = lib->crypto->create_hasher(lib->crypto, hash_algorithm); | |
182 | if (!hasher) | |
183 | { | |
184 | DBG1(DBG_PTS, "hasher could not be created"); | |
185 | return FALSE; | |
186 | } | |
187 | ||
188 | data = chunk_alloc(4 + cp_size); | |
189 | memcpy(data.ptr, cc_buffer, 4); | |
190 | memcpy(data.ptr + 4, cp_buffer, cp_size); | |
191 | ||
192 | success = hasher->get_hash(hasher, data, cpHash.buffer); | |
193 | cpHash.size = hasher->get_hash_size(hasher); | |
194 | hasher->destroy(hasher); | |
195 | chunk_free(&data); | |
196 | ||
197 | if (!success) | |
198 | { | |
199 | DBG1(DBG_PTS, "computation of cpHash failed"); | |
200 | return FALSE; | |
201 | } | |
202 | cp_hash = chunk_create(cpHash.buffer, cpHash.size); | |
203 | ||
204 | /* compute cp HMAC */ | |
205 | prf_alg = prf_alg_from_tpm_alg_id(this->hash_alg); | |
206 | prf = lib->crypto->create_prf(lib->crypto, prf_alg); | |
207 | if (!prf) | |
208 | { | |
209 | DBG1(DBG_PTS, "could not create PRF"); | |
210 | return FALSE; | |
211 | } | |
212 | if (!prf->set_key(prf, this->session_key)) | |
213 | { | |
214 | DBG1(DBG_PTS, "could not set PRF key"); | |
215 | prf->destroy(prf); | |
216 | return FALSE; | |
217 | } | |
218 | ||
219 | nonce_caller = chunk_create(this->nonceCaller.buffer, this->nonceCaller.size); | |
220 | nonce_tpm = chunk_create(this->nonceTPM.buffer, this->nonceTPM.size); | |
221 | ||
222 | success = prf->get_bytes(prf, cp_hash, NULL) && | |
223 | prf->get_bytes(prf, nonce_caller, NULL) && | |
224 | prf->get_bytes(prf, nonce_tpm, NULL) && | |
225 | prf->get_bytes(prf, session_attributes, cmd.auths[0].hmac.buffer); | |
226 | cmd.auths[0].hmac.size = prf->get_block_size(prf); | |
227 | prf->destroy(prf); | |
228 | ||
229 | if (!success) | |
230 | { | |
231 | DBG1(DBG_PTS, "cpHmac computation failed"); | |
232 | return FALSE; | |
233 | } | |
234 | cp_hmac = chunk_create(cmd.auths[0].hmac.buffer, cmd.auths[0].hmac.size); | |
235 | DBG2(DBG_PTS, LABEL "cpHmac: %B", &cp_hmac); | |
236 | ||
237 | rval = Tss2_Sys_SetCmdAuths(this->sys_context, &cmd); | |
238 | if (rval != TSS2_RC_SUCCESS) | |
239 | { | |
240 | DBG1(DBG_PTS, LABEL "Tss2_Sys_SetCmdAuths failed: 0x%06x", rval); | |
241 | return FALSE; | |
242 | } | |
243 | ||
244 | return TRUE; | |
245 | } | |
246 | ||
247 | /** | |
248 | * Key Derivation Function using Counter Mode as defined by NIST SP800-108 | |
249 | * - the label is expected to be NUL terminated | |
250 | */ | |
251 | static bool kdf_a(TPMI_ALG_HASH hash_alg, chunk_t key, chunk_t label, | |
252 | chunk_t context_u, chunk_t context_v, uint32_t bytes, | |
253 | chunk_t *key_mat) | |
254 | { | |
255 | pseudo_random_function_t prf_alg; | |
256 | chunk_t count_chunk, bits_chunk; | |
257 | uint32_t iterations, counter, count, bits; | |
258 | uint8_t *pos; | |
259 | size_t hlen; | |
260 | prf_t *prf; | |
261 | ||
262 | bits = htonl(8 * bytes); | |
263 | bits_chunk = chunk_create((uint8_t*)&bits, sizeof(bits)); | |
264 | ||
265 | prf_alg = prf_alg_from_tpm_alg_id(hash_alg); | |
266 | prf = lib->crypto->create_prf(lib->crypto, prf_alg); | |
267 | if (!prf) | |
268 | { | |
269 | DBG1(DBG_PTS, "could not create PRF"); | |
270 | return FALSE; | |
271 | } | |
272 | if (!prf->set_key(prf, key)) | |
273 | { | |
274 | DBG1(DBG_PTS, "could not set PRF key"); | |
275 | prf->destroy(prf); | |
276 | return FALSE; | |
277 | } | |
278 | ||
279 | hlen = prf->get_block_size(prf); | |
280 | iterations = (bytes + hlen - 1) / hlen; | |
281 | *key_mat = chunk_alloc(iterations * hlen); | |
282 | pos = key_mat->ptr; | |
283 | ||
284 | for (counter = 1; counter <= iterations; counter++) | |
285 | { | |
286 | count = htonl(counter); | |
287 | count_chunk = chunk_create((uint8_t*)&count, sizeof(count)); | |
288 | ||
289 | if (!prf->get_bytes(prf, count_chunk, NULL) || | |
290 | !prf->get_bytes(prf, label, NULL) || | |
291 | !prf->get_bytes(prf, context_u, NULL) || | |
292 | !prf->get_bytes(prf, context_v, NULL) || | |
293 | !prf->get_bytes(prf, bits_chunk, pos)) | |
294 | { | |
295 | DBG1(DBG_PTS, "KDFa computation failed"); | |
296 | chunk_free(key_mat); | |
297 | prf->destroy(prf); | |
298 | return FALSE; | |
299 | } | |
300 | pos += hlen; | |
301 | } | |
302 | prf->destroy(prf); | |
303 | ||
304 | return TRUE; | |
305 | } | |
306 | ||
307 | METHOD(tpm_tss_tss2_session_t, get_rsp_auths, bool, | |
308 | private_tpm_tss_tss2_session_t *this) | |
309 | { | |
310 | size_t param_size, rp_size, key_len, iv_len; | |
311 | const uint8_t *param_buffer, *rp_buffer; | |
312 | uint8_t rc_buffer[4] = { 0 }; | |
313 | uint8_t cc_buffer[4]; | |
314 | hash_algorithm_t hash_algorithm; | |
315 | hasher_t *hasher; | |
316 | pseudo_random_function_t prf_alg; | |
317 | prf_t *prf; | |
0b76ca13 | 318 | crypter_t *crypter; |
8249e6af AS |
319 | chunk_t kdf_label = chunk_from_chars('C','F','B', 0x00); |
320 | chunk_t data, rp_hash, rp_hmac, nonce_caller, nonce_tpm, session_attributes; | |
321 | chunk_t key_mat, aes_key, aes_iv; | |
322 | bool success; | |
323 | uint32_t rval; | |
324 | ||
325 | TSS2L_SYS_AUTH_RESPONSE rsp; | |
326 | TPM2B_DIGEST rpHash, rpHmac; | |
327 | ||
328 | rval = Tss2_Sys_GetRspAuths(this->sys_context, &rsp); | |
329 | if (rval != TSS2_RC_SUCCESS) | |
330 | { | |
331 | DBG1(DBG_PTS, LABEL "Tss2_Sys_GetRspAuths failed: 0x%06x", rval); | |
332 | return FALSE; | |
333 | } | |
334 | ||
335 | /* update nonceTPM */ | |
336 | memcpy(this->nonceTPM.buffer, rsp.auths[0].nonce.buffer, | |
337 | rsp.auths[0].nonce.size); | |
338 | this->nonceTPM.size = rsp.auths[0].nonce.size; | |
339 | ||
340 | rval = Tss2_Sys_GetRpBuffer(this->sys_context, &rp_size, &rp_buffer); | |
341 | if (rval != TSS2_RC_SUCCESS) | |
342 | { | |
343 | DBG1(DBG_PTS, LABEL "Tss2_Sys_GetRpBuffer failed: 0x%06x", rval); | |
344 | return FALSE; | |
345 | } | |
346 | ||
347 | rval = Tss2_Sys_GetCommandCode(this->sys_context, cc_buffer); | |
348 | if (rval != TSS2_RC_SUCCESS) | |
349 | { | |
350 | DBG1(DBG_PTS, LABEL "Tss2_Sys_GetCommandCode failed: 0x%06x", rval); | |
351 | return FALSE; | |
352 | } | |
353 | ||
354 | /* compute rpHash */ | |
355 | hash_algorithm = hash_alg_from_tpm_alg_id(this->hash_alg); | |
356 | hasher = lib->crypto->create_hasher(lib->crypto, hash_algorithm); | |
357 | if (!hasher) | |
358 | { | |
359 | DBG1(DBG_PTS, "hasher could not be created"); | |
360 | return FALSE; | |
361 | } | |
362 | ||
363 | data = chunk_alloc(4 + 4 + rp_size); | |
364 | memcpy(data.ptr, rc_buffer, 4); | |
365 | memcpy(data.ptr + 4, cc_buffer, 4); | |
366 | memcpy(data.ptr + 8, rp_buffer, rp_size); | |
367 | ||
368 | success = hasher->get_hash(hasher, data, rpHash.buffer); | |
369 | rpHash.size = hasher->get_hash_size(hasher); | |
370 | hasher->destroy(hasher); | |
371 | chunk_free(&data); | |
372 | ||
373 | if (!success) | |
374 | { | |
375 | DBG1(DBG_PTS, "computation of rpHash failed"); | |
376 | return FALSE; | |
377 | } | |
378 | rp_hash = chunk_create(rpHash.buffer, rpHash.size); | |
379 | ||
380 | /* compute rpHmac */ | |
381 | prf_alg = prf_alg_from_tpm_alg_id(this->hash_alg); | |
382 | prf = lib->crypto->create_prf(lib->crypto, prf_alg); | |
383 | if (!prf) | |
384 | { | |
385 | DBG1(DBG_PTS, "could not create PRF"); | |
386 | return FALSE; | |
387 | } | |
388 | if (!prf->set_key(prf, this->session_key)) | |
389 | { | |
390 | DBG1(DBG_PTS, "could not set PRF key"); | |
391 | prf->destroy(prf); | |
392 | return FALSE; | |
393 | } | |
394 | ||
395 | nonce_tpm = chunk_create(this->nonceTPM.buffer, this->nonceTPM.size); | |
396 | nonce_caller = chunk_create(this->nonceCaller.buffer, this->nonceCaller.size); | |
397 | session_attributes = chunk_create(&rsp.auths[0].sessionAttributes, 1); | |
398 | ||
399 | success = prf->get_bytes(prf, rp_hash, NULL) && | |
400 | prf->get_bytes(prf, nonce_tpm, NULL) && | |
401 | prf->get_bytes(prf, nonce_caller, NULL) && | |
402 | prf->get_bytes(prf, session_attributes, rpHmac.buffer); | |
403 | rpHmac.size = prf->get_block_size(prf); | |
404 | prf->destroy(prf); | |
405 | ||
406 | if (!success) | |
407 | { | |
408 | DBG1(DBG_PTS, "computation of rpHmac failed"); | |
409 | return FALSE; | |
410 | } | |
411 | rp_hmac = chunk_create(rpHmac.buffer, rpHmac.size); | |
412 | DBG2(DBG_PTS, LABEL "rpHMAC: %B", &rp_hmac); | |
413 | ||
414 | /* verify rpHmac */ | |
415 | if (!memeq(rsp.auths[0].hmac.buffer, rpHmac.buffer, rpHmac.size)) | |
416 | { | |
417 | DBG1(DBG_PTS, LABEL "invalid HMAC received for session 0x%08x", | |
418 | this->session_handle); | |
419 | return FALSE; | |
420 | } | |
421 | ||
422 | /* decrypt parameter */ | |
423 | rval = Tss2_Sys_GetEncryptParam(this->sys_context, ¶m_size, | |
424 | ¶m_buffer); | |
425 | if (rval != TSS2_RC_SUCCESS) | |
426 | { | |
427 | DBG1(DBG_PTS, LABEL "Tss2_Sys_GetEncryptParam failed: 0x%06x", rval); | |
428 | return FALSE; | |
429 | } | |
430 | ||
0b76ca13 AS |
431 | crypter = lib->crypto->create_crypter(lib->crypto, ENCR_AES_CFB, |
432 | this->aes_key_len); | |
433 | if (!crypter) | |
434 | { | |
435 | DBG1(DBG_PTS, "could not create %N crypter", encryption_algorithm_names, | |
436 | ENCR_AES_CFB); | |
437 | return FALSE; | |
438 | } | |
439 | ||
440 | key_len = crypter->get_key_size(crypter); | |
441 | iv_len = crypter->get_iv_size(crypter); | |
8249e6af AS |
442 | |
443 | /* derive decryption key using KDFa */ | |
444 | if (!kdf_a(this->hash_alg, this->session_key, kdf_label, nonce_tpm, | |
445 | nonce_caller, key_len + iv_len , &key_mat)) | |
446 | { | |
447 | return FALSE; | |
448 | } | |
449 | aes_key = chunk_create(key_mat.ptr, key_len); | |
450 | aes_iv = chunk_create(key_mat.ptr + key_len, iv_len); | |
451 | ||
0b76ca13 | 452 | if (!crypter->set_key(crypter, aes_key)) |
8249e6af | 453 | { |
0b76ca13 | 454 | crypter->destroy(crypter); |
8249e6af AS |
455 | chunk_clear(&key_mat); |
456 | return FALSE; | |
457 | } | |
458 | ||
459 | /* copy ciphertext */ | |
460 | data = chunk_alloc(param_size); | |
461 | memcpy(data.ptr, param_buffer, param_size); | |
462 | ||
463 | /* decrypt ciphertext */ | |
0b76ca13 AS |
464 | success = crypter->decrypt(crypter, data, aes_iv, NULL); |
465 | crypter->destroy(crypter); | |
8249e6af AS |
466 | chunk_clear(&key_mat); |
467 | if (!success) | |
468 | { | |
469 | chunk_free(&data); | |
470 | return FALSE; | |
471 | } | |
472 | DBG4(DBG_PTS, LABEL "plaintext: %B", &data); | |
473 | ||
474 | /* copy back plaintext */ | |
475 | rval = Tss2_Sys_SetEncryptParam(this->sys_context, data.len, data.ptr); | |
476 | chunk_clear(&data); | |
477 | ||
478 | if (rval != TSS2_RC_SUCCESS) | |
479 | { | |
480 | DBG1(DBG_PTS, LABEL "Tss2_Sys_SetEncryptParam failed: 0x%06x", rval); | |
481 | return FALSE; | |
482 | } | |
483 | ||
484 | return TRUE; | |
485 | } | |
486 | ||
487 | ||
488 | METHOD(tpm_tss_tss2_session_t, destroy, void, | |
489 | private_tpm_tss_tss2_session_t *this) | |
490 | { | |
491 | if (this->session_handle) | |
492 | { | |
493 | uint32_t rval; | |
494 | ||
495 | /* flush session context */ | |
496 | rval = Tss2_Sys_FlushContext(this->sys_context, this->session_handle); | |
497 | if (rval != TPM2_RC_SUCCESS) | |
498 | { | |
499 | DBG2(DBG_PTS, LABEL "Tss2_Sys_FlushContext failed: 0x%06x", rval); | |
500 | } | |
501 | chunk_clear(&this->session_key); | |
502 | } | |
8249e6af AS |
503 | free(this); |
504 | } | |
505 | ||
506 | static chunk_t secret_label = chunk_from_chars('S','E','C','R','E','T', 0x00); | |
507 | ||
508 | static bool rsa_salt(TPM2B_PUBLIC *public, TPMI_ALG_HASH hash_alg, | |
509 | chunk_t *secret, TPM2B_ENCRYPTED_SECRET *encryptedSalt) | |
510 | { | |
511 | encryption_scheme_t encryption_scheme; | |
512 | public_key_t *pubkey = NULL; | |
513 | nonce_gen_t *nonce_gen; | |
514 | chunk_t encrypted_salt = chunk_empty; | |
515 | chunk_t rsa_modulus; | |
516 | chunk_t rsa_exponent = chunk_from_chars(0x01, 0x00, 0x01); | |
517 | uint32_t exponent; | |
518 | size_t hash_len; | |
519 | bool success; | |
520 | ||
521 | TPM2B_PUBLIC_KEY_RSA *rsa; | |
522 | ||
523 | switch (hash_alg) | |
524 | { | |
525 | case TPM2_ALG_SHA1: | |
526 | encryption_scheme = ENCRYPT_RSA_OAEP_SHA1; | |
527 | break; | |
528 | case TPM2_ALG_SHA256: | |
529 | encryption_scheme = ENCRYPT_RSA_OAEP_SHA256; | |
530 | break; | |
531 | case TPM2_ALG_SHA384: | |
532 | encryption_scheme = ENCRYPT_RSA_OAEP_SHA384; | |
533 | break; | |
534 | case TPM2_ALG_SHA512: | |
535 | encryption_scheme = ENCRYPT_RSA_OAEP_SHA512; | |
536 | break; | |
537 | default: | |
538 | DBG1(DBG_PTS, LABEL "unsupported key hash algorithm"); | |
539 | return FALSE; | |
540 | } | |
541 | ||
542 | hash_len = hash_len_from_tpm_alg_id(hash_alg); | |
543 | ||
544 | /* create a salt nonce to be used as a shared secret */ | |
545 | nonce_gen = lib->crypto->create_nonce_gen(lib->crypto); | |
546 | if (!nonce_gen) | |
547 | { | |
548 | DBG1(DBG_PTS, "no nonce generator available"); | |
549 | return FALSE; | |
550 | } | |
551 | success = nonce_gen->allocate_nonce(nonce_gen, hash_len, secret); | |
552 | nonce_gen->destroy(nonce_gen); | |
553 | if (!success) | |
554 | { | |
555 | DBG1(DBG_PTS, "generation of salt nonce failed"); | |
556 | return FALSE; | |
557 | } | |
558 | ||
559 | /* get RSA public key */ | |
560 | rsa = &public->publicArea.unique.rsa; | |
561 | rsa_modulus = chunk_create(rsa->buffer, rsa->size); | |
562 | exponent = htonl(public->publicArea.parameters.rsaDetail.exponent); | |
563 | if (exponent) | |
564 | { | |
565 | rsa_exponent = chunk_from_thing(exponent); | |
566 | } | |
567 | pubkey = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, | |
568 | BUILD_RSA_MODULUS, rsa_modulus, BUILD_RSA_PUB_EXP, | |
569 | rsa_exponent, BUILD_END); | |
570 | if (!pubkey) | |
571 | { | |
572 | DBG1(DBG_PTS, "retrieval of EK public key failed"); | |
573 | chunk_clear(secret); | |
574 | return FALSE; | |
575 | } | |
576 | ||
577 | /* use RSA public key encryption to encrypt secret salt nonce */ | |
578 | success = pubkey->encrypt(pubkey, encryption_scheme, &secret_label, | |
579 | *secret, &encrypted_salt); | |
580 | pubkey->destroy(pubkey); | |
581 | if (!success) | |
582 | { | |
583 | DBG1(DBG_PTS, "encryption of salt failed"); | |
584 | chunk_clear(secret); | |
585 | return FALSE; | |
586 | } | |
587 | ||
588 | /* copy encryptedSalt to output parameter */ | |
589 | encryptedSalt->size = encrypted_salt.len; | |
590 | memcpy(encryptedSalt->secret, encrypted_salt.ptr, encrypted_salt.len); | |
591 | free(encrypted_salt.ptr); | |
592 | ||
593 | return TRUE; | |
594 | } | |
595 | ||
596 | ||
597 | /** | |
598 | * Key Derivation Function used to derive an ecc-based secret | |
599 | * - the label is expected to be NUL terminated | |
600 | */ | |
601 | static bool kdf_e(TPMI_ALG_HASH hash_alg, chunk_t z, chunk_t label, | |
602 | chunk_t context_u, chunk_t context_v, uint32_t bytes, | |
603 | chunk_t *key_mat) | |
604 | { | |
605 | hash_algorithm_t hash_algorithm; | |
606 | chunk_t count_chunk; | |
607 | uint32_t iterations, counter, count; | |
608 | uint8_t *pos; | |
609 | size_t hlen; | |
610 | hasher_t *hasher; | |
611 | ||
612 | hash_algorithm = hash_alg_from_tpm_alg_id(hash_alg); | |
613 | hasher = lib->crypto->create_hasher(lib->crypto, hash_algorithm); | |
614 | if (!hasher) | |
615 | { | |
616 | DBG1(DBG_PTS, "could not create hasher"); | |
617 | return FALSE; | |
618 | } | |
619 | ||
620 | hlen = hasher->get_hash_size(hasher); | |
621 | iterations = (bytes + hlen - 1) / hlen; | |
622 | *key_mat = chunk_alloc(iterations * hlen); | |
623 | pos = key_mat->ptr; | |
624 | ||
625 | for (counter = 1; counter <= iterations; counter++) | |
626 | { | |
627 | count = htonl(counter); | |
628 | count_chunk = chunk_create((uint8_t*)&count, sizeof(count)); | |
629 | ||
630 | if (!hasher->get_hash(hasher, count_chunk, NULL) || | |
631 | !hasher->get_hash(hasher, z, NULL) || | |
632 | !hasher->get_hash(hasher, label, NULL) || | |
633 | !hasher->get_hash(hasher, context_u, NULL) || | |
634 | !hasher->get_hash(hasher, context_v, pos)) | |
635 | { | |
636 | DBG1(DBG_PTS, "KDFe computation failed"); | |
637 | chunk_free(key_mat); | |
638 | hasher->destroy(hasher); | |
639 | return FALSE; | |
640 | } | |
641 | pos += hlen; | |
642 | } | |
643 | hasher->destroy(hasher); | |
644 | ||
645 | return TRUE; | |
646 | } | |
647 | ||
648 | static bool ecc_salt(TPM2B_PUBLIC *public, TPMI_ALG_HASH hash_alg, | |
649 | chunk_t *secret, TPM2B_ENCRYPTED_SECRET *encryptedSalt) | |
650 | { | |
651 | diffie_hellman_group_t ec_group; | |
652 | diffie_hellman_t *dh; | |
653 | chunk_t ecdh_pubkey = chunk_empty, ecdh_pubkey_x, ecdh_pubkey_y; | |
654 | chunk_t ecc_pubkey = chunk_empty, ecc_pubkey_x, ecc_pubkey_y; | |
655 | chunk_t z = chunk_empty; | |
656 | uint16_t len; | |
657 | uint8_t *pos; | |
658 | size_t hash_len; | |
659 | bool success = FALSE; | |
660 | ||
661 | switch (public->publicArea.parameters.eccDetail.curveID) | |
662 | { | |
663 | case TPM2_ECC_NIST_P256: | |
664 | ec_group = ECP_256_BIT; | |
665 | break; | |
666 | case TPM2_ECC_NIST_P384: | |
667 | ec_group = ECP_384_BIT; | |
668 | break; | |
669 | case TPM2_ECC_NIST_P521: | |
670 | ec_group = ECP_521_BIT; | |
671 | break; | |
672 | default: | |
673 | DBG1(DBG_PTS, "type of ECC EK key not supported"); | |
674 | return FALSE; | |
675 | } | |
676 | ||
677 | /* Generate ECDH key pair */ | |
678 | dh = lib->crypto->create_dh(lib->crypto, ec_group); | |
679 | if (!dh) | |
680 | { | |
681 | DBG1(DBG_PTS, "DH group could not be created"); | |
682 | return FALSE; | |
683 | } | |
684 | if (!dh->get_my_public_value(dh, &ecdh_pubkey)) | |
685 | { | |
686 | DBG1(DBG_PTS, "DH public key could not be generated"); | |
687 | dh->destroy(dh); | |
688 | return FALSE; | |
689 | } | |
690 | ecdh_pubkey_x = chunk_create(ecdh_pubkey.ptr, ecdh_pubkey.len / 2); | |
691 | ecdh_pubkey_y = chunk_create(ecdh_pubkey.ptr + ecdh_pubkey_x.len, | |
692 | ecdh_pubkey_x.len); | |
693 | ||
694 | /* get ECC public key */ | |
695 | ecc_pubkey_x = chunk_create(public->publicArea.unique.ecc.x.buffer, | |
696 | public->publicArea.unique.ecc.x.size); | |
697 | ecc_pubkey_y = chunk_create(public->publicArea.unique.ecc.y.buffer, | |
698 | public->publicArea.unique.ecc.y.size); | |
699 | ecc_pubkey = chunk_cat("cc", ecc_pubkey_x, ecc_pubkey_y); | |
700 | ||
701 | /* compute point multiplication of ecc_pubkey with ecdh_privkey */ | |
702 | if (!dh->set_other_public_value(dh, ecc_pubkey)) | |
703 | { | |
704 | DBG1(DBG_PTS, "ECC public could not be set"); | |
705 | goto error; | |
706 | } | |
707 | if (!dh->get_shared_secret(dh, &z)) | |
708 | { | |
709 | DBG1(DBG_PTS, "could not create shared secret"); | |
710 | goto error; | |
711 | } | |
712 | ||
713 | hash_len = hash_len_from_tpm_alg_id(hash_alg); | |
714 | ||
715 | /* derive secret using KDFe */ | |
716 | if (!kdf_e(hash_alg, z, secret_label, ecdh_pubkey_x, ecc_pubkey_x, | |
717 | hash_len, secret)) | |
718 | { | |
719 | goto error; | |
720 | } | |
721 | ||
722 | /* copy ECDH pubkey to encrypted salt parameter */ | |
723 | len = htons(ecdh_pubkey_x.len); | |
724 | encryptedSalt->size = 2 * sizeof(len) + ecdh_pubkey.len; | |
725 | pos = encryptedSalt->secret; | |
726 | memcpy(pos, (uint8_t*)&len, sizeof(len)); | |
727 | pos += sizeof(len); | |
728 | memcpy(pos, ecdh_pubkey_x.ptr, ecdh_pubkey_x.len); | |
729 | pos += ecdh_pubkey_x.len; | |
730 | memcpy(pos, (uint8_t*)&len, sizeof(len)); | |
731 | pos += sizeof(len); | |
732 | memcpy(pos, ecdh_pubkey_y.ptr, ecdh_pubkey_y.len); | |
733 | ||
734 | success = TRUE; | |
735 | ||
736 | error: | |
737 | dh->destroy(dh); | |
738 | chunk_free(&ecdh_pubkey); | |
739 | chunk_free(&ecc_pubkey); | |
740 | chunk_clear(&z); | |
741 | ||
742 | return success; | |
743 | } | |
744 | ||
745 | /** | |
746 | * See header | |
747 | */ | |
748 | tpm_tss_tss2_session_t* tpm_tss_tss2_session_create(uint32_t ek_handle, | |
749 | TPM2B_PUBLIC *public, TSS2_SYS_CONTEXT *sys_context) | |
750 | { | |
751 | private_tpm_tss_tss2_session_t *this; | |
752 | chunk_t secret = chunk_empty; | |
753 | chunk_t kdf_label = chunk_from_chars('A','T','H', 0x00); | |
754 | chunk_t nonce_caller, nonce_tpm; | |
755 | size_t hash_len; | |
756 | uint32_t rval; | |
757 | ||
758 | TPM2B_ENCRYPTED_SECRET encryptedSalt; | |
759 | TPM2_SE sessionType = TPM2_SE_HMAC; | |
0b76ca13 | 760 | TPMT_SYM_DEF *sym; |
8249e6af AS |
761 | |
762 | INIT(this, | |
763 | .public = { | |
764 | .set_cmd_auths = _set_cmd_auths, | |
765 | .get_rsp_auths = _get_rsp_auths, | |
766 | .destroy = _destroy, | |
767 | }, | |
768 | .sys_context = sys_context, | |
769 | .hash_alg = public->publicArea.nameAlg, | |
770 | ); | |
771 | ||
772 | hash_len = hash_len_from_tpm_alg_id(this->hash_alg); | |
773 | ||
8249e6af AS |
774 | if (!generate_nonce(hash_len, &this->nonceCaller)) |
775 | { | |
776 | goto error; | |
777 | } | |
778 | ||
779 | /* determine endorsement key type */ | |
780 | switch (public->publicArea.type) | |
781 | { | |
782 | case TPM2_ALG_RSA: | |
783 | DBG1(DBG_PTS, LABEL "RSA EK handle: 0x%08x", ek_handle); | |
784 | if (!rsa_salt(public, this->hash_alg, &secret, &encryptedSalt)) | |
785 | { | |
786 | goto error; | |
787 | } | |
788 | break; | |
789 | case TPM2_ALG_ECC: | |
0b76ca13 AS |
790 | DBG1(DBG_PTS, LABEL "ECC %N EK handle: 0x%08x", tpm_ecc_curve_names, |
791 | public->publicArea.parameters.eccDetail.curveID, ek_handle); | |
8249e6af AS |
792 | if (!ecc_salt(public, this->hash_alg, &secret, &encryptedSalt)) |
793 | { | |
794 | goto error; | |
795 | } | |
796 | break; | |
797 | default: | |
798 | DBG1(DBG_PTS, LABEL "unsupported ek key type"); | |
799 | goto error; | |
800 | } | |
801 | ||
0b76ca13 AS |
802 | sym = (TPMT_SYM_DEF*)&public->publicArea.parameters.asymDetail.symmetric; |
803 | DBG2(DBG_PTS, LABEL "AES-CFB with %u bits", sym->keyBits.aes); | |
804 | this->aes_key_len = sym->keyBits.aes / 8; | |
805 | ||
8249e6af | 806 | rval = Tss2_Sys_StartAuthSession(this->sys_context, ek_handle, TPM2_RH_NULL, |
0b76ca13 | 807 | NULL, &this->nonceCaller, &encryptedSalt, sessionType, sym, |
8249e6af AS |
808 | this->hash_alg, &this->session_handle, &this->nonceTPM, NULL); |
809 | if (rval != TSS2_RC_SUCCESS) | |
810 | { | |
811 | DBG1(DBG_PTS, LABEL "Tss2_Sys_StartAuthSession failed: 0x%06x", rval); | |
812 | goto error; | |
813 | } | |
814 | DBG2(DBG_PTS, LABEL "session handle: 0x%08x", this->session_handle); | |
815 | ||
816 | nonce_tpm = chunk_create(this->nonceTPM.buffer, this->nonceTPM.size); | |
817 | nonce_caller = chunk_create(this->nonceCaller.buffer, this->nonceCaller.size); | |
818 | ||
819 | /* derive sessionKey using KDFa */ | |
820 | if (!kdf_a(this->hash_alg, secret, kdf_label, nonce_tpm, nonce_caller, | |
821 | hash_len, &this->session_key)) | |
822 | { | |
823 | goto error; | |
824 | } | |
825 | chunk_clear(&secret); | |
826 | DBG4(DBG_PTS, LABEL "session key: %B", &this->session_key); | |
827 | ||
828 | return &this->public; | |
829 | ||
830 | error: | |
831 | chunk_clear(&secret); | |
832 | destroy(this); | |
833 | return NULL; | |
834 | } | |
835 | ||
836 | #endif /* TSS_TSS2_V2 */ |