2 * Copyright (C) 2016 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
5 * Copyright (c) 2008 Hal Finney
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 #include "tpm_tss_trousers.h"
31 /* MinGW defines _BASETSD_H_, but TSS checks for _BASETSD_H */
35 #include <trousers/tss.h>
36 #include <trousers/trousers.h>
38 #define LABEL "TPM 1.2 -"
40 /* size in bytes of a TSS AIK public key blob */
41 #define AIK_PUBKEY_BLOB_SIZE 284
43 /* maximum number of PCR registers */
44 #define PCR_NUM_MAX 24
46 typedef struct private_tpm_tss_trousers_t private_tpm_tss_trousers_t
;
47 typedef struct aik_t aik_t
;
50 * Private data of an tpm_tss_trousers_t object.
52 struct private_tpm_tss_trousers_t
{
55 * Public tpm_tss_trousers_t interface.
57 tpm_tss_trousers_t interface
;
62 TSS_HCONTEXT hContext
;
75 * List of AIKs retrievable by an object handle
77 linked_list_t
*aik_list
;
82 /** AIK object handle */
85 /** AIK private key blob */
92 static void free_aik(aik_t
*this)
95 free(this->pubkey
.ptr
);
100 * Initialize TSS context
102 * TPM 1.2 Specification, Part 2 TPM Structures, 21.6 TPM_CAP_VERSION_INFO
104 * typedef struct tdTPM_VERSION {
105 * TPM_VERSION_BYTE major;
106 * TPM_VERSION_BYTE minor;
111 * typedef struct tdTPM_CAP_VERSION_INFO {
112 * TPM_STRUCTURE_TAG tag;
113 * TPM_VERSION version;
116 * BYTE tpmVendorID[4];
117 * UINT16 vendorSpecificSize;
118 * [size_is(vendorSpecificSize)] BYTE* vendorSpecific;
119 * } TPM_CAP_VERSION_INFO;
121 static bool initialize_context(private_tpm_tss_trousers_t
*this)
123 uint8_t *version_ptr
;
124 uint32_t version_len
;
127 TPM_CAP_VERSION_INFO
*info
;
129 result
= Tspi_Context_Create(&this->hContext
);
130 if (result
!= TSS_SUCCESS
)
132 DBG1(DBG_PTS
, "%s could not created context: 0x%x",
137 result
= Tspi_Context_Connect(this->hContext
, NULL
);
138 if (result
!= TSS_SUCCESS
)
140 DBG1(DBG_PTS
, "%s could not connect with context: 0x%x",
145 result
= Tspi_Context_GetTpmObject (this->hContext
, &this->hTPM
);
146 if (result
!= TSS_SUCCESS
)
148 DBG1(DBG_PTS
, "%s could not get TPM object: 0x%x",
153 result
= Tspi_TPM_GetCapability(this->hTPM
, TSS_TPMCAP_VERSION_VAL
, 0,
154 NULL
, &version_len
, &version_ptr
);
155 if (result
!= TSS_SUCCESS
)
157 DBG1(DBG_PTS
, "%s Tspi_TPM_GetCapability failed: 0x%x",
162 info
= (TPM_CAP_VERSION_INFO
*)version_ptr
;
163 DBG2(DBG_PTS
, "TPM Version Info: Chip Version: %u.%u.%u.%u, "
164 "Spec Level: %u, Errata Rev: %u, Vendor ID: %.4s",
165 info
->version
.major
, info
->version
.minor
,
166 info
->version
.revMajor
, info
->version
.revMinor
,
167 untoh16(&info
->specLevel
), info
->errataRev
, info
->tpmVendorID
);
169 this->version_info
= chunk_clone(chunk_create(version_ptr
, version_len
));
175 * Finalize TSS context
177 static void finalize_context(private_tpm_tss_trousers_t
*this)
181 Tspi_Context_FreeMemory(this->hContext
, NULL
);
182 Tspi_Context_Close(this->hContext
);
186 METHOD(tpm_tss_t
, get_version
, tpm_version_t
,
187 private_tpm_tss_trousers_t
*this)
189 return TPM_VERSION_1_2
;
192 METHOD(tpm_tss_t
, get_version_info
, chunk_t
,
193 private_tpm_tss_trousers_t
*this)
195 return this->version_info
;
198 METHOD(tpm_tss_t
, generate_aik
, bool,
199 private_tpm_tss_trousers_t
*this, chunk_t ca_modulus
, chunk_t
*aik_blob
,
200 chunk_t
*aik_pubkey
, chunk_t
*identity_req
)
202 chunk_t aik_pubkey_blob
;
204 chunk_t aik_exponent
;
209 TSS_HPOLICY hSrkPolicy
;
210 TSS_HPOLICY hTPMPolicy
;
212 TSS_UUID SRK_UUID
= TSS_UUID_SRK
;
213 BYTE secret
[] = TSS_WELL_KNOWN_SECRET
;
215 UINT32 IdentityReqLen
;
219 /* get SRK plus SRK policy and set SRK secret */
220 result
= Tspi_Context_LoadKeyByUUID(this->hContext
, TSS_PS_TYPE_SYSTEM
,
222 if (result
!= TSS_SUCCESS
)
224 DBG1(DBG_PTS
, "%s Tspi_Context_LoadKeyByUUID for SRK failed: 0x%x",
228 result
= Tspi_GetPolicyObject(hSRK
, TSS_POLICY_USAGE
, &hSrkPolicy
);
229 if (result
!= TSS_SUCCESS
)
231 DBG1(DBG_PTS
, "%s Tspi_GetPolicyObject or SRK failed: 0x%x ",
235 result
= Tspi_Policy_SetSecret(hSrkPolicy
, TSS_SECRET_MODE_SHA1
, 20, secret
);
236 if (result
!= TSS_SUCCESS
)
238 DBG1(DBG_PTS
, "%s Tspi_Policy_SetSecret for SRK failed: 0x%x ",
243 /* get TPM plus TPM policy and set TPM secret */
244 result
= Tspi_Context_GetTpmObject (this->hContext
, &this->hTPM
);
245 if (result
!= TSS_SUCCESS
)
247 DBG1(DBG_PTS
, "%s Tspi_Context_GetTpmObject failed: 0x%x",
251 result
= Tspi_GetPolicyObject(this->hTPM
, TSS_POLICY_USAGE
, &hTPMPolicy
);
252 if (result
!= TSS_SUCCESS
)
254 DBG1(DBG_PTS
, "%s Tspi_GetPolicyObject for TPM failed: 0x%x",
258 result
= Tspi_Policy_SetSecret(hTPMPolicy
, TSS_SECRET_MODE_SHA1
, 20, secret
);
259 if (result
!= TSS_SUCCESS
)
261 DBG1(DBG_PTS
,"%s Tspi_Policy_SetSecret for TPM failed: 0x%x",
266 /* create context for a 2048 bit AIK */
267 result
= Tspi_Context_CreateObject(this->hContext
, TSS_OBJECT_TYPE_RSAKEY
,
268 TSS_KEY_TYPE_IDENTITY
| TSS_KEY_SIZE_2048
|
269 TSS_KEY_VOLATILE
| TSS_KEY_NOT_MIGRATABLE
, &hIdentKey
);
270 if (result
!= TSS_SUCCESS
)
272 DBG1(DBG_PTS
, "%s Tspi_Context_CreateObject for key failed: 0x%x",
277 /* create context for the Privacy CA public key and assign modulus */
278 result
= Tspi_Context_CreateObject(this->hContext
, TSS_OBJECT_TYPE_RSAKEY
,
279 TSS_KEY_TYPE_LEGACY
|TSS_KEY_SIZE_2048
, &hPCAKey
);
280 if (result
!= TSS_SUCCESS
)
282 DBG1(DBG_PTS
, "%s Tspi_Context_CreateObject for PCA failed: 0x%x",
286 result
= Tspi_SetAttribData (hPCAKey
, TSS_TSPATTRIB_RSAKEY_INFO
,
287 TSS_TSPATTRIB_KEYINFO_RSA_MODULUS
, ca_modulus
.len
,
289 if (result
!= TSS_SUCCESS
)
291 DBG1(DBG_PTS
, "%s Tspi_SetAttribData for PCA modulus failed: 0x%x",
295 result
= Tspi_SetAttribUint32(hPCAKey
, TSS_TSPATTRIB_KEY_INFO
,
296 TSS_TSPATTRIB_KEYINFO_ENCSCHEME
, TSS_ES_RSAESPKCSV15
);
297 if (result
!= TSS_SUCCESS
)
299 DBG1(DBG_PTS
,"%s Tspi_SetAttribUint32 for PCA encryption scheme "
300 "failed: 0x%x", LABEL
, result
);
305 DBG1(DBG_LIB
, "Generating identity key...");
306 result
= Tspi_TPM_CollateIdentityRequest(this->hTPM
, hSRK
, hPCAKey
, 0, NULL
,
307 hIdentKey
, TSS_ALG_AES
, &IdentityReqLen
, &IdentityReq
);
308 if (result
!= TSS_SUCCESS
)
310 DBG1(DBG_PTS
, "%s Tspi_TPM_CollateIdentityRequest failed: 0x%x",
314 *identity_req
= chunk_create(IdentityReq
, IdentityReqLen
);
315 DBG3(DBG_LIB
, "%s Identity Request: %B", LABEL
, identity_req
);
317 /* load identity key */
318 result
= Tspi_Key_LoadKey (hIdentKey
, hSRK
);
319 if (result
!= TSS_SUCCESS
)
321 DBG1(DBG_PTS
, "%s Tspi_Key_LoadKey for AIK failed: 0x%x",
326 /* output AIK private key in TSS blob format */
327 result
= Tspi_GetAttribData (hIdentKey
, TSS_TSPATTRIB_KEY_BLOB
,
328 TSS_TSPATTRIB_KEYBLOB_BLOB
, &blobLen
, &blob
);
329 if (result
!= TSS_SUCCESS
)
331 DBG1(DBG_PTS
, "%s Tspi_GetAttribData for private key blob failed: 0x%x",
335 *aik_blob
= chunk_create(blob
, blobLen
);
336 DBG3(DBG_LIB
, "%s AIK private key blob: %B", LABEL
, aik_blob
);
338 /* output AIK Public Key in TSS blob format */
339 result
= Tspi_GetAttribData (hIdentKey
, TSS_TSPATTRIB_KEY_BLOB
,
340 TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY
, &blobLen
, &blob
);
341 if (result
!= TSS_SUCCESS
)
343 DBG1(DBG_PTS
, "%s Tspi_GetAttribData for public key blob failed: 0x%x",
347 aik_pubkey_blob
= chunk_create(blob
, blobLen
);
348 DBG3(DBG_LIB
, "%s AIK public key blob: %B", LABEL
, &aik_pubkey_blob
);
350 /* create a trusted AIK public key */
351 if (aik_pubkey_blob
.len
!= AIK_PUBKEY_BLOB_SIZE
)
353 DBG1(DBG_PTS
, "%s AIK public key is not in TSS blob format",
357 aik_modulus
= chunk_skip(aik_pubkey_blob
, AIK_PUBKEY_BLOB_SIZE
- 256);
358 aik_exponent
= chunk_from_chars(0x01, 0x00, 0x01);
360 /* output subjectPublicKeyInfo encoding of AIK public key */
361 if (!lib
->encoding
->encode(lib
->encoding
, PUBKEY_SPKI_ASN1_DER
, NULL
,
362 aik_pubkey
, CRED_PART_RSA_MODULUS
, aik_modulus
,
363 CRED_PART_RSA_PUB_EXP
, aik_exponent
, CRED_PART_END
))
365 DBG1(DBG_PTS
, "%s subjectPublicKeyInfo encoding of AIK key failed",
372 METHOD(tpm_tss_t
, get_public
, chunk_t
,
373 private_tpm_tss_trousers_t
*this, uint32_t handle
)
375 enumerator_t
*enumerator
;
376 chunk_t aik_pubkey
= chunk_empty
;
379 enumerator
= this->aik_list
->create_enumerator(this->aik_list
);
380 while (enumerator
->enumerate(enumerator
, &aik
))
382 if (aik
->handle
== handle
)
384 aik_pubkey
= chunk_clone(aik
->pubkey
);
388 enumerator
->destroy(enumerator
);
393 METHOD(tpm_tss_t
, read_pcr
, bool,
394 private_tpm_tss_trousers_t
*this, uint32_t pcr_num
, chunk_t
*pcr_value
,
395 hash_algorithm_t alg
)
401 result
= Tspi_TPM_PcrRead(this->hTPM
, pcr_num
, &len
, &value
);
402 if (result
!= TSS_SUCCESS
)
404 DBG1(DBG_PTS
, "%s Tspi_TPM_PcrRead failed: 0x%x", LABEL
, result
);
407 *pcr_value
= chunk_clone(chunk_create(value
, len
));
412 METHOD(tpm_tss_t
, extend_pcr
, bool,
413 private_tpm_tss_trousers_t
*this, uint32_t pcr_num
, chunk_t
*pcr_value
,
414 chunk_t data
, hash_algorithm_t alg
)
420 result
= Tspi_TPM_PcrExtend(this->hTPM
, pcr_num
, data
.len
, data
.ptr
,
421 NULL
, &pcr_len
, &pcr_ptr
);
422 if (result
!= TSS_SUCCESS
)
424 DBG1(DBG_PTS
, "%s Tspi_TPM_PcrExtend failed: 0x%x", LABEL
, result
);
427 *pcr_value
= chunk_clone(chunk_create(pcr_ptr
, pcr_len
));
432 METHOD(tpm_tss_t
, quote
, bool,
433 private_tpm_tss_trousers_t
*this, uint32_t aik_handle
, uint32_t pcr_sel
,
434 hash_algorithm_t alg
, chunk_t data
, tpm_quote_mode_t
*quote_mode
,
435 tpm_tss_quote_info_t
**quote_info
, chunk_t
*quote_sig
)
439 TSS_HPOLICY srkUsagePolicy
;
440 TSS_UUID SRK_UUID
= TSS_UUID_SRK
;
441 TSS_HPCRS hPcrComposite
;
442 TSS_VALIDATION valData
;
444 uint8_t secret
[] = TSS_WELL_KNOWN_SECRET
;
445 uint8_t *version_info
, *comp_hash
;
446 uint32_t version_info_size
, pcr
;
448 chunk_t aik_blob
= chunk_empty
;
449 chunk_t quote_chunk
, pcr_digest
;
450 enumerator_t
*enumerator
;
451 bool success
= FALSE
;
453 /* Retrieve SRK from TPM and set the authentication to well known secret*/
454 result
= Tspi_Context_LoadKeyByUUID(this->hContext
, TSS_PS_TYPE_SYSTEM
,
456 if (result
!= TSS_SUCCESS
)
458 DBG1(DBG_PTS
, "%s Tspi_Context_LoadKeyByUUID for SRK failed: 0x%x",
462 result
= Tspi_GetPolicyObject(hSRK
, TSS_POLICY_USAGE
, &srkUsagePolicy
);
463 if (result
!= TSS_SUCCESS
)
465 DBG1(DBG_PTS
, "%s Tspi_GetPolicyObject for SRK failed: 0x%x",
469 result
= Tspi_Policy_SetSecret(srkUsagePolicy
, TSS_SECRET_MODE_SHA1
,
471 if (result
!= TSS_SUCCESS
)
473 DBG1(DBG_PTS
, "%s Tspi_Policy_SetSecret for SRK failed: 0x%x",
478 /* Retrieve AIK using its handle and load private key into TPM 1.2 */
479 enumerator
= this->aik_list
->create_enumerator(this->aik_list
);
480 while (enumerator
->enumerate(enumerator
, &aik
))
482 if (aik
->handle
== aik_handle
)
484 aik_blob
= aik
->blob
;
488 enumerator
->destroy(enumerator
);
490 if (aik_blob
.len
== 0)
492 DBG1(DBG_PTS
, "%s AIK private key for handle 0x%80x not found", LABEL
);
495 result
= Tspi_Context_LoadKeyByBlob(this->hContext
, hSRK
, aik_blob
.len
,
496 aik_blob
.ptr
, &hAIK
);
497 if (result
!= TSS_SUCCESS
)
499 DBG1(DBG_PTS
, "%s Tspi_Context_LoadKeyByBlob for AIK failed: 0x%x",
504 /* Create PCR composite object */
505 result
= Tspi_Context_CreateObject(this->hContext
, TSS_OBJECT_TYPE_PCRS
,
506 (*quote_mode
== TPM_QUOTE
) ? TSS_PCRS_STRUCT_INFO
:
507 TSS_PCRS_STRUCT_INFO_SHORT
,
509 if (result
!= TSS_SUCCESS
)
511 DBG1(DBG_PTS
, "%s Tspi_Context_CreateObject for pcrComposite failed: "
512 "0x%x", LABEL
, result
);
517 for (pcr
= 0; pcr
< PCR_NUM_MAX
; pcr
++)
519 if (pcr_sel
& (1 << pcr
))
521 result
= (*quote_mode
== TPM_QUOTE
) ?
522 Tspi_PcrComposite_SelectPcrIndex(hPcrComposite
, pcr
) :
523 Tspi_PcrComposite_SelectPcrIndexEx(hPcrComposite
, pcr
,
524 TSS_PCRS_DIRECTION_RELEASE
);
525 if (result
!= TSS_SUCCESS
)
527 DBG1(DBG_PTS
, "%s Tspi_PcrComposite_SelectPcrIndex failed: "
528 "0x%x", LABEL
, result
);
534 /* Set the Validation Data */
535 valData
.ulExternalDataLength
= data
.len
;
536 valData
.rgbExternalData
= data
.ptr
;
539 result
= (*quote_mode
== TPM_QUOTE
) ?
540 Tspi_TPM_Quote (this->hTPM
, hAIK
, hPcrComposite
, &valData
) :
541 Tspi_TPM_Quote2(this->hTPM
, hAIK
,
542 *quote_mode
== TPM_QUOTE2_VERSION_INFO
,
543 hPcrComposite
, &valData
, &version_info_size
,
545 if (result
!= TSS_SUCCESS
)
547 DBG1(DBG_PTS
, "%s Tspi_TPM_Quote%s failed: 0x%x", LABEL
,
548 (*quote_mode
== TPM_QUOTE
) ? "" : "2", result
);
552 if (*quote_mode
== TPM_QUOTE
)
554 /* TPM_Composite_Hash starts at byte 8 of TPM_Quote_Info structure */
555 comp_hash
= valData
.rgbData
+ 8;
559 /* TPM_Composite_Hash is last 20 bytes of TPM_Quote_Info2 structure */
560 comp_hash
= valData
.rgbData
+ valData
.ulDataLength
- version_info_size
-
563 pcr_digest
= chunk_create(comp_hash
, HASH_SIZE_SHA1
);
564 DBG2(DBG_PTS
, "PCR composite digest: %B", &pcr_digest
);
566 quote_chunk
= chunk_create(valData
.rgbData
, valData
.ulDataLength
);
567 DBG2(DBG_PTS
, "TPM Quote Info: %B", "e_chunk
);
569 *quote_info
= tpm_tss_quote_info_create(*quote_mode
, HASH_SHA1
, pcr_digest
);
571 *quote_sig
= chunk_clone(chunk_create(valData
.rgbValidationData
,
572 valData
.ulValidationDataLength
));
573 DBG2(DBG_PTS
, "TPM Quote Signature: %B", quote_sig
);
578 Tspi_Context_CloseObject(this->hContext
, hPcrComposite
);
580 Tspi_Context_CloseObject(this->hContext
, hAIK
);
585 METHOD(tpm_tss_t
, sign
, bool,
586 private_tpm_tss_trousers_t
*this, uint32_t hierarchy
, uint32_t handle
,
587 signature_scheme_t scheme
, chunk_t data
, chunk_t pin
, chunk_t
*signature
)
592 METHOD(tpm_tss_t
, get_random
, bool,
593 private_tpm_tss_trousers_t
*this, size_t bytes
, uint8_t *buffer
)
598 METHOD(tpm_tss_t
, get_data
, bool,
599 private_tpm_tss_trousers_t
*this, uint32_t hierarchy
, uint32_t handle
,
600 chunk_t pin
, chunk_t
*data
)
605 METHOD(tpm_tss_t
, load_key
, bool,
606 private_tpm_tss_trousers_t
*this, uint32_t hierarchy
, uint32_t handle
,
607 chunk_t pin
, key_type_t type
, chunk_t encoding
)
612 METHOD(tpm_tss_t
, destroy
, void,
613 private_tpm_tss_trousers_t
*this)
615 finalize_context(this);
616 this->aik_list
->destroy_function(this->aik_list
, (void*)free_aik
);
617 free(this->version_info
.ptr
);
621 METHOD(tpm_tss_trousers_t
, load_aik
, void,
622 private_tpm_tss_trousers_t
*this, chunk_t blob
, chunk_t pubkey
,
633 this->aik_list
->insert_last(this->aik_list
, item
);
639 tpm_tss_t
*tpm_tss_trousers_create()
641 private_tpm_tss_trousers_t
*this;
647 .get_version
= _get_version
,
648 .get_version_info
= _get_version_info
,
649 .generate_aik
= _generate_aik
,
650 .get_public
= _get_public
,
651 .read_pcr
= _read_pcr
,
652 .extend_pcr
= _extend_pcr
,
655 .get_random
= _get_random
,
656 .get_data
= _get_data
,
657 .load_key
= _load_key
,
660 .load_aik
= _load_aik
,
662 .aik_list
= linked_list_create(),
665 available
= initialize_context(this);
666 DBG1(DBG_PTS
, "TPM 1.2 via TrouSerS %savailable", available
? "" : "not ");
673 return &this->interface
.public;
676 #else /* TSS_TROUSERS */
678 tpm_tss_t
*tpm_tss_trousers_create()
683 #endif /* TSS_TROUSERS */