]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/tpm2-util.c
tpm2: support RSA primary keys as fallback if TPM2 devices don't support ECC
[thirdparty/systemd.git] / src / shared / tpm2-util.c
CommitLineData
5e521624
LP
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include "extract-word.h"
4#include "parse-util.h"
5#include "tpm2-util.h"
6
7#if HAVE_TPM2
8#include "alloc-util.h"
9#include "dirent-util.h"
10#include "dlfcn-util.h"
11#include "fd-util.h"
12#include "format-table.h"
13#include "fs-util.h"
14#include "hexdecoct.h"
15#include "memory-util.h"
16#include "random-util.h"
17#include "time-util.h"
18
19static void *libtss2_esys_dl = NULL;
20static void *libtss2_rc_dl = NULL;
21static void *libtss2_mu_dl = NULL;
22
23TSS2_RC (*sym_Esys_Create)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_SENSITIVE_CREATE *inSensitive, const TPM2B_PUBLIC *inPublic, const TPM2B_DATA *outsideInfo, const TPML_PCR_SELECTION *creationPCR, TPM2B_PRIVATE **outPrivate, TPM2B_PUBLIC **outPublic, TPM2B_CREATION_DATA **creationData, TPM2B_DIGEST **creationHash, TPMT_TK_CREATION **creationTicket) = NULL;
24TSS2_RC (*sym_Esys_CreatePrimary)(ESYS_CONTEXT *esysContext, ESYS_TR primaryHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_SENSITIVE_CREATE *inSensitive, const TPM2B_PUBLIC *inPublic, const TPM2B_DATA *outsideInfo, const TPML_PCR_SELECTION *creationPCR, ESYS_TR *objectHandle, TPM2B_PUBLIC **outPublic, TPM2B_CREATION_DATA **creationData, TPM2B_DIGEST **creationHash, TPMT_TK_CREATION **creationTicket) = NULL;
25void (*sym_Esys_Finalize)(ESYS_CONTEXT **context) = NULL;
26TSS2_RC (*sym_Esys_FlushContext)(ESYS_CONTEXT *esysContext, ESYS_TR flushHandle) = NULL;
27void (*sym_Esys_Free)(void *ptr) = NULL;
07697bfe 28TSS2_RC (*sym_Esys_GetCapability)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2_CAP capability, UINT32 property, UINT32 propertyCount, TPMI_YES_NO *moreData, TPMS_CAPABILITY_DATA **capabilityData);
5e521624
LP
29TSS2_RC (*sym_Esys_GetRandom)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, UINT16 bytesRequested, TPM2B_DIGEST **randomBytes) = NULL;
30TSS2_RC (*sym_Esys_Initialize)(ESYS_CONTEXT **esys_context, TSS2_TCTI_CONTEXT *tcti, TSS2_ABI_VERSION *abiVersion) = NULL;
31TSS2_RC (*sym_Esys_Load)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_PRIVATE *inPrivate, const TPM2B_PUBLIC *inPublic, ESYS_TR *objectHandle) = NULL;
32TSS2_RC (*sym_Esys_PolicyGetDigest)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_DIGEST **policyDigest) = NULL;
33TSS2_RC (*sym_Esys_PolicyPCR)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *pcrDigest, const TPML_PCR_SELECTION *pcrs) = NULL;
34TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle) = NULL;
35TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType) = NULL;
36TSS2_RC (*sym_Esys_Unseal)(ESYS_CONTEXT *esysContext, ESYS_TR itemHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_SENSITIVE_DATA **outData) = NULL;
37
38const char* (*sym_Tss2_RC_Decode)(TSS2_RC rc) = NULL;
39
40TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Marshal)(TPM2B_PRIVATE const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
41TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PRIVATE *dest) = NULL;
42TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Marshal)(TPM2B_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
43TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PUBLIC *dest) = NULL;
44
45int dlopen_tpm2(void) {
d32f7a8e
ZJS
46 int r;
47
48 r = dlopen_many_sym_or_warn(
49 &libtss2_esys_dl, "libtss2-esys.so.0", LOG_DEBUG,
50 DLSYM_ARG(Esys_Create),
51 DLSYM_ARG(Esys_CreatePrimary),
52 DLSYM_ARG(Esys_Finalize),
53 DLSYM_ARG(Esys_FlushContext),
54 DLSYM_ARG(Esys_Free),
07697bfe 55 DLSYM_ARG(Esys_GetCapability),
d32f7a8e
ZJS
56 DLSYM_ARG(Esys_GetRandom),
57 DLSYM_ARG(Esys_Initialize),
58 DLSYM_ARG(Esys_Load),
59 DLSYM_ARG(Esys_PolicyGetDigest),
60 DLSYM_ARG(Esys_PolicyPCR),
61 DLSYM_ARG(Esys_StartAuthSession),
62 DLSYM_ARG(Esys_Startup),
63 DLSYM_ARG(Esys_Unseal));
64 if (r < 0)
65 return r;
66
67 r = dlopen_many_sym_or_warn(
68 &libtss2_rc_dl, "libtss2-rc.so.0", LOG_DEBUG,
69 DLSYM_ARG(Tss2_RC_Decode));
70 if (r < 0)
71 return r;
72
73 return dlopen_many_sym_or_warn(
74 &libtss2_mu_dl, "libtss2-mu.so.0", LOG_DEBUG,
75 DLSYM_ARG(Tss2_MU_TPM2B_PRIVATE_Marshal),
76 DLSYM_ARG(Tss2_MU_TPM2B_PRIVATE_Unmarshal),
77 DLSYM_ARG(Tss2_MU_TPM2B_PUBLIC_Marshal),
78 DLSYM_ARG(Tss2_MU_TPM2B_PUBLIC_Unmarshal));
5e521624
LP
79}
80
81struct tpm2_context {
82 ESYS_CONTEXT *esys_context;
83 void *tcti_dl;
84 TSS2_TCTI_CONTEXT *tcti_context;
85};
86
87static void tpm2_context_destroy(struct tpm2_context *c) {
88 assert(c);
89
90 if (c->esys_context)
91 sym_Esys_Finalize(&c->esys_context);
92
93 c->tcti_context = mfree(c->tcti_context);
94
95 if (c->tcti_dl) {
96 dlclose(c->tcti_dl);
97 c->tcti_dl = NULL;
98 }
99}
100
101static inline void Esys_Finalize_wrapper(ESYS_CONTEXT **c) {
102 /* A wrapper around Esys_Finalize() for use with _cleanup_(). Only reasons we need this wrapper is
103 * because the function itself warn logs if we'd pass a pointer to NULL, and we don't want that. */
104 if (*c)
105 sym_Esys_Finalize(c);
106}
107
108static inline void Esys_Freep(void *p) {
109 if (*(void**) p)
110 sym_Esys_Free(*(void**) p);
111}
112
113static ESYS_TR flush_context_verbose(ESYS_CONTEXT *c, ESYS_TR handle) {
114 TSS2_RC rc;
115
116 if (!c || handle == ESYS_TR_NONE)
117 return ESYS_TR_NONE;
118
119 rc = sym_Esys_FlushContext(c, handle);
120 if (rc != TSS2_RC_SUCCESS) /* We ignore failures here (besides debug logging), since this is called
121 * in error paths, where we cannot do anything about failures anymore. And
122 * when it is called in successful codepaths by this time we already did
123 * what we wanted to do, and got the results we wanted so there's no
124 * reason to make this fail more loudly than necessary. */
125 log_debug("Failed to get flush context of TPM, ignoring: %s", sym_Tss2_RC_Decode(rc));
126
127 return ESYS_TR_NONE;
128}
129
130static int tpm2_init(const char *device, struct tpm2_context *ret) {
131 _cleanup_(Esys_Finalize_wrapper) ESYS_CONTEXT *c = NULL;
132 _cleanup_free_ TSS2_TCTI_CONTEXT *tcti = NULL;
133 _cleanup_(dlclosep) void *dl = NULL;
134 TSS2_RC rc;
135 int r;
136
137 r = dlopen_tpm2();
138 if (r < 0)
139 return log_error_errno(r, "TPM2 support not installed: %m");
140
141 if (!device)
142 device = secure_getenv("SYSTEMD_TPM2_DEVICE");
143
144 if (device) {
145 const char *param, *driver, *fn;
146 const TSS2_TCTI_INFO* info;
147 TSS2_TCTI_INFO_FUNC func;
148 size_t sz = 0;
149
150 param = strchr(device, ':');
151 if (param) {
152 driver = strndupa(device, param - device);
153 param++;
154 } else {
155 driver = "device";
156 param = device;
157 }
158
159 fn = strjoina("libtss2-tcti-", driver, ".so.0");
160
161 dl = dlopen(fn, RTLD_NOW);
162 if (!dl)
163 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to load %s: %s", fn, dlerror());
164
165 func = dlsym(dl, TSS2_TCTI_INFO_SYMBOL);
166 if (!func)
167 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
168 "Failed to find TCTI info symbol " TSS2_TCTI_INFO_SYMBOL ": %s",
169 dlerror());
170
171 info = func();
172 if (!info)
173 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Unable to get TCTI info data.");
174
175
176 log_debug("Loaded TCTI module '%s' (%s) [Version %" PRIu32 "]", info->name, info->description, info->version);
177
178 rc = info->init(NULL, &sz, NULL);
179 if (rc != TPM2_RC_SUCCESS)
180 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
181 "Failed to initialize TCTI context: %s", sym_Tss2_RC_Decode(rc));
182
183 tcti = malloc0(sz);
184 if (!tcti)
185 return log_oom();
186
d2bf22fb 187 rc = info->init(tcti, &sz, param);
5e521624
LP
188 if (rc != TPM2_RC_SUCCESS)
189 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
190 "Failed to initialize TCTI context: %s", sym_Tss2_RC_Decode(rc));
191 }
192
193 rc = sym_Esys_Initialize(&c, tcti, NULL);
194 if (rc != TSS2_RC_SUCCESS)
195 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
196 "Failed to initialize TPM context: %s", sym_Tss2_RC_Decode(rc));
197
198 rc = sym_Esys_Startup(c, TPM2_SU_CLEAR);
199 if (rc == TPM2_RC_INITIALIZE)
200 log_debug("TPM already started up.");
201 else if (rc == TSS2_RC_SUCCESS)
202 log_debug("TPM successfully started up.");
203 else
204 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
205 "Failed to start up TPM: %s", sym_Tss2_RC_Decode(rc));
206
207 *ret = (struct tpm2_context) {
208 .esys_context = TAKE_PTR(c),
209 .tcti_context = TAKE_PTR(tcti),
210 .tcti_dl = TAKE_PTR(dl),
211 };
212
213 return 0;
214}
215
216static int tpm2_credit_random(ESYS_CONTEXT *c) {
217 size_t rps, done = 0;
218 TSS2_RC rc;
219 int r;
220
221 assert(c);
222
223 /* Pulls some entropy from the TPM and adds it into the kernel RNG pool. That way we can say that the
224 * key we will ultimately generate with the kernel random pool is at least as good as the TPM's RNG,
225 * but likely better. Note that we don't trust the TPM RNG very much, hence do not actually credit
226 * any entropy. */
227
228 for (rps = random_pool_size(); rps > 0;) {
229 _cleanup_(Esys_Freep) TPM2B_DIGEST *buffer = NULL;
230
231 rc = sym_Esys_GetRandom(
232 c,
233 ESYS_TR_NONE,
234 ESYS_TR_NONE,
235 ESYS_TR_NONE,
236 MIN(rps, 32U), /* 32 is supposedly a safe choice, given that AES 256bit keys are this long, and TPM2 baseline requires support for those. */
237 &buffer);
238 if (rc != TSS2_RC_SUCCESS)
239 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
240 "Failed to acquire entropy from TPM: %s", sym_Tss2_RC_Decode(rc));
241
242 if (buffer->size == 0)
243 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
244 "Zero-sized entropy returned from TPM.");
245
246 r = random_write_entropy(-1, buffer->buffer, buffer->size, false);
247 if (r < 0)
248 return log_error_errno(r, "Failed wo write entropy to kernel: %m");
249
250 done += buffer->size;
251 rps = LESS_BY(rps, buffer->size);
252 }
253
254 log_debug("Added %zu bytes of entropy to the kernel random pool.", done);
255 return 0;
256}
257
258static int tpm2_make_primary(
259 ESYS_CONTEXT *c,
2b92a672
LP
260 ESYS_TR *ret_primary,
261 TPMI_ALG_PUBLIC alg,
262 TPMI_ALG_PUBLIC *ret_alg) {
5e521624
LP
263
264 static const TPM2B_SENSITIVE_CREATE primary_sensitive = {};
2b92a672 265 static const TPM2B_PUBLIC primary_template_ecc = {
5e521624
LP
266 .size = sizeof(TPMT_PUBLIC),
267 .publicArea = {
268 .type = TPM2_ALG_ECC,
269 .nameAlg = TPM2_ALG_SHA256,
270 .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
271 .parameters = {
272 .eccDetail = {
273 .symmetric = {
274 .algorithm = TPM2_ALG_AES,
275 .keyBits.aes = 128,
276 .mode.aes = TPM2_ALG_CFB,
277 },
278 .scheme.scheme = TPM2_ALG_NULL,
279 .curveID = TPM2_ECC_NIST_P256,
280 .kdf.scheme = TPM2_ALG_NULL,
281 },
282 },
283 },
284 };
2b92a672
LP
285 static const TPM2B_PUBLIC primary_template_rsa = {
286 .size = sizeof(TPMT_PUBLIC),
287 .publicArea = {
288 .type = TPM2_ALG_RSA,
289 .nameAlg = TPM2_ALG_SHA256,
290 .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
291 .parameters = {
292 .rsaDetail = {
293 .symmetric = {
294 .algorithm = TPM2_ALG_AES,
295 .keyBits.aes = 128,
296 .mode.aes = TPM2_ALG_CFB,
297 },
298 .scheme.scheme = TPM2_ALG_NULL,
299 .keyBits = 2048,
300 },
301 },
302 },
303 };
304
5e521624
LP
305 static const TPML_PCR_SELECTION creation_pcr = {};
306 ESYS_TR primary = ESYS_TR_NONE;
307 TSS2_RC rc;
2b92a672 308 usec_t ts;
5e521624
LP
309
310 log_debug("Creating primary key on TPM.");
311
2b92a672
LP
312 /* So apparently not all TPM2 devices support ECC. ECC is generally preferably, because it's so much
313 * faster, noticeably so (~10s vs. ~240ms on my system). Hence, unless explicitly configured let's
314 * try to use ECC first, and if that does not work, let's fall back to RSA. */
5e521624 315
2b92a672
LP
316 ts = now(CLOCK_MONOTONIC);
317
318 if (IN_SET(alg, 0, TPM2_ALG_ECC)) {
319 rc = sym_Esys_CreatePrimary(
320 c,
321 ESYS_TR_RH_OWNER,
322 ESYS_TR_PASSWORD,
323 ESYS_TR_NONE,
324 ESYS_TR_NONE,
325 &primary_sensitive,
326 &primary_template_ecc,
327 NULL,
328 &creation_pcr,
329 &primary,
330 NULL,
331 NULL,
332 NULL,
333 NULL);
334
335 if (rc != TSS2_RC_SUCCESS) {
336 if (alg != 0)
337 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
338 "Failed to generate ECC primary key in TPM: %s", sym_Tss2_RC_Decode(rc));
339
340 log_debug("Failed to generate ECC primary key in TPM, trying RSA: %s", sym_Tss2_RC_Decode(rc));
341 } else {
342 log_debug("Successfully created ECC primary key on TPM.");
343 alg = TPM2_ALG_ECC;
344 }
345 }
346
347 if (IN_SET(alg, 0, TPM2_ALG_RSA)) {
348 rc = sym_Esys_CreatePrimary(
349 c,
350 ESYS_TR_RH_OWNER,
351 ESYS_TR_PASSWORD,
352 ESYS_TR_NONE,
353 ESYS_TR_NONE,
354 &primary_sensitive,
355 &primary_template_rsa,
356 NULL,
357 &creation_pcr,
358 &primary,
359 NULL,
360 NULL,
361 NULL,
362 NULL);
363 if (rc != TSS2_RC_SUCCESS)
364 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
365 "Failed to generate RSA primary key in TPM: %s", sym_Tss2_RC_Decode(rc));
366 else if (alg == 0) {
367 log_notice("TPM2 chip apparently does not support ECC primary keys, falling back to RSA. "
368 "This likely means TPM2 operations will be relatively slow, please be patient.");
369 alg = TPM2_ALG_RSA;
370 }
371
372 log_debug("Successfully created RSA primary key on TPM.");
373 }
5e521624 374
2b92a672 375 log_debug("Generating primary key on TPM2 took %s.", FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - ts, USEC_PER_MSEC));
5e521624
LP
376
377 *ret_primary = primary;
2b92a672
LP
378 if (ret_alg)
379 *ret_alg = alg;
380
5e521624
LP
381 return 0;
382}
383
07697bfe
LP
384static int tpm2_get_best_pcr_bank(
385 ESYS_CONTEXT *c,
386 TPMI_ALG_HASH *ret) {
387
388 _cleanup_(Esys_Freep) TPMS_CAPABILITY_DATA *pcap = NULL;
389 TPMI_ALG_HASH hash = TPM2_ALG_SHA1;
390 bool found = false;
391 TPMI_YES_NO more;
392 TSS2_RC rc;
393
394 rc = sym_Esys_GetCapability(
395 c,
396 ESYS_TR_NONE,
397 ESYS_TR_NONE,
398 ESYS_TR_NONE,
399 TPM2_CAP_PCRS,
400 0,
401 1,
402 &more,
403 &pcap);
404 if (rc != TSS2_RC_SUCCESS)
405 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
406 "Failed to determine TPM2 PCR bank capabilities: %s", sym_Tss2_RC_Decode(rc));
407
408 assert(pcap->capability == TPM2_CAP_PCRS);
409
410 for (size_t i = 0; i < pcap->data.assignedPCR.count; i++) {
411 bool valid = true;
412
413 /* As per
414 * https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClient_PFP_r1p05_v23_pub.pdf a
415 * TPM2 on a Client PC must have at least 24 PCRs. If this TPM has less, just skip over
416 * it. */
417 if (pcap->data.assignedPCR.pcrSelections[i].sizeofSelect < TPM2_PCRS_MAX/8) {
418 log_debug("Skipping TPM2 PCR bank %s with fewer than 24 PCRs.",
419 strna(tpm2_pcr_bank_to_string(pcap->data.assignedPCR.pcrSelections[i].hash)));
420 continue;
421 }
422
423 assert_cc(TPM2_PCRS_MAX % 8 == 0);
424
bdbb61f6 425 /* It's not enough to check how many PCRs there are, we also need to check that the 24 are
07697bfe
LP
426 * enabled for this bank. Otherwise this TPM doesn't qualify. */
427 for (size_t j = 0; j < TPM2_PCRS_MAX/8; j++)
428 if (pcap->data.assignedPCR.pcrSelections[i].pcrSelect[j] != 0xFF) {
429 valid = false;
430 break;
431 }
432
433 if (!valid) {
434 log_debug("TPM2 PCR bank %s has fewer than 24 PCR bits enabled, ignoring.",
435 strna(tpm2_pcr_bank_to_string(pcap->data.assignedPCR.pcrSelections[i].hash)));
436 continue;
437 }
438
439 if (pcap->data.assignedPCR.pcrSelections[i].hash == TPM2_ALG_SHA256) {
440 hash = TPM2_ALG_SHA256;
441 found = true;
442 break;
443 }
444
445 if (pcap->data.assignedPCR.pcrSelections[i].hash == TPM2_ALG_SHA1)
446 found = true;
447 }
448
449 if (!found)
450 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
451 "TPM2 module supports neither SHA1 nor SHA256 PCR banks, cannot operate.");
452
453 if (hash == TPM2_ALG_SHA256)
454 log_debug("TPM2 device supports SHA256 PCR banks, yay!");
455 else {
456 assert(hash == TPM2_ALG_SHA1);
457 log_debug("TPM2 device lacks support for SHA256 PCR banks, falling back to SHA1 banks.");
458 }
459
460 *ret = hash;
461 return 0;
462}
463
5e521624
LP
464static int tpm2_make_pcr_session(
465 ESYS_CONTEXT *c,
466 uint32_t pcr_mask,
07697bfe 467 uint16_t pcr_bank, /* If UINT16_MAX, pick best bank automatically, otherwise specify bank explicitly. */
5e521624 468 ESYS_TR *ret_session,
07697bfe
LP
469 TPM2B_DIGEST **ret_policy_digest,
470 TPMI_ALG_HASH *ret_pcr_bank) {
5e521624
LP
471
472 static const TPMT_SYM_DEF symmetric = {
473 .algorithm = TPM2_ALG_AES,
474 .keyBits = {
475 .aes = 128
476 },
477 .mode = {
478 .aes = TPM2_ALG_CFB,
479 }
480 };
481 TPML_PCR_SELECTION pcr_selection = {
482 .count = 1,
bdbb61f6 483 .pcrSelections[0].hash = TPM2_ALG_SHA256, /* overridden below, depending on TPM2 capabilities */
5e521624
LP
484 .pcrSelections[0].sizeofSelect = 3,
485 .pcrSelections[0].pcrSelect[0] = pcr_mask & 0xFF,
486 .pcrSelections[0].pcrSelect[1] = (pcr_mask >> 8) & 0xFF,
487 .pcrSelections[0].pcrSelect[2] = (pcr_mask >> 16) & 0xFF,
488 };
489 _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
490 ESYS_TR session = ESYS_TR_NONE;
491 TSS2_RC rc;
492 int r;
493
494 assert(c);
495
496 log_debug("Starting authentication session.");
497
07697bfe
LP
498 if (pcr_bank != UINT16_MAX)
499 pcr_selection.pcrSelections[0].hash = pcr_bank;
500 else {
501 /* No bank configured, pick automatically. Some TPM2 devices only can do SHA1. If we detect
502 * that use that, but preferably use SHA256 */
503 r = tpm2_get_best_pcr_bank(c, &pcr_selection.pcrSelections[0].hash);
504 if (r < 0)
505 return r;
506 }
507
5e521624
LP
508 rc = sym_Esys_StartAuthSession(
509 c,
510 ESYS_TR_NONE,
511 ESYS_TR_NONE,
512 ESYS_TR_NONE,
513 ESYS_TR_NONE,
514 ESYS_TR_NONE,
515 NULL,
516 TPM2_SE_POLICY,
517 &symmetric,
518 TPM2_ALG_SHA256,
519 &session);
520 if (rc != TSS2_RC_SUCCESS)
521 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
522 "Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc));
523
524 log_debug("Configuring PCR policy.");
525
526 rc = sym_Esys_PolicyPCR(
527 c,
528 session,
529 ESYS_TR_NONE,
530 ESYS_TR_NONE,
531 ESYS_TR_NONE,
532 NULL,
533 &pcr_selection);
534 if (rc != TSS2_RC_SUCCESS) {
535 r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
536 "Failed to add PCR policy to TPM: %s", sym_Tss2_RC_Decode(rc));
537 goto finish;
538 }
539
540 if (DEBUG_LOGGING || ret_policy_digest) {
541 log_debug("Acquiring policy digest.");
542
543 rc = sym_Esys_PolicyGetDigest(
544 c,
545 session,
546 ESYS_TR_NONE,
547 ESYS_TR_NONE,
548 ESYS_TR_NONE,
549 &policy_digest);
550
551 if (rc != TSS2_RC_SUCCESS) {
552 r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
553 "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc));
554 goto finish;
555 }
556
557 if (DEBUG_LOGGING) {
558 _cleanup_free_ char *h = NULL;
559
560 h = hexmem(policy_digest->buffer, policy_digest->size);
561 if (!h) {
562 r = log_oom();
563 goto finish;
564 }
565
566 log_debug("Session policy digest: %s", h);
567 }
568 }
569
570 if (ret_session) {
571 *ret_session = session;
572 session = ESYS_TR_NONE;
573 }
574
575 if (ret_policy_digest)
576 *ret_policy_digest = TAKE_PTR(policy_digest);
577
07697bfe
LP
578 if (ret_pcr_bank)
579 *ret_pcr_bank = pcr_selection.pcrSelections[0].hash;
580
5e521624
LP
581 r = 0;
582
583finish:
584 session = flush_context_verbose(c, session);
585 return r;
586}
587
588int tpm2_seal(
589 const char *device,
590 uint32_t pcr_mask,
591 void **ret_secret,
592 size_t *ret_secret_size,
593 void **ret_blob,
594 size_t *ret_blob_size,
595 void **ret_pcr_hash,
07697bfe 596 size_t *ret_pcr_hash_size,
2b92a672
LP
597 uint16_t *ret_pcr_bank,
598 uint16_t *ret_primary_alg) {
5e521624
LP
599
600 _cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
601 _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
602 _cleanup_(Esys_Freep) TPM2B_PRIVATE *private = NULL;
603 _cleanup_(Esys_Freep) TPM2B_PUBLIC *public = NULL;
604 static const TPML_PCR_SELECTION creation_pcr = {};
605 _cleanup_(erase_and_freep) void *secret = NULL;
606 _cleanup_free_ void *blob = NULL, *hash = NULL;
607 TPM2B_SENSITIVE_CREATE hmac_sensitive;
608 ESYS_TR primary = ESYS_TR_NONE;
2b92a672 609 TPMI_ALG_PUBLIC primary_alg;
5e521624 610 TPM2B_PUBLIC hmac_template;
07697bfe 611 TPMI_ALG_HASH pcr_bank;
5e521624
LP
612 size_t k, blob_size;
613 usec_t start;
614 TSS2_RC rc;
615 int r;
616
617 assert(ret_secret);
618 assert(ret_secret_size);
619 assert(ret_blob);
620 assert(ret_blob_size);
621 assert(ret_pcr_hash);
622 assert(ret_pcr_hash_size);
07697bfe 623 assert(ret_pcr_bank);
5e521624
LP
624
625 assert(pcr_mask < (UINT32_C(1) << TPM2_PCRS_MAX)); /* Support 24 PCR banks */
626
627 /* So here's what we do here: we connect to the TPM2 chip. It persistently contains a "seed" key that
628 * is randomized when the TPM2 is first initialized or reset and remains stable across boots. We
2b92a672
LP
629 * generate a "primary" key pair derived from that (ECC if possible, RSA as fallback). Given the seed
630 * remains fixed this will result in the same key pair whenever we specify the exact same parameters
631 * for it. We then create a PCR-bound policy session, which calculates a hash on the current PCR
632 * values of the indexes we specify. We then generate a randomized key on the host (which is the key
633 * we actually enroll in the LUKS2 keyslots), which we upload into the TPM2, where it is encrypted
634 * with the "primary" key, taking the PCR policy session into account. We then download the encrypted
635 * key from the TPM2 ("sealing") and marshall it into binary form, which is ultimately placed in the
636 * LUKS2 JSON header.
5e521624
LP
637 *
638 * The TPM2 "seed" key and "primary" keys never leave the TPM2 chip (and cannot be extracted at
639 * all). The random key we enroll in LUKS2 we generate on the host using the Linux random device. It
640 * is stored in the LUKS2 JSON only in encrypted form with the "primary" key of the TPM2 chip, thus
641 * binding the unlocking to the TPM2 chip. */
642
643 start = now(CLOCK_MONOTONIC);
644
645 r = tpm2_init(device, &c);
646 if (r < 0)
647 return r;
648
2b92a672 649 r = tpm2_make_primary(c.esys_context, &primary, 0, &primary_alg);
5e521624
LP
650 if (r < 0)
651 return r;
652
07697bfe 653 r = tpm2_make_pcr_session(c.esys_context, pcr_mask, UINT16_MAX, NULL, &policy_digest, &pcr_bank);
5e521624
LP
654 if (r < 0)
655 goto finish;
656
657 /* We use a keyed hash object (i.e. HMAC) to store the secret key we want to use for unlocking the
658 * LUKS2 volume with. We don't ever use for HMAC/keyed hash operations however, we just use it
659 * because it's a key type that is universally supported and suitable for symmetric binary blobs. */
660 hmac_template = (TPM2B_PUBLIC) {
661 .size = sizeof(TPMT_PUBLIC),
662 .publicArea = {
663 .type = TPM2_ALG_KEYEDHASH,
664 .nameAlg = TPM2_ALG_SHA256,
665 .objectAttributes = TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT,
666 .parameters = {
667 .keyedHashDetail = {
668 .scheme.scheme = TPM2_ALG_NULL,
669 },
670 },
671 .unique = {
672 .keyedHash = {
673 .size = 32,
674 },
675 },
676 .authPolicy = *policy_digest,
677 },
678 };
679
680 hmac_sensitive = (TPM2B_SENSITIVE_CREATE) {
681 .size = sizeof(hmac_sensitive.sensitive),
682 .sensitive.data.size = 32,
683 };
684 assert(sizeof(hmac_sensitive.sensitive.data.buffer) >= hmac_sensitive.sensitive.data.size);
685
686 (void) tpm2_credit_random(c.esys_context);
687
688 log_debug("Generating secret key data.");
689
690 r = genuine_random_bytes(hmac_sensitive.sensitive.data.buffer, hmac_sensitive.sensitive.data.size, RANDOM_BLOCK);
691 if (r < 0) {
692 log_error_errno(r, "Failed to generate secret key: %m");
693 goto finish;
694 }
695
696 log_debug("Creating HMAC key.");
697
698 rc = sym_Esys_Create(
699 c.esys_context,
700 primary,
701 ESYS_TR_PASSWORD,
702 ESYS_TR_NONE,
703 ESYS_TR_NONE,
704 &hmac_sensitive,
705 &hmac_template,
706 NULL,
707 &creation_pcr,
708 &private,
709 &public,
710 NULL,
711 NULL,
712 NULL);
713 if (rc != TSS2_RC_SUCCESS) {
714 r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
715 "Failed to generate HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
716 goto finish;
717 }
718
719 secret = memdup(hmac_sensitive.sensitive.data.buffer, hmac_sensitive.sensitive.data.size);
720 explicit_bzero_safe(hmac_sensitive.sensitive.data.buffer, hmac_sensitive.sensitive.data.size);
721 if (!secret) {
722 r = log_oom();
723 goto finish;
724 }
725
726 log_debug("Marshalling private and public part of HMAC key.");
727
728 k = ALIGN8(sizeof(*private)) + ALIGN8(sizeof(*public)); /* Some roughly sensible start value */
729 for (;;) {
730 _cleanup_free_ void *buf = NULL;
731 size_t offset = 0;
732
733 buf = malloc(k);
734 if (!buf) {
735 r = log_oom();
736 goto finish;
737 }
738
739 rc = sym_Tss2_MU_TPM2B_PRIVATE_Marshal(private, buf, k, &offset);
740 if (rc == TSS2_RC_SUCCESS) {
741 rc = sym_Tss2_MU_TPM2B_PUBLIC_Marshal(public, buf, k, &offset);
742 if (rc == TSS2_RC_SUCCESS) {
743 blob = TAKE_PTR(buf);
744 blob_size = offset;
745 break;
746 }
747 }
748 if (rc != TSS2_MU_RC_INSUFFICIENT_BUFFER) {
749 r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
750 "Failed to marshal private/public key: %s", sym_Tss2_RC_Decode(rc));
751 goto finish;
752 }
753
754 if (k > SIZE_MAX / 2) {
755 r = log_oom();
756 goto finish;
757 }
758
759 k *= 2;
760 }
761
762 hash = memdup(policy_digest->buffer, policy_digest->size);
763 if (!hash)
764 return log_oom();
765
5291f26d
ZJS
766 if (DEBUG_LOGGING)
767 log_debug("Completed TPM2 key sealing in %s.", FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - start, 1));
5e521624
LP
768
769 *ret_secret = TAKE_PTR(secret);
770 *ret_secret_size = hmac_sensitive.sensitive.data.size;
771 *ret_blob = TAKE_PTR(blob);
772 *ret_blob_size = blob_size;
773 *ret_pcr_hash = TAKE_PTR(hash);
774 *ret_pcr_hash_size = policy_digest->size;
07697bfe 775 *ret_pcr_bank = pcr_bank;
2b92a672 776 *ret_primary_alg = primary_alg;
5e521624
LP
777
778 r = 0;
779
780finish:
781 primary = flush_context_verbose(c.esys_context, primary);
782 return r;
783}
784
785int tpm2_unseal(
786 const char *device,
787 uint32_t pcr_mask,
07697bfe 788 uint16_t pcr_bank,
2b92a672 789 uint16_t primary_alg,
5e521624
LP
790 const void *blob,
791 size_t blob_size,
792 const void *known_policy_hash,
793 size_t known_policy_hash_size,
794 void **ret_secret,
795 size_t *ret_secret_size) {
796
797 _cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
798 ESYS_TR primary = ESYS_TR_NONE, session = ESYS_TR_NONE, hmac_key = ESYS_TR_NONE;
799 _cleanup_(Esys_Freep) TPM2B_SENSITIVE_DATA* unsealed = NULL;
800 _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
801 _cleanup_(erase_and_freep) char *secret = NULL;
802 TPM2B_PRIVATE private = {};
803 TPM2B_PUBLIC public = {};
804 size_t offset = 0;
805 TSS2_RC rc;
806 usec_t start;
807 int r;
808
809 assert(blob);
810 assert(blob_size > 0);
811 assert(known_policy_hash_size == 0 || known_policy_hash);
812 assert(ret_secret);
813 assert(ret_secret_size);
814
815 assert(pcr_mask < (UINT32_C(1) << TPM2_PCRS_MAX)); /* Support 24 PCR banks */
816
1b30720c
LP
817 r = dlopen_tpm2();
818 if (r < 0)
819 return log_error_errno(r, "TPM2 support is not installed.");
820
5e521624
LP
821 /* So here's what we do here: We connect to the TPM2 chip. As we do when sealing we generate a
822 * "primary" key on the TPM2 chip, with the same parameters as well as a PCR-bound policy
823 * session. Given we pass the same parameters, this will result in the same "primary" key, and same
824 * policy hash (the latter of course, only if the PCR values didn't change in between). We unmarshal
825 * the encrypted key we stored in the LUKS2 JSON token header and upload it into the TPM2, where it
826 * is decrypted if the seed and the PCR policy were right ("unsealing"). We then download the result,
827 * and use it to unlock the LUKS2 volume. */
828
829 start = now(CLOCK_MONOTONIC);
830
831 log_debug("Unmarshalling private part of HMAC key.");
832
833 rc = sym_Tss2_MU_TPM2B_PRIVATE_Unmarshal(blob, blob_size, &offset, &private);
834 if (rc != TSS2_RC_SUCCESS)
835 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
836 "Failed to unmarshal private key: %s", sym_Tss2_RC_Decode(rc));
837
838 log_debug("Unmarshalling public part of HMAC key.");
839
840 rc = sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal(blob, blob_size, &offset, &public);
841 if (rc != TSS2_RC_SUCCESS)
842 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
843 "Failed to unmarshal public key: %s", sym_Tss2_RC_Decode(rc));
844
845 r = tpm2_init(device, &c);
846 if (r < 0)
847 return r;
848
07697bfe 849 r = tpm2_make_pcr_session(c.esys_context, pcr_mask, pcr_bank, &session, &policy_digest, NULL);
5e521624
LP
850 if (r < 0)
851 goto finish;
852
853 /* If we know the policy hash to expect, and it doesn't match, we can shortcut things here, and not
854 * wait until the TPM2 tells us to go away. */
855 if (known_policy_hash_size > 0 &&
856 memcmp_nn(policy_digest->buffer, policy_digest->size, known_policy_hash, known_policy_hash_size) != 0)
857 return log_error_errno(SYNTHETIC_ERRNO(EPERM),
858 "Current policy digest does not match stored policy digest, cancelling TPM2 authentication attempt.");
859
2b92a672 860 r = tpm2_make_primary(c.esys_context, &primary, primary_alg, NULL);
5e521624
LP
861 if (r < 0)
862 return r;
863
864 log_debug("Loading HMAC key into TPM.");
865
866 rc = sym_Esys_Load(
867 c.esys_context,
868 primary,
869 ESYS_TR_PASSWORD,
870 ESYS_TR_NONE,
871 ESYS_TR_NONE,
872 &private,
873 &public,
874 &hmac_key);
875 if (rc != TSS2_RC_SUCCESS) {
876 r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
877 "Failed to load HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
878 goto finish;
879 }
880
881 log_debug("Unsealing HMAC key.");
882
883 rc = sym_Esys_Unseal(
884 c.esys_context,
885 hmac_key,
886 session,
887 ESYS_TR_NONE,
888 ESYS_TR_NONE,
889 &unsealed);
890 if (rc != TSS2_RC_SUCCESS) {
891 r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
892 "Failed to unseal HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
893 goto finish;
894 }
895
896 secret = memdup(unsealed->buffer, unsealed->size);
897 explicit_bzero_safe(unsealed->buffer, unsealed->size);
898 if (!secret) {
899 r = log_oom();
900 goto finish;
901 }
902
5291f26d
ZJS
903 if (DEBUG_LOGGING)
904 log_debug("Completed TPM2 key unsealing in %s.", FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - start, 1));
5e521624
LP
905
906 *ret_secret = TAKE_PTR(secret);
907 *ret_secret_size = unsealed->size;
908
909 r = 0;
910
911finish:
912 primary = flush_context_verbose(c.esys_context, primary);
913 session = flush_context_verbose(c.esys_context, session);
914 hmac_key = flush_context_verbose(c.esys_context, hmac_key);
915 return r;
916}
917
918#endif
919
920int tpm2_list_devices(void) {
921#if HAVE_TPM2
922 _cleanup_(table_unrefp) Table *t = NULL;
923 _cleanup_(closedirp) DIR *d = NULL;
924 int r;
925
926 r = dlopen_tpm2();
927 if (r < 0)
928 return log_error_errno(r, "TPM2 support is not installed.");
929
930 t = table_new("path", "device", "driver");
931 if (!t)
932 return log_oom();
933
934 d = opendir("/sys/class/tpmrm");
935 if (!d) {
936 log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open /sys/class/tpmrm: %m");
937 if (errno != ENOENT)
938 return -errno;
939 } else {
940 for (;;) {
941 _cleanup_free_ char *device_path = NULL, *device = NULL, *driver_path = NULL, *driver = NULL, *node = NULL;
942 struct dirent *de;
943
944 de = readdir_no_dot(d);
945 if (!de)
946 break;
947
948 device_path = path_join("/sys/class/tpmrm", de->d_name, "device");
949 if (!device_path)
950 return log_oom();
951
952 r = readlink_malloc(device_path, &device);
953 if (r < 0)
954 log_debug_errno(r, "Failed to read device symlink %s, ignoring: %m", device_path);
955 else {
956 driver_path = path_join(device_path, "driver");
957 if (!driver_path)
958 return log_oom();
959
960 r = readlink_malloc(driver_path, &driver);
961 if (r < 0)
962 log_debug_errno(r, "Failed to read driver symlink %s, ignoring: %m", driver_path);
963 }
964
965 node = path_join("/dev", de->d_name);
966 if (!node)
967 return log_oom();
968
969 r = table_add_many(
970 t,
971 TABLE_PATH, node,
972 TABLE_STRING, device ? last_path_component(device) : NULL,
973 TABLE_STRING, driver ? last_path_component(driver) : NULL);
974 if (r < 0)
975 return table_log_add_error(r);
976 }
977 }
978
979 if (table_get_rows(t) <= 1) {
980 log_info("No suitable TPM2 devices found.");
981 return 0;
982 }
983
984 r = table_print(t, stdout);
985 if (r < 0)
986 return log_error_errno(r, "Failed to show device table: %m");
987
988 return 0;
989#else
990 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
991 "TPM2 not supported on this build.");
992#endif
993}
994
995int tpm2_find_device_auto(
996 int log_level, /* log level when no device is found */
997 char **ret) {
998#if HAVE_TPM2
999 _cleanup_(closedirp) DIR *d = NULL;
1000 int r;
1001
1002 r = dlopen_tpm2();
1003 if (r < 0)
1004 return log_error_errno(r, "TPM2 support is not installed.");
1005
1006 d = opendir("/sys/class/tpmrm");
1007 if (!d) {
1008 log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno,
1009 "Failed to open /sys/class/tpmrm: %m");
1010 if (errno != ENOENT)
1011 return -errno;
1012 } else {
1013 _cleanup_free_ char *node = NULL;
1014
1015 for (;;) {
1016 struct dirent *de;
1017
1018 de = readdir_no_dot(d);
1019 if (!de)
1020 break;
1021
1022 if (node)
1023 return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
1024 "More than one TPM2 (tpmrm) device found.");
1025
1026 node = path_join("/dev", de->d_name);
1027 if (!node)
1028 return log_oom();
1029 }
1030
1031 if (node) {
1032 *ret = TAKE_PTR(node);
1033 return 0;
1034 }
1035 }
1036
1037 return log_full_errno(log_level, SYNTHETIC_ERRNO(ENODEV), "No TPM2 (tpmrm) device found.");
1038#else
1039 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1040 "TPM2 not supported on this build.");
1041#endif
1042}
1043
1044int tpm2_parse_pcrs(const char *s, uint32_t *ret) {
1045 const char *p = s;
1046 uint32_t mask = 0;
1047 int r;
1048
d57f6340
LP
1049 assert(s);
1050
1051 if (isempty(s)) {
1052 *ret = 0;
1053 return 0;
1054 }
1055
a1788a69
LP
1056 /* Parses a "," or "+" separated list of PCR indexes. We support "," since this is a list after all,
1057 * and most other tools expect comma separated PCR specifications. We also support "+" since in
1058 * /etc/crypttab the "," is already used to separate options, hence a different separator is nice to
1059 * avoid escaping. */
5e521624
LP
1060
1061 for (;;) {
1062 _cleanup_free_ char *pcr = NULL;
1063 unsigned n;
1064
a1788a69 1065 r = extract_first_word(&p, &pcr, ",+", EXTRACT_DONT_COALESCE_SEPARATORS);
5e521624
LP
1066 if (r == 0)
1067 break;
1068 if (r < 0)
1069 return log_error_errno(r, "Failed to parse PCR list: %s", s);
1070
1071 r = safe_atou(pcr, &n);
1072 if (r < 0)
1073 return log_error_errno(r, "Failed to parse PCR number: %s", pcr);
1074 if (n >= TPM2_PCRS_MAX)
1075 return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
1076 "PCR number out of range (valid range 0…23): %u", n);
1077
1078 mask |= UINT32_C(1) << n;
1079 }
1080
1081 *ret = mask;
1082 return 0;
1083}
1084
1085int tpm2_make_luks2_json(
1086 int keyslot,
1087 uint32_t pcr_mask,
07697bfe 1088 uint16_t pcr_bank,
2b92a672 1089 uint16_t primary_alg,
5e521624
LP
1090 const void *blob,
1091 size_t blob_size,
1092 const void *policy_hash,
1093 size_t policy_hash_size,
1094 JsonVariant **ret) {
1095
1096 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *a = NULL;
1097 _cleanup_free_ char *keyslot_as_string = NULL;
1098 JsonVariant* pcr_array[TPM2_PCRS_MAX];
1099 unsigned n_pcrs = 0;
1100 int r;
1101
1102 assert(blob || blob_size == 0);
1103 assert(policy_hash || policy_hash_size == 0);
1104
1105 if (asprintf(&keyslot_as_string, "%i", keyslot) < 0)
1106 return -ENOMEM;
1107
1108 for (unsigned i = 0; i < ELEMENTSOF(pcr_array); i++) {
1109 if ((pcr_mask & (UINT32_C(1) << i)) == 0)
1110 continue;
1111
1112 r = json_variant_new_integer(pcr_array + n_pcrs, i);
1113 if (r < 0) {
1114 json_variant_unref_many(pcr_array, n_pcrs);
1115 return -ENOMEM;
1116 }
1117
1118 n_pcrs++;
1119 }
1120
1121 r = json_variant_new_array(&a, pcr_array, n_pcrs);
1122 json_variant_unref_many(pcr_array, n_pcrs);
1123 if (r < 0)
1124 return -ENOMEM;
1125
1126 r = json_build(&v,
1127 JSON_BUILD_OBJECT(
1128 JSON_BUILD_PAIR("type", JSON_BUILD_STRING("systemd-tpm2")),
1129 JSON_BUILD_PAIR("keyslots", JSON_BUILD_ARRAY(JSON_BUILD_STRING(keyslot_as_string))),
1130 JSON_BUILD_PAIR("tpm2-blob", JSON_BUILD_BASE64(blob, blob_size)),
1131 JSON_BUILD_PAIR("tpm2-pcrs", JSON_BUILD_VARIANT(a)),
07697bfe 1132 JSON_BUILD_PAIR_CONDITION(!!tpm2_pcr_bank_to_string(pcr_bank), "tpm2-pcr-bank", JSON_BUILD_STRING(tpm2_pcr_bank_to_string(pcr_bank))),
2b92a672 1133 JSON_BUILD_PAIR_CONDITION(!!tpm2_primary_alg_to_string(primary_alg), "tpm2-primary-alg", JSON_BUILD_STRING(tpm2_primary_alg_to_string(primary_alg))),
5e521624
LP
1134 JSON_BUILD_PAIR("tpm2-policy-hash", JSON_BUILD_HEX(policy_hash, policy_hash_size))));
1135 if (r < 0)
1136 return r;
1137
1138 if (ret)
1139 *ret = TAKE_PTR(v);
1140
1141 return keyslot;
1142}
07697bfe 1143
2b92a672 1144const char *tpm2_pcr_bank_to_string(uint16_t bank) {
07697bfe
LP
1145 /* For now, let's officially only support these two. We can extend this later on, should the need
1146 * arise. */
07697bfe
LP
1147 if (bank == TPM2_ALG_SHA256)
1148 return "sha256";
1149 if (bank == TPM2_ALG_SHA1)
1150 return "sha1";
1151 return NULL;
1152}
1153
1154int tpm2_pcr_bank_from_string(const char *bank) {
1155 if (streq_ptr(bank, "sha256"))
1156 return TPM2_ALG_SHA256;
1157 if (streq_ptr(bank, "sha1"))
1158 return TPM2_ALG_SHA1;
1159 return -EINVAL;
1160}
2b92a672
LP
1161
1162const char *tpm2_primary_alg_to_string(uint16_t alg) {
1163 if (alg == TPM2_ALG_ECC)
1164 return "ecc";
1165 if (alg == TPM2_ALG_RSA)
1166 return "rsa";
1167 return NULL;
1168}
1169
1170int tpm2_primary_alg_from_string(const char *alg) {
1171 if (streq_ptr(alg, "ecc"))
1172 return TPM2_ALG_ECC;
1173 if (streq_ptr(alg, "rsa"))
1174 return TPM2_ALG_RSA;
1175 return -EINVAL;
1176}