libimcv: Allow pts_t.set_peer_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, bool,
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 return TRUE;
250 }
251
252 METHOD(pts_t, calculate_secret, bool,
253 private_pts_t *this)
254 {
255 hasher_t *hasher;
256 hash_algorithm_t hash_alg;
257 chunk_t shared_secret;
258
259 /* Check presence of nonces */
260 if (!this->initiator_nonce.len || !this->responder_nonce.len)
261 {
262 DBG1(DBG_PTS, "initiator and/or responder nonce is not available");
263 return FALSE;
264 }
265 DBG3(DBG_PTS, "initiator nonce: %B", &this->initiator_nonce);
266 DBG3(DBG_PTS, "responder nonce: %B", &this->responder_nonce);
267
268 /* Calculate the DH secret */
269 if (!this->dh->get_shared_secret(this->dh, &shared_secret))
270 {
271 DBG1(DBG_PTS, "shared DH secret computation failed");
272 return FALSE;
273 }
274 DBG3(DBG_PTS, "shared DH secret: %B", &shared_secret);
275
276 /* Calculate the secret assessment value */
277 hash_alg = pts_meas_algo_to_hash(this->dh_hash_algorithm);
278 hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
279
280 if (!hasher ||
281 !hasher->get_hash(hasher, chunk_from_chars('1'), NULL) ||
282 !hasher->get_hash(hasher, this->initiator_nonce, NULL) ||
283 !hasher->get_hash(hasher, this->responder_nonce, NULL) ||
284 !hasher->allocate_hash(hasher, shared_secret, &this->secret))
285 {
286 DESTROY_IF(hasher);
287 return FALSE;
288 }
289 hasher->destroy(hasher);
290
291 /* The DH secret must be destroyed */
292 chunk_clear(&shared_secret);
293
294 /*
295 * Truncate the hash to 20 bytes to fit the ExternalData
296 * argument of the TPM Quote command
297 */
298 this->secret.len = min(this->secret.len, 20);
299 DBG3(DBG_PTS, "secret assessment value: %B", &this->secret);
300 return TRUE;
301 }
302
303 #ifdef TSS_TROUSERS
304
305 /**
306 * Print TPM 1.2 Version Info
307 */
308 static void print_tpm_version_info(private_pts_t *this)
309 {
310 TPM_CAP_VERSION_INFO *info;
311
312 info = (TPM_CAP_VERSION_INFO*)this->tpm_version_info.ptr;
313
314 if (this->tpm_version_info.len >=
315 sizeof(*info) - sizeof(info->vendorSpecific))
316 {
317 DBG2(DBG_PTS, "TPM Version Info: Chip Version: %u.%u.%u.%u, "
318 "Spec Level: %u, Errata Rev: %u, Vendor ID: %.4s",
319 info->version.major, info->version.minor,
320 info->version.revMajor, info->version.revMinor,
321 untoh16(&info->specLevel), info->errataRev, info->tpmVendorID);
322 }
323 else
324 {
325 DBG1(DBG_PTS, "could not parse tpm version info");
326 }
327 }
328
329 #else
330
331 static void print_tpm_version_info(private_pts_t *this)
332 {
333 DBG1(DBG_PTS, "unknown TPM version: no TSS implementation available");
334 }
335
336 #endif /* TSS_TROUSERS */
337
338 METHOD(pts_t, get_platform_id, int,
339 private_pts_t *this)
340 {
341 return this->platform_id;
342 }
343
344 METHOD(pts_t, set_platform_id, void,
345 private_pts_t *this, int pid)
346 {
347 this->platform_id = pid;
348 }
349
350 METHOD(pts_t, get_tpm_version_info, bool,
351 private_pts_t *this, chunk_t *info)
352 {
353 if (!this->has_tpm)
354 {
355 return FALSE;
356 }
357 *info = this->tpm_version_info;
358 print_tpm_version_info(this);
359 return TRUE;
360 }
361
362 METHOD(pts_t, set_tpm_version_info, void,
363 private_pts_t *this, chunk_t info)
364 {
365 this->tpm_version_info = chunk_clone(info);
366 print_tpm_version_info(this);
367 }
368
369 /**
370 * Load an AIK Blob (TSS_TSPATTRIB_KEYBLOB_BLOB attribute)
371 */
372 static void load_aik_blob(private_pts_t *this)
373 {
374 char *path;
375 chunk_t *map;
376
377 path = lib->settings->get_str(lib->settings,
378 "%s.plugins.imc-attestation.aik_blob", NULL, lib->ns);
379 if (path)
380 {
381 map = chunk_map(path, FALSE);
382 if (map)
383 {
384 DBG2(DBG_PTS, "loaded AIK Blob from '%s'", path);
385 DBG3(DBG_PTS, "AIK Blob: %B", map);
386 this->aik_blob = chunk_clone(*map);
387 chunk_unmap(map);
388 }
389 else
390 {
391 DBG1(DBG_PTS, "unable to map AIK Blob file '%s': %s",
392 path, strerror(errno));
393 }
394 }
395 else
396 {
397 DBG1(DBG_PTS, "AIK Blob is not available");
398 }
399 }
400
401 /**
402 * Load an AIK certificate or public key
403 * the certificate having precedence over the public key if both are present
404 */
405 static void load_aik(private_pts_t *this)
406 {
407 char *cert_path, *key_path;
408
409 cert_path = lib->settings->get_str(lib->settings,
410 "%s.plugins.imc-attestation.aik_cert", NULL, lib->ns);
411 key_path = lib->settings->get_str(lib->settings,
412 "%s.plugins.imc-attestation.aik_pubkey", NULL, lib->ns);
413
414 if (cert_path)
415 {
416 this->aik = lib->creds->create(lib->creds, CRED_CERTIFICATE,
417 CERT_X509, BUILD_FROM_FILE,
418 cert_path, BUILD_END);
419 if (this->aik)
420 {
421 DBG2(DBG_PTS, "loaded AIK certificate from '%s'", cert_path);
422 return;
423 }
424 }
425 if (key_path)
426 {
427 this->aik = lib->creds->create(lib->creds, CRED_CERTIFICATE,
428 CERT_TRUSTED_PUBKEY, BUILD_FROM_FILE,
429 key_path, BUILD_END);
430 if (this->aik)
431 {
432 DBG2(DBG_PTS, "loaded AIK public key from '%s'", key_path);
433 return;
434 }
435 }
436
437 DBG1(DBG_PTS, "neither AIK certificate nor public key is available");
438 }
439
440 METHOD(pts_t, get_aik, certificate_t*,
441 private_pts_t *this)
442 {
443 return this->aik;
444 }
445
446 METHOD(pts_t, set_aik, void,
447 private_pts_t *this, certificate_t *aik, int aik_id)
448 {
449 DESTROY_IF(this->aik);
450 this->aik = aik->get_ref(aik);
451 this->aik_id = aik_id;
452 }
453
454 METHOD(pts_t, get_aik_id, int,
455 private_pts_t *this)
456 {
457 return this->aik_id;
458 }
459
460 METHOD(pts_t, is_path_valid, bool,
461 private_pts_t *this, char *path, pts_error_code_t *error_code)
462 {
463 struct stat st;
464
465 *error_code = 0;
466
467 if (!stat(path, &st))
468 {
469 return TRUE;
470 }
471 else if (errno == ENOENT || errno == ENOTDIR)
472 {
473 DBG1(DBG_PTS, "file/directory does not exist %s", path);
474 *error_code = TCG_PTS_FILE_NOT_FOUND;
475 }
476 else if (errno == EFAULT)
477 {
478 DBG1(DBG_PTS, "bad address %s", path);
479 *error_code = TCG_PTS_INVALID_PATH;
480 }
481 else
482 {
483 DBG1(DBG_PTS, "error: %s occurred while validating path: %s",
484 strerror(errno), path);
485 return FALSE;
486 }
487
488 return TRUE;
489 }
490
491 /**
492 * Obtain statistical information describing a file
493 */
494 static bool file_metadata(char *pathname, pts_file_metadata_t **entry)
495 {
496 struct stat st;
497 pts_file_metadata_t *this;
498
499 this = malloc_thing(pts_file_metadata_t);
500
501 if (stat(pathname, &st))
502 {
503 DBG1(DBG_PTS, "unable to obtain statistics about '%s'", pathname);
504 free(this);
505 return FALSE;
506 }
507
508 if (S_ISREG(st.st_mode))
509 {
510 this->type = PTS_FILE_REGULAR;
511 }
512 else if (S_ISDIR(st.st_mode))
513 {
514 this->type = PTS_FILE_DIRECTORY;
515 }
516 else if (S_ISCHR(st.st_mode))
517 {
518 this->type = PTS_FILE_CHAR_SPEC;
519 }
520 else if (S_ISBLK(st.st_mode))
521 {
522 this->type = PTS_FILE_BLOCK_SPEC;
523 }
524 else if (S_ISFIFO(st.st_mode))
525 {
526 this->type = PTS_FILE_FIFO;
527 }
528 #ifndef WIN32
529 else if (S_ISLNK(st.st_mode))
530 {
531 this->type = PTS_FILE_SYM_LINK;
532 }
533 else if (S_ISSOCK(st.st_mode))
534 {
535 this->type = PTS_FILE_SOCKET;
536 }
537 #endif /* WIN32 */
538 else
539 {
540 this->type = PTS_FILE_OTHER;
541 }
542
543 this->filesize = st.st_size;
544 this->created = st.st_ctime;
545 this->modified = st.st_mtime;
546 this->accessed = st.st_atime;
547 this->owner = st.st_uid;
548 this->group = st.st_gid;
549
550 *entry = this;
551 return TRUE;
552 }
553
554 METHOD(pts_t, get_metadata, pts_file_meta_t*,
555 private_pts_t *this, char *pathname, bool is_directory)
556 {
557 pts_file_meta_t *metadata;
558 pts_file_metadata_t *entry;
559
560 /* Create a metadata object */
561 metadata = pts_file_meta_create();
562
563 if (is_directory)
564 {
565 enumerator_t *enumerator;
566 char *rel_name, *abs_name;
567 struct stat st;
568
569 enumerator = enumerator_create_directory(pathname);
570 if (!enumerator)
571 {
572 DBG1(DBG_PTS," directory '%s' can not be opened, %s", pathname,
573 strerror(errno));
574 metadata->destroy(metadata);
575 return NULL;
576 }
577 while (enumerator->enumerate(enumerator, &rel_name, &abs_name, &st))
578 {
579 /* measure regular files only */
580 if (S_ISREG(st.st_mode) && *rel_name != '.')
581 {
582 if (!file_metadata(abs_name, &entry))
583 {
584 enumerator->destroy(enumerator);
585 metadata->destroy(metadata);
586 return NULL;
587 }
588 entry->filename = strdup(rel_name);
589 metadata->add(metadata, entry);
590 }
591 }
592 enumerator->destroy(enumerator);
593 }
594 else
595 {
596 if (!file_metadata(pathname, &entry))
597 {
598 metadata->destroy(metadata);
599 return NULL;
600 }
601 entry->filename = path_basename(pathname);
602 metadata->add(metadata, entry);
603 }
604
605 return metadata;
606 }
607
608
609 #ifdef TSS_TROUSERS
610
611 METHOD(pts_t, read_pcr, bool,
612 private_pts_t *this, u_int32_t pcr_num, chunk_t *pcr_value)
613 {
614 TSS_HCONTEXT hContext;
615 TSS_HTPM hTPM;
616 TSS_RESULT result;
617 BYTE *buf;
618 UINT32 len;
619
620 bool success = FALSE;
621
622 result = Tspi_Context_Create(&hContext);
623 if (result != TSS_SUCCESS)
624 {
625 DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x", result);
626 return FALSE;
627 }
628
629 result = Tspi_Context_Connect(hContext, NULL);
630 if (result != TSS_SUCCESS)
631 {
632 goto err;
633 }
634 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
635 if (result != TSS_SUCCESS)
636 {
637 goto err;
638 }
639 result = Tspi_TPM_PcrRead(hTPM, pcr_num, &len, &buf);
640 if (result != TSS_SUCCESS)
641 {
642 goto err;
643 }
644 *pcr_value = chunk_clone(chunk_create(buf, len));
645 DBG3(DBG_PTS, "PCR %d value:%B", pcr_num, pcr_value);
646 success = TRUE;
647
648 err:
649 if (!success)
650 {
651 DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result);
652 }
653 Tspi_Context_FreeMemory(hContext, NULL);
654 Tspi_Context_Close(hContext);
655
656 return success;
657 }
658
659 METHOD(pts_t, extend_pcr, bool,
660 private_pts_t *this, u_int32_t pcr_num, chunk_t input, chunk_t *output)
661 {
662 TSS_HCONTEXT hContext;
663 TSS_HTPM hTPM;
664 TSS_RESULT result;
665 u_int32_t pcr_length;
666 chunk_t pcr_value = chunk_empty;
667
668 result = Tspi_Context_Create(&hContext);
669 if (result != TSS_SUCCESS)
670 {
671 DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x",
672 result);
673 return FALSE;
674 }
675 result = Tspi_Context_Connect(hContext, NULL);
676 if (result != TSS_SUCCESS)
677 {
678 goto err;
679 }
680 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
681 if (result != TSS_SUCCESS)
682 {
683 goto err;
684 }
685
686 pcr_value = chunk_alloc(PTS_PCR_LEN);
687 result = Tspi_TPM_PcrExtend(hTPM, pcr_num, PTS_PCR_LEN, input.ptr,
688 NULL, &pcr_length, &pcr_value.ptr);
689 if (result != TSS_SUCCESS)
690 {
691 goto err;
692 }
693
694 *output = pcr_value;
695 *output = chunk_clone(*output);
696
697 DBG3(DBG_PTS, "PCR %d extended with: %B", pcr_num, &input);
698 DBG3(DBG_PTS, "PCR %d value after extend: %B", pcr_num, output);
699
700 chunk_clear(&pcr_value);
701 Tspi_Context_FreeMemory(hContext, NULL);
702 Tspi_Context_Close(hContext);
703
704 return TRUE;
705
706 err:
707 DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result);
708
709 chunk_clear(&pcr_value);
710 Tspi_Context_FreeMemory(hContext, NULL);
711 Tspi_Context_Close(hContext);
712
713 return FALSE;
714 }
715
716 METHOD(pts_t, quote_tpm, bool,
717 private_pts_t *this, bool use_quote2, chunk_t *pcr_comp, chunk_t *quote_sig)
718 {
719 TSS_HCONTEXT hContext;
720 TSS_HTPM hTPM;
721 TSS_HKEY hAIK;
722 TSS_HKEY hSRK;
723 TSS_HPOLICY srkUsagePolicy;
724 TSS_UUID SRK_UUID = TSS_UUID_SRK;
725 BYTE secret[] = TSS_WELL_KNOWN_SECRET;
726 TSS_HPCRS hPcrComposite;
727 TSS_VALIDATION valData;
728 TSS_RESULT result;
729 chunk_t quote_info;
730 BYTE* versionInfo;
731 u_int32_t versionInfoSize, pcr;
732 enumerator_t *enumerator;
733 bool success = FALSE;
734
735 result = Tspi_Context_Create(&hContext);
736 if (result != TSS_SUCCESS)
737 {
738 DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x",
739 result);
740 return FALSE;
741 }
742 result = Tspi_Context_Connect(hContext, NULL);
743 if (result != TSS_SUCCESS)
744 {
745 goto err1;
746 }
747 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
748 if (result != TSS_SUCCESS)
749 {
750 goto err1;
751 }
752
753 /* Retrieve SRK from TPM and set the authentication to well known secret*/
754 result = Tspi_Context_LoadKeyByUUID(hContext, TSS_PS_TYPE_SYSTEM,
755 SRK_UUID, &hSRK);
756 if (result != TSS_SUCCESS)
757 {
758 goto err1;
759 }
760
761 result = Tspi_GetPolicyObject(hSRK, TSS_POLICY_USAGE, &srkUsagePolicy);
762 if (result != TSS_SUCCESS)
763 {
764 goto err1;
765 }
766 result = Tspi_Policy_SetSecret(srkUsagePolicy, TSS_SECRET_MODE_SHA1,
767 20, secret);
768 if (result != TSS_SUCCESS)
769 {
770 goto err1;
771 }
772
773 result = Tspi_Context_LoadKeyByBlob (hContext, hSRK, this->aik_blob.len,
774 this->aik_blob.ptr, &hAIK);
775 if (result != TSS_SUCCESS)
776 {
777 goto err1;
778 }
779
780 /* Create PCR composite object */
781 result = use_quote2 ?
782 Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_PCRS,
783 TSS_PCRS_STRUCT_INFO_SHORT, &hPcrComposite) :
784 Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_PCRS,
785 TSS_PCRS_STRUCT_DEFAULT, &hPcrComposite);
786 if (result != TSS_SUCCESS)
787 {
788 goto err2;
789 }
790
791 /* Select PCRs */
792 enumerator = this->pcrs->create_enumerator(this->pcrs);
793 while (enumerator->enumerate(enumerator, &pcr))
794 {
795 result = use_quote2 ?
796 Tspi_PcrComposite_SelectPcrIndexEx(hPcrComposite, pcr,
797 TSS_PCRS_DIRECTION_RELEASE) :
798 Tspi_PcrComposite_SelectPcrIndex(hPcrComposite, pcr);
799 if (result != TSS_SUCCESS)
800 {
801 break;
802 }
803 }
804 enumerator->destroy(enumerator);
805
806 if (result != TSS_SUCCESS)
807 {
808 goto err3;
809 }
810
811 /* Set the Validation Data */
812 valData.ulExternalDataLength = this->secret.len;
813 valData.rgbExternalData = (BYTE *)this->secret.ptr;
814
815
816 /* TPM Quote */
817 result = use_quote2 ?
818 Tspi_TPM_Quote2(hTPM, hAIK, FALSE, hPcrComposite, &valData,
819 &versionInfoSize, &versionInfo):
820 Tspi_TPM_Quote(hTPM, hAIK, hPcrComposite, &valData);
821 if (result != TSS_SUCCESS)
822 {
823 goto err4;
824 }
825
826 /* Set output chunks */
827 *pcr_comp = chunk_alloc(HASH_SIZE_SHA1);
828
829 if (use_quote2)
830 {
831 /* TPM_Composite_Hash is last 20 bytes of TPM_Quote_Info2 structure */
832 memcpy(pcr_comp->ptr, valData.rgbData + valData.ulDataLength - HASH_SIZE_SHA1,
833 HASH_SIZE_SHA1);
834 }
835 else
836 {
837 /* TPM_Composite_Hash is 8-28th bytes of TPM_Quote_Info structure */
838 memcpy(pcr_comp->ptr, valData.rgbData + 8, HASH_SIZE_SHA1);
839 }
840 DBG3(DBG_PTS, "Hash of PCR Composite: %#B", pcr_comp);
841
842 quote_info = chunk_create(valData.rgbData, valData.ulDataLength);
843 DBG3(DBG_PTS, "TPM Quote Info: %B",&quote_info);
844
845 *quote_sig = chunk_clone(chunk_create(valData.rgbValidationData,
846 valData.ulValidationDataLength));
847 DBG3(DBG_PTS, "TPM Quote Signature: %B",quote_sig);
848
849 success = TRUE;
850
851 /* Cleanup */
852 err4:
853 Tspi_Context_FreeMemory(hContext, NULL);
854
855 err3:
856 Tspi_Context_CloseObject(hContext, hPcrComposite);
857
858 err2:
859 Tspi_Context_CloseObject(hContext, hAIK);
860
861 err1:
862 Tspi_Context_Close(hContext);
863 if (!success)
864 {
865 DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result);
866 }
867 return success;
868 }
869
870 #else /* TSS_TROUSERS */
871
872 METHOD(pts_t, read_pcr, bool,
873 private_pts_t *this, u_int32_t pcr_num, chunk_t *pcr_value)
874 {
875 return FALSE;
876 }
877
878 METHOD(pts_t, extend_pcr, bool,
879 private_pts_t *this, u_int32_t pcr_num, chunk_t input, chunk_t *output)
880 {
881 return FALSE;
882 }
883
884 METHOD(pts_t, quote_tpm, bool,
885 private_pts_t *this, bool use_quote2, chunk_t *pcr_comp, chunk_t *quote_sig)
886 {
887 return FALSE;
888 }
889
890 #endif /* TSS_TROUSERS */
891
892 /**
893 * TPM_QUOTE_INFO structure:
894 * 4 bytes of version
895 * 4 bytes 'Q' 'U' 'O' 'T'
896 * 20 byte SHA1 of TCPA_PCR_COMPOSITE
897 * 20 byte nonce
898 *
899 * TPM_QUOTE_INFO2 structure:
900 * 2 bytes Tag 0x0036 TPM_Tag_Quote_info2
901 * 4 bytes 'Q' 'U' 'T' '2'
902 * 20 bytes nonce
903 * 26 bytes PCR_INFO_SHORT
904 */
905
906 METHOD(pts_t, get_quote_info, bool,
907 private_pts_t *this, bool use_quote2, bool use_ver_info,
908 pts_meas_algorithms_t comp_hash_algo,
909 chunk_t *out_pcr_comp, chunk_t *out_quote_info)
910 {
911 chunk_t selection, pcr_comp, hash_pcr_comp;
912 bio_writer_t *writer;
913 hasher_t *hasher;
914
915 if (!this->pcrs->get_count(this->pcrs))
916 {
917 DBG1(DBG_PTS, "No extended PCR entries available, "
918 "unable to construct TPM Quote Info");
919 return FALSE;
920 }
921 if (!this->secret.ptr)
922 {
923 DBG1(DBG_PTS, "Secret assessment value unavailable, ",
924 "unable to construct TPM Quote Info");
925 return FALSE;
926 }
927 if (use_quote2 && use_ver_info && !this->tpm_version_info.ptr)
928 {
929 DBG1(DBG_PTS, "TPM Version Information unavailable, ",
930 "unable to construct TPM Quote Info2");
931 return FALSE;
932 }
933
934 pcr_comp = this->pcrs->get_composite(this->pcrs);
935
936
937 /* Output the TPM_PCR_COMPOSITE expected from IMC */
938 if (comp_hash_algo)
939 {
940 hash_algorithm_t algo;
941
942 algo = pts_meas_algo_to_hash(comp_hash_algo);
943 hasher = lib->crypto->create_hasher(lib->crypto, algo);
944
945 /* Hash the PCR Composite Structure */
946 if (!hasher || !hasher->allocate_hash(hasher, pcr_comp, out_pcr_comp))
947 {
948 DESTROY_IF(hasher);
949 free(pcr_comp.ptr);
950 return FALSE;
951 }
952 DBG3(DBG_PTS, "constructed PCR Composite hash: %#B", out_pcr_comp);
953 hasher->destroy(hasher);
954 }
955 else
956 {
957 *out_pcr_comp = chunk_clone(pcr_comp);
958 }
959
960 /* SHA1 hash of PCR Composite to construct TPM_QUOTE_INFO */
961 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
962 if (!hasher || !hasher->allocate_hash(hasher, pcr_comp, &hash_pcr_comp))
963 {
964 DESTROY_IF(hasher);
965 chunk_free(out_pcr_comp);
966 free(pcr_comp.ptr);
967 return FALSE;
968 }
969 hasher->destroy(hasher);
970
971 /* Construct TPM_QUOTE_INFO/TPM_QUOTE_INFO2 structure */
972 writer = bio_writer_create(TPM_QUOTE_INFO_LEN);
973
974 if (use_quote2)
975 {
976 /* TPM Structure Tag */
977 writer->write_uint16(writer, TPM_TAG_QUOTE_INFO2);
978
979 /* Magic QUT2 value */
980 writer->write_data(writer, chunk_create("QUT2", 4));
981
982 /* Secret assessment value 20 bytes (nonce) */
983 writer->write_data(writer, this->secret);
984
985 /* PCR selection */
986 selection.ptr = pcr_comp.ptr;
987 selection.len = 2 + this->pcrs->get_selection_size(this->pcrs);
988 writer->write_data(writer, selection);
989
990 /* TPM Locality Selection */
991 writer->write_uint8(writer, TPM_LOC_ZERO);
992
993 /* PCR Composite Hash */
994 writer->write_data(writer, hash_pcr_comp);
995
996 if (use_ver_info)
997 {
998 /* TPM version Info */
999 writer->write_data(writer, this->tpm_version_info);
1000 }
1001 }
1002 else
1003 {
1004 /* Version number */
1005 writer->write_data(writer, chunk_from_chars(1, 1, 0, 0));
1006
1007 /* Magic QUOT value */
1008 writer->write_data(writer, chunk_create("QUOT", 4));
1009
1010 /* PCR Composite Hash */
1011 writer->write_data(writer, hash_pcr_comp);
1012
1013 /* Secret assessment value 20 bytes (nonce) */
1014 writer->write_data(writer, this->secret);
1015 }
1016
1017 /* TPM Quote Info */
1018 *out_quote_info = writer->extract_buf(writer);
1019 DBG3(DBG_PTS, "constructed TPM Quote Info: %B", out_quote_info);
1020
1021 writer->destroy(writer);
1022 free(pcr_comp.ptr);
1023 free(hash_pcr_comp.ptr);
1024
1025 return TRUE;
1026 }
1027
1028 METHOD(pts_t, verify_quote_signature, bool,
1029 private_pts_t *this, chunk_t data, chunk_t signature)
1030 {
1031 public_key_t *aik_pub_key;
1032
1033 aik_pub_key = this->aik->get_public_key(this->aik);
1034 if (!aik_pub_key)
1035 {
1036 DBG1(DBG_PTS, "failed to get public key from AIK certificate");
1037 return FALSE;
1038 }
1039
1040 if (!aik_pub_key->verify(aik_pub_key, SIGN_RSA_EMSA_PKCS1_SHA1,
1041 data, signature))
1042 {
1043 DBG1(DBG_PTS, "signature verification failed for TPM Quote Info");
1044 DESTROY_IF(aik_pub_key);
1045 return FALSE;
1046 }
1047
1048 aik_pub_key->destroy(aik_pub_key);
1049 return TRUE;
1050 }
1051
1052 METHOD(pts_t, get_pcrs, pts_pcr_t*,
1053 private_pts_t *this)
1054 {
1055 return this->pcrs;
1056 }
1057
1058 METHOD(pts_t, destroy, void,
1059 private_pts_t *this)
1060 {
1061 DESTROY_IF(this->pcrs);
1062 DESTROY_IF(this->aik);
1063 DESTROY_IF(this->dh);
1064 free(this->initiator_nonce.ptr);
1065 free(this->responder_nonce.ptr);
1066 free(this->secret.ptr);
1067 free(this->aik_blob.ptr);
1068 free(this->tpm_version_info.ptr);
1069 free(this);
1070 }
1071
1072
1073 #ifdef TSS_TROUSERS
1074
1075 /**
1076 * Check for a TPM by querying for TPM Version Info
1077 */
1078 static bool has_tpm(private_pts_t *this)
1079 {
1080 TSS_HCONTEXT hContext;
1081 TSS_HTPM hTPM;
1082 TSS_RESULT result;
1083 u_int32_t version_info_len;
1084
1085 result = Tspi_Context_Create(&hContext);
1086 if (result != TSS_SUCCESS)
1087 {
1088 DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x",
1089 result);
1090 return FALSE;
1091 }
1092 result = Tspi_Context_Connect(hContext, NULL);
1093 if (result != TSS_SUCCESS)
1094 {
1095 goto err;
1096 }
1097 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
1098 if (result != TSS_SUCCESS)
1099 {
1100 goto err;
1101 }
1102 result = Tspi_TPM_GetCapability(hTPM, TSS_TPMCAP_VERSION_VAL, 0, NULL,
1103 &version_info_len,
1104 &this->tpm_version_info.ptr);
1105 this->tpm_version_info.len = version_info_len;
1106 if (result != TSS_SUCCESS)
1107 {
1108 goto err;
1109 }
1110 this->tpm_version_info = chunk_clone(this->tpm_version_info);
1111
1112 Tspi_Context_FreeMemory(hContext, NULL);
1113 Tspi_Context_Close(hContext);
1114 return TRUE;
1115
1116 err:
1117 DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result);
1118 Tspi_Context_FreeMemory(hContext, NULL);
1119 Tspi_Context_Close(hContext);
1120 return FALSE;
1121 }
1122
1123 #else /* TSS_TROUSERS */
1124
1125 static bool has_tpm(private_pts_t *this)
1126 {
1127 return FALSE;
1128 }
1129
1130 #endif /* TSS_TROUSERS */
1131
1132
1133 /**
1134 * See header
1135 */
1136 pts_t *pts_create(bool is_imc)
1137 {
1138 private_pts_t *this;
1139 pts_pcr_t *pcrs;
1140
1141 pcrs = pts_pcr_create();
1142 if (!pcrs)
1143 {
1144 DBG1(DBG_PTS, "shadow PCR set could not be created");
1145 return NULL;
1146 }
1147
1148 INIT(this,
1149 .public = {
1150 .get_proto_caps = _get_proto_caps,
1151 .set_proto_caps = _set_proto_caps,
1152 .get_meas_algorithm = _get_meas_algorithm,
1153 .set_meas_algorithm = _set_meas_algorithm,
1154 .get_dh_hash_algorithm = _get_dh_hash_algorithm,
1155 .set_dh_hash_algorithm = _set_dh_hash_algorithm,
1156 .create_dh_nonce = _create_dh_nonce,
1157 .get_my_public_value = _get_my_public_value,
1158 .set_peer_public_value = _set_peer_public_value,
1159 .calculate_secret = _calculate_secret,
1160 .get_platform_id = _get_platform_id,
1161 .set_platform_id = _set_platform_id,
1162 .get_tpm_version_info = _get_tpm_version_info,
1163 .set_tpm_version_info = _set_tpm_version_info,
1164 .get_aik = _get_aik,
1165 .set_aik = _set_aik,
1166 .get_aik_id = _get_aik_id,
1167 .is_path_valid = _is_path_valid,
1168 .get_metadata = _get_metadata,
1169 .read_pcr = _read_pcr,
1170 .extend_pcr = _extend_pcr,
1171 .quote_tpm = _quote_tpm,
1172 .get_pcrs = _get_pcrs,
1173 .get_quote_info = _get_quote_info,
1174 .verify_quote_signature = _verify_quote_signature,
1175 .destroy = _destroy,
1176 },
1177 .is_imc = is_imc,
1178 .proto_caps = PTS_PROTO_CAPS_V,
1179 .algorithm = PTS_MEAS_ALGO_SHA256,
1180 .dh_hash_algorithm = PTS_MEAS_ALGO_SHA256,
1181 .pcrs = pcrs,
1182 );
1183
1184 if (is_imc)
1185 {
1186 if (has_tpm(this))
1187 {
1188 this->has_tpm = TRUE;
1189 this->proto_caps |= PTS_PROTO_CAPS_T | PTS_PROTO_CAPS_D;
1190 load_aik(this);
1191 load_aik_blob(this);
1192 }
1193 }
1194 else
1195 {
1196 this->proto_caps |= PTS_PROTO_CAPS_T | PTS_PROTO_CAPS_D;
1197 }
1198
1199 return &this->public;
1200 }