273 lines
11 KiB
Diff
273 lines
11 KiB
Diff
From 2c78f1e768c75bbea7076fa9242ba484c8f472b5 Mon Sep 17 00:00:00 2001
|
|
From: Dan Streetman <ddstreet@ieee.org>
|
|
Date: Fri, 17 Feb 2023 12:59:18 -0500
|
|
Subject: [PATCH] tpm2: verify symmetric parms in tpm2_context_new()
|
|
|
|
This adds tpm2_get_capability_algs(), tpm2_supports_alg(), and
|
|
tpm2_test_parms(). These functions allow verifying that the TPM supports
|
|
specific algs and parameters.
|
|
|
|
When creating a new context, this checks if the TPM supports the symmetric algs
|
|
we use. If the TPM does not support the symmetric algs and parameters we
|
|
require, we log and return error.
|
|
|
|
(cherry picked from commit a47060bb34c912ea9909fcf617f7b553488b5daf)
|
|
|
|
Related: RHEL-16182
|
|
---
|
|
src/shared/tpm2-util.c | 119 ++++++++++++++++++++++++++++++++++++-----
|
|
src/shared/tpm2-util.h | 4 ++
|
|
src/test/test-tpm2.c | 38 +++++++++++++
|
|
3 files changed, 149 insertions(+), 12 deletions(-)
|
|
|
|
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
|
|
index 460ebe62c7..91f66aaaf4 100644
|
|
--- a/src/shared/tpm2-util.c
|
|
+++ b/src/shared/tpm2-util.c
|
|
@@ -49,6 +49,7 @@ static TSS2_RC (*sym_Esys_PolicyPCR)(ESYS_CONTEXT *esysContext, ESYS_TR policySe
|
|
static TSS2_RC (*sym_Esys_ReadPublic)(ESYS_CONTEXT *esysContext, ESYS_TR objectHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_PUBLIC **outPublic, TPM2B_NAME **name, TPM2B_NAME **qualifiedName) = NULL;
|
|
static TSS2_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;
|
|
static TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType) = NULL;
|
|
+static TSS2_RC (*sym_Esys_TestParms)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPMT_PUBLIC_PARMS *parameters) = NULL;
|
|
static TSS2_RC (*sym_Esys_TR_Deserialize)(ESYS_CONTEXT *esys_context, uint8_t const *buffer, size_t buffer_size, ESYS_TR *esys_handle) = NULL;
|
|
static TSS2_RC (*sym_Esys_TR_FromTPMPublic)(ESYS_CONTEXT *esysContext, TPM2_HANDLE tpm_handle, ESYS_TR optionalSession1, ESYS_TR optionalSession2, ESYS_TR optionalSession3, ESYS_TR *object) = NULL;
|
|
static TSS2_RC (*sym_Esys_TR_GetName)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_NAME **name) = NULL;
|
|
@@ -95,6 +96,7 @@ int dlopen_tpm2(void) {
|
|
DLSYM_ARG(Esys_ReadPublic),
|
|
DLSYM_ARG(Esys_StartAuthSession),
|
|
DLSYM_ARG(Esys_Startup),
|
|
+ DLSYM_ARG(Esys_TestParms),
|
|
DLSYM_ARG(Esys_TR_Deserialize),
|
|
DLSYM_ARG(Esys_TR_FromTPMPublic),
|
|
DLSYM_ARG(Esys_TR_GetName),
|
|
@@ -218,6 +220,87 @@ static int tpm2_cache_capabilities(Tpm2Context *c) {
|
|
|
|
#define tpm2_capability_pcrs(c) ((c)->capability_pcrs)
|
|
|
|
+/* 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;
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ if (ret)
|
|
+ *ret = algorithms.algProperties[0].algProperties;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/* 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) {
|
|
+ return tpm2_get_capability_alg(c, alg, NULL);
|
|
+}
|
|
+
|
|
+/* Returns 1 if the TPM supports the parms, or 0 if the TPM does not support the parms. */
|
|
+bool tpm2_test_parms(Tpm2Context *c, TPMI_ALG_PUBLIC alg, const TPMU_PUBLIC_PARMS *parms) {
|
|
+ TSS2_RC rc;
|
|
+
|
|
+ assert(c);
|
|
+ assert(parms);
|
|
+
|
|
+ TPMT_PUBLIC_PARMS parameters = {
|
|
+ .type = alg,
|
|
+ .parameters = *parms,
|
|
+ };
|
|
+
|
|
+ rc = sym_Esys_TestParms(c->esys_context, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, ¶meters);
|
|
+ if (rc != TSS2_RC_SUCCESS)
|
|
+ /* The spec says if the parms are not supported the TPM returns "...the appropriate
|
|
+ * unmarshaling error if a parameter is not valid". Since the spec (currently) defines 15
|
|
+ * unmarshaling errors, instead of checking for them all here, let's just assume any error
|
|
+ * indicates unsupported parms, and log the specific error text. */
|
|
+ log_debug("TPM does not support tested parms: %s", sym_Tss2_RC_Decode(rc));
|
|
+
|
|
+ return rc == TSS2_RC_SUCCESS;
|
|
+}
|
|
+
|
|
+static inline bool tpm2_supports_tpmt_sym_def_object(Tpm2Context *c, const TPMT_SYM_DEF_OBJECT *parameters) {
|
|
+ assert(c);
|
|
+ assert(parameters);
|
|
+
|
|
+ TPMU_PUBLIC_PARMS parms = {
|
|
+ .symDetail.sym = *parameters,
|
|
+ };
|
|
+
|
|
+ return tpm2_test_parms(c, TPM2_ALG_SYMCIPHER, &parms);
|
|
+}
|
|
+
|
|
+static inline bool tpm2_supports_tpmt_sym_def(Tpm2Context *c, const TPMT_SYM_DEF *parameters) {
|
|
+ assert(c);
|
|
+ assert(parameters);
|
|
+
|
|
+ /* Unfortunately, TPMT_SYM_DEF and TPMT_SYM_DEF_OBEJECT are separately defined, even though they are
|
|
+ * functionally identical. */
|
|
+ TPMT_SYM_DEF_OBJECT object = {
|
|
+ .algorithm = parameters->algorithm,
|
|
+ .keyBits = parameters->keyBits,
|
|
+ .mode = parameters->mode,
|
|
+ };
|
|
+
|
|
+ return tpm2_supports_tpmt_sym_def_object(c, &object);
|
|
+}
|
|
+
|
|
static Tpm2Context *tpm2_context_free(Tpm2Context *c) {
|
|
if (!c)
|
|
return NULL;
|
|
@@ -233,6 +316,12 @@ static Tpm2Context *tpm2_context_free(Tpm2Context *c) {
|
|
|
|
DEFINE_TRIVIAL_REF_UNREF_FUNC(Tpm2Context, tpm2_context, tpm2_context_free);
|
|
|
|
+static const TPMT_SYM_DEF SESSION_TEMPLATE_SYM_AES_128_CFB = {
|
|
+ .algorithm = TPM2_ALG_AES,
|
|
+ .keyBits.aes = 128,
|
|
+ .mode.aes = TPM2_ALG_CFB, /* The spec requires sessions to use CFB. */
|
|
+};
|
|
+
|
|
int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
|
|
_cleanup_tpm2_context_ Tpm2Context *context = NULL;
|
|
TSS2_RC rc;
|
|
@@ -342,6 +431,22 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
|
|
if (r < 0)
|
|
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)
|
|
+ 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)
|
|
+ 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))
|
|
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support AES-128-CFB.");
|
|
+
|
|
*ret_context = TAKE_PTR(context);
|
|
|
|
return 0;
|
|
@@ -1570,11 +1675,6 @@ static int tpm2_make_encryption_session(
|
|
const Tpm2Handle *bind_key,
|
|
Tpm2Handle **ret_session) {
|
|
|
|
- static const TPMT_SYM_DEF symmetric = {
|
|
- .algorithm = TPM2_ALG_AES,
|
|
- .keyBits.aes = 128,
|
|
- .mode.aes = TPM2_ALG_CFB,
|
|
- };
|
|
const TPMA_SESSION sessionAttributes = TPMA_SESSION_DECRYPT | TPMA_SESSION_ENCRYPT |
|
|
TPMA_SESSION_CONTINUESESSION;
|
|
TSS2_RC rc;
|
|
@@ -1602,7 +1702,7 @@ static int tpm2_make_encryption_session(
|
|
ESYS_TR_NONE,
|
|
NULL,
|
|
TPM2_SE_HMAC,
|
|
- &symmetric,
|
|
+ &SESSION_TEMPLATE_SYM_AES_128_CFB,
|
|
TPM2_ALG_SHA256,
|
|
&session->esys_handle);
|
|
if (rc != TSS2_RC_SUCCESS)
|
|
@@ -1631,11 +1731,6 @@ static int tpm2_make_policy_session(
|
|
bool trial,
|
|
Tpm2Handle **ret_session) {
|
|
|
|
- static const TPMT_SYM_DEF symmetric = {
|
|
- .algorithm = TPM2_ALG_AES,
|
|
- .keyBits.aes = 128,
|
|
- .mode.aes = TPM2_ALG_CFB,
|
|
- };
|
|
TPM2_SE session_type = trial ? TPM2_SE_TRIAL : TPM2_SE_POLICY;
|
|
TSS2_RC rc;
|
|
int r;
|
|
@@ -1665,7 +1760,7 @@ static int tpm2_make_policy_session(
|
|
ESYS_TR_NONE,
|
|
NULL,
|
|
session_type,
|
|
- &symmetric,
|
|
+ &SESSION_TEMPLATE_SYM_AES_128_CFB,
|
|
TPM2_ALG_SHA256,
|
|
&session->esys_handle);
|
|
if (rc != TSS2_RC_SUCCESS)
|
|
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
|
|
index 5e5d9e2604..764104ed58 100644
|
|
--- a/src/shared/tpm2-util.h
|
|
+++ b/src/shared/tpm2-util.h
|
|
@@ -92,6 +92,10 @@ Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle);
|
|
DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Handle*, tpm2_handle_free);
|
|
#define _cleanup_tpm2_handle_ _cleanup_(tpm2_handle_freep)
|
|
|
|
+int tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg);
|
|
+
|
|
+bool tpm2_test_parms(Tpm2Context *c, TPMI_ALG_PUBLIC alg, const TPMU_PUBLIC_PARMS *parms);
|
|
+
|
|
int tpm2_get_good_pcr_banks(Tpm2Context *c, uint32_t pcr_mask, TPMI_ALG_HASH **ret_banks);
|
|
int tpm2_get_good_pcr_banks_strv(Tpm2Context *c, uint32_t pcr_mask, char ***ret);
|
|
|
|
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
|
|
index 0b123c25a7..130a968273 100644
|
|
--- a/src/test/test-tpm2.c
|
|
+++ b/src/test/test-tpm2.c
|
|
@@ -713,6 +713,44 @@ TEST(calculate_policy_pcr) {
|
|
assert_se(digest_check(&d, "7481fd1b116078eb3ac2456e4ad542c9b46b9b8eb891335771ca8e7c8f8e4415"));
|
|
}
|
|
|
|
+TEST(tpm_required_tests) {
|
|
+ int r;
|
|
+
|
|
+ _cleanup_tpm2_context_ Tpm2Context *c = NULL;
|
|
+ r = tpm2_context_new(NULL, &c);
|
|
+ if (r < 0) {
|
|
+ log_tests_skipped("Could not find TPM");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ TPMU_PUBLIC_PARMS parms = {
|
|
+ .symDetail.sym = {
|
|
+ .algorithm = TPM2_ALG_AES,
|
|
+ .keyBits.aes = 128,
|
|
+ .mode.aes = TPM2_ALG_CFB,
|
|
+ },
|
|
+ };
|
|
+
|
|
+ /* Test with invalid parms */
|
|
+ assert_se(!tpm2_test_parms(c, TPM2_ALG_CFB, &parms));
|
|
+
|
|
+ TPMU_PUBLIC_PARMS invalid_parms = parms;
|
|
+ invalid_parms.symDetail.sym.keyBits.aes = 1;
|
|
+ assert_se(!tpm2_test_parms(c, TPM2_ALG_SYMCIPHER, &invalid_parms));
|
|
+
|
|
+ /* Test with valid parms */
|
|
+ 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);
|
|
+
|
|
+ /* 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);
|
|
+}
|
|
+
|
|
#endif /* HAVE_TPM2 */
|
|
|
|
DEFINE_TEST_MAIN(LOG_DEBUG);
|