You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
8.1 KiB
187 lines
8.1 KiB
8 months ago
|
From 3fb5fadcb2c26e9e7da5de8f8bda0fa0e987c443 Mon Sep 17 00:00:00 2001
|
||
|
From: Dan Streetman <ddstreet@ieee.org>
|
||
|
Date: Wed, 14 Jun 2023 13:17:21 -0400
|
||
|
Subject: [PATCH] tpm2: cache TPM algorithms
|
||
|
|
||
|
Cache the supported algorithms when creating a new context.
|
||
|
|
||
|
(cherry picked from commit cbc92a3172609238db572b86fa7da5e543e6a4dd)
|
||
|
|
||
|
Related: RHEL-16182
|
||
|
---
|
||
|
src/shared/tpm2-util.c | 84 +++++++++++++++++++++++++++---------------
|
||
|
src/shared/tpm2-util.h | 4 +-
|
||
|
src/test/test-tpm2.c | 10 ++---
|
||
|
3 files changed, 62 insertions(+), 36 deletions(-)
|
||
|
|
||
|
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
|
||
|
index d38e260f9a..c05c636745 100644
|
||
|
--- a/src/shared/tpm2-util.c
|
||
|
+++ b/src/shared/tpm2-util.c
|
||
|
@@ -199,6 +199,44 @@ static int tpm2_cache_capabilities(Tpm2Context *c) {
|
||
|
|
||
|
assert(c);
|
||
|
|
||
|
+ /* Cache the algorithms. The spec indicates supported algorithms can only be modified during runtime
|
||
|
+ * by the SetAlgorithmSet() command. Unfortunately, the spec doesn't require a TPM reinitialization
|
||
|
+ * after changing the algorithm set (unless the PCR algorithms are changed). However, the spec also
|
||
|
+ * indicates the TPM behavior after SetAlgorithmSet() is "vendor-dependent", giving the example of
|
||
|
+ * flushing sessions and objects, erasing policies, etc. So, if the algorithm set is programatically
|
||
|
+ * changed while we are performing some operation, it's reasonable to assume it will break us even if
|
||
|
+ * we don't cache the algorithms, thus they should be "safe" to cache. */
|
||
|
+ TPM2_ALG_ID current_alg = TPM2_ALG_FIRST;
|
||
|
+ for (;;) {
|
||
|
+ r = tpm2_get_capability(
|
||
|
+ c,
|
||
|
+ TPM2_CAP_ALGS,
|
||
|
+ (uint32_t) current_alg, /* The spec states to cast TPM2_ALG_ID to uint32_t. */
|
||
|
+ TPM2_MAX_CAP_ALGS,
|
||
|
+ &capability);
|
||
|
+ if (r < 0)
|
||
|
+ return r;
|
||
|
+
|
||
|
+ TPML_ALG_PROPERTY algorithms = capability.algorithms;
|
||
|
+
|
||
|
+ /* We should never get 0; the TPM must support some algorithms, and it must not set 'more' if
|
||
|
+ * there are no more. */
|
||
|
+ assert(algorithms.count > 0);
|
||
|
+
|
||
|
+ if (!GREEDY_REALLOC_APPEND(
|
||
|
+ c->capability_algorithms,
|
||
|
+ c->n_capability_algorithms,
|
||
|
+ algorithms.algProperties,
|
||
|
+ algorithms.count))
|
||
|
+ return log_oom();
|
||
|
+
|
||
|
+ if (r == 0)
|
||
|
+ break;
|
||
|
+
|
||
|
+ /* Set current_alg to alg id after last alg id the TPM provided */
|
||
|
+ current_alg = algorithms.algProperties[algorithms.count - 1].alg + 1;
|
||
|
+ }
|
||
|
+
|
||
|
/* Cache the command capabilities. The spec isn't actually clear if commands can be added/removed
|
||
|
* while running, but that would be crazy, so let's hope it is not possbile. */
|
||
|
TPM2_CC current_cc = TPM2_CC_FIRST;
|
||
|
@@ -255,35 +293,26 @@ static int tpm2_cache_capabilities(Tpm2Context *c) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
-/* Get the TPMA_ALGORITHM for a TPM2_ALG_ID.
|
||
|
- *
|
||
|
- * Returns 1 if the TPM supports the algorithm and the TPMA_ALGORITHM is provided, or 0 if the TPM does not
|
||
|
- * support the algorithm, or < 0 for any errors. */
|
||
|
-static int tpm2_get_capability_alg(Tpm2Context *c, TPM2_ALG_ID alg, TPMA_ALGORITHM *ret) {
|
||
|
- TPMU_CAPABILITIES capability;
|
||
|
- int r;
|
||
|
-
|
||
|
+/* Get the TPMA_ALGORITHM for a TPM2_ALG_ID. Returns true if the TPM supports the algorithm and the
|
||
|
+ * TPMA_ALGORITHM is provided, otherwise false. */
|
||
|
+static bool tpm2_get_capability_alg(Tpm2Context *c, TPM2_ALG_ID alg, TPMA_ALGORITHM *ret) {
|
||
|
assert(c);
|
||
|
|
||
|
- /* The spec explicitly states the TPM2_ALG_ID should be cast to uint32_t. */
|
||
|
- r = tpm2_get_capability(c, TPM2_CAP_ALGS, (uint32_t) alg, 1, &capability);
|
||
|
- if (r < 0)
|
||
|
- return r;
|
||
|
-
|
||
|
- TPML_ALG_PROPERTY algorithms = capability.algorithms;
|
||
|
- if (algorithms.count == 0 || algorithms.algProperties[0].alg != alg) {
|
||
|
- log_debug("TPM does not support alg 0x%02" PRIx16 ".", alg);
|
||
|
- return 0;
|
||
|
- }
|
||
|
+ FOREACH_ARRAY(alg_prop, c->capability_algorithms, c->n_capability_algorithms)
|
||
|
+ if (alg_prop->alg == alg) {
|
||
|
+ if (ret)
|
||
|
+ *ret = alg_prop->algProperties;
|
||
|
+ return true;
|
||
|
+ }
|
||
|
|
||
|
+ log_debug("TPM does not support alg 0x%02" PRIx16 ".", alg);
|
||
|
if (ret)
|
||
|
- *ret = algorithms.algProperties[0].algProperties;
|
||
|
+ *ret = 0;
|
||
|
|
||
|
- return 1;
|
||
|
+ return false;
|
||
|
}
|
||
|
|
||
|
-/* Returns 1 if the TPM supports the alg, 0 if the TPM does not support the alg, or < 0 for any error. */
|
||
|
-int tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg) {
|
||
|
+bool tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg) {
|
||
|
return tpm2_get_capability_alg(c, alg, NULL);
|
||
|
}
|
||
|
|
||
|
@@ -473,6 +502,7 @@ static Tpm2Context *tpm2_context_free(Tpm2Context *c) {
|
||
|
c->tcti_context = mfree(c->tcti_context);
|
||
|
c->tcti_dl = safe_dlclose(c->tcti_dl);
|
||
|
|
||
|
+ c->capability_algorithms = mfree(c->capability_algorithms);
|
||
|
c->capability_commands = mfree(c->capability_commands);
|
||
|
|
||
|
return mfree(c);
|
||
|
@@ -598,16 +628,10 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
|
||
|
return r;
|
||
|
|
||
|
/* We require AES and CFB support for session encryption. */
|
||
|
- r = tpm2_supports_alg(context, TPM2_ALG_AES);
|
||
|
- if (r < 0)
|
||
|
- return r;
|
||
|
- if (r == 0)
|
||
|
+ if (!tpm2_supports_alg(context, TPM2_ALG_AES))
|
||
|
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support AES.");
|
||
|
|
||
|
- r = tpm2_supports_alg(context, TPM2_ALG_CFB);
|
||
|
- if (r < 0)
|
||
|
- return r;
|
||
|
- if (r == 0)
|
||
|
+ if (!tpm2_supports_alg(context, TPM2_ALG_CFB))
|
||
|
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support CFB.");
|
||
|
|
||
|
if (!tpm2_supports_tpmt_sym_def(context, &SESSION_TEMPLATE_SYM_AES_128_CFB))
|
||
|
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
|
||
|
index 1ca1a2e503..64a2fd3677 100644
|
||
|
--- a/src/shared/tpm2-util.h
|
||
|
+++ b/src/shared/tpm2-util.h
|
||
|
@@ -62,6 +62,8 @@ typedef struct {
|
||
|
ESYS_CONTEXT *esys_context;
|
||
|
|
||
|
/* Some selected cached capabilities of the TPM */
|
||
|
+ TPMS_ALG_PROPERTY *capability_algorithms;
|
||
|
+ size_t n_capability_algorithms;
|
||
|
TPMA_CC *capability_commands;
|
||
|
size_t n_capability_commands;
|
||
|
TPML_PCR_SELECTION capability_pcrs;
|
||
|
@@ -86,7 +88,7 @@ int tpm2_handle_new(Tpm2Context *context, Tpm2Handle **ret_handle);
|
||
|
Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle);
|
||
|
DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Handle*, tpm2_handle_free);
|
||
|
|
||
|
-int tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg);
|
||
|
+bool tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg);
|
||
|
bool tpm2_supports_command(Tpm2Context *c, TPM2_CC command);
|
||
|
|
||
|
bool tpm2_test_parms(Tpm2Context *c, TPMI_ALG_PUBLIC alg, const TPMU_PUBLIC_PARMS *parms);
|
||
|
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
|
||
|
index dfc8b98e08..8fd859b83d 100644
|
||
|
--- a/src/test/test-tpm2.c
|
||
|
+++ b/src/test/test-tpm2.c
|
||
|
@@ -651,13 +651,13 @@ TEST(tpm_required_tests) {
|
||
|
assert_se(tpm2_test_parms(c, TPM2_ALG_SYMCIPHER, &parms));
|
||
|
|
||
|
/* Test invalid algs */
|
||
|
- assert_se(tpm2_supports_alg(c, TPM2_ALG_ERROR) == 0);
|
||
|
- assert_se(tpm2_supports_alg(c, TPM2_ALG_LAST + 1) == 0);
|
||
|
+ assert_se(!tpm2_supports_alg(c, TPM2_ALG_ERROR));
|
||
|
+ assert_se(!tpm2_supports_alg(c, TPM2_ALG_LAST + 1));
|
||
|
|
||
|
/* Test valid algs */
|
||
|
- assert_se(tpm2_supports_alg(c, TPM2_ALG_RSA) == 1);
|
||
|
- assert_se(tpm2_supports_alg(c, TPM2_ALG_AES) == 1);
|
||
|
- assert_se(tpm2_supports_alg(c, TPM2_ALG_CFB) == 1);
|
||
|
+ assert_se(tpm2_supports_alg(c, TPM2_ALG_RSA));
|
||
|
+ 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));
|