]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tpm2: add tpm2_calculate_sealing_policy() 27517/head
authorDan Streetman <ddstreet@ieee.org>
Thu, 15 Dec 2022 17:56:35 +0000 (12:56 -0500)
committerDan Streetman <ddstreet@ieee.org>
Fri, 26 May 2023 15:12:22 +0000 (11:12 -0400)
This adds a function to fully calculate the authPolicy needed to seal a secret,
and updates tpm2_seal() to use the new function instead of a trial policy.

src/shared/tpm2-util.c

index 1bd2a12acb8b1ce0aba29d59c61fb81d1930ef22..e89c8d9d339dc6574b07d569e05383603a7c7742 100644 (file)
@@ -2274,6 +2274,40 @@ static int tpm2_policy_authorize(
         return tpm2_get_policy_digest(c, session, ret_policy_digest);
 }
 
+/* Extend 'digest' with the calculated policy hash. */
+static int tpm2_calculate_sealing_policy(
+                const TPML_PCR_SELECTION *hash_pcr_selection,
+                const TPM2B_DIGEST *hash_pcr_values,
+                size_t n_hash_pcr_values,
+                const TPM2B_PUBLIC *public,
+                const char *pin,
+                TPM2B_DIGEST *digest) {
+
+        int r;
+
+        assert(digest);
+
+        if (public) {
+                r = tpm2_calculate_policy_authorize(public, NULL, digest);
+                if (r < 0)
+                        return r;
+        }
+
+        if (hash_pcr_selection && !tpm2_tpml_pcr_selection_is_empty(hash_pcr_selection)) {
+                r = tpm2_calculate_policy_pcr(hash_pcr_selection, hash_pcr_values, n_hash_pcr_values, digest);
+                if (r < 0)
+                        return r;
+        }
+
+        if (pin) {
+                r = tpm2_calculate_policy_auth_value(digest);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
 static int tpm2_build_sealing_policy(
                 Tpm2Context *c,
                 const Tpm2Handle *session,
@@ -2356,7 +2390,6 @@ int tpm2_seal(const char *device,
         _cleanup_(erase_and_freep) void *secret = NULL;
         _cleanup_free_ void *hash = NULL;
         TPM2B_SENSITIVE_CREATE hmac_sensitive;
-        TPMI_ALG_PUBLIC primary_alg;
         TPM2B_PUBLIC hmac_template;
         usec_t start;
         TSS2_RC rc;
@@ -2409,59 +2442,37 @@ int tpm2_seal(const char *device,
                         return r;
         }
 
+        TPML_PCR_SELECTION hash_pcr_selection = {};
+        _cleanup_free_ TPM2B_DIGEST *hash_pcr_values = NULL;
+        size_t n_hash_pcr_values = 0;
+        if (hash_pcr_mask) {
+                /* For now, we just read the current values from the system; we need to be able to specify
+                 * expected values, eventually. */
+                tpm2_tpml_pcr_selection_from_mask(hash_pcr_mask, pcr_bank, &hash_pcr_selection);
+                r = tpm2_pcr_read(c, &hash_pcr_selection, &hash_pcr_selection, &hash_pcr_values, &n_hash_pcr_values);
+                if (r < 0)
+                        return r;
+        }
+
         TPM2B_PUBLIC pubkey_tpm2, *authorize_key = NULL;
-        _cleanup_free_ void *fp = NULL;
-        size_t fp_size = 0;
         if (pubkey) {
-                r = openssl_pubkey_to_tpm2_pubkey(pubkey, pubkey_size, &pubkey_tpm2, &fp, &fp_size);
+                r = openssl_pubkey_to_tpm2_pubkey(pubkey, pubkey_size, &pubkey_tpm2, NULL, NULL);
                 if (r < 0)
                         return r;
                 authorize_key = &pubkey_tpm2;
         }
 
-        _cleanup_tpm2_handle_ Tpm2Handle *primary = NULL;
-        r = tpm2_make_primary(c, /* alg = */0, !!ret_srk_buf, &primary_alg, &primary);
+        TPM2B_DIGEST policy_digest;
+        r = tpm2_digest_init(TPM2_ALG_SHA256, &policy_digest);
         if (r < 0)
                 return r;
 
-        /* we cannot use the bind key before its created */
-        _cleanup_tpm2_handle_ Tpm2Handle *encryption_session = NULL;
-        r = tpm2_make_encryption_session(c, primary, &TPM2_HANDLE_NONE, &encryption_session);
-        if (r < 0)
-                return r;
-
-        /* So apparently some TPM implementations don't implement trial mode correctly. To avoid issues let's
-         * avoid it when it is easy to. At the moment we only really need trial mode for the signed PCR
-         * policies (since only then we need to shove PCR values into the policy that don't match current
-         * state anyway), hence if we have none of those we don't need to bother. Hence, let's patch in
-         * TPM2_SE_POLICY even if trial mode is requested unless a pubkey PCR mask is specified that is
-         * non-zero, i.e. signed PCR policy is requested.
-         *
-         * One day we should switch to calculating policy hashes client side when trial mode is requested, to
-         * avoid this mess. */
-        bool trial = (pubkey_pcr_mask != 0);
-
-        _cleanup_tpm2_handle_ Tpm2Handle *policy_session = NULL;
-        r = tpm2_make_policy_session(
-                        c,
-                        primary,
-                        encryption_session,
-                        trial,
-                        &policy_session);
-        if (r < 0)
-                return r;
-
-        _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
-        r = tpm2_build_sealing_policy(
-                        c,
-                        policy_session,
-                        hash_pcr_mask,
-                        pcr_bank,
+        r = tpm2_calculate_sealing_policy(
+                        &hash_pcr_selection,
+                        hash_pcr_values,
+                        n_hash_pcr_values,
                         authorize_key,
-                        fp, fp_size,
-                        pubkey_pcr_mask,
-                        /* signature_json= */ NULL,
-                        !!pin,
+                        pin,
                         &policy_digest);
         if (r < 0)
                 return r;
@@ -2477,7 +2488,7 @@ int tpm2_seal(const char *device,
                         .objectAttributes = TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT,
                         .parameters.keyedHashDetail.scheme.scheme = TPM2_ALG_NULL,
                         .unique.keyedHash.size = SHA256_DIGEST_SIZE,
-                        .authPolicy = *policy_digest,
+                        .authPolicy = policy_digest,
                 },
         };
 
@@ -2501,11 +2512,22 @@ int tpm2_seal(const char *device,
         if (r < 0)
                 return log_error_errno(r, "Failed to generate secret key: %m");
 
+        _cleanup_tpm2_handle_ Tpm2Handle *primary_handle = NULL;
+        TPMI_ALG_PUBLIC primary_alg;
+        r = tpm2_make_primary(c, /* alg = */0, !!ret_srk_buf, &primary_alg, &primary_handle);
+        if (r < 0)
+                return r;
+
+        _cleanup_tpm2_handle_ Tpm2Handle *encryption_session = NULL;
+        r = tpm2_make_encryption_session(c, primary_handle, &TPM2_HANDLE_NONE, &encryption_session);
+        if (r < 0)
+                return r;
+
         log_debug("Creating HMAC key.");
 
         rc = sym_Esys_Create(
                         c->esys_context,
-                        primary->esys_handle,
+                        primary_handle->esys_handle,
                         encryption_session->esys_handle, /* use HMAC session to enable parameter encryption */
                         ESYS_TR_NONE,
                         ESYS_TR_NONE,
@@ -2545,7 +2567,7 @@ int tpm2_seal(const char *device,
                 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
                                        "Failed to marshal public key: %s", sym_Tss2_RC_Decode(rc));
 
-        hash = memdup(policy_digest->buffer, policy_digest->size);
+        hash = memdup(policy_digest.buffer, policy_digest.size);
         if (!hash)
                 return log_oom();
 
@@ -2555,7 +2577,7 @@ int tpm2_seal(const char *device,
          */
         if (ret_srk_buf) {
                 log_debug("Serializing SRK ESYS_TR reference");
-                rc = sym_Esys_TR_Serialize(c->esys_context, primary->esys_handle, &srk_buf, &srk_buf_size);
+                rc = sym_Esys_TR_Serialize(c->esys_context, primary_handle->esys_handle, &srk_buf, &srk_buf_size);
                 if (rc != TSS2_RC_SUCCESS)
                         return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
                                             "Failed to serialize primary key: %s", sym_Tss2_RC_Decode(rc));
@@ -2583,7 +2605,7 @@ int tpm2_seal(const char *device,
         *ret_blob = TAKE_PTR(blob);
         *ret_blob_size = blob_size;
         *ret_pcr_hash = TAKE_PTR(hash);
-        *ret_pcr_hash_size = policy_digest->size;
+        *ret_pcr_hash_size = policy_digest.size;
         *ret_pcr_bank = pcr_bank;
         *ret_primary_alg = primary_alg;