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.
263 lines
11 KiB
263 lines
11 KiB
8 months ago
|
From c25de62babe4e0734d769de0250544fe8de83d4a Mon Sep 17 00:00:00 2001
|
||
|
From: Dan Streetman <ddstreet@ieee.org>
|
||
|
Date: Fri, 9 Dec 2022 14:49:52 -0500
|
||
|
Subject: [PATCH] tpm2: add tpm2_policy_authorize()
|
||
|
|
||
|
This adds functions to get the digest for a PolicyAuthorize operation. For
|
||
|
building a policy hash, this provides a function to calculate the hash; and for
|
||
|
building a policy hash to satisfy the authPolicy for an existing object, this
|
||
|
provides a function to perform PolicyAuthorize with an existing session.
|
||
|
|
||
|
(cherry picked from commit 5c7852f78c0c2b44be60651430876165a37eea95)
|
||
|
|
||
|
Related: RHEL-16182
|
||
|
---
|
||
|
src/shared/tpm2-util.c | 197 +++++++++++++++++++++++++++++++++++++++++
|
||
|
src/shared/tpm2-util.h | 1 +
|
||
|
src/test/test-tpm2.c | 12 +++
|
||
|
3 files changed, 210 insertions(+)
|
||
|
|
||
|
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
|
||
|
index 35dfa3f371..4be07d8944 100644
|
||
|
--- a/src/shared/tpm2-util.c
|
||
|
+++ b/src/shared/tpm2-util.c
|
||
|
@@ -2066,6 +2066,203 @@ static int tpm2_policy_pcr(
|
||
|
return tpm2_get_policy_digest(c, session, ret_policy_digest);
|
||
|
}
|
||
|
|
||
|
+/* Extend 'digest' with the PolicyAuthorize calculated hash. */
|
||
|
+int tpm2_calculate_policy_authorize(
|
||
|
+ const TPM2B_PUBLIC *public,
|
||
|
+ const TPM2B_DIGEST *policy_ref,
|
||
|
+ TPM2B_DIGEST *digest) {
|
||
|
+
|
||
|
+ TPM2_CC command = TPM2_CC_PolicyAuthorize;
|
||
|
+ TSS2_RC rc;
|
||
|
+ int r;
|
||
|
+
|
||
|
+ assert(public);
|
||
|
+ assert(digest);
|
||
|
+ assert(digest->size == SHA256_DIGEST_SIZE);
|
||
|
+
|
||
|
+ r = dlopen_tpm2();
|
||
|
+ if (r < 0)
|
||
|
+ return log_error_errno(r, "TPM2 support not installed: %m");
|
||
|
+
|
||
|
+ uint8_t buf[sizeof(command)];
|
||
|
+ size_t offset = 0;
|
||
|
+
|
||
|
+ rc = sym_Tss2_MU_TPM2_CC_Marshal(command, buf, sizeof(buf), &offset);
|
||
|
+ if (rc != TSS2_RC_SUCCESS)
|
||
|
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||
|
+ "Failed to marshal PolicyAuthorize command: %s", sym_Tss2_RC_Decode(rc));
|
||
|
+
|
||
|
+ if (offset != sizeof(command))
|
||
|
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||
|
+ "Offset 0x%zx wrong after marshalling PolicyAuthorize command", offset);
|
||
|
+
|
||
|
+ TPM2B_NAME name = {};
|
||
|
+ r = tpm2_calculate_name(&public->publicArea, &name);
|
||
|
+ if (r < 0)
|
||
|
+ return r;
|
||
|
+
|
||
|
+ /* PolicyAuthorize does not use the previous hash value; we must zero and then extend it. */
|
||
|
+ zero(digest->buffer);
|
||
|
+
|
||
|
+ struct iovec data[] = {
|
||
|
+ IOVEC_MAKE(buf, offset),
|
||
|
+ IOVEC_MAKE(name.name, name.size),
|
||
|
+ };
|
||
|
+ r = tpm2_digest_many(TPM2_ALG_SHA256, digest, data, ELEMENTSOF(data), /* extend= */ true);
|
||
|
+ if (r < 0)
|
||
|
+ return r;
|
||
|
+
|
||
|
+ /* PolicyAuthorize requires hashing twice; this is either an extension or rehashing. */
|
||
|
+ if (policy_ref)
|
||
|
+ r = tpm2_digest_many_digests(TPM2_ALG_SHA256, digest, policy_ref, 1, /* extend= */ true);
|
||
|
+ else
|
||
|
+ r = tpm2_digest_rehash(TPM2_ALG_SHA256, digest);
|
||
|
+ if (r < 0)
|
||
|
+ return r;
|
||
|
+
|
||
|
+ tpm2_log_debug_digest(digest, "PolicyAuthorize calculated digest");
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int tpm2_policy_authorize(
|
||
|
+ Tpm2Context *c,
|
||
|
+ const Tpm2Handle *session,
|
||
|
+ TPML_PCR_SELECTION *pcr_selection,
|
||
|
+ const TPM2B_PUBLIC *public,
|
||
|
+ const void *fp,
|
||
|
+ size_t fp_size,
|
||
|
+ JsonVariant *signature_json,
|
||
|
+ TPM2B_DIGEST **ret_policy_digest) {
|
||
|
+
|
||
|
+ TSS2_RC rc;
|
||
|
+ int r;
|
||
|
+
|
||
|
+ assert(c);
|
||
|
+ assert(session);
|
||
|
+ assert(pcr_selection);
|
||
|
+ assert(public);
|
||
|
+ assert(fp && fp_size > 0);
|
||
|
+
|
||
|
+ log_debug("Adding PCR signature policy.");
|
||
|
+
|
||
|
+ _cleanup_tpm2_handle_ Tpm2Handle *pubkey_handle = NULL;
|
||
|
+ r = tpm2_handle_new(c, &pubkey_handle);
|
||
|
+ if (r < 0)
|
||
|
+ return r;
|
||
|
+
|
||
|
+ /* Load the key into the TPM */
|
||
|
+ rc = sym_Esys_LoadExternal(
|
||
|
+ c->esys_context,
|
||
|
+ ESYS_TR_NONE,
|
||
|
+ ESYS_TR_NONE,
|
||
|
+ ESYS_TR_NONE,
|
||
|
+ NULL,
|
||
|
+ public,
|
||
|
+#if HAVE_TSS2_ESYS3
|
||
|
+ /* tpm2-tss >= 3.0.0 requires a ESYS_TR_RH_* constant specifying the requested
|
||
|
+ * hierarchy, older versions need TPM2_RH_* instead. */
|
||
|
+ ESYS_TR_RH_OWNER,
|
||
|
+#else
|
||
|
+ TPM2_RH_OWNER,
|
||
|
+#endif
|
||
|
+ &pubkey_handle->esys_handle);
|
||
|
+ if (rc != TSS2_RC_SUCCESS)
|
||
|
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||
|
+ "Failed to load public key into TPM: %s", sym_Tss2_RC_Decode(rc));
|
||
|
+
|
||
|
+ /* Acquire the "name" of what we just loaded */
|
||
|
+ _cleanup_(Esys_Freep) TPM2B_NAME *pubkey_name = NULL;
|
||
|
+ r = tpm2_get_name(c, pubkey_handle, &pubkey_name);
|
||
|
+ if (r < 0)
|
||
|
+ return r;
|
||
|
+
|
||
|
+ /* If we have a signature, proceed with verifying the PCR digest */
|
||
|
+ const TPMT_TK_VERIFIED *check_ticket;
|
||
|
+ _cleanup_(Esys_Freep) TPMT_TK_VERIFIED *check_ticket_buffer = NULL;
|
||
|
+ _cleanup_(Esys_Freep) TPM2B_DIGEST *approved_policy = NULL;
|
||
|
+ if (signature_json) {
|
||
|
+ r = tpm2_policy_pcr(
|
||
|
+ c,
|
||
|
+ session,
|
||
|
+ pcr_selection,
|
||
|
+ &approved_policy);
|
||
|
+ if (r < 0)
|
||
|
+ return r;
|
||
|
+
|
||
|
+ _cleanup_free_ void *signature_raw = NULL;
|
||
|
+ size_t signature_size;
|
||
|
+
|
||
|
+ r = find_signature(
|
||
|
+ signature_json,
|
||
|
+ pcr_selection,
|
||
|
+ fp, fp_size,
|
||
|
+ approved_policy->buffer,
|
||
|
+ approved_policy->size,
|
||
|
+ &signature_raw,
|
||
|
+ &signature_size);
|
||
|
+ if (r < 0)
|
||
|
+ return r;
|
||
|
+
|
||
|
+ /* TPM2_VerifySignature() will only verify the RSA part of the RSA+SHA256 signature,
|
||
|
+ * hence we need to do the SHA256 part ourselves, first */
|
||
|
+ TPM2B_DIGEST signature_hash = *approved_policy;
|
||
|
+ r = tpm2_digest_rehash(TPM2_ALG_SHA256, &signature_hash);
|
||
|
+ if (r < 0)
|
||
|
+ return r;
|
||
|
+
|
||
|
+ TPMT_SIGNATURE policy_signature = {
|
||
|
+ .sigAlg = TPM2_ALG_RSASSA,
|
||
|
+ .signature.rsassa = {
|
||
|
+ .hash = TPM2_ALG_SHA256,
|
||
|
+ .sig.size = signature_size,
|
||
|
+ },
|
||
|
+ };
|
||
|
+ if (signature_size > sizeof(policy_signature.signature.rsassa.sig.buffer))
|
||
|
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Signature larger than buffer.");
|
||
|
+ memcpy(policy_signature.signature.rsassa.sig.buffer, signature_raw, signature_size);
|
||
|
+
|
||
|
+ rc = sym_Esys_VerifySignature(
|
||
|
+ c->esys_context,
|
||
|
+ pubkey_handle->esys_handle,
|
||
|
+ ESYS_TR_NONE,
|
||
|
+ ESYS_TR_NONE,
|
||
|
+ ESYS_TR_NONE,
|
||
|
+ &signature_hash,
|
||
|
+ &policy_signature,
|
||
|
+ &check_ticket_buffer);
|
||
|
+ if (rc != TSS2_RC_SUCCESS)
|
||
|
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||
|
+ "Failed to validate signature in TPM: %s", sym_Tss2_RC_Decode(rc));
|
||
|
+
|
||
|
+ check_ticket = check_ticket_buffer;
|
||
|
+ } else {
|
||
|
+ /* When enrolling, we pass a NULL ticket */
|
||
|
+ static const TPMT_TK_VERIFIED check_ticket_null = {
|
||
|
+ .tag = TPM2_ST_VERIFIED,
|
||
|
+ .hierarchy = TPM2_RH_OWNER,
|
||
|
+ };
|
||
|
+
|
||
|
+ check_ticket = &check_ticket_null;
|
||
|
+ }
|
||
|
+
|
||
|
+ rc = sym_Esys_PolicyAuthorize(
|
||
|
+ c->esys_context,
|
||
|
+ session->esys_handle,
|
||
|
+ ESYS_TR_NONE,
|
||
|
+ ESYS_TR_NONE,
|
||
|
+ ESYS_TR_NONE,
|
||
|
+ approved_policy,
|
||
|
+ /* policyRef= */ &(const TPM2B_NONCE) {},
|
||
|
+ pubkey_name,
|
||
|
+ check_ticket);
|
||
|
+ if (rc != TSS2_RC_SUCCESS)
|
||
|
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||
|
+ "Failed to push Authorize policy into TPM: %s", sym_Tss2_RC_Decode(rc));
|
||
|
+
|
||
|
+ return tpm2_get_policy_digest(c, session, ret_policy_digest);
|
||
|
+}
|
||
|
+
|
||
|
static int tpm2_build_sealing_policy(
|
||
|
Tpm2Context *c,
|
||
|
const Tpm2Handle *session,
|
||
|
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
|
||
|
index 706d228073..526e2fdfb2 100644
|
||
|
--- a/src/shared/tpm2-util.h
|
||
|
+++ b/src/shared/tpm2-util.h
|
||
|
@@ -91,6 +91,7 @@ static inline int tpm2_digest_init(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest) {
|
||
|
|
||
|
int tpm2_calculate_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name);
|
||
|
int tpm2_calculate_policy_auth_value(TPM2B_DIGEST *digest);
|
||
|
+int tpm2_calculate_policy_authorize(const TPM2B_PUBLIC *public, const TPM2B_DIGEST *policy_ref, TPM2B_DIGEST *digest);
|
||
|
int tpm2_calculate_policy_pcr(const TPML_PCR_SELECTION *pcr_selection, const TPM2B_DIGEST pcr_values[], size_t pcr_values_count, TPM2B_DIGEST *digest);
|
||
|
|
||
|
int tpm2_seal(const char *device, uint32_t hash_pcr_mask, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg, void **ret_srk_buf, size_t *ret_srk_buf_size);
|
||
|
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
|
||
|
index 3fbb31bae0..0b123c25a7 100644
|
||
|
--- a/src/test/test-tpm2.c
|
||
|
+++ b/src/test/test-tpm2.c
|
||
|
@@ -653,6 +653,18 @@ TEST(calculate_policy_auth_value) {
|
||
|
assert_se(digest_check(&d, "759ebd5ed65100e0b4aa2d04b4b789c2672d92ecc9cdda4b5fa16a303132e008"));
|
||
|
}
|
||
|
|
||
|
+TEST(calculate_policy_authorize) {
|
||
|
+ TPM2B_PUBLIC public;
|
||
|
+ TPM2B_DIGEST d;
|
||
|
+
|
||
|
+ tpm2b_public_init(&public);
|
||
|
+ digest_init_sha256(&d, "0000000000000000000000000000000000000000000000000000000000000000");
|
||
|
+ assert_se(tpm2_calculate_policy_authorize(&public, NULL, &d) == 0);
|
||
|
+ assert_se(digest_check(&d, "95213a3784eaab04f427bc7e8851c2f1df0903be8e42428ec25dcefd907baff1"));
|
||
|
+ assert_se(tpm2_calculate_policy_authorize(&public, NULL, &d) == 0);
|
||
|
+ assert_se(digest_check(&d, "95213a3784eaab04f427bc7e8851c2f1df0903be8e42428ec25dcefd907baff1"));
|
||
|
+}
|
||
|
+
|
||
|
TEST(calculate_policy_pcr) {
|
||
|
TPML_PCR_SELECTION pcr_selection;
|
||
|
TPM2B_DIGEST pcr_values[16];
|