From 73592a7cca194a375d78e15df9e33c2d40f2ea74 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Fri, 30 Jun 2023 12:57:23 -0400 Subject: [PATCH 1/5] tpm2: allow tpm2_make_encryption_session() without bind key Allow providing no bind key, and use ESYS_TR_NONE instead. --- src/shared/tpm2-util.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index ab7b58c34c27b..638e2e390f46b 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -2854,6 +2854,7 @@ static int tpm2_make_encryption_session( int r; assert(c); + assert(primary); assert(ret_session); log_debug("Starting HMAC encryption session."); @@ -2869,7 +2870,7 @@ static int tpm2_make_encryption_session( rc = sym_Esys_StartAuthSession( c->esys_context, primary->esys_handle, - bind_key->esys_handle, + bind_key ? bind_key->esys_handle : ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, @@ -4021,7 +4022,7 @@ int tpm2_seal(Tpm2Context *c, } _cleanup_(tpm2_handle_freep) Tpm2Handle *encryption_session = NULL; - r = tpm2_make_encryption_session(c, primary_handle, &TPM2_HANDLE_NONE, &encryption_session); + r = tpm2_make_encryption_session(c, primary_handle, /* bind_key= */ NULL, &encryption_session); if (r < 0) return r; From 171d5b69c025c4a86a5100e9437b934ad84daca4 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Wed, 2 Aug 2023 13:35:46 -0400 Subject: [PATCH 2/5] tpm2: update tpm2 test for supported commands The test expects TPM2_CC_FIRST - 1 and TPM2_CC_LAST + 1 to be unsupported, but those are not necessarily invalid commands. Instead test known-invalid commands. Also add some more valid commands. --- src/test/test-tpm2.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c index e2c700896a372..7195bd613bc50 100644 --- a/src/test/test-tpm2.c +++ b/src/test/test-tpm2.c @@ -987,11 +987,18 @@ TEST(tpm_required_tests) { assert_se(tpm2_supports_alg(c, TPM2_ALG_AES)); assert_se(tpm2_supports_alg(c, TPM2_ALG_CFB)); - /* Test invalid commands */ - assert_se(!tpm2_supports_command(c, TPM2_CC_FIRST - 1)); - assert_se(!tpm2_supports_command(c, TPM2_CC_LAST + 1)); - - /* Test valid commands */ + /* Test invalid commands. TPM specification Part 2 ("Structures") section "TPM_CC (Command Codes)" + * states bits 31:30 and 28:16 are reserved and must be 0. */ + assert_se(!tpm2_supports_command(c, UINT32_C(0x80000000))); + assert_se(!tpm2_supports_command(c, UINT32_C(0x40000000))); + assert_se(!tpm2_supports_command(c, UINT32_C(0x00100000))); + assert_se(!tpm2_supports_command(c, UINT32_C(0x80000144))); + assert_se(!tpm2_supports_command(c, UINT32_C(0x40000144))); + assert_se(!tpm2_supports_command(c, UINT32_C(0x00100144))); + + /* Test valid commands. We should be able to expect all TPMs support these. */ + assert_se(tpm2_supports_command(c, TPM2_CC_Startup)); + assert_se(tpm2_supports_command(c, TPM2_CC_StartAuthSession)); assert_se(tpm2_supports_command(c, TPM2_CC_Create)); assert_se(tpm2_supports_command(c, TPM2_CC_CreatePrimary)); assert_se(tpm2_supports_command(c, TPM2_CC_Unseal)); From 7014006906113acf35d4927ef7f287ddaa935fca Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Fri, 8 Sep 2023 14:22:11 -0400 Subject: [PATCH 3/5] tpm2: use GREEDY_REALLOC_APPEND() in tpm2_get_capability_handles(), cap max value Simplify the function with GREEDY_REALLOC_APPEND(). Also limit the size_t-sized max value to UINT32_MAX since that's the maximum of the range this searches, and the max parameter for tpm2_get_capability() is uint32_t. --- src/shared/tpm2-util.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 638e2e390f46b..21e0ad715990f 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -402,6 +402,8 @@ static int tpm2_get_capability_handles( assert(ret_handles); assert(ret_n_handles); + max = MIN(max, UINT32_MAX); + while (max > 0) { TPMU_CAPABILITIES capability; r = tpm2_get_capability(c, TPM2_CAP_HANDLES, current, (uint32_t) max, &capability); @@ -417,13 +419,10 @@ static int tpm2_get_capability_handles( if (n_handles > SIZE_MAX - handle_list.count) return log_oom_debug(); - if (!GREEDY_REALLOC(handles, n_handles + handle_list.count)) + if (!GREEDY_REALLOC_APPEND(handles, n_handles, handle_list.handle, handle_list.count)) return log_oom_debug(); - memcpy_safe(&handles[n_handles], handle_list.handle, sizeof(handles[0]) * handle_list.count); - max -= handle_list.count; - n_handles += handle_list.count; /* Update current to the handle index after the last handle in the list. */ current = handles[n_handles - 1] + 1; From db7fdf152b5811c2d83e967010bcde8d435e5bc4 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Thu, 31 Aug 2023 09:10:40 -0400 Subject: [PATCH 4/5] tpm2: change tpm2_unseal() to accept Tpm2Context instead of device string This matches the change to tpm2_seal(), which now accepts a Tpm2Context instead of a device string. This also allows using the same TPM context for sealing and unsealing, which will be required by (future) test code when sealing/unsealing using a transient key. --- src/cryptenroll/cryptenroll-tpm2.c | 2 +- src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c | 7 ++++++- src/cryptsetup/cryptsetup-tpm2.c | 9 +++++++-- src/shared/creds-util.c | 7 ++++++- src/shared/tpm2-util.c | 11 +---------- src/shared/tpm2-util.h | 2 +- 6 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/cryptenroll/cryptenroll-tpm2.c b/src/cryptenroll/cryptenroll-tpm2.c index 28bbb932f31e8..ca80058c66100 100644 --- a/src/cryptenroll/cryptenroll-tpm2.c +++ b/src/cryptenroll/cryptenroll-tpm2.c @@ -279,7 +279,7 @@ int enroll_tpm2(struct crypt_device *cd, size_t secret2_size; log_debug("Unsealing for verification..."); - r = tpm2_unseal(device, + r = tpm2_unseal(tpm2_context, hash_pcr_mask, hash_pcr_bank, pubkey, pubkey_size, diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c index dc06e55b6438c..5230a84025442 100644 --- a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c +++ b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c @@ -80,7 +80,12 @@ int acquire_luks2_key( return log_error_errno(r, "Failed to load PCR signature: %m"); } - r = tpm2_unseal(device, + _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL; + r = tpm2_context_new(device, &tpm2_context); + if (r < 0) + return log_error_errno(r, "Failed to create TPM2 context: %m"); + + r = tpm2_unseal(tpm2_context, hash_pcr_mask, pcr_bank, pubkey, pubkey_size, diff --git a/src/cryptsetup/cryptsetup-tpm2.c b/src/cryptsetup/cryptsetup-tpm2.c index fd21408d831c0..036f3d3a006ed 100644 --- a/src/cryptsetup/cryptsetup-tpm2.c +++ b/src/cryptsetup/cryptsetup-tpm2.c @@ -129,8 +129,13 @@ int acquire_tpm2_key( return log_error_errno(r, "Failed to load pcr signature: %m"); } + _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL; + r = tpm2_context_new(device, &tpm2_context); + if (r < 0) + return log_error_errno(r, "Failed to create TPM2 context: %m"); + if (!(flags & TPM2_FLAGS_USE_PIN)) { - r = tpm2_unseal(device, + r = tpm2_unseal(tpm2_context, hash_pcr_mask, pcr_bank, pubkey, pubkey_size, @@ -177,7 +182,7 @@ int acquire_tpm2_key( /* no salting needed, backwards compat with non-salted pins */ b64_salted_pin = TAKE_PTR(pin_str); - r = tpm2_unseal(device, + r = tpm2_unseal(tpm2_context, hash_pcr_mask, pcr_bank, pubkey, pubkey_size, diff --git a/src/shared/creds-util.c b/src/shared/creds-util.c index b755a2afa8eb5..71aff5ef29383 100644 --- a/src/shared/creds-util.c +++ b/src/shared/creds-util.c @@ -1203,9 +1203,14 @@ int decrypt_credential_and_warn( le32toh(z->size)); } + _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL; + r = tpm2_context_new(tpm2_device, &tpm2_context); + if (r < 0) + return r; + // TODO: Add the SRK data to the credential structure so it can be plumbed // through and used to verify the TPM session. - r = tpm2_unseal(tpm2_device, + r = tpm2_unseal(tpm2_context, le64toh(t->pcr_mask), le16toh(t->pcr_bank), z ? z->data : NULL, diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 21e0ad715990f..47a88d3d20f09 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -4081,7 +4081,7 @@ int tpm2_seal(Tpm2Context *c, #define RETRY_UNSEAL_MAX 30u -int tpm2_unseal(const char *device, +int tpm2_unseal(Tpm2Context *c, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, @@ -4112,10 +4112,6 @@ int tpm2_unseal(const char *device, assert(TPM2_PCR_MASK_VALID(hash_pcr_mask)); assert(TPM2_PCR_MASK_VALID(pubkey_pcr_mask)); - r = dlopen_tpm2(); - if (r < 0) - return r; - /* So here's what we do here: We connect to the TPM2 chip. As we do when sealing we generate a * "primary" key on the TPM2 chip, with the same parameters as well as a PCR-bound policy session. * Given we pass the same parameters, this will result in the same "primary" key, and same policy @@ -4132,11 +4128,6 @@ int tpm2_unseal(const char *device, if (r < 0) return log_debug_errno(r, "Could not extract parts from blob: %m"); - _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL; - r = tpm2_context_new(device, &c); - if (r < 0) - return r; - /* Older code did not save the pcr_bank, and unsealing needed to detect the best pcr bank to use, * so we need to handle that legacy situation. */ if (pcr_bank == UINT16_MAX) { diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index 05627492e3fdb..045f200fbb9d3 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -193,7 +193,7 @@ int tpm2_unmarshal_blob(const void *blob, size_t blob_size, TPM2B_PUBLIC *ret_pu int tpm2_get_or_create_srk(Tpm2Context *c, const Tpm2Handle *session, TPM2B_PUBLIC **ret_public, TPM2B_NAME **ret_name, TPM2B_NAME **ret_qname, Tpm2Handle **ret_handle); int tpm2_seal(Tpm2Context *c, const TPM2B_DIGEST *policy, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, uint16_t *ret_primary_alg, void **ret_srk_buf, size_t *ret_srk_buf_size); -int tpm2_unseal(const char *device, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *srk_buf, size_t srk_buf_size, void **ret_secret, size_t *ret_secret_size); +int tpm2_unseal(Tpm2Context *c, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *srk_buf, size_t srk_buf_size, void **ret_secret, size_t *ret_secret_size); #if HAVE_OPENSSL int tpm2_tpm2b_public_to_openssl_pkey(const TPM2B_PUBLIC *public, EVP_PKEY **ret); From 639dca030bc14b71b3dba0a486f282de316b3e65 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Fri, 8 Sep 2023 13:14:38 -0400 Subject: [PATCH 5/5] tpm2: cache TPM's supported ECC curves This brings the tpm2_supports_ecc_curve() api in line with the other tpm2_supports_*() functions, of returning a boolean. --- src/shared/tpm2-util.c | 57 +++++++++++++++++++++++++++++++----------- src/shared/tpm2-util.h | 3 +++ 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 47a88d3d20f09..1e560f39ceed3 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -289,6 +289,39 @@ static int tpm2_cache_capabilities(Tpm2Context *c) { current_cc = TPMA_CC_TO_TPM2_CC(commands.commandAttributes[commands.count - 1]) + 1; } + /* Cache the ECC curves. The spec isn't actually clear if ECC curves can be added/removed + * while running, but that would be crazy, so let's hope it is not possible. */ + TPM2_ECC_CURVE current_ecc_curve = TPM2_ECC_NONE; + for (;;) { + r = tpm2_get_capability( + c, + TPM2_CAP_ECC_CURVES, + current_ecc_curve, + TPM2_MAX_ECC_CURVES, + &capability); + if (r < 0) + return r; + + TPML_ECC_CURVE ecc_curves = capability.eccCurves; + + /* ECC support isn't required */ + if (ecc_curves.count == 0) + break; + + if (!GREEDY_REALLOC_APPEND( + c->capability_ecc_curves, + c->n_capability_ecc_curves, + ecc_curves.eccCurves, + ecc_curves.count)) + return log_oom_debug(); + + if (r == 0) + break; + + /* Set current_ecc_curve to index after last ecc curve the TPM provided */ + current_ecc_curve = ecc_curves.eccCurves[ecc_curves.count - 1] + 1; + } + /* Cache the PCR capabilities, which are safe to cache, as the only way they can change is * TPM2_PCR_Allocate(), which changes the allocation after the next _TPM_Init(). If the TPM is * reinitialized while we are using it, all our context and sessions will be invalid, so we can @@ -358,23 +391,16 @@ bool tpm2_supports_command(Tpm2Context *c, TPM2_CC command) { return tpm2_get_capability_command(c, command, NULL); } -/* Returns 1 if the TPM supports the ECC curve, 0 if not, or < 0 for any error. */ -static int tpm2_supports_ecc_curve(Tpm2Context *c, TPM2_ECC_CURVE curve) { - TPMU_CAPABILITIES capability; - int r; - - /* The spec explicitly states the TPM2_ECC_CURVE should be cast to uint32_t. */ - r = tpm2_get_capability(c, TPM2_CAP_ECC_CURVES, (uint32_t) curve, 1, &capability); - if (r < 0) - return r; +/* Returns true if the TPM supports the ECC curve, otherwise false. */ +bool tpm2_supports_ecc_curve(Tpm2Context *c, TPM2_ECC_CURVE ecc_curve) { + assert(c); - TPML_ECC_CURVE eccCurves = capability.eccCurves; - if (eccCurves.count == 0 || eccCurves.eccCurves[0] != curve) { - log_debug("TPM does not support ECC curve 0x%02" PRIx16 ".", curve); - return 0; - } + FOREACH_ARRAY(curve, c->capability_ecc_curves, c->n_capability_ecc_curves) + if (*curve == ecc_curve) + return true; - return 1; + log_debug("TPM does not support ECC curve 0x%" PRIx16 ".", ecc_curve); + return false; } /* Query the TPM for populated handles. @@ -522,6 +548,7 @@ static Tpm2Context *tpm2_context_free(Tpm2Context *c) { c->capability_algorithms = mfree(c->capability_algorithms); c->capability_commands = mfree(c->capability_commands); + c->capability_ecc_curves = mfree(c->capability_ecc_curves); return mfree(c); } diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index 045f200fbb9d3..3fd2a13f920e6 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -49,6 +49,8 @@ typedef struct { size_t n_capability_algorithms; TPMA_CC *capability_commands; size_t n_capability_commands; + TPM2_ECC_CURVE *capability_ecc_curves; + size_t n_capability_ecc_curves; TPML_PCR_SELECTION capability_pcrs; } Tpm2Context; @@ -107,6 +109,7 @@ int tpm2_create_loaded(Tpm2Context *c, const Tpm2Handle *parent, const Tpm2Handl bool tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg); bool tpm2_supports_command(Tpm2Context *c, TPM2_CC command); +bool tpm2_supports_ecc_curve(Tpm2Context *c, TPM2_ECC_CURVE ecc_curve); bool tpm2_test_parms(Tpm2Context *c, TPMI_ALG_PUBLIC alg, const TPMU_PUBLIC_PARMS *parms);