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>
21 #include <openssl/rsa.h>
22 #include <openssl/pem.h>
23 #include <curl/curl.h>
24 #include "fake_ek_cert.h"
26 #include <trousers/tss.h>
27 #include <trousers/trousers.h>
32 #define PTS_BUF_SIZE 4096
34 /* Size of endorsement key in bytes */
35 #define EKSIZE (2048/8)
36 /* URL of Privacy CA */
37 #define CAURL "http://www.privacyca.com/"
38 #define CERTURL CAURL "api/pca/level%d?ResponseFormat=PEM"
39 #define REQURL CAURL "api/pca/level%d?ResponseFormat=Binary"
41 /* TPM has EK Certificate */
44 typedef struct private_pts_t private_pts_t
;
47 * Private data of a pts_t object.
50 struct private_pts_t
{
53 * Public pts_t interface.
58 * PTS Protocol Capabilities
60 pts_proto_caps_flag_t proto_caps
;
63 * PTS Measurement Algorithm
65 pts_meas_algorithms_t algorithm
;
68 * Platform and OS Info
73 * Do we have an activated TPM
78 * Contains a TPM_CAP_VERSION_INFO struct
80 chunk_t tpm_version_info
;
83 * Contains a Attestation Identity Key
88 * True if AIK is naked public key, not a certificate
94 METHOD(pts_t
, get_proto_caps
, pts_proto_caps_flag_t
,
97 return this->proto_caps
;
100 METHOD(pts_t
, set_proto_caps
, void,
101 private_pts_t
*this, pts_proto_caps_flag_t flags
)
103 this->proto_caps
= flags
;
104 DBG2(DBG_TNC
, "supported PTS protocol capabilities: %s%s%s%s%s",
105 flags
& PTS_PROTO_CAPS_C ?
"C" : ".",
106 flags
& PTS_PROTO_CAPS_V ?
"V" : ".",
107 flags
& PTS_PROTO_CAPS_D ?
"D" : ".",
108 flags
& PTS_PROTO_CAPS_T ?
"T" : ".",
109 flags
& PTS_PROTO_CAPS_X ?
"X" : ".");
112 METHOD(pts_t
, get_meas_algorithm
, pts_meas_algorithms_t
,
115 return this->algorithm
;
118 METHOD(pts_t
, set_meas_algorithm
, void,
119 private_pts_t
*this, pts_meas_algorithms_t algorithm
)
121 hash_algorithm_t hash_alg
;
123 hash_alg
= pts_meas_to_hash_algorithm(algorithm
);
124 DBG2(DBG_TNC
, "selected PTS measurement algorithm is %N",
125 hash_algorithm_names
, hash_alg
);
126 if (hash_alg
!= HASH_UNKNOWN
)
128 this->algorithm
= algorithm
;
133 * Print TPM 1.2 Version Info
135 static void print_tpm_version_info(private_pts_t
*this)
137 TPM_CAP_VERSION_INFO versionInfo
;
141 result
= Trspi_UnloadBlob_CAP_VERSION_INFO(&offset
,
142 this->tpm_version_info
.ptr
, &versionInfo
);
143 if (result
!= TSS_SUCCESS
)
145 DBG1(DBG_TNC
, "could not parse tpm version info: tss error 0x%x",
150 DBG2(DBG_TNC
, "TPM 1.2 Version Info: Chip Version: %hhu.%hhu.%hhu.%hhu,"
151 " Spec Level: %hu, Errata Rev: %hhu, Vendor ID: %.4s",
152 versionInfo
.version
.major
, versionInfo
.version
.minor
,
153 versionInfo
.version
.revMajor
, versionInfo
.version
.revMinor
,
154 versionInfo
.specLevel
, versionInfo
.errataRev
,
155 versionInfo
.tpmVendorID
);
159 METHOD(pts_t
, get_platform_info
, char*,
162 return this->platform_info
;
165 METHOD(pts_t
, set_platform_info
, void,
166 private_pts_t
*this, char *info
)
168 free(this->platform_info
);
169 this->platform_info
= strdup(info
);
172 METHOD(pts_t
, get_tpm_version_info
, bool,
173 private_pts_t
*this, chunk_t
*info
)
179 *info
= this->tpm_version_info
;
180 print_tpm_version_info(this);
184 METHOD(pts_t
, set_tpm_version_info
, void,
185 private_pts_t
*this, chunk_t info
)
187 this->tpm_version_info
= chunk_clone(info
);
188 print_tpm_version_info(this);
192 * Create a fake endorsement key cert using system's actual EK
195 static TSS_RESULT
makeEKCert(TSS_HCONTEXT hContext
, TSS_HTPM hTPM
, UINT32
*pCertLen
, BYTE
**pCert
)
202 result
= Tspi_TPM_GetPubEndorsementKey (hTPM
, TRUE
, NULL
, &hPubek
);
203 if (result
!= TSS_SUCCESS
)
205 DBG1(DBG_IMC
, "Error in: Tspi_TPM_GetPubEndorsementKey");
208 result
= Tspi_GetAttribData (hPubek
, TSS_TSPATTRIB_RSAKEY_INFO
,
209 TSS_TSPATTRIB_KEYINFO_RSA_MODULUS
, &modulusLen
, &modulus
);
210 Tspi_Context_CloseObject (hContext
, hPubek
);
211 if (result
!= TSS_SUCCESS
)
213 DBG1(DBG_IMC
, "Error in: Tspi_Context_CloseObject");
216 if (modulusLen
!= 256)
218 DBG1(DBG_IMC
, "Tspi_GetAttribData modulusLen != 256");
219 Tspi_Context_FreeMemory (hContext
, modulus
);
222 /* TODO define fakeEKCert
223 * pCertLen = sizeof(fakeEKCert);
224 *pCert = malloc (*pCertLen);
225 memcpy (*pCert, fakeEKCert, *pCertLen);
226 memcpy (*pCert + 0xc6, modulus, modulusLen);
228 Tspi_Context_FreeMemory (hContext
, modulus
);
234 * Read the level N CA from privacyca.com
235 * Assume Curl library has been initialized
238 static X509
* readPCAcert (int level
)
242 FILE *f_tmp
= tmpfile();
246 hCurl
= curl_easy_init ();
247 sprintf (url
, CERTURL
, level
);
248 curl_easy_setopt (hCurl
, CURLOPT_URL
, url
);
249 curl_easy_setopt(hCurl
, CURLOPT_WRITEDATA
, (BYTE
**)f_tmp
);
251 if ((result
= curl_easy_perform(hCurl
)))
253 DBG1(DBG_IMC
, "Unable to connect to Privacy CA, curl library result code %d", result
);
259 x509
= PEM_read_X509 (f_tmp
, NULL
, NULL
, NULL
);
267 * Obtain an AIK, SRK and TPM Owner secret has to be both set to well known secret
268 * of 20 bytes of zero
270 static bool obtain_aik(private_pts_t
*this)
272 TSS_HCONTEXT hContext
;
277 TSS_HPOLICY hSrkPolicy
;
278 TSS_HPOLICY hTPMPolicy
;
279 TSS_UUID SRK_UUID
= TSS_UUID_SRK
;
280 BYTE secret
[] = TSS_WELL_KNOWN_SECRET
;
285 struct curl_slist
*slist
= NULL
;
289 BYTE
*rgbTCPAIdentityReq
;
290 UINT32 ulTCPAIdentityReqLength
;
291 UINT32 initFlags
= TSS_KEY_TYPE_IDENTITY
| TSS_KEY_SIZE_2048
|
292 TSS_KEY_VOLATILE
| TSS_KEY_NOT_MIGRATABLE
;
293 BYTE asymBuf
[EKSIZE
];
300 static int level
= 0;
302 UINT32 ekCertLen
= 0;
306 this->aik
= chunk_empty
;
307 this->is_naked_key
= false;
309 curl_global_init (CURL_GLOBAL_ALL
);
311 DBG3(DBG_IMC
, "Retrieving PCA certificate...");
313 /* TPM has EK Certificate */
318 x509
= readPCAcert (level
);
321 DBG1(DBG_IMC
, "Error reading PCA key");
324 pcaKey
= X509_get_pubkey(x509
);
325 rsa
= EVP_PKEY_get1_RSA(pcaKey
);
328 DBG1(DBG_IMC
, "Error reading RSA key from PCA");
333 result
= Tspi_Context_Create(&hContext
);
334 if (result
!= TSS_SUCCESS
)
336 DBG1(DBG_IMC
, "Error 0x%x on Tspi_Context_Create", result
);
339 result
= Tspi_Context_Connect(hContext
, NULL
);
340 if (result
!= TSS_SUCCESS
)
342 DBG1(DBG_IMC
, "Error 0x%x on Tspi_Context_Connect", result
);
345 result
= Tspi_Context_GetTpmObject (hContext
, &hTPM
);
346 if (result
!= TSS_SUCCESS
)
348 DBG1(DBG_IMC
, "Error 0x%x on Tspi_Context_GetTpmObject", result
);
351 result
= Tspi_Context_LoadKeyByUUID(hContext
,
352 TSS_PS_TYPE_SYSTEM
, SRK_UUID
, &hSRK
);
353 if (result
!= TSS_SUCCESS
)
355 DBG1(DBG_IMC
, "Error 0x%x on Tspi_Context_LoadKeyByUUID for SRK", result
);
358 result
= Tspi_GetPolicyObject(hSRK
, TSS_POLICY_USAGE
, &hSrkPolicy
);
359 if (result
!= TSS_SUCCESS
)
361 DBG1(DBG_IMC
, "Error 0x%x on Tspi_GetPolicyObject for SRK", result
);
364 result
= Tspi_Policy_SetSecret(hSrkPolicy
, TSS_SECRET_MODE_SHA1
, 20, secret
);
365 if (result
!= TSS_SUCCESS
)
367 DBG1(DBG_IMC
, "Error 0x%x on Tspi_Policy_SetSecret for SRK", result
);
370 result
= Tspi_GetPolicyObject(hTPM
, TSS_POLICY_USAGE
, &hTPMPolicy
);
371 if (result
!= TSS_SUCCESS
)
373 DBG1(DBG_IMC
, "Error 0x%x on Tspi_GetPolicyObject for TPM", result
);
376 result
= Tspi_Policy_SetSecret(hTPMPolicy
, TSS_SECRET_MODE_SHA1
, 20, secret
);
377 if (result
!= TSS_SUCCESS
)
379 DBG1(DBG_IMC
, "Error 0x%x on Tspi_Policy_SetSecret for TPM", result
);
383 result
= Tspi_Context_CreateObject(hContext
,
384 TSS_OBJECT_TYPE_RSAKEY
,
385 initFlags
, &hIdentKey
);
386 if (result
!= TSS_SUCCESS
)
388 DBG1(DBG_IMC
, "Error 0x%x on Tspi_Context_CreateObject for key", result
);
392 result
= Tspi_Context_CreateObject(hContext
,
393 TSS_OBJECT_TYPE_RSAKEY
,
394 TSS_KEY_TYPE_LEGACY
|TSS_KEY_SIZE_2048
,
396 if (result
!= TSS_SUCCESS
)
398 DBG1(DBG_IMC
, "Error 0x%x on Tspi_Context_CreateObject for PCA", result
);
401 if ((size_n
= BN_bn2bin(rsa
->n
, n
)) <= 0)
403 DBG1(DBG_IMC
, "BN_bn2bin failed");
406 result
= Tspi_SetAttribData (hPCAKey
, TSS_TSPATTRIB_RSAKEY_INFO
,
407 TSS_TSPATTRIB_KEYINFO_RSA_MODULUS
, size_n
, n
);
408 if (result
!= TSS_SUCCESS
)
410 DBG1(DBG_IMC
, "Error 0x%x on Tspi_SetAttribData for PCA modulus", result
);
413 result
= Tspi_SetAttribUint32(hPCAKey
, TSS_TSPATTRIB_KEY_INFO
,
414 TSS_TSPATTRIB_KEYINFO_ENCSCHEME
,
415 TSS_ES_RSAESPKCSV15
);
416 if (result
!= TSS_SUCCESS
)
418 DBG1(DBG_IMC
, "Error 0x%x on Tspi_SetAttribUint32 for PCA encscheme", result
);
424 result
= makeEKCert(hContext
, hTPM
, &ekCertLen
, &ekCert
);
425 if (result
!= TSS_SUCCESS
)
427 DBG1(DBG_IMC
, "Error 0x%x on makeEKCert", result
);
431 result
= Tspi_SetAttribData(hTPM
, TSS_TSPATTRIB_TPM_CREDENTIAL
,
432 TSS_TPMATTRIB_EKCERT
, ekCertLen
, ekCert
);
433 if (result
!= TSS_SUCCESS
)
435 DBG1(DBG_IMC
, "Error 0x%x on SetAttribData for EKCert", result
);
440 DBG3(DBG_IMC
, "Generating attestation identity key...");
441 result
= Tspi_TPM_CollateIdentityRequest(hTPM
, hSRK
, hPCAKey
, 0,
442 NULL
, hIdentKey
, TSS_ALG_AES
,
443 &ulTCPAIdentityReqLength
,
444 &rgbTCPAIdentityReq
);
445 if (result
!= TSS_SUCCESS
)
447 DBG1(DBG_IMC
, "Error 0x%x on Tspi_TPM_CollateIdentityRequest", result
);
451 DBG3(DBG_IMC
, "Sending request to PrivacyCA.com...");
455 hCurl
= curl_easy_init ();
456 sprintf (url
, REQURL
, level
);
457 curl_easy_setopt (hCurl
, CURLOPT_URL
, url
);
458 curl_easy_setopt (hCurl
, CURLOPT_POSTFIELDS
, (void *)rgbTCPAIdentityReq
);
459 curl_easy_setopt (hCurl
, CURLOPT_POSTFIELDSIZE
, ulTCPAIdentityReqLength
);
460 curl_easy_setopt (hCurl
, CURLOPT_WRITEDATA
, (BYTE
**)f_tmp
);
461 slist
= curl_slist_append (slist
, "Pragma: no-cache");
462 slist
= curl_slist_append (slist
, "Content-Type: application/octet-stream");
463 slist
= curl_slist_append (slist
, "Content-Transfer-Encoding: binary");
464 curl_easy_setopt (hCurl
, CURLOPT_HTTPHEADER
, slist
);
465 if ((result
= curl_easy_perform(hCurl
)))
467 DBG1(DBG_IMC
, "Unable to connect to Privacy CA, curl library result code %d", result
);
470 curl_slist_free_all(slist
);
472 DBG3(DBG_IMC
, "Processing response from PrivacyCA...");
475 symBufSize
= ftell(f_tmp
);
476 symBuf
= malloc(symBufSize
);
478 if (!fread (symBuf
, 1, symBufSize
, f_tmp
))
480 DBG1(DBG_IMC
, "Failed to read buffer");
486 asymBufSize
= sizeof(asymBuf
);
487 if (symBufSize
<= asymBufSize
)
489 DBG1(DBG_IMC
, "Bad response from PrivacyCA.com: %s", symBuf
);
493 memcpy (asymBuf
, symBuf
, asymBufSize
);
494 symBufSize
-= asymBufSize
;
495 symBuf
+= asymBufSize
;
497 result
= Tspi_Key_LoadKey (hIdentKey
, hSRK
);
498 if (result
!= TSS_SUCCESS
)
500 DBG1(DBG_IMC
, "Error 0x%x on Tspi_Key_LoadKey for AIK", result
);
504 result
= Tspi_TPM_ActivateIdentity (hTPM
, hIdentKey
, asymBufSize
, asymBuf
,
506 &credBufSize
, &credBuf
);
507 if (result
!= TSS_SUCCESS
)
509 DBG1(DBG_IMC
, "Error 0x%x on Tspi_TPM_ActivateIdentity", result
);
513 /* Output credential in PEM format */
515 x509
= d2i_X509(NULL
, (const BYTE
**)&tbuf
, credBufSize
);
518 DBG1(DBG_IMC
, "Unable to parse returned credential");
521 if (tbuf
-credBuf
!= credBufSize
)
523 DBG1(DBG_IMC
, "Note, not all data from privacy ca was parsed correctly");
532 bp
= BIO_new(BIO_s_mem());
533 PEM_write_bio_X509(bp
, x509
);
535 len
= BIO_get_mem_data(bp
, &mem_buf
);
538 memcpy(tmp
, mem_buf
, len
);
541 DBG3(DBG_IMC
,"X509 Certificate (PEM format):");
542 DBG3(DBG_IMC
,"%s", tmp
);
543 this->aik
= chunk_create(tmp
, len
+ 1);
544 this->aik
= chunk_clone(this->aik
);
551 DBG1(DBG_IMC
, "Neither AIK Key blob, nor AIK Certificate is available");
555 DBG3(DBG_IMC
, "Succeeded at obtaining AIK Certificate from Privacy CA!");
562 METHOD(pts_t
, get_aik
, bool,
563 private_pts_t
*this, chunk_t
*aik
, bool *is_naked_key
)
565 if (obtain_aik(this) != TRUE
)
571 *is_naked_key
= this->is_naked_key
;
576 METHOD(pts_t
, set_aik
, void,
577 private_pts_t
*this, chunk_t aik
, bool is_naked_key
)
579 this->aik
= chunk_clone(aik
);
580 this->is_naked_key
= is_naked_key
;
584 * Compute a hash over a file
586 static bool hash_file(hasher_t
*hasher
, char *pathname
, u_char
*hash
)
588 u_char buffer
[PTS_BUF_SIZE
];
592 file
= fopen(pathname
, "rb");
595 DBG1(DBG_IMC
," file '%s' can not be opened, %s", pathname
,
601 bytes_read
= fread(buffer
, 1, sizeof(buffer
), file
);
604 hasher
->get_hash(hasher
, chunk_create(buffer
, bytes_read
), NULL
);
608 hasher
->get_hash(hasher
, chunk_empty
, hash
);
618 * Get the relative filename of a fully qualified file pathname
620 static char* get_filename(char *pathname
)
622 char *pos
, *filename
;
624 pos
= filename
= pathname
;
625 while (pos
&& *(++pos
) != '\0')
628 pos
= strchr(filename
, '/');
633 METHOD(pts_t
, do_measurements
, pts_file_meas_t
*,
634 private_pts_t
*this, u_int16_t request_id
, char *pathname
, bool is_directory
)
637 hash_algorithm_t hash_alg
;
638 u_char hash
[HASH_SIZE_SHA384
];
640 pts_file_meas_t
*measurements
;
642 /* Create a hasher */
643 hash_alg
= pts_meas_to_hash_algorithm(this->algorithm
);
644 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, hash_alg
);
647 DBG1(DBG_IMC
, " hasher %N not available", hash_algorithm_names
, hash_alg
);
651 /* Create a measurement object */
652 measurements
= pts_file_meas_create(request_id
);
654 /* Link the hash to the measurement and set the measurement length */
655 measurement
= chunk_create(hash
, hasher
->get_hash_size(hasher
));
659 enumerator_t
*enumerator
;
660 char *rel_name
, *abs_name
;
663 enumerator
= enumerator_create_directory(pathname
);
666 DBG1(DBG_IMC
," directory '%s' can not be opened, %s", pathname
,
668 hasher
->destroy(hasher
);
669 measurements
->destroy(measurements
);
672 while (enumerator
->enumerate(enumerator
, &rel_name
, &abs_name
, &st
))
674 /* measure regular files only */
675 if (S_ISREG(st
.st_mode
) && *rel_name
!= '.')
677 if (!hash_file(hasher
, abs_name
, hash
))
679 enumerator
->destroy(enumerator
);
680 hasher
->destroy(hasher
);
681 measurements
->destroy(measurements
);
684 DBG2(DBG_IMC
, " %#B for '%s'", &measurement
, rel_name
);
685 measurements
->add(measurements
, rel_name
, measurement
);
688 enumerator
->destroy(enumerator
);
694 if (!hash_file(hasher
, pathname
, hash
))
696 hasher
->destroy(hasher
);
697 measurements
->destroy(measurements
);
700 filename
= get_filename(pathname
);
701 DBG2(DBG_IMC
, " %#B for '%s'", &measurement
, filename
);
702 measurements
->add(measurements
, filename
, measurement
);
704 hasher
->destroy(hasher
);
709 METHOD(pts_t
, destroy
, void,
712 free(this->platform_info
);
713 free(this->tpm_version_info
.ptr
);
718 * Check for a TPM by querying for TPM Version Info
720 static bool has_tpm(private_pts_t
*this)
722 TSS_HCONTEXT hContext
;
726 result
= Tspi_Context_Create(&hContext
);
727 if (result
!= TSS_SUCCESS
)
731 result
= Tspi_Context_Connect(hContext
, NULL
);
732 if (result
!= TSS_SUCCESS
)
736 result
= Tspi_Context_GetTpmObject (hContext
, &hTPM
);
737 if (result
!= TSS_SUCCESS
)
741 result
= Tspi_TPM_GetCapability(hTPM
, TSS_TPMCAP_VERSION_VAL
, 0, NULL
,
742 &this->tpm_version_info
.len
,
743 &this->tpm_version_info
.ptr
);
744 if (result
!= TSS_SUCCESS
)
748 this->tpm_version_info
= chunk_clone(this->tpm_version_info
);
752 DBG1(DBG_TNC
, "TPM not available: tss error 0x%x", result
);
759 pts_t
*pts_create(bool is_imc
)
765 .get_proto_caps
= _get_proto_caps
,
766 .set_proto_caps
= _set_proto_caps
,
767 .get_meas_algorithm
= _get_meas_algorithm
,
768 .set_meas_algorithm
= _set_meas_algorithm
,
769 .get_platform_info
= _get_platform_info
,
770 .set_platform_info
= _set_platform_info
,
771 .get_tpm_version_info
= _get_tpm_version_info
,
772 .set_tpm_version_info
= _set_tpm_version_info
,
775 .do_measurements
= _do_measurements
,
778 .proto_caps
= PTS_PROTO_CAPS_V
,
779 .algorithm
= PTS_MEAS_ALGO_SHA256
,
786 this->has_tpm
= TRUE
;
787 this->proto_caps
|= PTS_PROTO_CAPS_T
;
792 this->proto_caps
|= PTS_PROTO_CAPS_T
| PTS_PROTO_CAPS_C
;
795 return &this->public;