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