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