f528d4520b82b6d264f06e5c5abb053eb36976d0
[strongswan.git] / src / libpts / pts / pts.c
1 /*
2 * Copyright (C) 2011-2012 Sansar Choinyambuu, Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
4 *
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>.
9 *
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
13 * for more details.
14 */
15
16 #include "pts.h"
17
18 #include <utils/debug.h>
19 #include <crypto/hashers/hasher.h>
20 #include <bio/bio_writer.h>
21 #include <bio/bio_reader.h>
22
23 #ifdef TSS_TROUSERS
24 #include <trousers/tss.h>
25 #include <trousers/trousers.h>
26 #else
27 #ifndef TPM_TAG_QUOTE_INFO2
28 #define TPM_TAG_QUOTE_INFO2 0x0036
29 #endif
30 #ifndef TPM_LOC_ZERO
31 #define TPM_LOC_ZERO 0x01
32 #endif
33 #endif
34
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <sys/utsname.h>
38 #include <libgen.h>
39 #include <unistd.h>
40 #include <errno.h>
41
42 typedef struct private_pts_t private_pts_t;
43
44 /**
45 * Private data of a pts_t object.
46 *
47 */
48 struct private_pts_t {
49
50 /**
51 * Public pts_t interface.
52 */
53 pts_t public;
54
55 /**
56 * PTS Protocol Capabilities
57 */
58 pts_proto_caps_flag_t proto_caps;
59
60 /**
61 * PTS Measurement Algorithm
62 */
63 pts_meas_algorithms_t algorithm;
64
65 /**
66 * DH Hash Algorithm
67 */
68 pts_meas_algorithms_t dh_hash_algorithm;
69
70 /**
71 * PTS Diffie-Hellman Secret
72 */
73 diffie_hellman_t *dh;
74
75 /**
76 * PTS Diffie-Hellman Initiator Nonce
77 */
78 chunk_t initiator_nonce;
79
80 /**
81 * PTS Diffie-Hellman Responder Nonce
82 */
83 chunk_t responder_nonce;
84
85 /**
86 * Secret assessment value to be used for TPM Quote as an external data
87 */
88 chunk_t secret;
89
90 /**
91 * Platform and OS Info
92 */
93 char *platform_info;
94
95 /**
96 * TRUE if IMC-PTS, FALSE if IMV-PTS
97 */
98 bool is_imc;
99
100 /**
101 * Do we have an activated TPM
102 */
103 bool has_tpm;
104
105 /**
106 * Contains a TPM_CAP_VERSION_INFO struct
107 */
108 chunk_t tpm_version_info;
109
110 /**
111 * Contains TSS Blob structure for AIK
112 */
113 chunk_t aik_blob;
114
115 /**
116 * Contains a Attestation Identity Key or Certificate
117 */
118 certificate_t *aik;
119
120 /**
121 * Shadow PCR set
122 */
123 pts_pcr_t *pcrs;
124
125 };
126
127 METHOD(pts_t, get_proto_caps, pts_proto_caps_flag_t,
128 private_pts_t *this)
129 {
130 return this->proto_caps;
131 }
132
133 METHOD(pts_t, set_proto_caps, void,
134 private_pts_t *this, pts_proto_caps_flag_t flags)
135 {
136 this->proto_caps = flags;
137 DBG2(DBG_PTS, "supported PTS protocol capabilities: %s%s%s%s%s",
138 flags & PTS_PROTO_CAPS_C ? "C" : ".",
139 flags & PTS_PROTO_CAPS_V ? "V" : ".",
140 flags & PTS_PROTO_CAPS_D ? "D" : ".",
141 flags & PTS_PROTO_CAPS_T ? "T" : ".",
142 flags & PTS_PROTO_CAPS_X ? "X" : ".");
143 }
144
145 METHOD(pts_t, get_meas_algorithm, pts_meas_algorithms_t,
146 private_pts_t *this)
147 {
148 return this->algorithm;
149 }
150
151 METHOD(pts_t, set_meas_algorithm, void,
152 private_pts_t *this, pts_meas_algorithms_t algorithm)
153 {
154 hash_algorithm_t hash_alg;
155
156 hash_alg = pts_meas_algo_to_hash(algorithm);
157 DBG2(DBG_PTS, "selected PTS measurement algorithm is %N",
158 hash_algorithm_names, hash_alg);
159 if (hash_alg != HASH_UNKNOWN)
160 {
161 this->algorithm = algorithm;
162 }
163 }
164
165 METHOD(pts_t, get_dh_hash_algorithm, pts_meas_algorithms_t,
166 private_pts_t *this)
167 {
168 return this->dh_hash_algorithm;
169 }
170
171 METHOD(pts_t, set_dh_hash_algorithm, void,
172 private_pts_t *this, pts_meas_algorithms_t algorithm)
173 {
174 hash_algorithm_t hash_alg;
175
176 hash_alg = pts_meas_algo_to_hash(algorithm);
177 DBG2(DBG_PTS, "selected DH hash algorithm is %N",
178 hash_algorithm_names, hash_alg);
179 if (hash_alg != HASH_UNKNOWN)
180 {
181 this->dh_hash_algorithm = algorithm;
182 }
183 }
184
185
186 METHOD(pts_t, create_dh_nonce, bool,
187 private_pts_t *this, pts_dh_group_t group, int nonce_len)
188 {
189 diffie_hellman_group_t dh_group;
190 chunk_t *nonce;
191 rng_t *rng;
192
193 dh_group = pts_dh_group_to_ike(group);
194 DBG2(DBG_PTS, "selected PTS DH group is %N",
195 diffie_hellman_group_names, dh_group);
196 DESTROY_IF(this->dh);
197 this->dh = lib->crypto->create_dh(lib->crypto, dh_group);
198
199 rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
200 if (!rng)
201 {
202 DBG1(DBG_PTS, "no rng available");
203 return FALSE;
204 }
205 DBG2(DBG_PTS, "nonce length is %d", nonce_len);
206 nonce = this->is_imc ? &this->responder_nonce : &this->initiator_nonce;
207 chunk_free(nonce);
208 if (!rng->allocate_bytes(rng, nonce_len, nonce))
209 {
210 DBG1(DBG_PTS, "failed to allocate nonce");
211 rng->destroy(rng);
212 return FALSE;
213 }
214 rng->destroy(rng);
215 return TRUE;
216 }
217
218 METHOD(pts_t, get_my_public_value, void,
219 private_pts_t *this, chunk_t *value, chunk_t *nonce)
220 {
221 this->dh->get_my_public_value(this->dh, value);
222 *nonce = this->is_imc ? this->responder_nonce : this->initiator_nonce;
223 }
224
225 METHOD(pts_t, set_peer_public_value, void,
226 private_pts_t *this, chunk_t value, chunk_t nonce)
227 {
228 this->dh->set_other_public_value(this->dh, value);
229
230 nonce = chunk_clone(nonce);
231 if (this->is_imc)
232 {
233 this->initiator_nonce = nonce;
234 }
235 else
236 {
237 this->responder_nonce = nonce;
238 }
239 }
240
241 METHOD(pts_t, calculate_secret, bool,
242 private_pts_t *this)
243 {
244 hasher_t *hasher;
245 hash_algorithm_t hash_alg;
246 chunk_t shared_secret;
247
248 /* Check presence of nonces */
249 if (!this->initiator_nonce.len || !this->responder_nonce.len)
250 {
251 DBG1(DBG_PTS, "initiator and/or responder nonce is not available");
252 return FALSE;
253 }
254 DBG3(DBG_PTS, "initiator nonce: %B", &this->initiator_nonce);
255 DBG3(DBG_PTS, "responder nonce: %B", &this->responder_nonce);
256
257 /* Calculate the DH secret */
258 if (this->dh->get_shared_secret(this->dh, &shared_secret) != SUCCESS)
259 {
260 DBG1(DBG_PTS, "shared DH secret computation failed");
261 return FALSE;
262 }
263 DBG3(DBG_PTS, "shared DH secret: %B", &shared_secret);
264
265 /* Calculate the secret assessment value */
266 hash_alg = pts_meas_algo_to_hash(this->dh_hash_algorithm);
267 hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
268
269 if (!hasher ||
270 !hasher->get_hash(hasher, chunk_from_chars('1'), NULL) ||
271 !hasher->get_hash(hasher, this->initiator_nonce, NULL) ||
272 !hasher->get_hash(hasher, this->responder_nonce, NULL) ||
273 !hasher->allocate_hash(hasher, shared_secret, &this->secret))
274 {
275 DESTROY_IF(hasher);
276 return FALSE;
277 }
278 hasher->destroy(hasher);
279
280 /* The DH secret must be destroyed */
281 chunk_clear(&shared_secret);
282
283 /*
284 * Truncate the hash to 20 bytes to fit the ExternalData
285 * argument of the TPM Quote command
286 */
287 this->secret.len = min(this->secret.len, 20);
288 DBG3(DBG_PTS, "secret assessment value: %B", &this->secret);
289 return TRUE;
290 }
291
292 #ifdef TSS_TROUSERS
293
294 /**
295 * Print TPM 1.2 Version Info
296 */
297 static void print_tpm_version_info(private_pts_t *this)
298 {
299 TPM_CAP_VERSION_INFO versionInfo;
300 UINT64 offset = 0;
301 TSS_RESULT result;
302
303 result = Trspi_UnloadBlob_CAP_VERSION_INFO(&offset,
304 this->tpm_version_info.ptr, &versionInfo);
305 if (result != TSS_SUCCESS)
306 {
307 DBG1(DBG_PTS, "could not parse tpm version info: tss error 0x%x",
308 result);
309 }
310 else
311 {
312 DBG2(DBG_PTS, "TPM 1.2 Version Info: Chip Version: %hhu.%hhu.%hhu.%hhu,"
313 " Spec Level: %hu, Errata Rev: %hhu, Vendor ID: %.4s [%.*s]",
314 versionInfo.version.major, versionInfo.version.minor,
315 versionInfo.version.revMajor, versionInfo.version.revMinor,
316 versionInfo.specLevel, versionInfo.errataRev,
317 versionInfo.tpmVendorID, versionInfo.vendorSpecificSize,
318 versionInfo.vendorSpecificSize ?
319 (char*)versionInfo.vendorSpecific : "");
320 }
321 free(versionInfo.vendorSpecific);
322 }
323
324 #else
325
326 static void print_tpm_version_info(private_pts_t *this)
327 {
328 DBG1(DBG_PTS, "unknown TPM version: no TSS implementation available");
329 }
330
331 #endif /* TSS_TROUSERS */
332
333 METHOD(pts_t, get_platform_info, char*,
334 private_pts_t *this)
335 {
336 return this->platform_info;
337 }
338
339 METHOD(pts_t, set_platform_info, void,
340 private_pts_t *this, chunk_t name, chunk_t version)
341 {
342 int len = name.len + 1 + version.len + 1;
343
344 /* platform info is a concatenation of OS name and OS version */
345 free(this->platform_info);
346 this->platform_info = malloc(len);
347 snprintf(this->platform_info, len, "%.*s %.*s", (int)name.len, name.ptr,
348 (int)version.len, version.ptr);
349 }
350
351 METHOD(pts_t, get_tpm_version_info, bool,
352 private_pts_t *this, chunk_t *info)
353 {
354 if (!this->has_tpm)
355 {
356 return FALSE;
357 }
358 *info = this->tpm_version_info;
359 print_tpm_version_info(this);
360 return TRUE;
361 }
362
363 METHOD(pts_t, set_tpm_version_info, void,
364 private_pts_t *this, chunk_t info)
365 {
366 this->tpm_version_info = chunk_clone(info);
367 print_tpm_version_info(this);
368 }
369
370 /**
371 * Load an AIK Blob (TSS_TSPATTRIB_KEYBLOB_BLOB attribute)
372 */
373 static void load_aik_blob(private_pts_t *this)
374 {
375 char *blob_path;
376 FILE *fp;
377 u_int32_t aikBlobLen;
378
379 blob_path = lib->settings->get_str(lib->settings,
380 "%s.plugins.imc-attestation.aik_blob", NULL, lib->ns);
381
382 if (blob_path)
383 {
384 /* Read aik key blob from a file */
385 if ((fp = fopen(blob_path, "r")) == NULL)
386 {
387 DBG1(DBG_PTS, "unable to open AIK Blob file: %s", blob_path);
388 return;
389 }
390
391 fseek(fp, 0, SEEK_END);
392 aikBlobLen = ftell(fp);
393 fseek(fp, 0L, SEEK_SET);
394
395 this->aik_blob = chunk_alloc(aikBlobLen);
396 if (fread(this->aik_blob.ptr, 1, aikBlobLen, fp))
397 {
398 DBG2(DBG_PTS, "loaded AIK Blob from '%s'", blob_path);
399 DBG3(DBG_PTS, "AIK Blob: %B", &this->aik_blob);
400 }
401 else
402 {
403 DBG1(DBG_PTS, "unable to read AIK Blob file '%s'", blob_path);
404 }
405 fclose(fp);
406 return;
407 }
408
409 DBG1(DBG_PTS, "AIK Blob is not available");
410 }
411
412 /**
413 * Load an AIK certificate or public key
414 * the certificate having precedence over the public key if both are present
415 */
416 static void load_aik(private_pts_t *this)
417 {
418 char *cert_path, *key_path;
419
420 cert_path = lib->settings->get_str(lib->settings,
421 "%s.plugins.imc-attestation.aik_cert", NULL, lib->ns);
422 key_path = lib->settings->get_str(lib->settings,
423 "%s.plugins.imc-attestation.aik_key", NULL, lib->ns);
424
425 if (cert_path)
426 {
427 this->aik = lib->creds->create(lib->creds, CRED_CERTIFICATE,
428 CERT_X509, BUILD_FROM_FILE,
429 cert_path, BUILD_END);
430 if (this->aik)
431 {
432 DBG2(DBG_PTS, "loaded AIK certificate from '%s'", cert_path);
433 return;
434 }
435 }
436 if (key_path)
437 {
438 this->aik = lib->creds->create(lib->creds, CRED_CERTIFICATE,
439 CERT_TRUSTED_PUBKEY, BUILD_FROM_FILE,
440 key_path, BUILD_END);
441 if (this->aik)
442 {
443 DBG2(DBG_PTS, "loaded AIK public key from '%s'", key_path);
444 return;
445 }
446 }
447
448 DBG1(DBG_PTS, "neither AIK certificate nor public key is available");
449 }
450
451 METHOD(pts_t, get_aik, certificate_t*,
452 private_pts_t *this)
453 {
454 return this->aik;
455 }
456
457 METHOD(pts_t, set_aik, void,
458 private_pts_t *this, certificate_t *aik)
459 {
460 DESTROY_IF(this->aik);
461 this->aik = aik->get_ref(aik);
462 }
463
464 METHOD(pts_t, get_aik_keyid, bool,
465 private_pts_t *this, chunk_t *keyid)
466 {
467 public_key_t *public;
468 bool success;
469
470 if (!this->aik)
471 {
472 DBG1(DBG_PTS, "no AIK certificate available");
473 return FALSE;
474 }
475 public = this->aik->get_public_key(this->aik);
476 if (!public)
477 {
478 DBG1(DBG_PTS, "no AIK public key available");
479 return FALSE;
480 }
481 success = public->get_fingerprint(public, KEYID_PUBKEY_INFO_SHA1, keyid);
482 if (!success)
483 {
484 DBG1(DBG_PTS, "no SHA-1 AIK public key info ID available");
485 }
486 public->destroy(public);
487
488 return success;
489 }
490
491 METHOD(pts_t, is_path_valid, bool,
492 private_pts_t *this, char *path, pts_error_code_t *error_code)
493 {
494 struct stat st;
495
496 *error_code = 0;
497
498 if (!stat(path, &st))
499 {
500 return TRUE;
501 }
502 else if (errno == ENOENT || errno == ENOTDIR)
503 {
504 DBG1(DBG_PTS, "file/directory does not exist %s", path);
505 *error_code = TCG_PTS_FILE_NOT_FOUND;
506 }
507 else if (errno == EFAULT)
508 {
509 DBG1(DBG_PTS, "bad address %s", path);
510 *error_code = TCG_PTS_INVALID_PATH;
511 }
512 else
513 {
514 DBG1(DBG_PTS, "error: %s occurred while validating path: %s",
515 strerror(errno), path);
516 return FALSE;
517 }
518
519 return TRUE;
520 }
521
522 /**
523 * Obtain statistical information describing a file
524 */
525 static bool file_metadata(char *pathname, pts_file_metadata_t **entry)
526 {
527 struct stat st;
528 pts_file_metadata_t *this;
529
530 this = malloc_thing(pts_file_metadata_t);
531
532 if (stat(pathname, &st))
533 {
534 DBG1(DBG_PTS, "unable to obtain statistics about '%s'", pathname);
535 free(this);
536 return FALSE;
537 }
538
539 if (S_ISREG(st.st_mode))
540 {
541 this->type = PTS_FILE_REGULAR;
542 }
543 else if (S_ISDIR(st.st_mode))
544 {
545 this->type = PTS_FILE_DIRECTORY;
546 }
547 else if (S_ISCHR(st.st_mode))
548 {
549 this->type = PTS_FILE_CHAR_SPEC;
550 }
551 else if (S_ISBLK(st.st_mode))
552 {
553 this->type = PTS_FILE_BLOCK_SPEC;
554 }
555 else if (S_ISFIFO(st.st_mode))
556 {
557 this->type = PTS_FILE_FIFO;
558 }
559 else if (S_ISLNK(st.st_mode))
560 {
561 this->type = PTS_FILE_SYM_LINK;
562 }
563 else if (S_ISSOCK(st.st_mode))
564 {
565 this->type = PTS_FILE_SOCKET;
566 }
567 else
568 {
569 this->type = PTS_FILE_OTHER;
570 }
571
572 this->filesize = st.st_size;
573 this->created = st.st_ctime;
574 this->modified = st.st_mtime;
575 this->accessed = st.st_atime;
576 this->owner = st.st_uid;
577 this->group = st.st_gid;
578
579 *entry = this;
580 return TRUE;
581 }
582
583 METHOD(pts_t, get_metadata, pts_file_meta_t*,
584 private_pts_t *this, char *pathname, bool is_directory)
585 {
586 pts_file_meta_t *metadata;
587 pts_file_metadata_t *entry;
588
589 /* Create a metadata object */
590 metadata = pts_file_meta_create();
591
592 if (is_directory)
593 {
594 enumerator_t *enumerator;
595 char *rel_name, *abs_name;
596 struct stat st;
597
598 enumerator = enumerator_create_directory(pathname);
599 if (!enumerator)
600 {
601 DBG1(DBG_PTS," directory '%s' can not be opened, %s", pathname,
602 strerror(errno));
603 metadata->destroy(metadata);
604 return NULL;
605 }
606 while (enumerator->enumerate(enumerator, &rel_name, &abs_name, &st))
607 {
608 /* measure regular files only */
609 if (S_ISREG(st.st_mode) && *rel_name != '.')
610 {
611 if (!file_metadata(abs_name, &entry))
612 {
613 enumerator->destroy(enumerator);
614 metadata->destroy(metadata);
615 return NULL;
616 }
617 entry->filename = strdup(rel_name);
618 metadata->add(metadata, entry);
619 }
620 }
621 enumerator->destroy(enumerator);
622 }
623 else
624 {
625 if (!file_metadata(pathname, &entry))
626 {
627 metadata->destroy(metadata);
628 return NULL;
629 }
630 entry->filename = strdup(basename(pathname));
631 metadata->add(metadata, entry);
632 }
633
634 return metadata;
635 }
636
637
638 #ifdef TSS_TROUSERS
639
640 METHOD(pts_t, read_pcr, bool,
641 private_pts_t *this, u_int32_t pcr_num, chunk_t *pcr_value)
642 {
643 TSS_HCONTEXT hContext;
644 TSS_HTPM hTPM;
645 TSS_RESULT result;
646 chunk_t rgbPcrValue;
647
648 bool success = FALSE;
649
650 result = Tspi_Context_Create(&hContext);
651 if (result != TSS_SUCCESS)
652 {
653 DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x", result);
654 return FALSE;
655 }
656
657 result = Tspi_Context_Connect(hContext, NULL);
658 if (result != TSS_SUCCESS)
659 {
660 goto err;
661 }
662 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
663 if (result != TSS_SUCCESS)
664 {
665 goto err;
666 }
667 result = Tspi_TPM_PcrRead(hTPM, pcr_num, (UINT32*)&rgbPcrValue.len, &rgbPcrValue.ptr);
668 if (result != TSS_SUCCESS)
669 {
670 goto err;
671 }
672 *pcr_value = chunk_clone(rgbPcrValue);
673 DBG3(DBG_PTS, "PCR %d value:%B", pcr_num, pcr_value);
674 success = TRUE;
675
676 err:
677 if (!success)
678 {
679 DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result);
680 }
681 Tspi_Context_FreeMemory(hContext, NULL);
682 Tspi_Context_Close(hContext);
683
684 return success;
685 }
686
687 METHOD(pts_t, extend_pcr, bool,
688 private_pts_t *this, u_int32_t pcr_num, chunk_t input, chunk_t *output)
689 {
690 TSS_HCONTEXT hContext;
691 TSS_HTPM hTPM;
692 TSS_RESULT result;
693 u_int32_t pcr_length;
694 chunk_t pcr_value = chunk_empty;
695
696 result = Tspi_Context_Create(&hContext);
697 if (result != TSS_SUCCESS)
698 {
699 DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x",
700 result);
701 return FALSE;
702 }
703 result = Tspi_Context_Connect(hContext, NULL);
704 if (result != TSS_SUCCESS)
705 {
706 goto err;
707 }
708 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
709 if (result != TSS_SUCCESS)
710 {
711 goto err;
712 }
713
714 pcr_value = chunk_alloc(PTS_PCR_LEN);
715 result = Tspi_TPM_PcrExtend(hTPM, pcr_num, PTS_PCR_LEN, input.ptr,
716 NULL, &pcr_length, &pcr_value.ptr);
717 if (result != TSS_SUCCESS)
718 {
719 goto err;
720 }
721
722 *output = pcr_value;
723 *output = chunk_clone(*output);
724
725 DBG3(DBG_PTS, "PCR %d extended with: %B", pcr_num, &input);
726 DBG3(DBG_PTS, "PCR %d value after extend: %B", pcr_num, output);
727
728 chunk_clear(&pcr_value);
729 Tspi_Context_FreeMemory(hContext, NULL);
730 Tspi_Context_Close(hContext);
731
732 return TRUE;
733
734 err:
735 DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result);
736
737 chunk_clear(&pcr_value);
738 Tspi_Context_FreeMemory(hContext, NULL);
739 Tspi_Context_Close(hContext);
740
741 return FALSE;
742 }
743
744 METHOD(pts_t, quote_tpm, bool,
745 private_pts_t *this, bool use_quote2, chunk_t *pcr_comp, chunk_t *quote_sig)
746 {
747 TSS_HCONTEXT hContext;
748 TSS_HTPM hTPM;
749 TSS_HKEY hAIK;
750 TSS_HKEY hSRK;
751 TSS_HPOLICY srkUsagePolicy;
752 TSS_UUID SRK_UUID = TSS_UUID_SRK;
753 BYTE secret[] = TSS_WELL_KNOWN_SECRET;
754 TSS_HPCRS hPcrComposite;
755 TSS_VALIDATION valData;
756 TSS_RESULT result;
757 chunk_t quote_info;
758 BYTE* versionInfo;
759 u_int32_t versionInfoSize, pcr;
760 enumerator_t *enumerator;
761 bool success = FALSE;
762
763 result = Tspi_Context_Create(&hContext);
764 if (result != TSS_SUCCESS)
765 {
766 DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x",
767 result);
768 return FALSE;
769 }
770 result = Tspi_Context_Connect(hContext, NULL);
771 if (result != TSS_SUCCESS)
772 {
773 goto err1;
774 }
775 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
776 if (result != TSS_SUCCESS)
777 {
778 goto err1;
779 }
780
781 /* Retrieve SRK from TPM and set the authentication to well known secret*/
782 result = Tspi_Context_LoadKeyByUUID(hContext, TSS_PS_TYPE_SYSTEM,
783 SRK_UUID, &hSRK);
784 if (result != TSS_SUCCESS)
785 {
786 goto err1;
787 }
788
789 result = Tspi_GetPolicyObject(hSRK, TSS_POLICY_USAGE, &srkUsagePolicy);
790 if (result != TSS_SUCCESS)
791 {
792 goto err1;
793 }
794 result = Tspi_Policy_SetSecret(srkUsagePolicy, TSS_SECRET_MODE_SHA1,
795 20, secret);
796 if (result != TSS_SUCCESS)
797 {
798 goto err1;
799 }
800
801 result = Tspi_Context_LoadKeyByBlob (hContext, hSRK, this->aik_blob.len,
802 this->aik_blob.ptr, &hAIK);
803 if (result != TSS_SUCCESS)
804 {
805 goto err1;
806 }
807
808 /* Create PCR composite object */
809 result = use_quote2 ?
810 Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_PCRS,
811 TSS_PCRS_STRUCT_INFO_SHORT, &hPcrComposite) :
812 Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_PCRS,
813 TSS_PCRS_STRUCT_DEFAULT, &hPcrComposite);
814 if (result != TSS_SUCCESS)
815 {
816 goto err2;
817 }
818
819 /* Select PCRs */
820 enumerator = this->pcrs->create_enumerator(this->pcrs);
821 while (enumerator->enumerate(enumerator, &pcr))
822 {
823 result = use_quote2 ?
824 Tspi_PcrComposite_SelectPcrIndexEx(hPcrComposite, pcr,
825 TSS_PCRS_DIRECTION_RELEASE) :
826 Tspi_PcrComposite_SelectPcrIndex(hPcrComposite, pcr);
827 if (result != TSS_SUCCESS)
828 {
829 break;
830 }
831 }
832 enumerator->destroy(enumerator);
833
834 if (result != TSS_SUCCESS)
835 {
836 goto err3;
837 }
838
839 /* Set the Validation Data */
840 valData.ulExternalDataLength = this->secret.len;
841 valData.rgbExternalData = (BYTE *)this->secret.ptr;
842
843
844 /* TPM Quote */
845 result = use_quote2 ?
846 Tspi_TPM_Quote2(hTPM, hAIK, FALSE, hPcrComposite, &valData,
847 &versionInfoSize, &versionInfo):
848 Tspi_TPM_Quote(hTPM, hAIK, hPcrComposite, &valData);
849 if (result != TSS_SUCCESS)
850 {
851 goto err4;
852 }
853
854 /* Set output chunks */
855 *pcr_comp = chunk_alloc(HASH_SIZE_SHA1);
856
857 if (use_quote2)
858 {
859 /* TPM_Composite_Hash is last 20 bytes of TPM_Quote_Info2 structure */
860 memcpy(pcr_comp->ptr, valData.rgbData + valData.ulDataLength - HASH_SIZE_SHA1,
861 HASH_SIZE_SHA1);
862 }
863 else
864 {
865 /* TPM_Composite_Hash is 8-28th bytes of TPM_Quote_Info structure */
866 memcpy(pcr_comp->ptr, valData.rgbData + 8, HASH_SIZE_SHA1);
867 }
868 DBG3(DBG_PTS, "Hash of PCR Composite: %#B", pcr_comp);
869
870 quote_info = chunk_create(valData.rgbData, valData.ulDataLength);
871 DBG3(DBG_PTS, "TPM Quote Info: %B",&quote_info);
872
873 *quote_sig = chunk_clone(chunk_create(valData.rgbValidationData,
874 valData.ulValidationDataLength));
875 DBG3(DBG_PTS, "TPM Quote Signature: %B",quote_sig);
876
877 success = TRUE;
878
879 /* Cleanup */
880 err4:
881 Tspi_Context_FreeMemory(hContext, NULL);
882
883 err3:
884 Tspi_Context_CloseObject(hContext, hPcrComposite);
885
886 err2:
887 Tspi_Context_CloseObject(hContext, hAIK);
888
889 err1:
890 Tspi_Context_Close(hContext);
891 if (!success)
892 {
893 DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result);
894 }
895 return success;
896 }
897
898 #else /* TSS_TROUSERS */
899
900 METHOD(pts_t, read_pcr, bool,
901 private_pts_t *this, u_int32_t pcr_num, chunk_t *pcr_value)
902 {
903 return FALSE;
904 }
905
906 METHOD(pts_t, extend_pcr, bool,
907 private_pts_t *this, u_int32_t pcr_num, chunk_t input, chunk_t *output)
908 {
909 return FALSE;
910 }
911
912 METHOD(pts_t, quote_tpm, bool,
913 private_pts_t *this, bool use_quote2, chunk_t *pcr_comp, chunk_t *quote_sig)
914 {
915 return FALSE;
916 }
917
918 #endif /* TSS_TROUSERS */
919
920 /**
921 * TPM_QUOTE_INFO structure:
922 * 4 bytes of version
923 * 4 bytes 'Q' 'U' 'O' 'T'
924 * 20 byte SHA1 of TCPA_PCR_COMPOSITE
925 * 20 byte nonce
926 *
927 * TPM_QUOTE_INFO2 structure:
928 * 2 bytes Tag 0x0036 TPM_Tag_Quote_info2
929 * 4 bytes 'Q' 'U' 'T' '2'
930 * 20 bytes nonce
931 * 26 bytes PCR_INFO_SHORT
932 */
933
934 METHOD(pts_t, get_quote_info, bool,
935 private_pts_t *this, bool use_quote2, bool use_ver_info,
936 pts_meas_algorithms_t comp_hash_algo,
937 chunk_t *out_pcr_comp, chunk_t *out_quote_info)
938 {
939 chunk_t selection, pcr_comp, hash_pcr_comp;
940 bio_writer_t *writer;
941 hasher_t *hasher;
942
943 if (!this->pcrs->get_count(this->pcrs))
944 {
945 DBG1(DBG_PTS, "No extended PCR entries available, "
946 "unable to construct TPM Quote Info");
947 return FALSE;
948 }
949 if (!this->secret.ptr)
950 {
951 DBG1(DBG_PTS, "Secret assessment value unavailable, ",
952 "unable to construct TPM Quote Info");
953 return FALSE;
954 }
955 if (use_quote2 && use_ver_info && !this->tpm_version_info.ptr)
956 {
957 DBG1(DBG_PTS, "TPM Version Information unavailable, ",
958 "unable to construct TPM Quote Info2");
959 return FALSE;
960 }
961
962 pcr_comp = this->pcrs->get_composite(this->pcrs);
963
964
965 /* Output the TPM_PCR_COMPOSITE expected from IMC */
966 if (comp_hash_algo)
967 {
968 hash_algorithm_t algo;
969
970 algo = pts_meas_algo_to_hash(comp_hash_algo);
971 hasher = lib->crypto->create_hasher(lib->crypto, algo);
972
973 /* Hash the PCR Composite Structure */
974 if (!hasher || !hasher->allocate_hash(hasher, pcr_comp, out_pcr_comp))
975 {
976 DESTROY_IF(hasher);
977 free(pcr_comp.ptr);
978 return FALSE;
979 }
980 DBG3(DBG_PTS, "constructed PCR Composite hash: %#B", out_pcr_comp);
981 hasher->destroy(hasher);
982 }
983 else
984 {
985 *out_pcr_comp = chunk_clone(pcr_comp);
986 }
987
988 /* SHA1 hash of PCR Composite to construct TPM_QUOTE_INFO */
989 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
990 if (!hasher || !hasher->allocate_hash(hasher, pcr_comp, &hash_pcr_comp))
991 {
992 DESTROY_IF(hasher);
993 chunk_free(out_pcr_comp);
994 free(pcr_comp.ptr);
995 return FALSE;
996 }
997 hasher->destroy(hasher);
998
999 /* Construct TPM_QUOTE_INFO/TPM_QUOTE_INFO2 structure */
1000 writer = bio_writer_create(TPM_QUOTE_INFO_LEN);
1001
1002 if (use_quote2)
1003 {
1004 /* TPM Structure Tag */
1005 writer->write_uint16(writer, TPM_TAG_QUOTE_INFO2);
1006
1007 /* Magic QUT2 value */
1008 writer->write_data(writer, chunk_create("QUT2", 4));
1009
1010 /* Secret assessment value 20 bytes (nonce) */
1011 writer->write_data(writer, this->secret);
1012
1013 /* PCR selection */
1014 selection.ptr = pcr_comp.ptr;
1015 selection.len = 2 + this->pcrs->get_selection_size(this->pcrs);
1016 writer->write_data(writer, selection);
1017
1018 /* TPM Locality Selection */
1019 writer->write_uint8(writer, TPM_LOC_ZERO);
1020
1021 /* PCR Composite Hash */
1022 writer->write_data(writer, hash_pcr_comp);
1023
1024 if (use_ver_info)
1025 {
1026 /* TPM version Info */
1027 writer->write_data(writer, this->tpm_version_info);
1028 }
1029 }
1030 else
1031 {
1032 /* Version number */
1033 writer->write_data(writer, chunk_from_chars(1, 1, 0, 0));
1034
1035 /* Magic QUOT value */
1036 writer->write_data(writer, chunk_create("QUOT", 4));
1037
1038 /* PCR Composite Hash */
1039 writer->write_data(writer, hash_pcr_comp);
1040
1041 /* Secret assessment value 20 bytes (nonce) */
1042 writer->write_data(writer, this->secret);
1043 }
1044
1045 /* TPM Quote Info */
1046 *out_quote_info = writer->extract_buf(writer);
1047 DBG3(DBG_PTS, "constructed TPM Quote Info: %B", out_quote_info);
1048
1049 writer->destroy(writer);
1050 free(pcr_comp.ptr);
1051 free(hash_pcr_comp.ptr);
1052
1053 return TRUE;
1054 }
1055
1056 METHOD(pts_t, verify_quote_signature, bool,
1057 private_pts_t *this, chunk_t data, chunk_t signature)
1058 {
1059 public_key_t *aik_pub_key;
1060
1061 aik_pub_key = this->aik->get_public_key(this->aik);
1062 if (!aik_pub_key)
1063 {
1064 DBG1(DBG_PTS, "failed to get public key from AIK certificate");
1065 return FALSE;
1066 }
1067
1068 if (!aik_pub_key->verify(aik_pub_key, SIGN_RSA_EMSA_PKCS1_SHA1,
1069 data, signature))
1070 {
1071 DBG1(DBG_PTS, "signature verification failed for TPM Quote Info");
1072 DESTROY_IF(aik_pub_key);
1073 return FALSE;
1074 }
1075
1076 aik_pub_key->destroy(aik_pub_key);
1077 return TRUE;
1078 }
1079
1080 METHOD(pts_t, get_pcrs, pts_pcr_t*,
1081 private_pts_t *this)
1082 {
1083 return this->pcrs;
1084 }
1085
1086 METHOD(pts_t, destroy, void,
1087 private_pts_t *this)
1088 {
1089 DESTROY_IF(this->pcrs);
1090 DESTROY_IF(this->aik);
1091 DESTROY_IF(this->dh);
1092 free(this->initiator_nonce.ptr);
1093 free(this->responder_nonce.ptr);
1094 free(this->secret.ptr);
1095 free(this->platform_info);
1096 free(this->aik_blob.ptr);
1097 free(this->tpm_version_info.ptr);
1098 free(this);
1099 }
1100
1101
1102 #ifdef TSS_TROUSERS
1103
1104 /**
1105 * Check for a TPM by querying for TPM Version Info
1106 */
1107 static bool has_tpm(private_pts_t *this)
1108 {
1109 TSS_HCONTEXT hContext;
1110 TSS_HTPM hTPM;
1111 TSS_RESULT result;
1112 u_int32_t version_info_len;
1113
1114 result = Tspi_Context_Create(&hContext);
1115 if (result != TSS_SUCCESS)
1116 {
1117 DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x",
1118 result);
1119 return FALSE;
1120 }
1121 result = Tspi_Context_Connect(hContext, NULL);
1122 if (result != TSS_SUCCESS)
1123 {
1124 goto err;
1125 }
1126 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
1127 if (result != TSS_SUCCESS)
1128 {
1129 goto err;
1130 }
1131 result = Tspi_TPM_GetCapability(hTPM, TSS_TPMCAP_VERSION_VAL, 0, NULL,
1132 &version_info_len,
1133 &this->tpm_version_info.ptr);
1134 this->tpm_version_info.len = version_info_len;
1135 if (result != TSS_SUCCESS)
1136 {
1137 goto err;
1138 }
1139 this->tpm_version_info = chunk_clone(this->tpm_version_info);
1140
1141 Tspi_Context_FreeMemory(hContext, NULL);
1142 Tspi_Context_Close(hContext);
1143 return TRUE;
1144
1145 err:
1146 DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result);
1147 Tspi_Context_FreeMemory(hContext, NULL);
1148 Tspi_Context_Close(hContext);
1149 return FALSE;
1150 }
1151
1152 #else /* TSS_TROUSERS */
1153
1154 static bool has_tpm(private_pts_t *this)
1155 {
1156 return FALSE;
1157 }
1158
1159 #endif /* TSS_TROUSERS */
1160
1161
1162 /**
1163 * See header
1164 */
1165 pts_t *pts_create(bool is_imc)
1166 {
1167 private_pts_t *this;
1168 pts_pcr_t *pcrs;
1169
1170 pcrs = pts_pcr_create();
1171 if (!pcrs)
1172 {
1173 DBG1(DBG_PTS, "shadow PCR set could not be created");
1174 return NULL;
1175 }
1176
1177 INIT(this,
1178 .public = {
1179 .get_proto_caps = _get_proto_caps,
1180 .set_proto_caps = _set_proto_caps,
1181 .get_meas_algorithm = _get_meas_algorithm,
1182 .set_meas_algorithm = _set_meas_algorithm,
1183 .get_dh_hash_algorithm = _get_dh_hash_algorithm,
1184 .set_dh_hash_algorithm = _set_dh_hash_algorithm,
1185 .create_dh_nonce = _create_dh_nonce,
1186 .get_my_public_value = _get_my_public_value,
1187 .set_peer_public_value = _set_peer_public_value,
1188 .calculate_secret = _calculate_secret,
1189 .get_platform_info = _get_platform_info,
1190 .set_platform_info = _set_platform_info,
1191 .get_tpm_version_info = _get_tpm_version_info,
1192 .set_tpm_version_info = _set_tpm_version_info,
1193 .get_aik = _get_aik,
1194 .set_aik = _set_aik,
1195 .get_aik_keyid = _get_aik_keyid,
1196 .is_path_valid = _is_path_valid,
1197 .get_metadata = _get_metadata,
1198 .read_pcr = _read_pcr,
1199 .extend_pcr = _extend_pcr,
1200 .quote_tpm = _quote_tpm,
1201 .get_pcrs = _get_pcrs,
1202 .get_quote_info = _get_quote_info,
1203 .verify_quote_signature = _verify_quote_signature,
1204 .destroy = _destroy,
1205 },
1206 .is_imc = is_imc,
1207 .proto_caps = PTS_PROTO_CAPS_V,
1208 .algorithm = PTS_MEAS_ALGO_SHA256,
1209 .dh_hash_algorithm = PTS_MEAS_ALGO_SHA256,
1210 .pcrs = pcrs,
1211 );
1212
1213 if (is_imc)
1214 {
1215 if (has_tpm(this))
1216 {
1217 this->has_tpm = TRUE;
1218 this->proto_caps |= PTS_PROTO_CAPS_T | PTS_PROTO_CAPS_D;
1219 load_aik(this);
1220 load_aik_blob(this);
1221 }
1222 }
1223 else
1224 {
1225 this->proto_caps |= PTS_PROTO_CAPS_T | PTS_PROTO_CAPS_D;
1226 }
1227
1228 return &this->public;
1229 }