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