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 this->filename
= strdup(pathname
);
601 this->meta_length
= PTS_FILE_METADATA_SIZE
+ strlen(this->filename
);
603 if (S_ISREG(st
.st_mode
))
605 this->type
= PTS_FILE_REGULAR
;
607 else if (S_ISDIR(st
.st_mode
))
609 this->type
= PTS_FILE_DIRECTORY
;
611 else if (S_ISCHR(st
.st_mode
))
613 this->type
= PTS_FILE_CHAR_SPEC
;
615 else if (S_ISBLK(st
.st_mode
))
617 this->type
= PTS_FILE_BLOCK_SPEC
;
619 else if (S_ISFIFO(st
.st_mode
))
621 this->type
= PTS_FILE_FIFO
;
623 else if (S_ISLNK(st
.st_mode
))
625 this->type
= PTS_FILE_SYM_LINK
;
627 else if (S_ISSOCK(st
.st_mode
))
629 this->type
= PTS_FILE_SOCKET
;
633 this->type
= PTS_FILE_OTHER
;
636 this->filesize
= st
.st_size
;
637 this->created
= st
.st_ctime
;
638 this->modified
= st
.st_mtime
;
639 this->accessed
= st
.st_atime
;
640 this->owner
= st
.st_uid
;
641 this->group
= st
.st_gid
;
647 METHOD(pts_t
, get_metadata
, pts_file_meta_t
*,
648 private_pts_t
*this, char *pathname
, bool is_directory
)
650 pts_file_meta_t
*metadata
;
651 pts_file_metadata_t
*entry
;
653 /* Create a metadata object */
654 metadata
= pts_file_meta_create();
658 enumerator_t
*enumerator
;
659 char *rel_name
, *abs_name
;
662 enumerator
= enumerator_create_directory(pathname
);
665 DBG1(DBG_PTS
," directory '%s' can not be opened, %s", pathname
,
667 metadata
->destroy(metadata
);
670 while (enumerator
->enumerate(enumerator
, &rel_name
, &abs_name
, &st
))
672 /* measure regular files only */
673 if (S_ISREG(st
.st_mode
) && *rel_name
!= '.')
675 if (!file_metadata(abs_name
, &entry
))
677 enumerator
->destroy(enumerator
);
678 metadata
->destroy(metadata
);
681 metadata
->add(metadata
, entry
);
684 enumerator
->destroy(enumerator
);
690 if (!file_metadata(pathname
, &entry
))
692 metadata
->destroy(metadata
);
695 filename
= get_filename(pathname
);
696 metadata
->add(metadata
, entry
);
702 METHOD(pts_t
, read_pcr
, bool,
703 private_pts_t
*this, u_int32_t pcr_num
, chunk_t
*output
)
705 TSS_HCONTEXT hContext
;
708 u_int32_t pcr_length
;
711 result
= Tspi_Context_Create(&hContext
);
712 if (result
!= TSS_SUCCESS
)
714 DBG1(DBG_PTS
, "TPM context could not be created: tss error 0x%x",
719 result
= Tspi_Context_Connect(hContext
, NULL
);
720 if (result
!= TSS_SUCCESS
)
724 result
= Tspi_Context_GetTpmObject (hContext
, &hTPM
);
725 if (result
!= TSS_SUCCESS
)
729 pcr_value
= chunk_alloc(PCR_LEN
);
730 result
= Tspi_TPM_PcrRead(hTPM
, pcr_num
, &pcr_length
, &pcr_value
.ptr
);
731 if (result
!= TSS_SUCCESS
)
737 *output
= chunk_clone(*output
);
739 chunk_clear(&pcr_value
);
740 Tspi_Context_Close(hContext
);
741 DBG3(DBG_PTS
, "PCR %d value:%B", pcr_num
, output
);
745 chunk_clear(&pcr_value
);
746 DBG1(DBG_PTS
, "TPM not available: tss error 0x%x", result
);
747 Tspi_Context_Close(hContext
);
751 METHOD(pts_t
, extend_pcr
, bool,
752 private_pts_t
*this, u_int32_t pcr_num
, chunk_t input
, chunk_t
*output
)
754 TSS_HCONTEXT hContext
;
757 u_int32_t pcr_length
;
760 result
= Tspi_Context_Create(&hContext
);
761 if (result
!= TSS_SUCCESS
)
763 DBG1(DBG_PTS
, "TPM context could not be created: tss error 0x%x",
767 result
= Tspi_Context_Connect(hContext
, NULL
);
768 if (result
!= TSS_SUCCESS
)
772 result
= Tspi_Context_GetTpmObject (hContext
, &hTPM
);
773 if (result
!= TSS_SUCCESS
)
778 pcr_value
= chunk_alloc(PCR_LEN
);
779 result
= Tspi_TPM_PcrExtend(hTPM
, pcr_num
, PCR_LEN
, input
.ptr
,
780 NULL
, &pcr_length
, &pcr_value
.ptr
);
781 if (result
!= TSS_SUCCESS
)
787 *output
= chunk_clone(*output
);
789 chunk_clear(&pcr_value
);
790 Tspi_Context_Close(hContext
);
791 DBG3(DBG_PTS
, "PCR %d extended with: %B", pcr_num
, &input
);
792 DBG3(DBG_PTS
, "PCR %d value after extend: %B", pcr_num
, output
);
796 chunk_clear(&pcr_value
);
797 DBG1(DBG_PTS
, "TPM not available: tss error 0x%x", result
);
798 Tspi_Context_Close(hContext
);
802 METHOD(pts_t
, quote_tpm
, bool,
803 private_pts_t
*this, u_int32_t
*pcrs
, u_int32_t num_of_pcrs
,
804 chunk_t
*pcr_composite
, chunk_t
*quote_signature
)
806 TSS_HCONTEXT hContext
;
810 TSS_HPOLICY srkUsagePolicy
;
811 TSS_UUID SRK_UUID
= TSS_UUID_SRK
;
812 BYTE secret
[] = TSS_WELL_KNOWN_SECRET
;
813 TSS_HPCRS hPcrComposite
;
814 TSS_VALIDATION valData
;
817 chunk_t pcr_comp
, quote_sign
;
819 result
= Tspi_Context_Create(&hContext
);
820 if (result
!= TSS_SUCCESS
)
822 DBG1(DBG_PTS
, "TPM context could not be created: tss error 0x%x",
826 result
= Tspi_Context_Connect(hContext
, NULL
);
827 if (result
!= TSS_SUCCESS
)
831 result
= Tspi_Context_GetTpmObject (hContext
, &hTPM
);
832 if (result
!= TSS_SUCCESS
)
837 /* Retrieve SRK from TPM and set the authentication to well known secret*/
838 result
= Tspi_Context_LoadKeyByUUID(hContext
, TSS_PS_TYPE_SYSTEM
,
840 if (result
!= TSS_SUCCESS
)
845 result
= Tspi_GetPolicyObject(hSRK
, TSS_POLICY_USAGE
, &srkUsagePolicy
);
846 if (result
!= TSS_SUCCESS
)
850 result
= Tspi_Policy_SetSecret(srkUsagePolicy
, TSS_SECRET_MODE_SHA1
,
852 if (result
!= TSS_SUCCESS
)
857 result
= Tspi_Context_LoadKeyByBlob (hContext
, hSRK
, this->aik_blob
.len
,
858 this->aik_blob
.ptr
, &hAIK
);
859 if (result
!= TSS_SUCCESS
)
864 /* Create PCR composite object */
865 result
= Tspi_Context_CreateObject(hContext
,
866 TSS_OBJECT_TYPE_PCRS
, 0, &hPcrComposite
);
867 if (result
!= TSS_SUCCESS
)
873 for (i
= 0; i
< num_of_pcrs
; i
++)
875 if (pcrs
[i
] < 0 || pcrs
[i
] >= MAX_NUM_PCR
)
877 DBG1(DBG_PTS
, "Invalid PCR number: %d", pcrs
[i
]);
880 result
= Tspi_PcrComposite_SelectPcrIndex(hPcrComposite
, pcrs
[i
]);
881 if (result
!= TSS_SUCCESS
)
887 /* Set the Validation Data */
888 valData
.ulExternalDataLength
= this->secret
.len
;
889 valData
.rgbExternalData
= (BYTE
*)this->secret
.ptr
;
893 result
= Tspi_TPM_Quote(hTPM
, hAIK
, hPcrComposite
, &valData
);
894 if (result
!= TSS_SUCCESS
)
899 /* Set output chunks */
900 pcr_comp
= chunk_alloc(HASH_SIZE_SHA1
);
901 memcpy(pcr_comp
.ptr
, valData
.rgbData
+ 8, HASH_SIZE_SHA1
);
902 *pcr_composite
= pcr_comp
;
903 *pcr_composite
= chunk_clone(*pcr_composite
);
904 DBG3(DBG_PTS
, "Hash of PCR Composite: %B",pcr_composite
);
906 quote_sign
= chunk_alloc(valData
.ulValidationDataLength
);
907 memcpy(quote_sign
.ptr
, valData
.rgbValidationData
,
908 valData
.ulValidationDataLength
);
909 *quote_signature
= quote_sign
;
910 *quote_signature
= chunk_clone(*quote_signature
);
911 DBG3(DBG_PTS
, "TOM Quote Signature: %B",quote_signature
);
913 chunk_clear("e_sign
);
914 Tspi_Context_FreeMemory(hContext
, NULL
);
915 Tspi_Context_CloseObject(hContext
, hPcrComposite
);
916 Tspi_Context_CloseObject(hContext
, hAIK
);
917 Tspi_Context_Close(hContext
);
923 Tspi_Context_FreeMemory(hContext
, NULL
);
926 Tspi_Context_CloseObject(hContext
, hPcrComposite
);
929 Tspi_Context_CloseObject(hContext
, hAIK
);
932 Tspi_Context_Close(hContext
);
934 DBG1(DBG_PTS
, "TPM not available: tss error 0x%x", result
);
939 * Comparison function for pcr_entry_t struct
941 static int pcr_entry_compare(const pcr_entry_t
*a
, const pcr_entry_t
*b
)
943 return (a
->pcr_number
- b
->pcr_number
);
946 static int pcr_entry_compare_qsort(const void *a
, const void *b
)
948 return pcr_entry_compare(*(const pcr_entry_t
*const *)a
949 , *(const pcr_entry_t
*const *)b
);
952 METHOD(pts_t
, add_pcr_entry
, void,
953 private_pts_t
*this, pcr_entry_t
*new)
960 this->pcrs
= linked_list_create();
963 e
= this->pcrs
->create_enumerator(this->pcrs
);
964 while (e
->enumerate(e
, &entry
))
966 if (entry
->pcr_number
== new->pcr_number
)
968 DBG4(DBG_PTS
, "updating already added PCR%d value",
970 this->pcrs
->remove_at(this->pcrs
, e
);
977 this->pcrs
->insert_last(this->pcrs
, new);
979 qsort(this->pcrs
, this->pcrs
->get_count(this->pcrs
),
980 sizeof(pcr_entry_t
*), pcr_entry_compare_qsort
);
984 * 1. build a TCPA_PCR_COMPOSITE structure which contains (pcrCompositeBuf)
985 * TCPA_PCR_SELECTION structure (bitmask length + bitmask)
986 * UINT32 (network order) gives the number of bytes following (pcr entries * 20)
987 * TCPA_PCRVALUE[] with the pcr values
989 * The first two bytes of the message represent the length
990 * of the bitmask that follows. The bitmask represents the
991 * requested PCRs to be quoted.
993 * TPM Main-Part 2 TPM Structures_v1.2 8.1
994 * The bitmask is in big endian order"
997 * Bit: 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 ...
998 * Pcr: 7 6 5 4 3 2 1 0 15 14 13 12 11 10 9 8 ...
1000 * 2. SHA1(pcrCompositeBuf)
1002 * 3. build a TCPA_QUOTE_INFO structure which contains
1003 * 4 bytes of version
1004 * 4 bytes 'Q' 'U' 'O' 'T'
1005 * 20 byte SHA1 of TCPA_PCR_COMPOSITE
1009 METHOD(pts_t
, get_quote_info
, bool,
1010 private_pts_t
*this, chunk_t
*out_pcr_composite
, chunk_t
*out_quote_info
)
1013 pcr_entry_t
*pcr_entry
;
1014 chunk_t pcr_composite
;
1015 u_int32_t pcr_composite_len
;
1016 bio_writer_t
*writer
;
1017 u_int8_t mask_bytes
[PCR_MASK_LEN
] = {0,0,0}, i
;
1020 if (this->pcrs
->get_count(this->pcrs
) == 0)
1022 DBG1(DBG_PTS
, "PCR entries unavailable, unable to construct "
1027 pcr_composite_len
= 2 + PCR_MASK_LEN
+ 4 +
1028 this->pcrs
->get_count(this->pcrs
) * PCR_LEN
;
1030 writer
= bio_writer_create(pcr_composite_len
);
1031 /* Lenght of the bist mask field */
1032 writer
->write_uint16(writer
, PCR_MASK_LEN
);
1033 /* Bit mask indicating selected PCRs */
1034 e
= this->pcrs
->create_enumerator(this->pcrs
);
1035 while (e
->enumerate(e
, &pcr_entry
))
1037 u_int32_t index
= pcr_entry
->pcr_number
;
1038 mask_bytes
[index
/ 8] |= (1 << (index
% 8));
1042 for (i
= 0; i
< PCR_MASK_LEN
; i
++)
1044 writer
->write_uint8(writer
, mask_bytes
[i
]);
1047 /* Lenght of the pcr entries */
1048 writer
->write_uint32(writer
, this->pcrs
->get_count(this->pcrs
) * PCR_LEN
);
1049 /* Actual PCR values */
1050 e
= this->pcrs
->create_enumerator(this->pcrs
);
1051 while (e
->enumerate(e
, &pcr_entry
))
1053 writer
->write_data(writer
, chunk_create(pcr_entry
->pcr_value
, PCR_LEN
));
1058 /* PCR Composite structure */
1059 pcr_composite
= chunk_clone(writer
->get_buf(writer
));
1060 writer
->destroy(writer
);
1062 writer
= bio_writer_create(TPM_QUOTE_INFO_LEN
);
1063 /* Version number */
1064 writer
->write_uint8(writer
, 1);
1065 writer
->write_uint8(writer
, 1);
1066 writer
->write_uint8(writer
, 0);
1067 writer
->write_uint8(writer
, 0);
1069 /* Magic QUOT value, depends on TPM Ordinal */
1070 writer
->write_uint8(writer
, 'Q');
1071 writer
->write_uint8(writer
, 'U');
1072 writer
->write_uint8(writer
, 'O');
1073 writer
->write_uint8(writer
, 'T');
1075 /* SHA1 hash of PCR Composite Structure */
1076 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_SHA1
);
1077 hasher
->allocate_hash(hasher
, pcr_composite
, out_pcr_composite
);
1078 DBG4(DBG_PTS
, "Hash of calculated PCR Composite: %B", out_pcr_composite
);
1080 chunk_clear(&pcr_composite
);
1081 hasher
->destroy(hasher
);
1082 writer
->write_data(writer
, *out_pcr_composite
);
1084 if (!this->secret
.ptr
)
1086 DBG1(DBG_PTS
, "Secret assessment value unavailable",
1087 "unable to construct TPM Quote Info");
1088 chunk_clear(out_pcr_composite
);
1089 writer
->destroy(writer
);
1092 /* Secret assessment value 20 bytes (nonce) */
1093 writer
->write_data(writer
, this->secret
);
1094 /* TPM Quote Info */
1095 *out_quote_info
= chunk_clone(writer
->get_buf(writer
));
1096 DBG4(DBG_PTS
, "Calculated TPM Quote Info: %B", out_quote_info
);
1097 writer
->destroy(writer
);
1102 METHOD(pts_t
, verify_quote_signature
, bool,
1103 private_pts_t
*this, chunk_t data
, chunk_t signature
)
1105 public_key_t
*aik_pub_key
;
1106 chunk_t key_encoding
;
1107 EVP_PKEY
*pkey
= NULL
;
1111 aik_pub_key
= this->aik
->get_public_key(this->aik
);
1114 DBG1(DBG_PTS
, "failed to get public key from AIK certificate");
1118 /** Implementation using strongswan -> not working */
1119 /**if (!aik_pub_key->verify(aik_pub_key, SIGN_RSA_EMSA_PKCS1_SHA1,
1122 DBG1(DBG_PTS, "signature verification failed for TPM Quote Info");
1127 if (!aik_pub_key
->get_encoding(aik_pub_key
,
1128 PUBKEY_SPKI_ASN1_DER
, &key_encoding
))
1130 DBG1(DBG_PTS
, "failed to get encoding of AIK public key");
1134 p
= key_encoding
.ptr
;
1135 pkey
= d2i_PUBKEY(NULL
, (const unsigned char**)&p
, key_encoding
.len
);
1138 DBG1(DBG_PTS
, "failed to get EVP_PKEY object from AIK public key");
1142 rsa
= EVP_PKEY_get1_RSA(pkey
);
1145 DBG1(DBG_PTS
, "failed to get RSA object from EVP_PKEY");
1149 if (RSA_verify(NID_sha1
, data
.ptr
, data
.len
,
1150 signature
.ptr
, signature
.len
, rsa
) != 1)
1152 DBG1(DBG_PTS
, "signature verification failed for TPM Quote Info");
1157 EVP_PKEY_free(pkey
);
1158 if (key_encoding
.ptr
)
1160 chunk_clear(&key_encoding
);
1162 aik_pub_key
->destroy(aik_pub_key
);
1172 EVP_PKEY_free(pkey
);
1174 if (key_encoding
.ptr
)
1176 chunk_clear(&key_encoding
);
1178 DESTROY_IF(aik_pub_key
);
1182 METHOD(pts_t
, destroy
, void,
1183 private_pts_t
*this)
1185 DESTROY_IF(this->aik
);
1186 DESTROY_IF(this->dh
);
1187 DESTROY_IF(this->pcrs
);
1188 free(this->initiator_nonce
.ptr
);
1189 free(this->responder_nonce
.ptr
);
1190 free(this->secret
.ptr
);
1191 free(this->platform_info
);
1192 free(this->aik_blob
.ptr
);
1193 free(this->tpm_version_info
.ptr
);
1198 * Determine Linux distribution and hardware platform
1200 static char* extract_platform_info(void)
1203 char buf
[BUF_LEN
], *pos
, *value
= NULL
;
1205 struct utsname uninfo
;
1207 /* Linux/Unix distribution release info (from http://linuxmafia.com) */
1208 const char* releases
[] = {
1209 "/etc/lsb-release", "/etc/debian_version",
1210 "/etc/SuSE-release", "/etc/novell-release",
1211 "/etc/sles-release", "/etc/redhat-release",
1212 "/etc/fedora-release", "/etc/gentoo-release",
1213 "/etc/slackware-version", "/etc/annvix-release",
1214 "/etc/arch-release", "/etc/arklinux-release",
1215 "/etc/aurox-release", "/etc/blackcat-release",
1216 "/etc/cobalt-release", "/etc/conectiva-release",
1217 "/etc/debian_release", "/etc/immunix-release",
1218 "/etc/lfs-release", "/etc/linuxppc-release",
1219 "/etc/mandrake-release", "/etc/mandriva-release",
1220 "/etc/mandrakelinux-release", "/etc/mklinux-release",
1221 "/etc/pld-release", "/etc/redhat_version",
1222 "/etc/slackware-release", "/etc/e-smith-release",
1223 "/etc/release", "/etc/sun-release",
1224 "/etc/tinysofa-release", "/etc/turbolinux-release",
1225 "/etc/ultrapenguin-release", "/etc/UnitedLinux-release",
1226 "/etc/va-release", "/etc/yellowdog-release"
1229 const char description
[] = "DISTRIB_DESCRIPTION=\"";
1231 for (i
= 0; i
< countof(releases
); i
++)
1233 file
= fopen(releases
[i
], "r");
1238 fseek(file
, 0, SEEK_END
);
1239 len
= min(ftell(file
), sizeof(buf
)-1);
1242 if (fread(buf
, 1, len
, file
) != len
)
1244 DBG1(DBG_PTS
, "failed to read file '%s'", releases
[i
]);
1250 if (i
== 0) /* LSB release */
1252 pos
= strstr(buf
, description
);
1255 DBG1(DBG_PTS
, "failed to find begin of lsb-release "
1256 "DESCRIPTION field");
1259 value
= pos
+ strlen(description
);
1260 pos
= strchr(value
, '"');
1263 DBG1(DBG_PTS
, "failed to find end of lsb-release "
1264 "DESCRIPTION field");
1271 pos
= strchr(value
, '\n');
1274 DBG1(DBG_PTS
, "failed to find end of release string");
1283 DBG1(DBG_PTS
, "no distribution release file found");
1287 if (uname(&uninfo
) < 0)
1289 DBG1(DBG_PTS
, "could not retrieve machine architecture");
1294 len
= sizeof(buf
)-1 + (pos
- buf
);
1295 strncpy(pos
, uninfo
.machine
, len
);
1297 DBG1(DBG_PTS
, "platform is '%s'", value
);
1298 return strdup(value
);
1302 * Check for a TPM by querying for TPM Version Info
1304 static bool has_tpm(private_pts_t
*this)
1306 TSS_HCONTEXT hContext
;
1309 u_int32_t version_info_len
;
1311 result
= Tspi_Context_Create(&hContext
);
1312 if (result
!= TSS_SUCCESS
)
1314 DBG1(DBG_PTS
, "TPM context could not be created: tss error 0x%x",
1318 result
= Tspi_Context_Connect(hContext
, NULL
);
1319 if (result
!= TSS_SUCCESS
)
1323 result
= Tspi_Context_GetTpmObject (hContext
, &hTPM
);
1324 if (result
!= TSS_SUCCESS
)
1328 result
= Tspi_TPM_GetCapability(hTPM
, TSS_TPMCAP_VERSION_VAL
, 0, NULL
,
1330 &this->tpm_version_info
.ptr
);
1331 this->tpm_version_info
.len
= version_info_len
;
1332 if (result
!= TSS_SUCCESS
)
1336 this->tpm_version_info
= chunk_clone(this->tpm_version_info
);
1337 Tspi_Context_Close(hContext
);
1341 DBG1(DBG_PTS
, "TPM not available: tss error 0x%x", result
);
1342 Tspi_Context_Close(hContext
);
1349 pts_t
*pts_create(bool is_imc
)
1351 private_pts_t
*this;
1355 .get_proto_caps
= _get_proto_caps
,
1356 .set_proto_caps
= _set_proto_caps
,
1357 .get_meas_algorithm
= _get_meas_algorithm
,
1358 .set_meas_algorithm
= _set_meas_algorithm
,
1359 .get_dh_hash_algorithm
= _get_dh_hash_algorithm
,
1360 .set_dh_hash_algorithm
= _set_dh_hash_algorithm
,
1361 .create_dh_nonce
= _create_dh_nonce
,
1362 .get_my_public_value
= _get_my_public_value
,
1363 .set_peer_public_value
= _set_peer_public_value
,
1364 .calculate_secret
= _calculate_secret
,
1365 .get_platform_info
= _get_platform_info
,
1366 .set_platform_info
= _set_platform_info
,
1367 .get_tpm_version_info
= _get_tpm_version_info
,
1368 .set_tpm_version_info
= _set_tpm_version_info
,
1369 .get_aik
= _get_aik
,
1370 .set_aik
= _set_aik
,
1371 .is_path_valid
= _is_path_valid
,
1372 .hash_file
= _hash_file
,
1373 .do_measurements
= _do_measurements
,
1374 .get_metadata
= _get_metadata
,
1375 .read_pcr
= _read_pcr
,
1376 .extend_pcr
= _extend_pcr
,
1377 .quote_tpm
= _quote_tpm
,
1378 .add_pcr_entry
= _add_pcr_entry
,
1379 .get_quote_info
= _get_quote_info
,
1380 .verify_quote_signature
= _verify_quote_signature
,
1381 .destroy
= _destroy
,
1384 .proto_caps
= PTS_PROTO_CAPS_V
,
1385 .algorithm
= PTS_MEAS_ALGO_SHA256
,
1386 .dh_hash_algorithm
= PTS_MEAS_ALGO_SHA256
,
1391 this->platform_info
= extract_platform_info();
1395 this->has_tpm
= TRUE
;
1396 this->proto_caps
|= PTS_PROTO_CAPS_T
;
1398 load_aik_blob(this);
1403 this->proto_caps
|= PTS_PROTO_CAPS_T
| PTS_PROTO_CAPS_C
;
1406 return &this->public;