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