]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tpm2-util: add generic helpers for sealing/unsealing data
authorLennart Poettering <lennart@poettering.net>
Mon, 23 Oct 2023 08:30:25 +0000 (10:30 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 3 Nov 2023 10:22:55 +0000 (11:22 +0100)
These helpers tpm2_seal_data()/tpm2_unseal_data() are useful for
sealing/unsealing data without any further semantics around them. This
is different from the existing tpm2_seal()/tpm2_unseal() which seal with
a specific policy and serialize in a specific way, as we use it for disk
encryption.

These new helpers are more generic, they do not serialize in a specific
way or imply policy, they are just the core of the sealing/unsealing.

(We should look into porting tpm2_seal()/tpm2_unseal() onto these new
helpers, but this isn#t trivial, since the classic serialization we use
uses a merged marshalling of private/public key, which we'd have to
change in one way or another)

src/shared/tpm2-util.c
src/shared/tpm2-util.h

index 1e776938a43bddecd6704b96ff4e3ffa3cdbd76e..7dbe02be2e2e30950a702bd8a879a63a72d1b1d6 100644 (file)
@@ -4919,6 +4919,131 @@ int tpm2_undefine_policy_nv_index(
         log_debug("Undefined NV index 0x%x", nv_index);
         return 0;
 }
+
+int tpm2_seal_data(
+                Tpm2Context *c,
+                const struct iovec *data,
+                const Tpm2Handle *primary_handle,
+                const Tpm2Handle *encryption_session,
+                const TPM2B_DIGEST *policy,
+                struct iovec *ret_public,
+                struct iovec *ret_private) {
+
+        int r;
+
+        assert(c);
+        assert(data);
+        assert(primary_handle);
+
+        /* This is a generic version of tpm2_seal(), that doesn't imply any policy or any specific
+         * combination of the two keypairs in their marshalling. tpm2_seal() is somewhat specific to the FDE
+         * usecase. We probably should migrate tpm2_seal() to use tpm2_seal_data() eventually. */
+
+        if (data->iov_len >= sizeof_field(TPMS_SENSITIVE_CREATE, data.buffer))
+                return -E2BIG;
+
+        TPMT_PUBLIC hmac_template = {
+                .type = TPM2_ALG_KEYEDHASH,
+                .nameAlg = TPM2_ALG_SHA256,
+                .objectAttributes = TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT,
+                .parameters.keyedHashDetail.scheme.scheme = TPM2_ALG_NULL,
+                .unique.keyedHash.size = data->iov_len,
+                .authPolicy = policy ? *policy : TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE),
+        };
+
+        TPMS_SENSITIVE_CREATE hmac_sensitive = {
+                .data.size = hmac_template.unique.keyedHash.size,
+        };
+
+        CLEANUP_ERASE(hmac_sensitive);
+
+        memcpy_safe(hmac_sensitive.data.buffer, data->iov_base, data->iov_len);
+
+        _cleanup_(Esys_Freep) TPM2B_PUBLIC *public = NULL;
+        _cleanup_(Esys_Freep) TPM2B_PRIVATE *private = NULL;
+        r = tpm2_create(c, primary_handle, encryption_session, &hmac_template, &hmac_sensitive, &public, &private);
+        if (r < 0)
+                return r;
+
+        _cleanup_(iovec_done) struct iovec public_blob = {}, private_blob = {};
+
+        r = tpm2_marshal_private(private, &private_blob.iov_base, &private_blob.iov_len);
+        if (r < 0)
+                return r;
+
+        r = tpm2_marshal_public(public, &public_blob.iov_base, &public_blob.iov_len);
+        if (r < 0)
+                return r;
+
+        if (ret_public)
+                *ret_public = TAKE_STRUCT(public_blob);
+        if (ret_private)
+                *ret_private = TAKE_STRUCT(private_blob);
+
+        return 0;
+}
+
+int tpm2_unseal_data(
+                Tpm2Context *c,
+                const struct iovec *public_blob,
+                const struct iovec *private_blob,
+                const Tpm2Handle *primary_handle,
+                const Tpm2Handle *policy_session,
+                const Tpm2Handle *encryption_session,
+                struct iovec *ret_data) {
+
+        TSS2_RC rc;
+        int r;
+
+        assert(c);
+        assert(public_blob);
+        assert(private_blob);
+        assert(primary_handle);
+
+        TPM2B_PUBLIC public;
+        r = tpm2_unmarshal_public(public_blob->iov_base, public_blob->iov_len, &public);
+        if (r < 0)
+                return r;
+
+        TPM2B_PRIVATE private;
+        r = tpm2_unmarshal_private(private_blob->iov_base, private_blob->iov_len, &private);
+        if (r < 0)
+                return r;
+
+        _cleanup_(tpm2_handle_freep) Tpm2Handle *what = NULL;
+        r = tpm2_load(c, primary_handle, NULL, &public, &private, &what);
+        if (r < 0)
+                return r;
+
+        _cleanup_(Esys_Freep) TPM2B_SENSITIVE_DATA* unsealed = NULL;
+        rc = sym_Esys_Unseal(
+                        c->esys_context,
+                        what->esys_handle,
+                        policy_session ? policy_session->esys_handle : ESYS_TR_NONE,
+                        encryption_session ? encryption_session->esys_handle : ESYS_TR_NONE,
+                        ESYS_TR_NONE,
+                        &unsealed);
+        if (rc == TPM2_RC_PCR_CHANGED)
+                return log_debug_errno(SYNTHETIC_ERRNO(ESTALE),
+                                       "PCR changed while unsealing.");
+        if (rc != TSS2_RC_SUCCESS)
+                return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                       "Failed to unseal data: %s", sym_Tss2_RC_Decode(rc));
+
+        _cleanup_(iovec_done) struct iovec d = {};
+        d = (struct iovec) {
+                .iov_base = memdup(unsealed->buffer, unsealed->size),
+                .iov_len = unsealed->size,
+        };
+
+        explicit_bzero_safe(unsealed->buffer, unsealed->size);
+
+        if (!d.iov_base)
+                return log_oom_debug();
+
+        *ret_data = TAKE_STRUCT(d);
+        return 0;
+}
 #endif /* HAVE_TPM2 */
 
 int tpm2_list_devices(void) {
index 71ddfc514a800c26951308702c13fae889145695..167d141a4450a37d139d4733e3e50fd88ebfddfc 100644 (file)
@@ -238,6 +238,9 @@ int tpm2_define_policy_nv_index(Tpm2Context *c, const Tpm2Handle *session, TPM2_
 int tpm2_write_policy_nv_index(Tpm2Context *c, const Tpm2Handle *policy_session, TPM2_HANDLE nv_index, const Tpm2Handle *nv_handle, const TPM2B_DIGEST *policy_digest);
 int tpm2_undefine_policy_nv_index(Tpm2Context *c, const Tpm2Handle *session, TPM2_HANDLE nv_index, const Tpm2Handle *nv_handle);
 
+int tpm2_seal_data(Tpm2Context *c, const struct iovec *data, const Tpm2Handle *primary_handle, const Tpm2Handle *encryption_session, const TPM2B_DIGEST *policy, struct iovec *ret_public, struct iovec *ret_private);
+int tpm2_unseal_data(Tpm2Context *c, const struct iovec *public, const struct iovec *private, const Tpm2Handle *primary_handle, const Tpm2Handle *policy_session, const Tpm2Handle *encryption_session, struct iovec *ret_data);
+
 int tpm2_serialize(Tpm2Context *c, const Tpm2Handle *handle, void **ret_serialized, size_t *ret_serialized_size);
 int tpm2_deserialize(Tpm2Context *c, const void *serialized, size_t serialized_size, Tpm2Handle **ret_handle);