]> git.ipfire.org Git - thirdparty/strongswan.git/blame - src/libtpmtss/tpm_tss_tss2.c
libtpmtss: Implemented TSS2 quote() method
[thirdparty/strongswan.git] / src / libtpmtss / tpm_tss_tss2.c
CommitLineData
c08753bd
AS
1/*
2 * Copyright (C) 2016 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 "tpm_tss_tss2.h"
8301dc85 17#include "tpm_tss_tss2_names.h"
c08753bd
AS
18
19#ifdef TSS_TSS2
20
21#include <asn1/asn1.h>
22#include <asn1/oid.h>
23
24#include <tss2/tpm20.h>
25#include <tcti/tcti_socket.h>
26
27#define LABEL "TPM 2.0 -"
28
29typedef struct private_tpm_tss_tss2_t private_tpm_tss_tss2_t;
30
31/**
32 * Private data of an tpm_tss_tss2_t object.
33 */
34struct private_tpm_tss_tss2_t {
35
36 /**
37 * Public tpm_tss_tss2_t interface.
38 */
39 tpm_tss_t public;
40
41 /**
42 * TCTI context
43 */
44 TSS2_TCTI_CONTEXT *tcti_context;
45
46 /**
47 * SYS context
48 */
49 TSS2_SYS_CONTEXT *sys_context;
50
bc67802a
AS
51 /**
52 * Number of supported algorithms
53 */
54 size_t supported_algs_count;
55
56 /**
57 * List of supported algorithms
58 */
59 TPM_ALG_ID supported_algs[TPM_PT_ALGORITHM_SET];
c08753bd
AS
60};
61
62/**
63 * Some symbols required by libtctisocket
64 */
65FILE *outFp;
66uint8_t simulator = 1;
67
68int TpmClientPrintf (uint8_t type, const char *format, ...)
69{
70 return 0;
71}
72
bc67802a
AS
73/**
74 * Convert hash algorithm to TPM_ALG_ID
75 */
76static TPM_ALG_ID hash_alg_to_tpm_alg_id(hash_algorithm_t alg)
77{
78 switch (alg)
79 {
80 case HASH_SHA1:
81 return TPM_ALG_SHA1;
82 case HASH_SHA256:
83 return TPM_ALG_SHA256;
84 case HASH_SHA384:
85 return TPM_ALG_SHA384;
86 case HASH_SHA512:
87 return TPM_ALG_SHA512;
88 default:
89 return TPM_ALG_ERROR;
90 }
91}
92
93/**
94 * Check if an algorithm given by its TPM_ALG_ID is supported by the TPM
95 */
96static bool is_supported_alg(private_tpm_tss_tss2_t *this, TPM_ALG_ID alg_id)
97{
98 int i;
99
100 if (alg_id == TPM_ALG_ERROR)
101 {
102 return FALSE;
103 }
104
105 for (i = 0; i < this->supported_algs_count; i++)
106 {
107 if (this->supported_algs[i] == alg_id)
108 {
109 return TRUE;
110 }
111 }
112
113 return FALSE;
114}
115
8301dc85
AS
116/**
117 * Get a list of supported algorithms
118 */
119static bool get_algs_capability(private_tpm_tss_tss2_t *this)
120{
121 TPMS_CAPABILITY_DATA cap_data;
122 TPMI_YES_NO more_data;
bc67802a 123 TPM_ALG_ID alg;
8301dc85
AS
124 uint32_t rval, i;
125 size_t len = BUF_LEN;
126 char buf[BUF_LEN];
127 char *pos = buf;
128 int written;
129
130 /* get supported algorithms */
131 rval = Tss2_Sys_GetCapability(this->sys_context, 0, TPM_CAP_ALGS,
132 0, TPM_PT_ALGORITHM_SET, &more_data, &cap_data, 0);
133 if (rval != TPM_RC_SUCCESS)
134 {
135 DBG1(DBG_PTS, "%s GetCapability failed for TPM_CAP_ALGS: 0x%06x",
136 LABEL, rval);
137 return FALSE;
138 }
139
bc67802a
AS
140 /* Number of supported algorithms */
141 this->supported_algs_count = cap_data.data.algorithms.count;
142
143 /* store and print supported algorithms */
144 for (i = 0; i < this->supported_algs_count; i++)
8301dc85 145 {
bc67802a
AS
146 alg = cap_data.data.algorithms.algProperties[i].alg;
147 this->supported_algs[i] = alg;
148
149 written = snprintf(pos, len, " %N", tpm_alg_id_names, alg);
8301dc85
AS
150 if (written < 0 || written >= len)
151 {
152 break;
153 }
154 pos += written;
155 len -= written;
156 }
157 DBG2(DBG_PTS, "%s algorithms:%s", LABEL, buf);
158
159 /* get supported ECC curves */
160 rval = Tss2_Sys_GetCapability(this->sys_context, 0, TPM_CAP_ECC_CURVES,
161 0, TPM_PT_LOADED_CURVES, &more_data, &cap_data, 0);
162 if (rval != TPM_RC_SUCCESS)
163 {
164 DBG1(DBG_PTS, "%s GetCapability failed for TPM_ECC_CURVES: 0x%06x",
165 LABEL, rval);
166 return FALSE;
167 }
168
169 /* reset print buffer */
170 pos = buf;
171 len = BUF_LEN;
172
173 /* print supported ECC curves */
174 for (i = 0; i < cap_data.data.eccCurves.count; i++)
175 {
176 written = snprintf(pos, len, " %N", tpm_ecc_curve_names,
177 cap_data.data.eccCurves.eccCurves[i]);
178 if (written < 0 || written >= len)
179 {
180 break;
181 }
182 pos += written;
183 len -= written;
184 }
185 DBG2(DBG_PTS, "%s ECC curves:%s", LABEL, buf);
186
187 return TRUE;
188}
189
c08753bd
AS
190/**
191 * Initialize TSS context
192 */
193static bool initialize_context(private_tpm_tss_tss2_t *this)
194{
195 size_t tcti_context_size;
196 uint32_t sys_context_size;
197 uint32_t rval;
198
199 TCTI_SOCKET_CONF rm_if_config = { DEFAULT_HOSTNAME,
200 DEFAULT_RESMGR_TPM_PORT
201 };
202
203 TSS2_ABI_VERSION abi_version = { TSSWG_INTEROP,
204 TSS_SAPI_FIRST_FAMILY,
205 TSS_SAPI_FIRST_LEVEL,
206 TSS_SAPI_FIRST_VERSION
207 };
208
209 /* determine size of tcti context */
210 rval = InitSocketTcti(NULL, &tcti_context_size, &rm_if_config, 0);
211 if (rval != TSS2_RC_SUCCESS)
212 {
213 DBG1(DBG_PTS, "%s could not get tcti_context size: 0x%06x",
214 LABEL, rval);
215 return FALSE;
216 }
217
218 /* allocate memory for tcti context */
219 this->tcti_context = (TSS2_TCTI_CONTEXT*)malloc(tcti_context_size);
220
221 /* initialize tcti context */
222 rval = InitSocketTcti(this->tcti_context, &tcti_context_size,
223 &rm_if_config, 0);
224 if (rval != TSS2_RC_SUCCESS)
225 {
226 DBG1(DBG_PTS, "%s could not get tcti_context: 0x%06x",
227 LABEL, rval);
228 return FALSE;
229 }
230
231 /* determine size of sys context */
232 sys_context_size = Tss2_Sys_GetContextSize(0);
233
234 /* allocate memory for sys context */
235 this->sys_context = malloc(sys_context_size);
236
237 /* initialize sys context */
238 rval = Tss2_Sys_Initialize(this->sys_context, sys_context_size,
239 this->tcti_context, &abi_version);
240 if (rval != TSS2_RC_SUCCESS)
241 {
242 DBG1(DBG_PTS, "%s could not get sys_context: 0x%06x",
243 LABEL, rval);
244 return FALSE;
245 }
8301dc85
AS
246
247 /* get a list of supported algorithms and ECC curves */
248 return get_algs_capability(this);
c08753bd
AS
249}
250
251/**
252 * Finalize TSS context
253 */
254static void finalize_context(private_tpm_tss_tss2_t *this)
255{
256 if (this->tcti_context)
257 {
258 TeardownSocketTcti(this->tcti_context);
259 }
260 if (this->sys_context)
261 {
262 Tss2_Sys_Finalize(this->sys_context);
263 free(this->sys_context);
264 }
265}
266
267METHOD(tpm_tss_t, get_version, tpm_version_t,
268 private_tpm_tss_tss2_t *this)
269{
270 return TPM_VERSION_2_0;
271}
272
fedc6769
AS
273METHOD(tpm_tss_t, get_version_info, chunk_t,
274 private_tpm_tss_tss2_t *this)
275{
276 return chunk_empty;
277}
278
c08753bd
AS
279/**
280 * read the public key portion of a TSS 2.0 AIK key from NVRAM
281 */
282bool read_public(private_tpm_tss_tss2_t *this, TPMI_DH_OBJECT handle,
283 TPM2B_PUBLIC *public)
284{
285 uint32_t rval;
286
287 TPM2B_NAME name = { { sizeof(TPM2B_NAME)-2, } };
288 TPM2B_NAME qualified_name = { { sizeof(TPM2B_NAME)-2, } };
289
290 TPMS_AUTH_RESPONSE session_data;
291 TSS2_SYS_RSP_AUTHS sessions_data;
292 TPMS_AUTH_RESPONSE *session_data_array[1];
293
294 session_data_array[0] = &session_data;
295 sessions_data.rspAuths = &session_data_array[0];
296 sessions_data.rspAuthsCount = 1;
297
298 /* always send simulator platform command, ignored by true RM */
299 PlatformCommand(this->tcti_context ,MS_SIM_POWER_ON );
300 PlatformCommand(this->tcti_context, MS_SIM_NV_ON );
301
302 /* read public key for a given object handle from TPM 2.0 NVRAM */
303 rval = Tss2_Sys_ReadPublic(this->sys_context, handle, 0, public, &name,
304 &qualified_name, &sessions_data);
305
306 PlatformCommand(this->tcti_context, MS_SIM_POWER_OFF);
307
308 if (rval != TPM_RC_SUCCESS)
309 {
310 DBG1(DBG_PTS, "%s could not read public key from handle 0x%08x: 0x%06x",
311 LABEL, handle, rval);
312 return FALSE;
313 }
314 return TRUE;
315}
316
317METHOD(tpm_tss_t, generate_aik, bool,
318 private_tpm_tss_tss2_t *this, chunk_t ca_modulus, chunk_t *aik_blob,
319 chunk_t *aik_pubkey, chunk_t *identity_req)
320{
321 return FALSE;
322}
323
324METHOD(tpm_tss_t, get_public, chunk_t,
325 private_tpm_tss_tss2_t *this, uint32_t handle)
326{
327 TPM2B_PUBLIC public = { { 0, } };
328 chunk_t aik_blob, aik_pubkey = chunk_empty;
329
330 if (!read_public(this, handle, &public))
331 {
332 return chunk_empty;
333 }
334
335 aik_blob = chunk_create((u_char*)&public, sizeof(public));
336 DBG3(DBG_LIB, "%s AIK public key blob: %B", LABEL, &aik_blob);
337
338 /* convert TSS 2.0 AIK public key blot into PKCS#1 format */
339 switch (public.t.publicArea.type)
340 {
341 case TPM_ALG_RSA:
342 {
343 TPM2B_PUBLIC_KEY_RSA *rsa;
344 chunk_t aik_exponent, aik_modulus;
345
346 rsa = &public.t.publicArea.unique.rsa;
347 aik_modulus = chunk_create(rsa->t.buffer, rsa->t.size);
348 aik_exponent = chunk_from_chars(0x01, 0x00, 0x01);
349
350 /* subjectPublicKeyInfo encoding of AIK RSA key */
351 if (!lib->encoding->encode(lib->encoding, PUBKEY_SPKI_ASN1_DER,
352 NULL, &aik_pubkey, CRED_PART_RSA_MODULUS, aik_modulus,
353 CRED_PART_RSA_PUB_EXP, aik_exponent, CRED_PART_END))
354 {
355 DBG1(DBG_PTS, "%s subjectPublicKeyInfo encoding of AIK key "
356 "failed", LABEL);
357 }
358 break;
359 }
360 case TPM_ALG_ECC:
361 {
362 TPMS_ECC_POINT *ecc;
363 chunk_t ecc_point;
364 uint8_t *pos;
365
366 ecc = &public.t.publicArea.unique.ecc;
367
368 /* allocate space for bit string */
369 pos = asn1_build_object(&ecc_point, ASN1_BIT_STRING,
370 2 + ecc->x.t.size + ecc->y.t.size);
371 /* bit string length is a multiple of octets */
372 *pos++ = 0x00;
373 /* uncompressed ECC point format */
374 *pos++ = 0x04;
375 /* copy x coordinate of ECC point */
376 memcpy(pos, ecc->x.t.buffer, ecc->x.t.size);
377 pos += ecc->x.t.size;
378 /* copy y coordinate of ECC point */
379 memcpy(pos, ecc->y.t.buffer, ecc->y.t.size);
380 /* subjectPublicKeyInfo encoding of AIK ECC key */
381 aik_pubkey = asn1_wrap(ASN1_SEQUENCE, "mm",
382 asn1_wrap(ASN1_SEQUENCE, "mm",
383 asn1_build_known_oid(OID_EC_PUBLICKEY),
384 asn1_build_known_oid(ecc->x.t.size == 32 ?
385 OID_PRIME256V1 : OID_SECT384R1)),
386 ecc_point);
387 break;
388 }
389 default:
390 DBG1(DBG_PTS, "%s unsupported AIK key type", LABEL);
391 }
392
393 return aik_pubkey;
394}
395
57e80492
AS
396/**
397 * Configure a PCR Selection assuming a maximum of 24 registers
398 */
399static bool init_pcr_selection(private_tpm_tss_tss2_t *this, uint32_t pcrs,
400 hash_algorithm_t alg, TPML_PCR_SELECTION *pcr_sel)
30d4989a 401{
bc67802a 402 TPM_ALG_ID alg_id;
57e80492 403 uint32_t pcr;
bc67802a 404
57e80492 405 /* check if hash algorithm is supported by TPM */
bc67802a
AS
406 alg_id = hash_alg_to_tpm_alg_id(alg);
407 if (!is_supported_alg(this, alg_id))
408 {
409 DBG1(DBG_PTS, "%s %N hash algorithm not supported by TPM",
410 LABEL, hash_algorithm_short_names, alg);
411 return FALSE;
412 }
413
57e80492
AS
414 /* initialize the PCR Selection structure,*/
415 pcr_sel->count = 1;
416 pcr_sel->pcrSelections[0].hash = alg_id;
417 pcr_sel->pcrSelections[0].sizeofSelect = 3;
418 pcr_sel->pcrSelections[0].pcrSelect[0] = 0;
419 pcr_sel->pcrSelections[0].pcrSelect[1] = 0;
420 pcr_sel->pcrSelections[0].pcrSelect[2] = 0;
421
422 /* set the selected PCRs */
423 for (pcr = 0; pcr < PLATFORM_PCR; pcr++)
424 {
425 if (pcrs & (1 << pcr))
426 {
427 pcr_sel->pcrSelections[0].pcrSelect[pcr / 8] |= ( 1 << (pcr % 8) );
428 }
429 }
430 return TRUE;
431}
432
433METHOD(tpm_tss_t, read_pcr, bool,
434 private_tpm_tss_tss2_t *this, uint32_t pcr_num, chunk_t *pcr_value,
435 hash_algorithm_t alg)
436{
437 TPML_PCR_SELECTION pcr_selection;
438 TPML_DIGEST pcr_values;
439
440 uint32_t pcr_update_counter, rval;
441 uint8_t *pcr_value_ptr;
442 size_t pcr_value_len;
443
bc67802a
AS
444 if (pcr_num >= PLATFORM_PCR)
445 {
446 DBG1(DBG_PTS, "%s maximum number of supported PCR is %d",
447 LABEL, PLATFORM_PCR);
448 return FALSE;
449 }
450
57e80492
AS
451 if (!init_pcr_selection(this, (1 << pcr_num), alg, &pcr_selection))
452 {
453 return FALSE;
454 }
bc67802a
AS
455
456 /* initialize the PCR Digest structure */
457 memset(&pcr_values, 0, sizeof(TPML_DIGEST));
458
459 /* read the PCR value */
57e80492
AS
460 rval = Tss2_Sys_PCR_Read(this->sys_context, 0, &pcr_selection,
461 &pcr_update_counter, &pcr_selection, &pcr_values, 0);
bc67802a
AS
462 if (rval != TPM_RC_SUCCESS)
463 {
464 DBG1(DBG_PTS, "%s PCR bank could not be read: 0x%60x",
465 LABEL, rval);
466 return FALSE;
467 }
468 pcr_value_ptr = (uint8_t *)pcr_values.digests[0].t.buffer;
469 pcr_value_len = (size_t) pcr_values.digests[0].t.size;
470
471 *pcr_value = chunk_clone(chunk_create(pcr_value_ptr, pcr_value_len));
472
473 return TRUE;
30d4989a
AS
474}
475
476METHOD(tpm_tss_t, extend_pcr, bool,
477 private_tpm_tss_tss2_t *this, uint32_t pcr_num, chunk_t *pcr_value,
478 chunk_t data, hash_algorithm_t alg)
479{
480 /* TODO */
481 return FALSE;
482}
483
484METHOD(tpm_tss_t, quote, bool,
485 private_tpm_tss_tss2_t *this, uint32_t aik_handle, uint32_t pcr_sel,
486 hash_algorithm_t alg, chunk_t data, tpm_quote_mode_t mode, chunk_t *pcr_comp,
487 chunk_t *quote_sig)
488{
57e80492
AS
489 chunk_t quote_info;
490 uint32_t rval;
491
492 TPM2B_DATA qualifying_data;
493 TPML_PCR_SELECTION pcr_selection;
494 TPMS_ATTEST *attest;
495 TPM2B_ATTEST quoted = { { sizeof(TPM2B_ATTEST)-2, } };
496 TPM2B_DIGEST digest;
497 TPMT_SIGNATURE sig;
498 TPMT_SIG_SCHEME scheme;
499 TPMS_AUTH_COMMAND session_data_cmd;
500 TPMS_AUTH_RESPONSE session_data_rsp;
501 TSS2_SYS_CMD_AUTHS sessions_data_cmd;
502 TSS2_SYS_RSP_AUTHS sessions_data_rsp;
503 TPMS_AUTH_COMMAND *session_data_cmd_array[1];
504 TPMS_AUTH_RESPONSE *session_data_rsp_array[1];
505
506 session_data_cmd_array[0] = &session_data_cmd;
507 session_data_rsp_array[0] = &session_data_rsp;
508
509 sessions_data_cmd.cmdAuths = &session_data_cmd_array[0];
510 sessions_data_rsp.rspAuths = &session_data_rsp_array[0];
511
512 sessions_data_cmd.cmdAuthsCount = 1;
513 sessions_data_rsp.rspAuthsCount = 1;
514
515 session_data_cmd.sessionHandle = TPM_RS_PW;
516 session_data_cmd.hmac.t.size = 0;
517 session_data_cmd.nonce.t.size = 0;
518
519 *( (uint8_t *)((void *)&session_data_cmd.sessionAttributes ) ) = 0;
520
521 qualifying_data.t.size = data.len;
522 memcpy(qualifying_data.t.buffer, data.ptr, data.len);
523
524 scheme.scheme = TPM_ALG_NULL;
525 memset(&sig, 0x00, sizeof(sig));
526
527 if (mode == TPM_QUOTE || mode == TPM_QUOTE2_VERSION_INFO)
528 {
529 DBG1(DBG_PTS, "%s TPM Quote mode not supported", LABEL);
530 return FALSE;
531 }
532
533 if (!init_pcr_selection(this, pcr_sel, alg, &pcr_selection))
534 {
535 return FALSE;
536 }
537
538 rval = Tss2_Sys_Quote(this->sys_context, aik_handle, &sessions_data_cmd,
539 &qualifying_data, &scheme, &pcr_selection, &quoted,
540 &sig, &sessions_data_rsp);
541 if (rval != TPM_RC_SUCCESS)
542 {
543 DBG1(DBG_PTS,"%s Tss2_Sys_Quote failed: 0x%06x", LABEL, rval);
544 return FALSE;
545 }
546
547 attest = (TPMS_ATTEST *)quoted.t.attestationData;
548 digest = attest->attested.quote.pcrDigest;
549 *pcr_comp = chunk_clone(chunk_create(digest.t.buffer, digest.t.size));
550 DBG2(DBG_PTS, "Hash of PCR Composite: %#B", pcr_comp);
551
552 quote_info = chunk_create(quoted.t.attestationData, quoted.t.size);
553 DBG2(DBG_PTS, "TPM Quote Info: %B",&quote_info);
554
555 /* extract signature */
556 switch (sig.sigAlg)
557 {
558 case TPM_ALG_RSASSA:
559 case TPM_ALG_RSAPSS:
560 *quote_sig = chunk_clone(
561 chunk_create(
562 sig.signature.rsassa.sig.t.buffer,
563 sig.signature.rsassa.sig.t.size));
564 break;
565 case TPM_ALG_ECDSA:
566 case TPM_ALG_ECDAA:
567 case TPM_ALG_SM2:
568 case TPM_ALG_ECSCHNORR:
569 *quote_sig = chunk_cat("cc",
570 chunk_create(
571 sig.signature.ecdsa.signatureR.t.buffer,
572 sig.signature.ecdsa.signatureR.t.size),
573 chunk_create(
574 sig.signature.ecdsa.signatureS.t.buffer,
575 sig.signature.ecdsa.signatureS.t.size));
576 break;
577 default:
578 DBG1(DBG_PTS, "%s unsupported %N signature algorithm",
579 LABEL, tpm_alg_id_names, sig.sigAlg);
580 return FALSE;
581 };
582 DBG2(DBG_PTS, "TPM Quote Signature: %B", quote_sig);
583
584 return TRUE;
30d4989a
AS
585}
586
c08753bd
AS
587METHOD(tpm_tss_t, destroy, void,
588 private_tpm_tss_tss2_t *this)
589{
590 finalize_context(this);
591 free(this);
592}
593
594/**
595 * See header
596 */
597tpm_tss_t *tpm_tss_tss2_create()
598{
599 private_tpm_tss_tss2_t *this;
600 bool available;
601
602 INIT(this,
603 .public = {
604 .get_version = _get_version,
fedc6769 605 .get_version_info = _get_version_info,
c08753bd
AS
606 .generate_aik = _generate_aik,
607 .get_public = _get_public,
30d4989a
AS
608 .read_pcr = _read_pcr,
609 .extend_pcr = _extend_pcr,
610 .quote = _quote,
c08753bd
AS
611 .destroy = _destroy,
612 },
613 );
614
615 available = initialize_context(this);
616 DBG1(DBG_PTS, "TPM 2.0 via TSS2 %savailable", available ? "" : "not ");
617
618 if (!available)
619 {
620 destroy(this);
621 return NULL;
622 }
623 return &this->public;
624}
625
626#else /* TSS_TSS2 */
627
628tpm_tss_t *tpm_tss_tss2_create()
629{
630 return NULL;
631}
632
633#endif /* TSS_TSS2 */
634
635