2 * Copyright (C) 2011 Sansar Choinyambuu
3 * HSR Hochschule fuer Technik Rapperswil
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 #include <crypto/hashers/hasher.h>
20 #include <bio/bio_writer.h>
21 #include <bio/bio_reader.h>
25 #include <trousers/tss.h>
26 #include <trousers/trousers.h>
29 #include <sys/utsname.h>
32 #include <openssl/rsa.h>
33 #include <openssl/evp.h>
34 #include <openssl/x509.h>
36 #define PTS_BUF_SIZE 4096
38 typedef struct private_pts_t private_pts_t
;
41 * Private data of a pts_t object.
44 struct private_pts_t
{
47 * Public pts_t interface.
52 * PTS Protocol Capabilities
54 pts_proto_caps_flag_t proto_caps
;
57 * PTS Measurement Algorithm
59 pts_meas_algorithms_t algorithm
;
64 pts_meas_algorithms_t dh_hash_algorithm
;
67 * PTS Diffie-Hellman Secret
72 * PTS Diffie-Hellman Initiator Nonce
74 chunk_t initiator_nonce
;
77 * PTS Diffie-Hellman Responder Nonce
79 chunk_t responder_nonce
;
82 * Secret assessment value to be used for TPM Quote as an external data
87 * Platform and OS Info
92 * TRUE if IMC-PTS, FALSE if IMV-PTS
97 * Do we have an activated TPM
102 * Contains a TPM_CAP_VERSION_INFO struct
104 chunk_t tpm_version_info
;
107 * Contains TSS Blob structure for AIK
112 * Contains a Attestation Identity Key or Certificate
117 * List of extended PCR's with corresponding values
122 METHOD(pts_t
, get_proto_caps
, pts_proto_caps_flag_t
,
125 return this->proto_caps
;
128 METHOD(pts_t
, set_proto_caps
, void,
129 private_pts_t
*this, pts_proto_caps_flag_t flags
)
131 this->proto_caps
= flags
;
132 DBG2(DBG_PTS
, "supported PTS protocol capabilities: %s%s%s%s%s",
133 flags
& PTS_PROTO_CAPS_C ?
"C" : ".",
134 flags
& PTS_PROTO_CAPS_V ?
"V" : ".",
135 flags
& PTS_PROTO_CAPS_D ?
"D" : ".",
136 flags
& PTS_PROTO_CAPS_T ?
"T" : ".",
137 flags
& PTS_PROTO_CAPS_X ?
"X" : ".");
140 METHOD(pts_t
, get_meas_algorithm
, pts_meas_algorithms_t
,
143 return this->algorithm
;
146 METHOD(pts_t
, set_meas_algorithm
, void,
147 private_pts_t
*this, pts_meas_algorithms_t algorithm
)
149 hash_algorithm_t hash_alg
;
151 hash_alg
= pts_meas_algo_to_hash(algorithm
);
152 DBG2(DBG_PTS
, "selected PTS measurement algorithm is %N",
153 hash_algorithm_names
, hash_alg
);
154 if (hash_alg
!= HASH_UNKNOWN
)
156 this->algorithm
= algorithm
;
160 METHOD(pts_t
, get_dh_hash_algorithm
, pts_meas_algorithms_t
,
163 return this->dh_hash_algorithm
;
166 METHOD(pts_t
, set_dh_hash_algorithm
, void,
167 private_pts_t
*this, pts_meas_algorithms_t algorithm
)
169 hash_algorithm_t hash_alg
;
171 hash_alg
= pts_meas_algo_to_hash(algorithm
);
172 DBG2(DBG_PTS
, "selected DH hash algorithm is %N",
173 hash_algorithm_names
, hash_alg
);
174 if (hash_alg
!= HASH_UNKNOWN
)
176 this->dh_hash_algorithm
= algorithm
;
181 METHOD(pts_t
, create_dh_nonce
, bool,
182 private_pts_t
*this, pts_dh_group_t group
, int nonce_len
)
184 diffie_hellman_group_t dh_group
;
188 dh_group
= pts_dh_group_to_ike(group
);
189 DBG2(DBG_PTS
, "selected PTS DH group is %N",
190 diffie_hellman_group_names
, dh_group
);
191 DESTROY_IF(this->dh
);
192 this->dh
= lib
->crypto
->create_dh(lib
->crypto
, dh_group
);
194 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_STRONG
);
197 DBG1(DBG_PTS
, "no rng available");
200 DBG2(DBG_PTS
, "nonce length is %d", nonce_len
);
201 nonce
= this->is_imc ?
&this->responder_nonce
: &this->initiator_nonce
;
203 rng
->allocate_bytes(rng
, nonce_len
, nonce
);
209 METHOD(pts_t
, get_my_public_value
, void,
210 private_pts_t
*this, chunk_t
*value
, chunk_t
*nonce
)
212 this->dh
->get_my_public_value(this->dh
, value
);
213 *nonce
= this->is_imc ?
this->responder_nonce
: this->initiator_nonce
;
216 METHOD(pts_t
, set_peer_public_value
, void,
217 private_pts_t
*this, chunk_t value
, chunk_t nonce
)
219 this->dh
->set_other_public_value(this->dh
, value
);
221 nonce
= chunk_clone(nonce
);
224 this->initiator_nonce
= nonce
;
228 this->responder_nonce
= nonce
;
232 METHOD(pts_t
, calculate_secret
, bool,
236 hash_algorithm_t hash_alg
;
237 chunk_t shared_secret
;
239 /* Check presence of nonces */
240 if (!this->initiator_nonce
.len
|| !this->responder_nonce
.len
)
242 DBG1(DBG_PTS
, "initiator and/or responder nonce is not available");
245 DBG3(DBG_PTS
, "initiator nonce: %B", &this->initiator_nonce
);
246 DBG3(DBG_PTS
, "responder nonce: %B", &this->responder_nonce
);
248 /* Calculate the DH secret */
249 if (this->dh
->get_shared_secret(this->dh
, &shared_secret
) != SUCCESS
)
251 DBG1(DBG_PTS
, "shared DH secret computation failed");
254 DBG4(DBG_PTS
, "shared DH secret: %B", &shared_secret
);
256 /* Calculate the secret assessment value */
257 hash_alg
= pts_meas_algo_to_hash(this->dh_hash_algorithm
);
258 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, hash_alg
);
260 hasher
->allocate_hash(hasher
, chunk_from_chars('1'), NULL
);
261 hasher
->allocate_hash(hasher
, this->initiator_nonce
, NULL
);
262 hasher
->allocate_hash(hasher
, this->responder_nonce
, NULL
);
263 hasher
->allocate_hash(hasher
, shared_secret
, &this->secret
);
264 hasher
->destroy(hasher
);
266 /* The DH secret must be destroyed */
267 chunk_clear(&shared_secret
);
270 * Truncate the hash to 20 bytes to fit the ExternalData
271 * argument of the TPM Quote command
273 this->secret
.len
= min(this->secret
.len
, 20);
274 DBG4(DBG_PTS
, "secret assessment value: %B", &this->secret
);
279 * Print TPM 1.2 Version Info
281 static void print_tpm_version_info(private_pts_t
*this)
283 TPM_CAP_VERSION_INFO versionInfo
;
287 result
= Trspi_UnloadBlob_CAP_VERSION_INFO(&offset
,
288 this->tpm_version_info
.ptr
, &versionInfo
);
289 if (result
!= TSS_SUCCESS
)
291 DBG1(DBG_PTS
, "could not parse tpm version info: tss error 0x%x",
296 DBG2(DBG_PTS
, "TPM 1.2 Version Info: Chip Version: %hhu.%hhu.%hhu.%hhu,"
297 " Spec Level: %hu, Errata Rev: %hhu, Vendor ID: %.4s",
298 versionInfo
.version
.major
, versionInfo
.version
.minor
,
299 versionInfo
.version
.revMajor
, versionInfo
.version
.revMinor
,
300 versionInfo
.specLevel
, versionInfo
.errataRev
,
301 versionInfo
.tpmVendorID
);
305 METHOD(pts_t
, get_platform_info
, char*,
308 return this->platform_info
;
311 METHOD(pts_t
, set_platform_info
, void,
312 private_pts_t
*this, char *info
)
314 free(this->platform_info
);
315 this->platform_info
= strdup(info
);
318 METHOD(pts_t
, get_tpm_version_info
, bool,
319 private_pts_t
*this, chunk_t
*info
)
325 *info
= this->tpm_version_info
;
326 print_tpm_version_info(this);
330 METHOD(pts_t
, set_tpm_version_info
, void,
331 private_pts_t
*this, chunk_t info
)
333 this->tpm_version_info
= chunk_clone(info
);
334 print_tpm_version_info(this);
338 * Load an AIK Blob (TSS_TSPATTRIB_KEYBLOB_BLOB attribute)
340 static void load_aik_blob(private_pts_t
*this)
344 u_int32_t aikBlobLen
;
346 blob_path
= lib
->settings
->get_str(lib
->settings
,
347 "libimcv.plugins.imc-attestation.aik_blob", NULL
);
351 /* Read aik key blob from a file */
352 if ((fp
= fopen(blob_path
, "r")) == NULL
)
354 DBG1(DBG_PTS
, "unable to open AIK Blob file: %s", blob_path
);
358 fseek(fp
, 0, SEEK_END
);
359 aikBlobLen
= ftell(fp
);
360 fseek(fp
, 0L, SEEK_SET
);
362 this->aik_blob
= chunk_alloc(aikBlobLen
);
363 if (fread(this->aik_blob
.ptr
, 1, aikBlobLen
, fp
))
365 DBG2(DBG_PTS
, "loaded AIK Blob from '%s'", blob_path
);
366 DBG3(DBG_PTS
, "AIK Blob: %B", &this->aik_blob
);
370 DBG1(DBG_PTS
, "unable to read AIK Blob file '%s'", blob_path
);
376 DBG1(DBG_PTS
, "AIK Blob is not available");
380 * Load an AIK certificate or public key
381 * the certificate having precedence over the public key if both are present
383 static void load_aik(private_pts_t
*this)
385 char *cert_path
, *key_path
;
387 cert_path
= lib
->settings
->get_str(lib
->settings
,
388 "libimcv.plugins.imc-attestation.aik_cert", NULL
);
389 key_path
= lib
->settings
->get_str(lib
->settings
,
390 "libimcv.plugins.imc-attestation.aik_key", NULL
);
394 this->aik
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
,
395 CERT_X509
, BUILD_FROM_FILE
,
396 cert_path
, BUILD_END
);
399 DBG2(DBG_PTS
, "loaded AIK certificate from '%s'", cert_path
);
405 this->aik
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
,
406 CERT_TRUSTED_PUBKEY
, BUILD_FROM_FILE
,
407 key_path
, BUILD_END
);
410 DBG2(DBG_PTS
, "loaded AIK public key from '%s'", key_path
);
415 DBG1(DBG_PTS
, "neither AIK certificate nor public key is available");
418 METHOD(pts_t
, get_aik
, certificate_t
*,
424 METHOD(pts_t
, set_aik
, void,
425 private_pts_t
*this, certificate_t
*aik
)
427 DESTROY_IF(this->aik
);
428 this->aik
= aik
->get_ref(aik
);
431 METHOD(pts_t
, hash_file
, bool,
432 private_pts_t
*this, hasher_t
*hasher
, char *pathname
, u_char
*hash
)
434 u_char buffer
[PTS_BUF_SIZE
];
438 file
= fopen(pathname
, "rb");
441 DBG1(DBG_PTS
," file '%s' can not be opened, %s", pathname
,
447 bytes_read
= fread(buffer
, 1, sizeof(buffer
), file
);
450 hasher
->get_hash(hasher
, chunk_create(buffer
, bytes_read
), NULL
);
454 hasher
->get_hash(hasher
, chunk_empty
, hash
);
464 * Get the relative filename of a fully qualified file pathname
466 static char* get_filename(char *pathname
)
468 char *pos
, *filename
;
470 pos
= filename
= pathname
;
471 while (pos
&& *(++pos
) != '\0')
474 pos
= strchr(filename
, '/');
479 METHOD(pts_t
, is_path_valid
, bool,
480 private_pts_t
*this, char *path
, pts_error_code_t
*error_code
)
486 if (!stat(path
, &st
))
490 else if (errno
== ENOENT
|| errno
== ENOTDIR
)
492 DBG1(DBG_PTS
, "file/directory does not exist %s", path
);
493 *error_code
= TCG_PTS_FILE_NOT_FOUND
;
495 else if (errno
== EFAULT
)
497 DBG1(DBG_PTS
, "bad address %s", path
);
498 *error_code
= TCG_PTS_INVALID_PATH
;
502 DBG1(DBG_PTS
, "error: %s occured while validating path: %s",
503 strerror(errno
), path
);
510 METHOD(pts_t
, do_measurements
, pts_file_meas_t
*,
511 private_pts_t
*this, u_int16_t request_id
, char *pathname
, bool is_directory
)
514 hash_algorithm_t hash_alg
;
515 u_char hash
[HASH_SIZE_SHA384
];
517 pts_file_meas_t
*measurements
;
519 /* Create a hasher */
520 hash_alg
= pts_meas_algo_to_hash(this->algorithm
);
521 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, hash_alg
);
524 DBG1(DBG_PTS
, "hasher %N not available", hash_algorithm_names
, hash_alg
);
528 /* Create a measurement object */
529 measurements
= pts_file_meas_create(request_id
);
531 /* Link the hash to the measurement and set the measurement length */
532 measurement
= chunk_create(hash
, hasher
->get_hash_size(hasher
));
536 enumerator_t
*enumerator
;
537 char *rel_name
, *abs_name
;
540 enumerator
= enumerator_create_directory(pathname
);
543 DBG1(DBG_PTS
," directory '%s' can not be opened, %s", pathname
,
545 hasher
->destroy(hasher
);
546 measurements
->destroy(measurements
);
549 while (enumerator
->enumerate(enumerator
, &rel_name
, &abs_name
, &st
))
551 /* measure regular files only */
552 if (S_ISREG(st
.st_mode
) && *rel_name
!= '.')
554 if (!hash_file(this, hasher
, abs_name
, hash
))
556 enumerator
->destroy(enumerator
);
557 hasher
->destroy(hasher
);
558 measurements
->destroy(measurements
);
561 DBG2(DBG_PTS
, " %#B for '%s'", &measurement
, rel_name
);
562 measurements
->add(measurements
, rel_name
, measurement
);
565 enumerator
->destroy(enumerator
);
571 if (!hash_file(this, hasher
, pathname
, hash
))
573 hasher
->destroy(hasher
);
574 measurements
->destroy(measurements
);
577 filename
= get_filename(pathname
);
578 DBG2(DBG_PTS
, " %#B for '%s'", &measurement
, filename
);
579 measurements
->add(measurements
, filename
, measurement
);
581 hasher
->destroy(hasher
);
587 * Obtain statistical information describing a file
589 static bool file_metadata(char *pathname
, pts_file_metadata_t
**entry
)
592 pts_file_metadata_t
*tmp
;
594 tmp
= malloc_thing(pts_file_metadata_t
);
596 if (stat(pathname
, &st
))
598 DBG1(DBG_PTS
, "Unable to obtain statistical information about %s",
603 tmp
->filename
= strdup(pathname
);
604 tmp
->meta_length
= PTS_FILE_METADATA_SIZE
+ strlen(tmp
->filename
);
606 if (S_ISREG(st
.st_mode
))
608 tmp
->type
= PTS_FILE_REGULAR
;
610 else if (S_ISDIR(st
.st_mode
))
612 tmp
->type
= PTS_FILE_DIRECTORY
;
614 else if (S_ISCHR(st
.st_mode
))
616 tmp
->type
= PTS_FILE_CHAR_SPEC
;
618 else if (S_ISBLK(st
.st_mode
))
620 tmp
->type
= PTS_FILE_BLOCK_SPEC
;
622 else if (S_ISFIFO(st
.st_mode
))
624 tmp
->type
= PTS_FILE_FIFO
;
626 else if (S_ISLNK(st
.st_mode
))
628 tmp
->type
= PTS_FILE_SYM_LINK
;
630 else if (S_ISSOCK(st
.st_mode
))
632 tmp
->type
= PTS_FILE_SOCKET
;
636 tmp
->type
= PTS_FILE_OTHER
;
639 tmp
->filesize
= (u_int64_t
)st
.st_size
;
640 tmp
->create_time
= st
.st_ctime
;
641 tmp
->last_modify_time
= st
.st_mtime
;
642 tmp
->last_access_time
= st
.st_atime
;
643 tmp
->owner_id
= (u_int64_t
)st
.st_uid
;
644 tmp
->group_id
= (u_int64_t
)st
.st_gid
;
651 METHOD(pts_t
, get_metadata
, pts_file_meta_t
*,
652 private_pts_t
*this, char *pathname
, bool is_directory
)
654 pts_file_meta_t
*metadata
;
655 pts_file_metadata_t
*entry
;
657 /* Create a metadata object */
658 metadata
= pts_file_meta_create();
662 enumerator_t
*enumerator
;
663 char *rel_name
, *abs_name
;
666 enumerator
= enumerator_create_directory(pathname
);
669 DBG1(DBG_PTS
," directory '%s' can not be opened, %s", pathname
,
671 metadata
->destroy(metadata
);
674 while (enumerator
->enumerate(enumerator
, &rel_name
, &abs_name
, &st
))
676 /* measure regular files only */
677 if (S_ISREG(st
.st_mode
) && *rel_name
!= '.')
679 if (!file_metadata(abs_name
, &entry
))
681 enumerator
->destroy(enumerator
);
682 metadata
->destroy(metadata
);
685 metadata
->add(metadata
, entry
);
688 enumerator
->destroy(enumerator
);
694 if (!file_metadata(pathname
, &entry
))
696 metadata
->destroy(metadata
);
699 filename
= get_filename(pathname
);
700 metadata
->add(metadata
, entry
);
706 METHOD(pts_t
, read_pcr
, bool,
707 private_pts_t
*this, u_int32_t pcr_num
, chunk_t
*output
)
709 TSS_HCONTEXT hContext
;
712 u_int32_t pcr_length
;
715 result
= Tspi_Context_Create(&hContext
);
716 if (result
!= TSS_SUCCESS
)
718 DBG1(DBG_PTS
, "TPM context could not be created: tss error 0x%x",
723 result
= Tspi_Context_Connect(hContext
, NULL
);
724 if (result
!= TSS_SUCCESS
)
728 result
= Tspi_Context_GetTpmObject (hContext
, &hTPM
);
729 if (result
!= TSS_SUCCESS
)
733 pcr_value
= chunk_alloc(PCR_LEN
);
734 result
= Tspi_TPM_PcrRead(hTPM
, pcr_num
, &pcr_length
, &pcr_value
.ptr
);
735 if (result
!= TSS_SUCCESS
)
741 *output
= chunk_clone(*output
);
743 chunk_clear(&pcr_value
);
744 Tspi_Context_Close(hContext
);
745 DBG3(DBG_PTS
, "PCR %d value:%B", pcr_num
, output
);
749 chunk_clear(&pcr_value
);
750 DBG1(DBG_PTS
, "TPM not available: tss error 0x%x", result
);
751 Tspi_Context_Close(hContext
);
755 METHOD(pts_t
, extend_pcr
, bool,
756 private_pts_t
*this, u_int32_t pcr_num
, chunk_t input
, chunk_t
*output
)
758 TSS_HCONTEXT hContext
;
761 u_int32_t pcr_length
;
764 result
= Tspi_Context_Create(&hContext
);
765 if (result
!= TSS_SUCCESS
)
767 DBG1(DBG_PTS
, "TPM context could not be created: tss error 0x%x",
771 result
= Tspi_Context_Connect(hContext
, NULL
);
772 if (result
!= TSS_SUCCESS
)
776 result
= Tspi_Context_GetTpmObject (hContext
, &hTPM
);
777 if (result
!= TSS_SUCCESS
)
782 pcr_value
= chunk_alloc(PCR_LEN
);
783 result
= Tspi_TPM_PcrExtend(hTPM
, pcr_num
, PCR_LEN
, input
.ptr
,
784 NULL
, &pcr_length
, &pcr_value
.ptr
);
785 if (result
!= TSS_SUCCESS
)
791 *output
= chunk_clone(*output
);
793 chunk_clear(&pcr_value
);
794 Tspi_Context_Close(hContext
);
795 DBG3(DBG_PTS
, "PCR %d extended with: %B", pcr_num
, &input
);
796 DBG3(DBG_PTS
, "PCR %d value after extend: %B", pcr_num
, output
);
800 chunk_clear(&pcr_value
);
801 DBG1(DBG_PTS
, "TPM not available: tss error 0x%x", result
);
802 Tspi_Context_Close(hContext
);
806 METHOD(pts_t
, quote_tpm
, bool,
807 private_pts_t
*this, u_int32_t
*pcrs
, u_int32_t num_of_pcrs
,
808 chunk_t
*pcr_composite
, chunk_t
*quote_signature
)
810 TSS_HCONTEXT hContext
;
814 TSS_HPOLICY srkUsagePolicy
;
815 TSS_UUID SRK_UUID
= TSS_UUID_SRK
;
816 BYTE secret
[] = TSS_WELL_KNOWN_SECRET
;
817 TSS_HPCRS hPcrComposite
;
818 TSS_VALIDATION valData
;
821 chunk_t pcr_comp
, quote_sign
;
823 result
= Tspi_Context_Create(&hContext
);
824 if (result
!= TSS_SUCCESS
)
826 DBG1(DBG_PTS
, "TPM context could not be created: tss error 0x%x",
830 result
= Tspi_Context_Connect(hContext
, NULL
);
831 if (result
!= TSS_SUCCESS
)
835 result
= Tspi_Context_GetTpmObject (hContext
, &hTPM
);
836 if (result
!= TSS_SUCCESS
)
841 /* Retrieve SRK from TPM and set the authentication to well known secret*/
842 result
= Tspi_Context_LoadKeyByUUID(hContext
, TSS_PS_TYPE_SYSTEM
,
844 if (result
!= TSS_SUCCESS
)
849 result
= Tspi_GetPolicyObject(hSRK
, TSS_POLICY_USAGE
, &srkUsagePolicy
);
850 if (result
!= TSS_SUCCESS
)
854 result
= Tspi_Policy_SetSecret(srkUsagePolicy
, TSS_SECRET_MODE_SHA1
,
856 if (result
!= TSS_SUCCESS
)
861 result
= Tspi_Context_LoadKeyByBlob (hContext
, hSRK
, this->aik_blob
.len
,
862 this->aik_blob
.ptr
, &hAIK
);
863 if (result
!= TSS_SUCCESS
)
868 /* Create PCR composite object */
869 result
= Tspi_Context_CreateObject(hContext
,
870 TSS_OBJECT_TYPE_PCRS
, 0, &hPcrComposite
);
871 if (result
!= TSS_SUCCESS
)
877 for (i
= 0; i
< num_of_pcrs
; i
++)
879 if (pcrs
[i
] < 0 || pcrs
[i
] >= MAX_NUM_PCR
)
881 DBG1(DBG_PTS
, "Invalid PCR number: %d", pcrs
[i
]);
884 result
= Tspi_PcrComposite_SelectPcrIndex(hPcrComposite
, pcrs
[i
]);
885 if (result
!= TSS_SUCCESS
)
891 /* Set the Validation Data */
892 valData
.ulExternalDataLength
= this->secret
.len
;
893 valData
.rgbExternalData
= (BYTE
*)this->secret
.ptr
;
897 result
= Tspi_TPM_Quote(hTPM
, hAIK
, hPcrComposite
, &valData
);
898 if (result
!= TSS_SUCCESS
)
903 /* Set output chunks */
904 pcr_comp
= chunk_alloc(HASH_SIZE_SHA1
);
905 memcpy(pcr_comp
.ptr
, valData
.rgbData
+ 8, HASH_SIZE_SHA1
);
906 *pcr_composite
= pcr_comp
;
907 *pcr_composite
= chunk_clone(*pcr_composite
);
908 DBG3(DBG_PTS
, "Hash of PCR Composite: %B",pcr_composite
);
910 quote_sign
= chunk_alloc(valData
.ulValidationDataLength
);
911 memcpy(quote_sign
.ptr
, valData
.rgbValidationData
,
912 valData
.ulValidationDataLength
);
913 *quote_signature
= quote_sign
;
914 *quote_signature
= chunk_clone(*quote_signature
);
915 DBG3(DBG_PTS
, "TOM Quote Signature: %B",quote_signature
);
917 chunk_clear("e_sign
);
918 Tspi_Context_FreeMemory(hContext
, NULL
);
919 Tspi_Context_CloseObject(hContext
, hPcrComposite
);
920 Tspi_Context_CloseObject(hContext
, hAIK
);
921 Tspi_Context_Close(hContext
);
927 Tspi_Context_FreeMemory(hContext
, NULL
);
930 Tspi_Context_CloseObject(hContext
, hPcrComposite
);
933 Tspi_Context_CloseObject(hContext
, hAIK
);
936 Tspi_Context_Close(hContext
);
938 DBG1(DBG_PTS
, "TPM not available: tss error 0x%x", result
);
943 * Comparison function for pcr_entry_t struct
945 static int pcr_entry_compare(const pcr_entry_t
*a
, const pcr_entry_t
*b
)
947 return (a
->pcr_number
- b
->pcr_number
);
950 static int pcr_entry_compare_qsort(const void *a
, const void *b
)
952 return pcr_entry_compare(*(const pcr_entry_t
*const *)a
953 , *(const pcr_entry_t
*const *)b
);
956 METHOD(pts_t
, add_pcr_entry
, void,
957 private_pts_t
*this, pcr_entry_t
*new)
964 this->pcrs
= linked_list_create();
967 e
= this->pcrs
->create_enumerator(this->pcrs
);
968 while (e
->enumerate(e
, &entry
))
970 if (entry
->pcr_number
== new->pcr_number
)
972 DBG4(DBG_PTS
, "updating already added PCR%d value",
974 this->pcrs
->remove_at(this->pcrs
, e
);
981 this->pcrs
->insert_last(this->pcrs
, new);
983 qsort(this->pcrs
, this->pcrs
->get_count(this->pcrs
),
984 sizeof(pcr_entry_t
*), pcr_entry_compare_qsort
);
988 * 1. build a TCPA_PCR_COMPOSITE structure which contains (pcrCompositeBuf)
989 * TCPA_PCR_SELECTION structure (bitmask length + bitmask)
990 * UINT32 (network order) gives the number of bytes following (pcr entries * 20)
991 * TCPA_PCRVALUE[] with the pcr values
993 * The first two bytes of the message represent the length
994 * of the bitmask that follows. The bitmask represents the
995 * requested PCRs to be quoted.
997 * TPM Main-Part 2 TPM Structures_v1.2 8.1
998 * The bitmask is in big endian order"
1001 * Bit: 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 ...
1002 * Pcr: 7 6 5 4 3 2 1 0 15 14 13 12 11 10 9 8 ...
1004 * 2. SHA1(pcrCompositeBuf)
1006 * 3. build a TCPA_QUOTE_INFO structure which contains
1007 * 4 bytes of version
1008 * 4 bytes 'Q' 'U' 'O' 'T'
1009 * 20 byte SHA1 of TCPA_PCR_COMPOSITE
1013 METHOD(pts_t
, get_quote_info
, bool,
1014 private_pts_t
*this, chunk_t
*out_pcr_composite
, chunk_t
*out_quote_info
)
1017 pcr_entry_t
*pcr_entry
;
1018 chunk_t pcr_composite
;
1019 u_int32_t pcr_composite_len
;
1020 bio_writer_t
*writer
;
1021 u_int8_t mask_bytes
[PCR_MASK_LEN
] = {0,0,0}, i
;
1024 if (this->pcrs
->get_count(this->pcrs
) == 0)
1026 DBG1(DBG_PTS
, "PCR entries unavailable, unable to construct "
1031 pcr_composite_len
= 2 + PCR_MASK_LEN
+ 4 +
1032 this->pcrs
->get_count(this->pcrs
) * PCR_LEN
;
1034 writer
= bio_writer_create(pcr_composite_len
);
1035 /* Lenght of the bist mask field */
1036 writer
->write_uint16(writer
, PCR_MASK_LEN
);
1037 /* Bit mask indicating selected PCRs */
1038 e
= this->pcrs
->create_enumerator(this->pcrs
);
1039 while (e
->enumerate(e
, &pcr_entry
))
1041 u_int32_t index
= pcr_entry
->pcr_number
;
1042 mask_bytes
[index
/ 8] |= (1 << (index
% 8));
1046 for (i
= 0; i
< PCR_MASK_LEN
; i
++)
1048 writer
->write_uint8(writer
, mask_bytes
[i
]);
1051 /* Lenght of the pcr entries */
1052 writer
->write_uint32(writer
, this->pcrs
->get_count(this->pcrs
) * PCR_LEN
);
1053 /* Actual PCR values */
1054 e
= this->pcrs
->create_enumerator(this->pcrs
);
1055 while (e
->enumerate(e
, &pcr_entry
))
1057 writer
->write_data(writer
, chunk_create(pcr_entry
->pcr_value
, PCR_LEN
));
1062 /* PCR Composite structure */
1063 pcr_composite
= chunk_clone(writer
->get_buf(writer
));
1064 writer
->destroy(writer
);
1066 writer
= bio_writer_create(TPM_QUOTE_INFO_LEN
);
1067 /* Version number */
1068 writer
->write_uint8(writer
, 1);
1069 writer
->write_uint8(writer
, 1);
1070 writer
->write_uint8(writer
, 0);
1071 writer
->write_uint8(writer
, 0);
1073 /* Magic QUOT value, depends on TPM Ordinal */
1074 writer
->write_uint8(writer
, 'Q');
1075 writer
->write_uint8(writer
, 'U');
1076 writer
->write_uint8(writer
, 'O');
1077 writer
->write_uint8(writer
, 'T');
1079 /* SHA1 hash of PCR Composite Structure */
1080 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_SHA1
);
1081 hasher
->allocate_hash(hasher
, pcr_composite
, out_pcr_composite
);
1082 DBG4(DBG_PTS
, "Hash of calculated PCR Composite: %B", out_pcr_composite
);
1084 chunk_clear(&pcr_composite
);
1085 hasher
->destroy(hasher
);
1086 writer
->write_data(writer
, *out_pcr_composite
);
1088 if (!this->secret
.ptr
)
1090 DBG1(DBG_PTS
, "Secret assessment value unavailable",
1091 "unable to construct TPM Quote Info");
1092 chunk_clear(out_pcr_composite
);
1093 writer
->destroy(writer
);
1096 /* Secret assessment value 20 bytes (nonce) */
1097 writer
->write_data(writer
, this->secret
);
1098 /* TPM Quote Info */
1099 *out_quote_info
= chunk_clone(writer
->get_buf(writer
));
1100 DBG4(DBG_PTS
, "Calculated TPM Quote Info: %B", out_quote_info
);
1101 writer
->destroy(writer
);
1106 METHOD(pts_t
, verify_quote_signature
, bool,
1107 private_pts_t
*this, chunk_t data
, chunk_t signature
)
1109 public_key_t
*aik_pub_key
;
1110 chunk_t key_encoding
;
1111 EVP_PKEY
*pkey
= NULL
;
1115 aik_pub_key
= this->aik
->get_public_key(this->aik
);
1118 DBG1(DBG_PTS
, "failed to get public key from AIK certificate");
1122 /** Implementation using strongswan -> not working */
1123 /**if (!aik_pub_key->verify(aik_pub_key, SIGN_RSA_EMSA_PKCS1_SHA1,
1126 DBG1(DBG_PTS, "signature verification failed for TPM Quote Info");
1131 if (!aik_pub_key
->get_encoding(aik_pub_key
,
1132 PUBKEY_SPKI_ASN1_DER
, &key_encoding
))
1134 DBG1(DBG_PTS
, "failed to get encoding of AIK public key");
1138 p
= key_encoding
.ptr
;
1139 pkey
= d2i_PUBKEY(NULL
, (const unsigned char**)&p
, key_encoding
.len
);
1142 DBG1(DBG_PTS
, "failed to get EVP_PKEY object from AIK public key");
1146 rsa
= EVP_PKEY_get1_RSA(pkey
);
1149 DBG1(DBG_PTS
, "failed to get RSA object from EVP_PKEY");
1153 if (RSA_verify(NID_sha1
, data
.ptr
, data
.len
,
1154 signature
.ptr
, signature
.len
, rsa
) != 1)
1156 DBG1(DBG_PTS
, "signature verification failed for TPM Quote Info");
1161 EVP_PKEY_free(pkey
);
1162 if (key_encoding
.ptr
)
1164 chunk_clear(&key_encoding
);
1166 aik_pub_key
->destroy(aik_pub_key
);
1176 EVP_PKEY_free(pkey
);
1178 if (key_encoding
.ptr
)
1180 chunk_clear(&key_encoding
);
1182 DESTROY_IF(aik_pub_key
);
1186 METHOD(pts_t
, destroy
, void,
1187 private_pts_t
*this)
1189 DESTROY_IF(this->aik
);
1190 DESTROY_IF(this->dh
);
1191 DESTROY_IF(this->pcrs
);
1192 free(this->initiator_nonce
.ptr
);
1193 free(this->responder_nonce
.ptr
);
1194 free(this->secret
.ptr
);
1195 free(this->platform_info
);
1196 free(this->aik_blob
.ptr
);
1197 free(this->tpm_version_info
.ptr
);
1202 * Determine Linux distribution and hardware platform
1204 static char* extract_platform_info(void)
1207 char buf
[BUF_LEN
], *pos
, *value
= NULL
;
1209 struct utsname uninfo
;
1211 /* Linux/Unix distribution release info (from http://linuxmafia.com) */
1212 const char* releases
[] = {
1213 "/etc/lsb-release", "/etc/debian_version",
1214 "/etc/SuSE-release", "/etc/novell-release",
1215 "/etc/sles-release", "/etc/redhat-release",
1216 "/etc/fedora-release", "/etc/gentoo-release",
1217 "/etc/slackware-version", "/etc/annvix-release",
1218 "/etc/arch-release", "/etc/arklinux-release",
1219 "/etc/aurox-release", "/etc/blackcat-release",
1220 "/etc/cobalt-release", "/etc/conectiva-release",
1221 "/etc/debian_release", "/etc/immunix-release",
1222 "/etc/lfs-release", "/etc/linuxppc-release",
1223 "/etc/mandrake-release", "/etc/mandriva-release",
1224 "/etc/mandrakelinux-release", "/etc/mklinux-release",
1225 "/etc/pld-release", "/etc/redhat_version",
1226 "/etc/slackware-release", "/etc/e-smith-release",
1227 "/etc/release", "/etc/sun-release",
1228 "/etc/tinysofa-release", "/etc/turbolinux-release",
1229 "/etc/ultrapenguin-release", "/etc/UnitedLinux-release",
1230 "/etc/va-release", "/etc/yellowdog-release"
1233 const char description
[] = "DISTRIB_DESCRIPTION=\"";
1235 for (i
= 0; i
< countof(releases
); i
++)
1237 file
= fopen(releases
[i
], "r");
1242 fseek(file
, 0, SEEK_END
);
1243 len
= min(ftell(file
), sizeof(buf
)-1);
1246 if (fread(buf
, 1, len
, file
) != len
)
1248 DBG1(DBG_PTS
, "failed to read file '%s'", releases
[i
]);
1254 if (i
== 0) /* LSB release */
1256 pos
= strstr(buf
, description
);
1259 DBG1(DBG_PTS
, "failed to find begin of lsb-release "
1260 "DESCRIPTION field");
1263 value
= pos
+ strlen(description
);
1264 pos
= strchr(value
, '"');
1267 DBG1(DBG_PTS
, "failed to find end of lsb-release "
1268 "DESCRIPTION field");
1275 pos
= strchr(value
, '\n');
1278 DBG1(DBG_PTS
, "failed to find end of release string");
1287 DBG1(DBG_PTS
, "no distribution release file found");
1291 if (uname(&uninfo
) < 0)
1293 DBG1(DBG_PTS
, "could not retrieve machine architecture");
1298 len
= sizeof(buf
)-1 + (pos
- buf
);
1299 strncpy(pos
, uninfo
.machine
, len
);
1301 DBG1(DBG_PTS
, "platform is '%s'", value
);
1302 return strdup(value
);
1306 * Check for a TPM by querying for TPM Version Info
1308 static bool has_tpm(private_pts_t
*this)
1310 TSS_HCONTEXT hContext
;
1313 u_int32_t version_info_len
;
1315 result
= Tspi_Context_Create(&hContext
);
1316 if (result
!= TSS_SUCCESS
)
1318 DBG1(DBG_PTS
, "TPM context could not be created: tss error 0x%x",
1322 result
= Tspi_Context_Connect(hContext
, NULL
);
1323 if (result
!= TSS_SUCCESS
)
1327 result
= Tspi_Context_GetTpmObject (hContext
, &hTPM
);
1328 if (result
!= TSS_SUCCESS
)
1332 result
= Tspi_TPM_GetCapability(hTPM
, TSS_TPMCAP_VERSION_VAL
, 0, NULL
,
1334 &this->tpm_version_info
.ptr
);
1335 this->tpm_version_info
.len
= version_info_len
;
1336 if (result
!= TSS_SUCCESS
)
1340 this->tpm_version_info
= chunk_clone(this->tpm_version_info
);
1341 Tspi_Context_Close(hContext
);
1345 DBG1(DBG_PTS
, "TPM not available: tss error 0x%x", result
);
1346 Tspi_Context_Close(hContext
);
1353 pts_t
*pts_create(bool is_imc
)
1355 private_pts_t
*this;
1359 .get_proto_caps
= _get_proto_caps
,
1360 .set_proto_caps
= _set_proto_caps
,
1361 .get_meas_algorithm
= _get_meas_algorithm
,
1362 .set_meas_algorithm
= _set_meas_algorithm
,
1363 .get_dh_hash_algorithm
= _get_dh_hash_algorithm
,
1364 .set_dh_hash_algorithm
= _set_dh_hash_algorithm
,
1365 .create_dh_nonce
= _create_dh_nonce
,
1366 .get_my_public_value
= _get_my_public_value
,
1367 .set_peer_public_value
= _set_peer_public_value
,
1368 .calculate_secret
= _calculate_secret
,
1369 .get_platform_info
= _get_platform_info
,
1370 .set_platform_info
= _set_platform_info
,
1371 .get_tpm_version_info
= _get_tpm_version_info
,
1372 .set_tpm_version_info
= _set_tpm_version_info
,
1373 .get_aik
= _get_aik
,
1374 .set_aik
= _set_aik
,
1375 .is_path_valid
= _is_path_valid
,
1376 .hash_file
= _hash_file
,
1377 .do_measurements
= _do_measurements
,
1378 .get_metadata
= _get_metadata
,
1379 .read_pcr
= _read_pcr
,
1380 .extend_pcr
= _extend_pcr
,
1381 .quote_tpm
= _quote_tpm
,
1382 .add_pcr_entry
= _add_pcr_entry
,
1383 .get_quote_info
= _get_quote_info
,
1384 .verify_quote_signature
= _verify_quote_signature
,
1385 .destroy
= _destroy
,
1388 .proto_caps
= PTS_PROTO_CAPS_V
,
1389 .algorithm
= PTS_MEAS_ALGO_SHA256
,
1390 .dh_hash_algorithm
= PTS_MEAS_ALGO_SHA256
,
1395 this->platform_info
= extract_platform_info();
1399 this->has_tpm
= TRUE
;
1400 this->proto_caps
|= PTS_PROTO_CAPS_T
;
1402 load_aik_blob(this);
1407 this->proto_caps
|= PTS_PROTO_CAPS_T
| PTS_PROTO_CAPS_C
;
1410 return &this->public;