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>
23 #include <trousers/tss.h>
24 #include <trousers/trousers.h>
27 #include <sys/utsname.h>
30 #include <openssl/rsa.h>
31 #include <openssl/evp.h>
32 #include <openssl/x509.h>
34 #define PTS_BUF_SIZE 4096
36 typedef struct private_pts_t private_pts_t
;
39 * Private data of a pts_t object.
42 struct private_pts_t
{
45 * Public pts_t interface.
50 * PTS Protocol Capabilities
52 pts_proto_caps_flag_t proto_caps
;
55 * PTS Measurement Algorithm
57 pts_meas_algorithms_t algorithm
;
62 pts_meas_algorithms_t dh_hash_algorithm
;
65 * PTS Diffie-Hellman Secret
70 * PTS Diffie-Hellman Initiator Nonce
72 chunk_t initiator_nonce
;
75 * PTS Diffie-Hellman Responder Nonce
77 chunk_t responder_nonce
;
80 * Secret assessment value to be used for TPM Quote as an external data
85 * Platform and OS Info
90 * TRUE if IMC-PTS, FALSE if IMV-PTS
95 * Do we have an activated TPM
100 * Contains a TPM_CAP_VERSION_INFO struct
102 chunk_t tpm_version_info
;
105 * Contains TSS Blob structure for AIK
110 * Contains a Attestation Identity Key or Certificate
115 * List of extended PCR's with corresponding values
120 METHOD(pts_t
, get_proto_caps
, pts_proto_caps_flag_t
,
123 return this->proto_caps
;
126 METHOD(pts_t
, set_proto_caps
, void,
127 private_pts_t
*this, pts_proto_caps_flag_t flags
)
129 this->proto_caps
= flags
;
130 DBG2(DBG_PTS
, "supported PTS protocol capabilities: %s%s%s%s%s",
131 flags
& PTS_PROTO_CAPS_C ?
"C" : ".",
132 flags
& PTS_PROTO_CAPS_V ?
"V" : ".",
133 flags
& PTS_PROTO_CAPS_D ?
"D" : ".",
134 flags
& PTS_PROTO_CAPS_T ?
"T" : ".",
135 flags
& PTS_PROTO_CAPS_X ?
"X" : ".");
138 METHOD(pts_t
, get_meas_algorithm
, pts_meas_algorithms_t
,
141 return this->algorithm
;
144 METHOD(pts_t
, set_meas_algorithm
, void,
145 private_pts_t
*this, pts_meas_algorithms_t algorithm
)
147 hash_algorithm_t hash_alg
;
149 hash_alg
= pts_meas_algo_to_hash(algorithm
);
150 DBG2(DBG_PTS
, "selected PTS measurement algorithm is %N",
151 hash_algorithm_names
, hash_alg
);
152 if (hash_alg
!= HASH_UNKNOWN
)
154 this->algorithm
= algorithm
;
158 METHOD(pts_t
, get_dh_hash_algorithm
, pts_meas_algorithms_t
,
161 return this->dh_hash_algorithm
;
164 METHOD(pts_t
, set_dh_hash_algorithm
, void,
165 private_pts_t
*this, pts_meas_algorithms_t algorithm
)
167 hash_algorithm_t hash_alg
;
169 hash_alg
= pts_meas_algo_to_hash(algorithm
);
170 DBG2(DBG_PTS
, "selected DH hash algorithm is %N",
171 hash_algorithm_names
, hash_alg
);
172 if (hash_alg
!= HASH_UNKNOWN
)
174 this->dh_hash_algorithm
= algorithm
;
179 METHOD(pts_t
, create_dh_nonce
, bool,
180 private_pts_t
*this, pts_dh_group_t group
, int nonce_len
)
182 diffie_hellman_group_t dh_group
;
186 dh_group
= pts_dh_group_to_ike(group
);
187 DBG2(DBG_PTS
, "selected PTS DH group is %N",
188 diffie_hellman_group_names
, dh_group
);
189 DESTROY_IF(this->dh
);
190 this->dh
= lib
->crypto
->create_dh(lib
->crypto
, dh_group
);
192 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_STRONG
);
195 DBG1(DBG_PTS
, "no rng available");
198 DBG2(DBG_PTS
, "nonce length is %d", nonce_len
);
199 nonce
= this->is_imc ?
&this->responder_nonce
: &this->initiator_nonce
;
201 rng
->allocate_bytes(rng
, nonce_len
, nonce
);
207 METHOD(pts_t
, get_my_public_value
, void,
208 private_pts_t
*this, chunk_t
*value
, chunk_t
*nonce
)
210 this->dh
->get_my_public_value(this->dh
, value
);
211 *nonce
= this->is_imc ?
this->responder_nonce
: this->initiator_nonce
;
214 METHOD(pts_t
, set_peer_public_value
, void,
215 private_pts_t
*this, chunk_t value
, chunk_t nonce
)
217 this->dh
->set_other_public_value(this->dh
, value
);
219 nonce
= chunk_clone(nonce
);
222 this->initiator_nonce
= nonce
;
226 this->responder_nonce
= nonce
;
230 METHOD(pts_t
, calculate_secret
, bool,
234 hash_algorithm_t hash_alg
;
235 chunk_t shared_secret
;
237 /* Check presence of nonces */
238 if (!this->initiator_nonce
.len
|| !this->responder_nonce
.len
)
240 DBG1(DBG_PTS
, "initiator and/or responder nonce is not available");
243 DBG3(DBG_PTS
, "initiator nonce: %B", &this->initiator_nonce
);
244 DBG3(DBG_PTS
, "responder nonce: %B", &this->responder_nonce
);
246 /* Calculate the DH secret */
247 if (this->dh
->get_shared_secret(this->dh
, &shared_secret
) != SUCCESS
)
249 DBG1(DBG_PTS
, "shared DH secret computation failed");
252 DBG4(DBG_PTS
, "shared DH secret: %B", &shared_secret
);
254 /* Calculate the secret assessment value */
255 hash_alg
= pts_meas_algo_to_hash(this->dh_hash_algorithm
);
256 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, hash_alg
);
258 hasher
->allocate_hash(hasher
, chunk_from_chars('1'), NULL
);
259 hasher
->allocate_hash(hasher
, this->initiator_nonce
, NULL
);
260 hasher
->allocate_hash(hasher
, this->responder_nonce
, NULL
);
261 hasher
->allocate_hash(hasher
, shared_secret
, &this->secret
);
262 hasher
->destroy(hasher
);
264 /* The DH secret must be destroyed */
265 chunk_clear(&shared_secret
);
268 * Truncate the hash to 20 bytes to fit the ExternalData
269 * argument of the TPM Quote command
271 this->secret
.len
= min(this->secret
.len
, 20);
272 DBG4(DBG_PTS
, "secret assessment value: %B", &this->secret
);
277 * Print TPM 1.2 Version Info
279 static void print_tpm_version_info(private_pts_t
*this)
281 TPM_CAP_VERSION_INFO versionInfo
;
285 result
= Trspi_UnloadBlob_CAP_VERSION_INFO(&offset
,
286 this->tpm_version_info
.ptr
, &versionInfo
);
287 if (result
!= TSS_SUCCESS
)
289 DBG1(DBG_PTS
, "could not parse tpm version info: tss error 0x%x",
294 DBG2(DBG_PTS
, "TPM 1.2 Version Info: Chip Version: %hhu.%hhu.%hhu.%hhu,"
295 " Spec Level: %hu, Errata Rev: %hhu, Vendor ID: %.4s",
296 versionInfo
.version
.major
, versionInfo
.version
.minor
,
297 versionInfo
.version
.revMajor
, versionInfo
.version
.revMinor
,
298 versionInfo
.specLevel
, versionInfo
.errataRev
,
299 versionInfo
.tpmVendorID
);
303 METHOD(pts_t
, get_platform_info
, char*,
306 return this->platform_info
;
309 METHOD(pts_t
, set_platform_info
, void,
310 private_pts_t
*this, char *info
)
312 free(this->platform_info
);
313 this->platform_info
= strdup(info
);
316 METHOD(pts_t
, get_tpm_version_info
, bool,
317 private_pts_t
*this, chunk_t
*info
)
323 *info
= this->tpm_version_info
;
324 print_tpm_version_info(this);
328 METHOD(pts_t
, set_tpm_version_info
, void,
329 private_pts_t
*this, chunk_t info
)
331 this->tpm_version_info
= chunk_clone(info
);
332 print_tpm_version_info(this);
336 * Load an AIK Blob (TSS_TSPATTRIB_KEYBLOB_BLOB attribute)
338 static void load_aik_blob(private_pts_t
*this)
342 u_int32_t aikBlobLen
;
344 blob_path
= lib
->settings
->get_str(lib
->settings
,
345 "libimcv.plugins.imc-attestation.aik_blob", NULL
);
349 /* Read aik key blob from a file */
350 if ((fp
= fopen(blob_path
, "r")) == NULL
)
352 DBG1(DBG_PTS
, "unable to open AIK Blob file: %s", blob_path
);
356 fseek(fp
, 0, SEEK_END
);
357 aikBlobLen
= ftell(fp
);
358 fseek(fp
, 0L, SEEK_SET
);
360 this->aik_blob
= chunk_alloc(aikBlobLen
);
361 if (fread(this->aik_blob
.ptr
, 1, aikBlobLen
, fp
))
363 DBG2(DBG_PTS
, "loaded AIK Blob from '%s'", blob_path
);
364 DBG3(DBG_PTS
, "AIK Blob: %B", &this->aik_blob
);
368 DBG1(DBG_PTS
, "unable to read AIK Blob file '%s'", blob_path
);
374 DBG1(DBG_PTS
, "AIK Blob is not available");
378 * Load an AIK certificate or public key
379 * the certificate having precedence over the public key if both are present
381 static void load_aik(private_pts_t
*this)
383 char *cert_path
, *key_path
;
385 cert_path
= lib
->settings
->get_str(lib
->settings
,
386 "libimcv.plugins.imc-attestation.aik_cert", NULL
);
387 key_path
= lib
->settings
->get_str(lib
->settings
,
388 "libimcv.plugins.imc-attestation.aik_key", NULL
);
392 this->aik
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
,
393 CERT_X509
, BUILD_FROM_FILE
,
394 cert_path
, BUILD_END
);
397 DBG2(DBG_PTS
, "loaded AIK certificate from '%s'", cert_path
);
403 this->aik
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
,
404 CERT_TRUSTED_PUBKEY
, BUILD_FROM_FILE
,
405 key_path
, BUILD_END
);
408 DBG2(DBG_PTS
, "loaded AIK public key from '%s'", key_path
);
413 DBG1(DBG_PTS
, "neither AIK certificate nor public key is available");
416 METHOD(pts_t
, get_aik
, certificate_t
*,
422 METHOD(pts_t
, set_aik
, void,
423 private_pts_t
*this, certificate_t
*aik
)
425 DESTROY_IF(this->aik
);
426 this->aik
= aik
->get_ref(aik
);
429 METHOD(pts_t
, hash_file
, bool,
430 private_pts_t
*this, hasher_t
*hasher
, char *pathname
, u_char
*hash
)
432 u_char buffer
[PTS_BUF_SIZE
];
436 file
= fopen(pathname
, "rb");
439 DBG1(DBG_PTS
," file '%s' can not be opened, %s", pathname
,
445 bytes_read
= fread(buffer
, 1, sizeof(buffer
), file
);
448 hasher
->get_hash(hasher
, chunk_create(buffer
, bytes_read
), NULL
);
452 hasher
->get_hash(hasher
, chunk_empty
, hash
);
462 * Get the relative filename of a fully qualified file pathname
464 static char* get_filename(char *pathname
)
466 char *pos
, *filename
;
468 pos
= filename
= pathname
;
469 while (pos
&& *(++pos
) != '\0')
472 pos
= strchr(filename
, '/');
477 METHOD(pts_t
, is_path_valid
, bool,
478 private_pts_t
*this, char *path
, pts_error_code_t
*error_code
)
484 if (!stat(path
, &st
))
488 else if (errno
== ENOENT
|| errno
== ENOTDIR
)
490 DBG1(DBG_PTS
, "file/directory does not exist %s", path
);
491 *error_code
= TCG_PTS_FILE_NOT_FOUND
;
493 else if (errno
== EFAULT
)
495 DBG1(DBG_PTS
, "bad address %s", path
);
496 *error_code
= TCG_PTS_INVALID_PATH
;
500 DBG1(DBG_PTS
, "error: %s occured while validating path: %s",
501 strerror(errno
), path
);
508 METHOD(pts_t
, do_measurements
, pts_file_meas_t
*,
509 private_pts_t
*this, u_int16_t request_id
, char *pathname
, bool is_directory
)
512 hash_algorithm_t hash_alg
;
513 u_char hash
[HASH_SIZE_SHA384
];
515 pts_file_meas_t
*measurements
;
517 /* Create a hasher */
518 hash_alg
= pts_meas_algo_to_hash(this->algorithm
);
519 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, hash_alg
);
522 DBG1(DBG_PTS
, "hasher %N not available", hash_algorithm_names
, hash_alg
);
526 /* Create a measurement object */
527 measurements
= pts_file_meas_create(request_id
);
529 /* Link the hash to the measurement and set the measurement length */
530 measurement
= chunk_create(hash
, hasher
->get_hash_size(hasher
));
534 enumerator_t
*enumerator
;
535 char *rel_name
, *abs_name
;
538 enumerator
= enumerator_create_directory(pathname
);
541 DBG1(DBG_PTS
," directory '%s' can not be opened, %s", pathname
,
543 hasher
->destroy(hasher
);
544 measurements
->destroy(measurements
);
547 while (enumerator
->enumerate(enumerator
, &rel_name
, &abs_name
, &st
))
549 /* measure regular files only */
550 if (S_ISREG(st
.st_mode
) && *rel_name
!= '.')
552 if (!hash_file(this, hasher
, abs_name
, hash
))
554 enumerator
->destroy(enumerator
);
555 hasher
->destroy(hasher
);
556 measurements
->destroy(measurements
);
559 DBG2(DBG_PTS
, " %#B for '%s'", &measurement
, rel_name
);
560 measurements
->add(measurements
, rel_name
, measurement
);
563 enumerator
->destroy(enumerator
);
569 if (!hash_file(this, hasher
, pathname
, hash
))
571 hasher
->destroy(hasher
);
572 measurements
->destroy(measurements
);
575 filename
= get_filename(pathname
);
576 DBG2(DBG_PTS
, " %#B for '%s'", &measurement
, filename
);
577 measurements
->add(measurements
, filename
, measurement
);
579 hasher
->destroy(hasher
);
585 * Obtain statistical information describing a file
587 static bool file_metadata(char *pathname
, pts_file_metadata_t
**entry
)
590 pts_file_metadata_t
*this;
592 this = malloc_thing(pts_file_metadata_t
);
594 if (stat(pathname
, &st
))
596 DBG1(DBG_PTS
, "Unable to obtain statistics about '%s'", pathname
);
600 if (S_ISREG(st
.st_mode
))
602 this->type
= PTS_FILE_REGULAR
;
604 else if (S_ISDIR(st
.st_mode
))
606 this->type
= PTS_FILE_DIRECTORY
;
608 else if (S_ISCHR(st
.st_mode
))
610 this->type
= PTS_FILE_CHAR_SPEC
;
612 else if (S_ISBLK(st
.st_mode
))
614 this->type
= PTS_FILE_BLOCK_SPEC
;
616 else if (S_ISFIFO(st
.st_mode
))
618 this->type
= PTS_FILE_FIFO
;
620 else if (S_ISLNK(st
.st_mode
))
622 this->type
= PTS_FILE_SYM_LINK
;
624 else if (S_ISSOCK(st
.st_mode
))
626 this->type
= PTS_FILE_SOCKET
;
630 this->type
= PTS_FILE_OTHER
;
633 this->filesize
= st
.st_size
;
634 this->created
= st
.st_ctime
;
635 this->modified
= st
.st_mtime
;
636 this->accessed
= st
.st_atime
;
637 this->owner
= st
.st_uid
;
638 this->group
= st
.st_gid
;
644 METHOD(pts_t
, get_metadata
, pts_file_meta_t
*,
645 private_pts_t
*this, char *pathname
, bool is_directory
)
647 pts_file_meta_t
*metadata
;
648 pts_file_metadata_t
*entry
;
650 /* Create a metadata object */
651 metadata
= pts_file_meta_create();
655 enumerator_t
*enumerator
;
656 char *rel_name
, *abs_name
;
659 enumerator
= enumerator_create_directory(pathname
);
662 DBG1(DBG_PTS
," directory '%s' can not be opened, %s", pathname
,
664 metadata
->destroy(metadata
);
667 while (enumerator
->enumerate(enumerator
, &rel_name
, &abs_name
, &st
))
669 /* measure regular files only */
670 if (S_ISREG(st
.st_mode
) && *rel_name
!= '.')
672 if (!file_metadata(abs_name
, &entry
))
674 enumerator
->destroy(enumerator
);
675 metadata
->destroy(metadata
);
678 entry
->filename
= strdup(rel_name
);
679 metadata
->add(metadata
, entry
);
682 enumerator
->destroy(enumerator
);
686 if (!file_metadata(pathname
, &entry
))
688 metadata
->destroy(metadata
);
691 entry
->filename
= strdup(get_filename(pathname
));
692 metadata
->add(metadata
, entry
);
698 METHOD(pts_t
, read_pcr
, bool,
699 private_pts_t
*this, u_int32_t pcr_num
, chunk_t
*output
)
701 TSS_HCONTEXT hContext
;
704 u_int32_t pcr_length
;
707 result
= Tspi_Context_Create(&hContext
);
708 if (result
!= TSS_SUCCESS
)
710 DBG1(DBG_PTS
, "TPM context could not be created: tss error 0x%x",
715 result
= Tspi_Context_Connect(hContext
, NULL
);
716 if (result
!= TSS_SUCCESS
)
720 result
= Tspi_Context_GetTpmObject (hContext
, &hTPM
);
721 if (result
!= TSS_SUCCESS
)
725 pcr_value
= chunk_alloc(PCR_LEN
);
726 result
= Tspi_TPM_PcrRead(hTPM
, pcr_num
, &pcr_length
, &pcr_value
.ptr
);
727 if (result
!= TSS_SUCCESS
)
733 *output
= chunk_clone(*output
);
735 chunk_clear(&pcr_value
);
736 DBG3(DBG_PTS
, "PCR %d value:%B", pcr_num
, output
);
737 Tspi_Context_Close(hContext
);
741 chunk_clear(&pcr_value
);
742 DBG1(DBG_PTS
, "TPM not available: tss error 0x%x", result
);
743 Tspi_Context_Close(hContext
);
747 METHOD(pts_t
, extend_pcr
, bool,
748 private_pts_t
*this, u_int32_t pcr_num
, chunk_t input
, chunk_t
*output
)
750 TSS_HCONTEXT hContext
;
753 u_int32_t pcr_length
;
756 result
= Tspi_Context_Create(&hContext
);
757 if (result
!= TSS_SUCCESS
)
759 DBG1(DBG_PTS
, "TPM context could not be created: tss error 0x%x",
763 result
= Tspi_Context_Connect(hContext
, NULL
);
764 if (result
!= TSS_SUCCESS
)
768 result
= Tspi_Context_GetTpmObject (hContext
, &hTPM
);
769 if (result
!= TSS_SUCCESS
)
774 pcr_value
= chunk_alloc(PCR_LEN
);
775 result
= Tspi_TPM_PcrExtend(hTPM
, pcr_num
, PCR_LEN
, input
.ptr
,
776 NULL
, &pcr_length
, &pcr_value
.ptr
);
777 if (result
!= TSS_SUCCESS
)
783 *output
= chunk_clone(*output
);
785 chunk_clear(&pcr_value
);
786 Tspi_Context_Close(hContext
);
787 DBG3(DBG_PTS
, "PCR %d extended with: %B", pcr_num
, &input
);
788 DBG3(DBG_PTS
, "PCR %d value after extend: %B", pcr_num
, output
);
792 chunk_clear(&pcr_value
);
793 DBG1(DBG_PTS
, "TPM not available: tss error 0x%x", result
);
794 Tspi_Context_Close(hContext
);
798 METHOD(pts_t
, quote_tpm
, bool,
799 private_pts_t
*this, u_int32_t
*pcrs
, u_int32_t num_of_pcrs
,
800 chunk_t
*pcr_composite
, chunk_t
*quote_signature
)
802 TSS_HCONTEXT hContext
;
806 TSS_HPOLICY srkUsagePolicy
;
807 TSS_UUID SRK_UUID
= TSS_UUID_SRK
;
808 BYTE secret
[] = TSS_WELL_KNOWN_SECRET
;
809 TSS_HPCRS hPcrComposite
;
810 TSS_VALIDATION valData
;
813 chunk_t pcr_comp
, quote_sign
;
815 result
= Tspi_Context_Create(&hContext
);
816 if (result
!= TSS_SUCCESS
)
818 DBG1(DBG_PTS
, "TPM context could not be created: tss error 0x%x",
822 result
= Tspi_Context_Connect(hContext
, NULL
);
823 if (result
!= TSS_SUCCESS
)
827 result
= Tspi_Context_GetTpmObject (hContext
, &hTPM
);
828 if (result
!= TSS_SUCCESS
)
833 /* Retrieve SRK from TPM and set the authentication to well known secret*/
834 result
= Tspi_Context_LoadKeyByUUID(hContext
, TSS_PS_TYPE_SYSTEM
,
836 if (result
!= TSS_SUCCESS
)
841 result
= Tspi_GetPolicyObject(hSRK
, TSS_POLICY_USAGE
, &srkUsagePolicy
);
842 if (result
!= TSS_SUCCESS
)
846 result
= Tspi_Policy_SetSecret(srkUsagePolicy
, TSS_SECRET_MODE_SHA1
,
848 if (result
!= TSS_SUCCESS
)
853 result
= Tspi_Context_LoadKeyByBlob (hContext
, hSRK
, this->aik_blob
.len
,
854 this->aik_blob
.ptr
, &hAIK
);
855 if (result
!= TSS_SUCCESS
)
860 /* Create PCR composite object */
861 result
= Tspi_Context_CreateObject(hContext
,
862 TSS_OBJECT_TYPE_PCRS
, 0, &hPcrComposite
);
863 if (result
!= TSS_SUCCESS
)
869 for (i
= 0; i
< num_of_pcrs
; i
++)
871 if (pcrs
[i
] < 0 || pcrs
[i
] >= MAX_NUM_PCR
)
873 DBG1(DBG_PTS
, "Invalid PCR number: %d", pcrs
[i
]);
876 result
= Tspi_PcrComposite_SelectPcrIndex(hPcrComposite
, pcrs
[i
]);
877 if (result
!= TSS_SUCCESS
)
883 /* Set the Validation Data */
884 valData
.ulExternalDataLength
= this->secret
.len
;
885 valData
.rgbExternalData
= (BYTE
*)this->secret
.ptr
;
889 result
= Tspi_TPM_Quote(hTPM
, hAIK
, hPcrComposite
, &valData
);
890 if (result
!= TSS_SUCCESS
)
895 /* Set output chunks */
896 pcr_comp
= chunk_alloc(HASH_SIZE_SHA1
);
897 memcpy(pcr_comp
.ptr
, valData
.rgbData
+ 8, HASH_SIZE_SHA1
);
898 *pcr_composite
= pcr_comp
;
899 *pcr_composite
= chunk_clone(*pcr_composite
);
900 DBG3(DBG_PTS
, "Hash of PCR Composite: %B",pcr_composite
);
902 quote_sign
= chunk_alloc(valData
.ulValidationDataLength
);
903 memcpy(quote_sign
.ptr
, valData
.rgbValidationData
,
904 valData
.ulValidationDataLength
);
905 *quote_signature
= quote_sign
;
906 *quote_signature
= chunk_clone(*quote_signature
);
907 DBG3(DBG_PTS
, "TOM Quote Signature: %B",quote_signature
);
909 chunk_clear("e_sign
);
910 Tspi_Context_FreeMemory(hContext
, NULL
);
911 Tspi_Context_CloseObject(hContext
, hPcrComposite
);
912 Tspi_Context_CloseObject(hContext
, hAIK
);
913 Tspi_Context_Close(hContext
);
919 Tspi_Context_FreeMemory(hContext
, NULL
);
922 Tspi_Context_CloseObject(hContext
, hPcrComposite
);
925 Tspi_Context_CloseObject(hContext
, hAIK
);
928 Tspi_Context_Close(hContext
);
930 DBG1(DBG_PTS
, "TPM not available: tss error 0x%x", result
);
935 * Comparison function for pcr_entry_t struct
937 static int pcr_entry_compare(const pcr_entry_t
*a
, const pcr_entry_t
*b
)
939 return (a
->pcr_number
- b
->pcr_number
);
942 static int pcr_entry_compare_qsort(const void *a
, const void *b
)
944 return pcr_entry_compare(*(const pcr_entry_t
*const *)a
945 , *(const pcr_entry_t
*const *)b
);
948 METHOD(pts_t
, add_pcr_entry
, void,
949 private_pts_t
*this, pcr_entry_t
*new)
956 this->pcrs
= linked_list_create();
959 e
= this->pcrs
->create_enumerator(this->pcrs
);
960 while (e
->enumerate(e
, &entry
))
962 if (entry
->pcr_number
== new->pcr_number
)
964 DBG4(DBG_PTS
, "updating already added PCR%d value",
966 this->pcrs
->remove_at(this->pcrs
, e
);
973 this->pcrs
->insert_last(this->pcrs
, new);
975 qsort(this->pcrs
, this->pcrs
->get_count(this->pcrs
),
976 sizeof(pcr_entry_t
*), pcr_entry_compare_qsort
);
980 * 1. build a TCPA_PCR_COMPOSITE structure which contains (pcrCompositeBuf)
981 * TCPA_PCR_SELECTION structure (bitmask length + bitmask)
982 * UINT32 (network order) gives the number of bytes following (pcr entries * 20)
983 * TCPA_PCRVALUE[] with the pcr values
985 * The first two bytes of the message represent the length
986 * of the bitmask that follows. The bitmask represents the
987 * requested PCRs to be quoted.
989 * TPM Main-Part 2 TPM Structures_v1.2 8.1
990 * The bitmask is in big endian order"
993 * Bit: 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 ...
994 * Pcr: 7 6 5 4 3 2 1 0 15 14 13 12 11 10 9 8 ...
996 * 2. SHA1(pcrCompositeBuf)
998 * 3. build a TCPA_QUOTE_INFO structure which contains
1000 * 4 bytes 'Q' 'U' 'O' 'T'
1001 * 20 byte SHA1 of TCPA_PCR_COMPOSITE
1005 METHOD(pts_t
, get_quote_info
, bool,
1006 private_pts_t
*this, pts_meas_algorithms_t composite_algo
,
1007 chunk_t
*out_pcr_composite
, chunk_t
*out_quote_info
)
1010 pcr_entry_t
*pcr_entry
;
1011 chunk_t pcr_composite
, hash_pcr_composite
;
1012 u_int32_t pcr_composite_len
;
1013 bio_writer_t
*writer
;
1014 u_int8_t mask_bytes
[PCR_MASK_LEN
] = {0,0,0}, i
;
1017 if (this->pcrs
->get_count(this->pcrs
) == 0)
1019 DBG1(DBG_PTS
, "PCR entries unavailable, unable to construct "
1024 pcr_composite_len
= 2 + PCR_MASK_LEN
+ 4 +
1025 this->pcrs
->get_count(this->pcrs
) * PCR_LEN
;
1027 writer
= bio_writer_create(pcr_composite_len
);
1028 /* Lenght of the bist mask field */
1029 writer
->write_uint16(writer
, PCR_MASK_LEN
);
1030 /* Bit mask indicating selected PCRs */
1031 e
= this->pcrs
->create_enumerator(this->pcrs
);
1032 while (e
->enumerate(e
, &pcr_entry
))
1034 u_int32_t index
= pcr_entry
->pcr_number
;
1035 mask_bytes
[index
/ 8] |= (1 << (index
% 8));
1039 for (i
= 0; i
< PCR_MASK_LEN
; i
++)
1041 writer
->write_uint8(writer
, mask_bytes
[i
]);
1044 /* Lenght of the pcr entries */
1045 writer
->write_uint32(writer
, this->pcrs
->get_count(this->pcrs
) * PCR_LEN
);
1046 /* Actual PCR values */
1047 e
= this->pcrs
->create_enumerator(this->pcrs
);
1048 while (e
->enumerate(e
, &pcr_entry
))
1050 writer
->write_data(writer
, chunk_create(pcr_entry
->pcr_value
, PCR_LEN
));
1055 /* PCR Composite structure */
1056 pcr_composite
= chunk_clone(writer
->get_buf(writer
));
1057 writer
->destroy(writer
);
1059 writer
= bio_writer_create(TPM_QUOTE_INFO_LEN
);
1060 /* Version number */
1061 writer
->write_uint8(writer
, 1);
1062 writer
->write_uint8(writer
, 1);
1063 writer
->write_uint8(writer
, 0);
1064 writer
->write_uint8(writer
, 0);
1066 /* Magic QUOT value, depends on TPM Ordinal */
1067 writer
->write_uint8(writer
, 'Q');
1068 writer
->write_uint8(writer
, 'U');
1069 writer
->write_uint8(writer
, 'O');
1070 writer
->write_uint8(writer
, 'T');
1072 /* Output the TPM_PCR_COMPOSITE expected from IMC */
1075 hash_algorithm_t algo
;
1077 algo
= pts_meas_algo_to_hash(composite_algo
);
1078 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, algo
);
1080 /* Hash the PCR Composite Structure */
1081 hasher
->allocate_hash(hasher
, pcr_composite
, out_pcr_composite
);
1082 DBG4(DBG_PTS
, "Hash of calculated PCR Composite: %B", out_pcr_composite
);
1083 hasher
->destroy(hasher
);
1087 *out_pcr_composite
= chunk_clone(pcr_composite
);
1088 DBG4(DBG_PTS
, "calculated PCR Composite: %B", out_pcr_composite
);
1091 /* SHA1 hash of PCR Composite to construct TPM_QUOTE_INFO */
1092 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_SHA1
);
1093 hasher
->allocate_hash(hasher
, pcr_composite
, &hash_pcr_composite
);
1094 hasher
->destroy(hasher
);
1096 writer
->write_data(writer
, hash_pcr_composite
);
1097 chunk_clear(&pcr_composite
);
1098 chunk_clear(&hash_pcr_composite
);
1100 if (!this->secret
.ptr
)
1102 DBG1(DBG_PTS
, "Secret assessment value unavailable",
1103 "unable to construct TPM Quote Info");
1104 chunk_clear(out_pcr_composite
);
1105 writer
->destroy(writer
);
1108 /* Secret assessment value 20 bytes (nonce) */
1109 writer
->write_data(writer
, this->secret
);
1110 /* TPM Quote Info */
1111 *out_quote_info
= chunk_clone(writer
->get_buf(writer
));
1112 DBG4(DBG_PTS
, "Calculated TPM Quote Info: %B", out_quote_info
);
1113 writer
->destroy(writer
);
1118 METHOD(pts_t
, verify_quote_signature
, bool,
1119 private_pts_t
*this, chunk_t data
, chunk_t signature
)
1121 public_key_t
*aik_pub_key
;
1123 aik_pub_key
= this->aik
->get_public_key(this->aik
);
1126 DBG1(DBG_PTS
, "failed to get public key from AIK certificate");
1130 if (!aik_pub_key
->verify(aik_pub_key
, SIGN_RSA_SHA1
, data
, signature
))
1132 DBG1(DBG_PTS
, "signature verification failed for TPM Quote Info");
1133 DESTROY_IF(aik_pub_key
);
1137 aik_pub_key
->destroy(aik_pub_key
);
1141 METHOD(pts_t
, destroy
, void,
1142 private_pts_t
*this)
1144 DESTROY_IF(this->aik
);
1145 DESTROY_IF(this->dh
);
1146 DESTROY_IF(this->pcrs
);
1147 free(this->initiator_nonce
.ptr
);
1148 free(this->responder_nonce
.ptr
);
1149 free(this->secret
.ptr
);
1150 free(this->platform_info
);
1151 free(this->aik_blob
.ptr
);
1152 free(this->tpm_version_info
.ptr
);
1157 * Determine Linux distribution and hardware platform
1159 static char* extract_platform_info(void)
1162 char buf
[BUF_LEN
], *pos
, *value
= NULL
;
1164 struct utsname uninfo
;
1166 /* Linux/Unix distribution release info (from http://linuxmafia.com) */
1167 const char* releases
[] = {
1168 "/etc/lsb-release", "/etc/debian_version",
1169 "/etc/SuSE-release", "/etc/novell-release",
1170 "/etc/sles-release", "/etc/redhat-release",
1171 "/etc/fedora-release", "/etc/gentoo-release",
1172 "/etc/slackware-version", "/etc/annvix-release",
1173 "/etc/arch-release", "/etc/arklinux-release",
1174 "/etc/aurox-release", "/etc/blackcat-release",
1175 "/etc/cobalt-release", "/etc/conectiva-release",
1176 "/etc/debian_release", "/etc/immunix-release",
1177 "/etc/lfs-release", "/etc/linuxppc-release",
1178 "/etc/mandrake-release", "/etc/mandriva-release",
1179 "/etc/mandrakelinux-release", "/etc/mklinux-release",
1180 "/etc/pld-release", "/etc/redhat_version",
1181 "/etc/slackware-release", "/etc/e-smith-release",
1182 "/etc/release", "/etc/sun-release",
1183 "/etc/tinysofa-release", "/etc/turbolinux-release",
1184 "/etc/ultrapenguin-release", "/etc/UnitedLinux-release",
1185 "/etc/va-release", "/etc/yellowdog-release"
1188 const char description
[] = "DISTRIB_DESCRIPTION=\"";
1190 for (i
= 0; i
< countof(releases
); i
++)
1192 file
= fopen(releases
[i
], "r");
1197 fseek(file
, 0, SEEK_END
);
1198 len
= min(ftell(file
), sizeof(buf
)-1);
1201 if (fread(buf
, 1, len
, file
) != len
)
1203 DBG1(DBG_PTS
, "failed to read file '%s'", releases
[i
]);
1209 if (i
== 0) /* LSB release */
1211 pos
= strstr(buf
, description
);
1214 DBG1(DBG_PTS
, "failed to find begin of lsb-release "
1215 "DESCRIPTION field");
1218 value
= pos
+ strlen(description
);
1219 pos
= strchr(value
, '"');
1222 DBG1(DBG_PTS
, "failed to find end of lsb-release "
1223 "DESCRIPTION field");
1230 pos
= strchr(value
, '\n');
1233 DBG1(DBG_PTS
, "failed to find end of release string");
1242 DBG1(DBG_PTS
, "no distribution release file found");
1246 if (uname(&uninfo
) < 0)
1248 DBG1(DBG_PTS
, "could not retrieve machine architecture");
1253 len
= sizeof(buf
)-1 + (pos
- buf
);
1254 strncpy(pos
, uninfo
.machine
, len
);
1256 DBG1(DBG_PTS
, "platform is '%s'", value
);
1257 return strdup(value
);
1261 * Check for a TPM by querying for TPM Version Info
1263 static bool has_tpm(private_pts_t
*this)
1265 TSS_HCONTEXT hContext
;
1268 u_int32_t version_info_len
;
1270 result
= Tspi_Context_Create(&hContext
);
1271 if (result
!= TSS_SUCCESS
)
1273 DBG1(DBG_PTS
, "TPM context could not be created: tss error 0x%x",
1277 result
= Tspi_Context_Connect(hContext
, NULL
);
1278 if (result
!= TSS_SUCCESS
)
1282 result
= Tspi_Context_GetTpmObject (hContext
, &hTPM
);
1283 if (result
!= TSS_SUCCESS
)
1287 result
= Tspi_TPM_GetCapability(hTPM
, TSS_TPMCAP_VERSION_VAL
, 0, NULL
,
1289 &this->tpm_version_info
.ptr
);
1290 this->tpm_version_info
.len
= version_info_len
;
1291 if (result
!= TSS_SUCCESS
)
1295 this->tpm_version_info
= chunk_clone(this->tpm_version_info
);
1297 Tspi_Context_FreeMemory(hContext
, NULL
);
1298 Tspi_Context_Close(hContext
);
1302 DBG1(DBG_PTS
, "TPM not available: tss error 0x%x", result
);
1303 Tspi_Context_FreeMemory(hContext
, NULL
);
1304 Tspi_Context_Close(hContext
);
1311 pts_t
*pts_create(bool is_imc
)
1313 private_pts_t
*this;
1317 .get_proto_caps
= _get_proto_caps
,
1318 .set_proto_caps
= _set_proto_caps
,
1319 .get_meas_algorithm
= _get_meas_algorithm
,
1320 .set_meas_algorithm
= _set_meas_algorithm
,
1321 .get_dh_hash_algorithm
= _get_dh_hash_algorithm
,
1322 .set_dh_hash_algorithm
= _set_dh_hash_algorithm
,
1323 .create_dh_nonce
= _create_dh_nonce
,
1324 .get_my_public_value
= _get_my_public_value
,
1325 .set_peer_public_value
= _set_peer_public_value
,
1326 .calculate_secret
= _calculate_secret
,
1327 .get_platform_info
= _get_platform_info
,
1328 .set_platform_info
= _set_platform_info
,
1329 .get_tpm_version_info
= _get_tpm_version_info
,
1330 .set_tpm_version_info
= _set_tpm_version_info
,
1331 .get_aik
= _get_aik
,
1332 .set_aik
= _set_aik
,
1333 .is_path_valid
= _is_path_valid
,
1334 .hash_file
= _hash_file
,
1335 .do_measurements
= _do_measurements
,
1336 .get_metadata
= _get_metadata
,
1337 .read_pcr
= _read_pcr
,
1338 .extend_pcr
= _extend_pcr
,
1339 .quote_tpm
= _quote_tpm
,
1340 .add_pcr_entry
= _add_pcr_entry
,
1341 .get_quote_info
= _get_quote_info
,
1342 .verify_quote_signature
= _verify_quote_signature
,
1343 .destroy
= _destroy
,
1346 .proto_caps
= PTS_PROTO_CAPS_V
,
1347 .algorithm
= PTS_MEAS_ALGO_SHA256
,
1348 .dh_hash_algorithm
= PTS_MEAS_ALGO_SHA256
,
1353 this->platform_info
= extract_platform_info();
1357 this->has_tpm
= TRUE
;
1358 this->proto_caps
|= PTS_PROTO_CAPS_T
| PTS_PROTO_CAPS_D
;
1360 load_aik_blob(this);
1365 this->proto_caps
|= PTS_PROTO_CAPS_T
| PTS_PROTO_CAPS_D
;
1368 return &this->public;