summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaan De Meyer <daan.j.demeyer@gmail.com>2023-05-26 23:08:15 +0200
committerGitHub <noreply@github.com>2023-05-26 23:08:15 +0200
commitdcc5547b8b13c21e9d85cb568b7772f771189d2b (patch)
treea9508fb09976dcdc7c0f19dfef375bb2abb0991d
parentMerge pull request #27787 from keszybz/firstboot-synchronous-restart (diff)
parenttpm2: add tpm2_calculate_sealing_policy() (diff)
downloadsystemd-dcc5547b8b13c21e9d85cb568b7772f771189d2b.tar.xz
systemd-dcc5547b8b13c21e9d85cb568b7772f771189d2b.zip
Merge pull request #27517 from ddstreet/tpm2_calculate_policy
Tpm2 calculate policy
-rw-r--r--src/shared/tpm2-util.c915
-rw-r--r--src/shared/tpm2-util.h22
-rw-r--r--src/test/test-tpm2.c213
3 files changed, 900 insertions, 250 deletions
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 213691f0d4..e89c8d9d33 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -64,10 +64,14 @@ TSS2_RC (*sym_Esys_VerifySignature)(ESYS_CONTEXT *esysContext, ESYS_TR keyHandle
const char* (*sym_Tss2_RC_Decode)(TSS2_RC rc) = NULL;
+TSS2_RC (*sym_Tss2_MU_TPM2_CC_Marshal)(TPM2_CC src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Marshal)(TPM2B_PRIVATE const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PRIVATE *dest) = NULL;
TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Marshal)(TPM2B_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PUBLIC *dest) = NULL;
+TSS2_RC (*sym_Tss2_MU_TPML_PCR_SELECTION_Marshal)(TPML_PCR_SELECTION const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
+TSS2_RC (*sym_Tss2_MU_TPMT_HA_Marshal)(TPMT_HA const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
+TSS2_RC (*sym_Tss2_MU_TPMT_PUBLIC_Marshal)(TPMT_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
int dlopen_tpm2(void) {
int r;
@@ -114,10 +118,14 @@ int dlopen_tpm2(void) {
return dlopen_many_sym_or_warn(
&libtss2_mu_dl, "libtss2-mu.so.0", LOG_DEBUG,
+ DLSYM_ARG(Tss2_MU_TPM2_CC_Marshal),
DLSYM_ARG(Tss2_MU_TPM2B_PRIVATE_Marshal),
DLSYM_ARG(Tss2_MU_TPM2B_PRIVATE_Unmarshal),
DLSYM_ARG(Tss2_MU_TPM2B_PUBLIC_Marshal),
- DLSYM_ARG(Tss2_MU_TPM2B_PUBLIC_Unmarshal));
+ DLSYM_ARG(Tss2_MU_TPM2B_PUBLIC_Unmarshal),
+ DLSYM_ARG(Tss2_MU_TPML_PCR_SELECTION_Marshal),
+ DLSYM_ARG(Tss2_MU_TPMT_HA_Marshal),
+ DLSYM_ARG(Tss2_MU_TPMT_PUBLIC_Marshal));
}
static Tpm2Context *tpm2_context_free(Tpm2Context *c) {
@@ -977,6 +985,11 @@ static void tpm2_log_debug_digest(const TPM2B_DIGEST *digest, const char *msg) {
tpm2_log_debug_buffer(digest->buffer, digest->size, msg ?: "Digest");
}
+static void tpm2_log_debug_name(const TPM2B_NAME *name, const char *msg) {
+ if (name)
+ tpm2_log_debug_buffer(name->name, name->size, msg ?: "Name");
+}
+
static int tpm2_get_policy_digest(
Tpm2Context *c,
const Tpm2Handle *session,
@@ -1017,11 +1030,11 @@ static int tpm2_pcr_read(
const TPML_PCR_SELECTION *pcr_selection,
TPML_PCR_SELECTION *ret_pcr_selection,
TPM2B_DIGEST **ret_pcr_values,
- size_t *ret_pcr_values_size) {
+ size_t *ret_n_pcr_values) {
_cleanup_free_ TPM2B_DIGEST *pcr_values = NULL;
TPML_PCR_SELECTION remaining, total_read = {};
- size_t pcr_values_size = 0;
+ size_t n_pcr_values = 0;
TSS2_RC rc;
assert(c);
@@ -1056,12 +1069,12 @@ static int tpm2_pcr_read(
tpm2_tpml_pcr_selection_sub(&remaining, current_read);
tpm2_tpml_pcr_selection_add(&total_read, current_read);
- if (!GREEDY_REALLOC(pcr_values, pcr_values_size + current_values->count))
+ if (!GREEDY_REALLOC(pcr_values, n_pcr_values + current_values->count))
return log_oom();
- memcpy_safe(&pcr_values[pcr_values_size], current_values->digests,
+ memcpy_safe(&pcr_values[n_pcr_values], current_values->digests,
current_values->count * sizeof(TPM2B_DIGEST));
- pcr_values_size += current_values->count;
+ n_pcr_values += current_values->count;
if (DEBUG_LOGGING) {
unsigned i = 0;
@@ -1084,8 +1097,8 @@ static int tpm2_pcr_read(
*ret_pcr_selection = total_read;
if (ret_pcr_values)
*ret_pcr_values = TAKE_PTR(pcr_values);
- if (ret_pcr_values_size)
- *ret_pcr_values_size = pcr_values_size;
+ if (ret_n_pcr_values)
+ *ret_n_pcr_values = n_pcr_values;
return 0;
}
@@ -1097,7 +1110,7 @@ static int tpm2_pcr_mask_good(
_cleanup_free_ TPM2B_DIGEST *pcr_values = NULL;
TPML_PCR_SELECTION selection;
- size_t pcr_values_size = 0;
+ size_t n_pcr_values = 0;
int r;
assert(c);
@@ -1108,14 +1121,14 @@ static int tpm2_pcr_mask_good(
tpm2_tpml_pcr_selection_from_mask(mask, bank, &selection);
- r = tpm2_pcr_read(c, &selection, &selection, &pcr_values, &pcr_values_size);
+ r = tpm2_pcr_read(c, &selection, &selection, &pcr_values, &n_pcr_values);
if (r < 0)
return r;
/* If at least one of the selected PCR values is something other than all 0x00 or all 0xFF we are happy. */
unsigned i = 0;
FOREACH_PCR_IN_TPML_PCR_SELECTION(pcr, s, &selection) {
- assert(i < pcr_values_size);
+ assert(i < n_pcr_values);
if (!memeqbyte(0x00, pcr_values[i].buffer, pcr_values[i].size) &&
!memeqbyte(0xFF, pcr_values[i].buffer, pcr_values[i].size))
@@ -1380,19 +1393,106 @@ int tpm2_get_good_pcr_banks_strv(
#endif
}
-static void hash_pin(const char *pin, size_t len, TPM2B_AUTH *auth) {
- struct sha256_ctx hash;
+/* Hash data into the digest.
+ *
+ * If 'extend' is true, the hashing operation starts with the existing digest hash (and the digest is
+ * required to have a hash and its size must be correct). If 'extend' is false, the digest size is
+ * initialized to the correct size for 'alg' and the hashing operation does not include any existing digest
+ * hash. If 'extend' is false and no data is provided, the digest is initialized to a zero digest.
+ *
+ * On success, the digest hash will be updated with the hashing operation result and the digest size will be
+ * correct for 'alg'.
+ *
+ * This currently only provides SHA256, so 'alg' must be TPM2_ALG_SHA256. */
+int tpm2_digest_many(
+ TPMI_ALG_HASH alg,
+ TPM2B_DIGEST *digest,
+ const struct iovec data[],
+ size_t n_data,
+ bool extend) {
+
+ struct sha256_ctx ctx;
+
+ assert(digest);
+ assert(data || n_data == 0);
+
+ if (alg != TPM2_ALG_SHA256)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Hash algorithm not supported: 0x%x", alg);
+
+ if (extend && digest->size != SHA256_DIGEST_SIZE)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Digest size 0x%x, require 0x%x",
+ digest->size, (unsigned)SHA256_DIGEST_SIZE);
+
+ /* Since we're hardcoding SHA256 (for now), we can check this at compile time. */
+ assert_cc(sizeof(digest->buffer) >= SHA256_DIGEST_SIZE);
+
+ CLEANUP_ERASE(ctx);
+
+ sha256_init_ctx(&ctx);
+
+ if (extend)
+ sha256_process_bytes(digest->buffer, digest->size, &ctx);
+ else {
+ *digest = (TPM2B_DIGEST){ .size = SHA256_DIGEST_SIZE, };
+ if (n_data == 0) /* If not extending and no data, return zero hash */
+ return 0;
+ }
+
+ for (size_t i = 0; i < n_data; i++)
+ sha256_process_bytes(data[i].iov_base, data[i].iov_len, &ctx);
+
+ sha256_finish_ctx(&ctx, digest->buffer);
+
+ return 0;
+}
+
+/* Same as tpm2_digest_many() but data is contained in TPM2B_DIGEST[]. The digests may be any size digests. */
+int tpm2_digest_many_digests(
+ TPMI_ALG_HASH alg,
+ TPM2B_DIGEST *digest,
+ const TPM2B_DIGEST data[],
+ size_t n_data,
+ bool extend) {
+
+ _cleanup_free_ struct iovec *iovecs = NULL;
+
+ assert(data || n_data == 0);
+
+ iovecs = new(struct iovec, n_data);
+ if (!iovecs)
+ return log_oom();
+
+ for (size_t i = 0; i < n_data; i++)
+ iovecs[i] = IOVEC_MAKE((void*) data[i].buffer, data[i].size);
+
+ return tpm2_digest_many(alg, digest, iovecs, n_data, extend);
+}
+
+static int tpm2_set_auth(Tpm2Context *c, const Tpm2Handle *handle, const char *pin) {
+ TPM2B_AUTH auth = {};
+ TSS2_RC rc;
+ int r;
+
+ assert(c);
+ assert(handle);
- assert(auth);
- assert(pin);
+ if (!pin)
+ return 0;
- auth->size = SHA256_DIGEST_SIZE;
+ CLEANUP_ERASE(auth);
- CLEANUP_ERASE(hash);
+ r = tpm2_digest_buffer(TPM2_ALG_SHA256, &auth, pin, strlen(pin), /* extend= */ false);
+ if (r < 0)
+ return r;
- sha256_init_ctx(&hash);
- sha256_process_bytes(pin, len, &hash);
- sha256_finish_ctx(&hash, auth->buffer);
+ rc = sym_Esys_TR_SetAuth(c->esys_context, handle->esys_handle, &auth);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to load PIN in TPM: %s", sym_Tss2_RC_Decode(rc));
+
+ return 0;
}
static bool tpm2_is_encryption_session(Tpm2Context *c, const Tpm2Handle *session) {
@@ -1413,7 +1513,6 @@ static int tpm2_make_encryption_session(
Tpm2Context *c,
const Tpm2Handle *primary,
const Tpm2Handle *bind_key,
- const char *pin,
Tpm2Handle **ret_session) {
static const TPMT_SYM_DEF symmetric = {
@@ -1429,28 +1528,6 @@ static int tpm2_make_encryption_session(
assert(c);
assert(ret_session);
- /*
- * if a pin is set for the seal object, use it to bind the session
- * key to that object. This prevents active bus interposers from
- * faking a TPM and seeing the unsealed value. An active interposer
- * could fake a TPM, satisfying the encrypted session, and just
- * forward everything to the *real* TPM.
- */
- if (pin) {
- TPM2B_AUTH auth = {};
-
- CLEANUP_ERASE(auth);
-
- hash_pin(pin, strlen(pin), &auth);
-
- rc = sym_Esys_TR_SetAuth(c->esys_context, bind_key->esys_handle, &auth);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(
- SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to load PIN in TPM: %s",
- sym_Tss2_RC_Decode(rc));
- }
-
log_debug("Starting HMAC encryption session.");
/* Start a salted, unbound HMAC session with a well-known key (e.g. primary key) as tpmKey, which
@@ -1762,16 +1839,311 @@ static int find_signature(
#endif
}
-static int tpm2_build_sealing_policy(
+/* Calculates the "name" of a public key.
+ *
+ * As specified in TPM2 spec "Part 1: Architecture", a key's "name" is its nameAlg value followed by a hash
+ * of its TPM2 public area, all properly marshalled. This allows a key's "name" to be dependent not only on
+ * the key fingerprint, but also on the TPM2-specific fields that associated with the key (i.e. all fields in
+ * TPMT_PUBLIC). Note that this means an existing key may not change any of its TPMT_PUBLIC fields, since
+ * that would also change the key name.
+ *
+ * Since we (currently) hardcode to always using SHA256 for hashing, this returns an error if the public key
+ * nameAlg is not TPM2_ALG_SHA256. */
+int tpm2_calculate_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name) {
+ TSS2_RC rc;
+ int r;
+
+ assert(public);
+ assert(ret_name);
+
+ r = dlopen_tpm2();
+ if (r < 0)
+ return log_error_errno(r, "TPM2 support not installed: %m");
+
+ if (public->nameAlg != TPM2_ALG_SHA256)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Unsupported nameAlg: 0x%x",
+ public->nameAlg);
+
+ _cleanup_free_ uint8_t *buf = NULL;
+ size_t size = 0;
+
+ buf = (uint8_t*) new(TPMT_PUBLIC, 1);
+ if (!buf)
+ return log_oom();
+
+ rc = sym_Tss2_MU_TPMT_PUBLIC_Marshal(public, buf, sizeof(TPMT_PUBLIC), &size);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to marshal public key: %s", sym_Tss2_RC_Decode(rc));
+
+ TPM2B_DIGEST digest = {};
+ r = tpm2_digest_buffer(TPM2_ALG_SHA256, &digest, buf, size, /* extend= */ false);
+ if (r < 0)
+ return r;
+
+ TPMT_HA ha = {
+ .hashAlg = TPM2_ALG_SHA256,
+ };
+ assert(digest.size <= sizeof(ha.digest.sha256));
+ memcpy_safe(ha.digest.sha256, digest.buffer, digest.size);
+
+ TPM2B_NAME name;
+ size = 0;
+ rc = sym_Tss2_MU_TPMT_HA_Marshal(&ha, name.name, sizeof(name.name), &size);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to marshal key name: %s", sym_Tss2_RC_Decode(rc));
+ name.size = size;
+
+ tpm2_log_debug_name(&name, "Calculated name");
+
+ *ret_name = name;
+
+ return 0;
+}
+
+/* Get the "name" of a key from the TPM.
+ *
+ * The "name" of a key is explained above in tpm2_calculate_name().
+ *
+ * The handle must reference a key already present in the TPM. It may be either a public key only, or a
+ * public/private keypair. */
+static int tpm2_get_name(
+ Tpm2Context *c,
+ const Tpm2Handle *handle,
+ TPM2B_NAME **ret_name) {
+
+ _cleanup_(Esys_Freep) TPM2B_NAME *name = NULL;
+ TSS2_RC rc;
+
+ assert(c);
+ assert(handle);
+ assert(ret_name);
+
+ rc = sym_Esys_TR_GetName(c->esys_context, handle->esys_handle, &name);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to get name of public key from TPM: %s", sym_Tss2_RC_Decode(rc));
+
+ tpm2_log_debug_name(name, "Object name");
+
+ *ret_name = TAKE_PTR(name);
+
+ return 0;
+}
+
+/* Extend 'digest' with the PolicyAuthValue calculated hash. */
+int tpm2_calculate_policy_auth_value(TPM2B_DIGEST *digest) {
+ TPM2_CC command = TPM2_CC_PolicyAuthValue;
+ TSS2_RC rc;
+ int r;
+
+ 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 PolicyAuthValue command: %s", sym_Tss2_RC_Decode(rc));
+
+ if (offset != sizeof(command))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Offset 0x%zx wrong after marshalling PolicyAuthValue command", offset);
+
+ r = tpm2_digest_buffer(TPM2_ALG_SHA256, digest, buf, offset, /* extend= */ true);
+ if (r < 0)
+ return r;
+
+ tpm2_log_debug_digest(digest, "PolicyAuthValue calculated digest");
+
+ return 0;
+}
+
+static int tpm2_policy_auth_value(
Tpm2Context *c,
const Tpm2Handle *session,
- uint32_t hash_pcr_mask,
- uint16_t pcr_bank,
- const void *pubkey,
- size_t pubkey_size,
- uint32_t pubkey_pcr_mask,
+ TPM2B_DIGEST **ret_policy_digest) {
+
+ TSS2_RC rc;
+
+ assert(c);
+ assert(session);
+
+ log_debug("Adding authValue policy.");
+
+ rc = sym_Esys_PolicyAuthValue(
+ c->esys_context,
+ session->esys_handle,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to add authValue policy to TPM: %s",
+ sym_Tss2_RC_Decode(rc));
+
+ return tpm2_get_policy_digest(c, session, ret_policy_digest);
+}
+
+/* Extend 'digest' with the PolicyPCR calculated hash. */
+int tpm2_calculate_policy_pcr(
+ const TPML_PCR_SELECTION *pcr_selection,
+ const TPM2B_DIGEST pcr_values[],
+ size_t n_pcr_values,
+ TPM2B_DIGEST *digest) {
+
+ TPM2_CC command = TPM2_CC_PolicyPCR;
+ TSS2_RC rc;
+ int r;
+
+ assert(pcr_selection);
+ assert(pcr_values || n_pcr_values == 0);
+ assert(digest);
+ assert(digest->size == SHA256_DIGEST_SIZE);
+
+ r = dlopen_tpm2();
+ if (r < 0)
+ return log_error_errno(r, "TPM2 support not installed: %m");
+
+ TPM2B_DIGEST hash = {};
+ r = tpm2_digest_many_digests(TPM2_ALG_SHA256, &hash, pcr_values, n_pcr_values, /* extend= */ false);
+ if (r < 0)
+ return r;
+
+ _cleanup_free_ uint8_t *buf = NULL;
+ size_t size = 0, maxsize = sizeof(command) + sizeof(*pcr_selection);
+
+ buf = malloc(maxsize);
+ if (!buf)
+ return log_oom();
+
+ rc = sym_Tss2_MU_TPM2_CC_Marshal(command, buf, maxsize, &size);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to marshal PolicyPCR command: %s", sym_Tss2_RC_Decode(rc));
+
+ rc = sym_Tss2_MU_TPML_PCR_SELECTION_Marshal(pcr_selection, buf, maxsize, &size);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to marshal PCR selection: %s", sym_Tss2_RC_Decode(rc));
+
+ struct iovec data[] = {
+ IOVEC_MAKE(buf, size),
+ IOVEC_MAKE(hash.buffer, hash.size),
+ };
+ r = tpm2_digest_many(TPM2_ALG_SHA256, digest, data, ELEMENTSOF(data), /* extend= */ true);
+ if (r < 0)
+ return r;
+
+ tpm2_log_debug_digest(digest, "PolicyPCR calculated digest");
+
+ return 0;
+}
+
+static int tpm2_policy_pcr(
+ Tpm2Context *c,
+ const Tpm2Handle *session,
+ const TPML_PCR_SELECTION *pcr_selection,
+ TPM2B_DIGEST **ret_policy_digest) {
+
+ TSS2_RC rc;
+
+ assert(c);
+ assert(session);
+ assert(pcr_selection);
+
+ log_debug("Adding PCR hash policy.");
+
+ rc = sym_Esys_PolicyPCR(
+ c->esys_context,
+ session->esys_handle,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ NULL,
+ pcr_selection);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to add PCR policy to TPM: %s", sym_Tss2_RC_Decode(rc));
+
+ 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,
- bool use_pin,
TPM2B_DIGEST **ret_policy_digest) {
TSS2_RC rc;
@@ -1779,193 +2151,212 @@ static int tpm2_build_sealing_policy(
assert(c);
assert(session);
- assert(pubkey || pubkey_size == 0);
- assert(pubkey_pcr_mask == 0 || pubkey_size > 0);
+ assert(pcr_selection);
+ assert(public);
+ assert(fp && fp_size > 0);
- log_debug("Building sealing policy.");
+ log_debug("Adding PCR signature policy.");
- if ((hash_pcr_mask | pubkey_pcr_mask) != 0) {
- r = tpm2_pcr_mask_good(c, pcr_bank, hash_pcr_mask|pubkey_pcr_mask);
- if (r < 0)
- return r;
- if (r == 0)
- log_warning("Selected TPM2 PCRs are not initialized on this system.");
- }
+ _cleanup_tpm2_handle_ Tpm2Handle *pubkey_handle = NULL;
+ r = tpm2_handle_new(c, &pubkey_handle);
+ if (r < 0)
+ return r;
- if (pubkey_pcr_mask != 0) {
- _cleanup_free_ void *fp = NULL;
- size_t fp_size = 0;
- TPM2B_PUBLIC pubkey_tpm2;
+ /* 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));
- log_debug("Configuring public key based PCR policy.");
+ /* 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;
- /* Convert the PEM key to TPM2 format */
- r = openssl_pubkey_to_tpm2_pubkey(pubkey, pubkey_size, &pubkey_tpm2, &fp, &fp_size);
+ /* 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_tpm2_handle_ Tpm2Handle *pubkey_handle = NULL;
- r = tpm2_handle_new(c, &pubkey_handle);
+ _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;
- rc = sym_Esys_LoadExternal(
- c->esys_context,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- NULL,
- &pubkey_tpm2,
-#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));
+ /* 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;
- /* Acquire the "name" of what we just loaded */
- _cleanup_(Esys_Freep) TPM2B_NAME *pubkey_name = NULL;
- rc = sym_Esys_TR_GetName(
- c->esys_context,
- pubkey_handle->esys_handle,
- &pubkey_name);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to get name of public key from TPM: %s", sym_Tss2_RC_Decode(rc));
+ 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);
- /* Put together the PCR policy we want to use */
- TPML_PCR_SELECTION pcr_selection;
- tpm2_tpml_pcr_selection_from_mask(pubkey_pcr_mask, (TPMI_ALG_HASH)pcr_bank, &pcr_selection);
- rc = sym_Esys_PolicyPCR(
+ rc = sym_Esys_VerifySignature(
c->esys_context,
- session->esys_handle,
+ pubkey_handle->esys_handle,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
- NULL,
- &pcr_selection);
+ &signature_hash,
+ &policy_signature,
+ &check_ticket_buffer);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to add PCR policy to TPM: %s", sym_Tss2_RC_Decode(rc));
+ "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);
+}
+
+/* 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) {
- /* Get the policy hash of the PCR policy */
- _cleanup_(Esys_Freep) TPM2B_DIGEST *approved_policy = NULL;
- r = tpm2_get_policy_digest(c, session, &approved_policy);
+ int r;
+
+ assert(digest);
+
+ if (public) {
+ r = tpm2_calculate_policy_authorize(public, NULL, digest);
if (r < 0)
return r;
+ }
- /* When we are unlocking and have a signature, let's pass it to the TPM */
- _cleanup_(Esys_Freep) TPMT_TK_VERIFIED *check_ticket_buffer = NULL;
- const TPMT_TK_VERIFIED *check_ticket;
- if (signature_json) {
- _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 = {
- .size = SHA256_DIGEST_SIZE,
- };
- assert(sizeof(signature_hash.buffer) >= SHA256_DIGEST_SIZE);
- sha256_direct(approved_policy->buffer, approved_policy->size, signature_hash.buffer);
-
- 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));
+ 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;
+ }
- 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,
- };
+ if (pin) {
+ r = tpm2_calculate_policy_auth_value(digest);
+ if (r < 0)
+ return r;
+ }
- check_ticket = &check_ticket_null;
- }
+ return 0;
+}
- 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));
+static int tpm2_build_sealing_policy(
+ Tpm2Context *c,
+ const Tpm2Handle *session,
+ uint32_t hash_pcr_mask,
+ uint16_t pcr_bank,
+ const TPM2B_PUBLIC *public,
+ const void *fp,
+ size_t fp_size,
+ uint32_t pubkey_pcr_mask,
+ JsonVariant *signature_json,
+ bool use_pin,
+ TPM2B_DIGEST **ret_policy_digest) {
+
+ int r;
+
+ assert(c);
+ assert(session);
+ assert(pubkey_pcr_mask == 0 || public);
+
+ log_debug("Building sealing policy.");
+
+ if ((hash_pcr_mask | pubkey_pcr_mask) != 0) {
+ r = tpm2_pcr_mask_good(c, pcr_bank, hash_pcr_mask|pubkey_pcr_mask);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ log_warning("Selected TPM2 PCRs are not initialized on this system.");
}
- if (hash_pcr_mask != 0) {
- log_debug("Configuring hash-based PCR policy.");
+ if (pubkey_pcr_mask != 0) {
+ TPML_PCR_SELECTION pcr_selection;
+ tpm2_tpml_pcr_selection_from_mask(pubkey_pcr_mask, (TPMI_ALG_HASH)pcr_bank, &pcr_selection);
+ r = tpm2_policy_authorize(c, session, &pcr_selection, public, fp, fp_size, signature_json, NULL);
+ if (r < 0)
+ return r;
+ }
+ if (hash_pcr_mask != 0) {
TPML_PCR_SELECTION pcr_selection;
tpm2_tpml_pcr_selection_from_mask(hash_pcr_mask, (TPMI_ALG_HASH)pcr_bank, &pcr_selection);
- rc = sym_Esys_PolicyPCR(
- c->esys_context,
- session->esys_handle,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- NULL,
- &pcr_selection);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to add PCR policy to TPM: %s", sym_Tss2_RC_Decode(rc));
+ r = tpm2_policy_pcr(c, session, &pcr_selection, NULL);
+ if (r < 0)
+ return r;
}
if (use_pin) {
- log_debug("Configuring PIN policy.");
-
- rc = sym_Esys_PolicyAuthValue(
- c->esys_context,
- session->esys_handle,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to add authValue policy to TPM: %s",
- sym_Tss2_RC_Decode(rc));
+ r = tpm2_policy_auth_value(c, session, NULL);
+ if (r < 0)
+ return r;
}
r = tpm2_get_policy_digest(c, session, ret_policy_digest);
@@ -1999,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;
@@ -2052,48 +2442,37 @@ int tpm2_seal(const char *device,
return r;
}
- _cleanup_tpm2_handle_ Tpm2Handle *primary = NULL;
- r = tpm2_make_primary(c, /* alg = */0, !!ret_srk_buf, &primary_alg, &primary);
- if (r < 0)
- 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;
+ }
- /* 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, NULL, &encryption_session);
- if (r < 0)
- return r;
+ TPM2B_PUBLIC pubkey_tpm2, *authorize_key = NULL;
+ if (pubkey) {
+ r = openssl_pubkey_to_tpm2_pubkey(pubkey, pubkey_size, &pubkey_tpm2, NULL, NULL);
+ if (r < 0)
+ return r;
+ authorize_key = &pubkey_tpm2;
+ }
- /* 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);
+ TPM2B_DIGEST policy_digest;
+ r = tpm2_digest_init(TPM2_ALG_SHA256, &policy_digest);
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,
- pubkey, pubkey_size,
- pubkey_pcr_mask,
- /* signature_json= */ NULL,
- !!pin,
+ r = tpm2_calculate_sealing_policy(
+ &hash_pcr_selection,
+ hash_pcr_values,
+ n_hash_pcr_values,
+ authorize_key,
+ pin,
&policy_digest);
if (r < 0)
return r;
@@ -2109,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,
},
};
@@ -2117,8 +2496,11 @@ int tpm2_seal(const char *device,
.size = sizeof(hmac_sensitive.sensitive),
.sensitive.data.size = 32,
};
- if (pin)
- hash_pin(pin, strlen(pin), &hmac_sensitive.sensitive.userAuth);
+ if (pin) {
+ r = tpm2_digest_buffer(TPM2_ALG_SHA256, &hmac_sensitive.sensitive.userAuth, pin, strlen(pin), /* extend= */ false);
+ if (r < 0)
+ return r;
+ }
assert(sizeof(hmac_sensitive.sensitive.data.buffer) >= hmac_sensitive.sensitive.data.size);
@@ -2130,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,
@@ -2174,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();
@@ -2184,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));
@@ -2212,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;
@@ -2353,8 +2746,29 @@ int tpm2_unseal(const char *device,
sym_Tss2_RC_Decode(rc));
}
+ 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);
+ if (r < 0)
+ return r;
+ authorize_key = &pubkey_tpm2;
+ }
+
+ /*
+ * if a pin is set for the seal object, use it to bind the session
+ * key to that object. This prevents active bus interposers from
+ * faking a TPM and seeing the unsealed value. An active interposer
+ * could fake a TPM, satisfying the encrypted session, and just
+ * forward everything to the *real* TPM.
+ */
+ r = tpm2_set_auth(c, hmac_key, pin);
+ if (r < 0)
+ return r;
+
_cleanup_tpm2_handle_ Tpm2Handle *encryption_session = NULL;
- r = tpm2_make_encryption_session(c, primary, hmac_key, pin, &encryption_session);
+ r = tpm2_make_encryption_session(c, primary, hmac_key, &encryption_session);
if (r < 0)
return r;
@@ -2375,7 +2789,8 @@ int tpm2_unseal(const char *device,
policy_session,
hash_pcr_mask,
pcr_bank,
- pubkey, pubkey_size,
+ authorize_key,
+ fp, fp_size,
pubkey_pcr_mask,
signature,
!!pin,
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 2c4485345c..f71bbf920c 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include "bitfield.h"
+#include "io-util.h"
#include "json.h"
#include "macro.h"
#include "sha256.h"
@@ -65,13 +66,34 @@ extern TSS2_RC (*sym_Esys_VerifySignature)(ESYS_CONTEXT *esysContext, ESYS_TR ke
extern const char* (*sym_Tss2_RC_Decode)(TSS2_RC rc);
+extern TSS2_RC (*sym_Tss2_MU_TPM2_CC_Marshal)(TPM2_CC src, uint8_t buffer[], size_t buffer_size, size_t *offset);
extern TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Marshal)(TPM2B_PRIVATE const *src, uint8_t buffer[], size_t buffer_size, size_t *offset);
extern TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PRIVATE *dest);
extern TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Marshal)(TPM2B_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset);
extern TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PUBLIC *dest);
+extern TSS2_RC (*sym_Tss2_MU_TPML_PCR_SELECTION_Marshal)(TPML_PCR_SELECTION const *src, uint8_t buffer[], size_t buffer_size, size_t *offset);
+extern TSS2_RC (*sym_Tss2_MU_TPMT_HA_Marshal)(TPMT_HA const *src, uint8_t buffer[], size_t buffer_size, size_t *offset);
+extern TSS2_RC (*sym_Tss2_MU_TPMT_PUBLIC_Marshal)(TPMT_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset);
int dlopen_tpm2(void);
+int tpm2_digest_many(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest, const struct iovec data[], size_t count, bool extend);
+static inline int tpm2_digest_buffer(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest, const void *data, size_t len, bool extend) {
+ return tpm2_digest_many(alg, digest, &IOVEC_MAKE((void*) data, len), 1, extend);
+}
+int tpm2_digest_many_digests(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest, const TPM2B_DIGEST data[], size_t count, bool extend);
+static inline int tpm2_digest_rehash(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest) {
+ return tpm2_digest_many(alg, digest, NULL, 0, true);
+}
+static inline int tpm2_digest_init(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest) {
+ return tpm2_digest_many(alg, digest, NULL, 0, false);
+}
+
+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);
int tpm2_unseal(const char *device, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *srk_buf, size_t srk_buf_size, void **ret_secret, size_t *ret_secret_size);
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
index fb23d4319a..e878ab33f2 100644
--- a/src/test/test-tpm2.c
+++ b/src/test/test-tpm2.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "hexdecoct.h"
#include "tpm2-util.h"
#include "tests.h"
@@ -547,6 +548,218 @@ TEST(tpm2_get_primary_template) {
}
}
+static bool digest_check(const TPM2B_DIGEST *digest, const char *expect) {
+ _cleanup_free_ char *h = NULL;
+
+ assert_se(digest);
+ assert_se(expect);
+
+ h = hexmem(digest->buffer, digest->size);
+ assert_se(h);
+
+ return streq(expect, h);
+}
+
+static void digest_init_sha256(TPM2B_DIGEST *digest, const char *hash) {
+ _cleanup_free_ void *h = NULL;
+ size_t s = 0;
+
+ assert_se(strlen(hash) == SHA256_DIGEST_SIZE * 2);
+ assert_se(strlen(hash) <= sizeof(digest->buffer) * 2);
+
+ assert_se(unhexmem(hash, strlen(hash), &h, &s) == 0);
+ assert_se(s == SHA256_DIGEST_SIZE);
+
+ memcpy_safe(digest->buffer, h, s);
+ digest->size = s;
+
+ assert_se(digest_check(digest, hash));
+}
+
+TEST(digest_many) {
+ TPM2B_DIGEST d, d0, d1, d2, d3, d4;
+
+ digest_init_sha256(&d0, "0000000000000000000000000000000000000000000000000000000000000000");
+ digest_init_sha256(&d1, "17b7703d9d00776310ba032e88c1a8c2a9c630ebdd799db622f6631530789175");
+ digest_init_sha256(&d2, "12998c017066eb0d2a70b94e6ed3192985855ce390f321bbdb832022888bd251");
+ digest_init_sha256(&d3, "c3a65887fedd3fb4f5d0047e906dff830bcbd1293160909eb4b05f485e7387ad");
+ digest_init_sha256(&d4, "6491fb4bc08fc0b2ef47fc63db57e249917885e69d8c0d99667df83a59107a33");
+
+ /* tpm2_digest_init, tpm2_digest_rehash */
+ d = (TPM2B_DIGEST){ .size = 1, .buffer = { 2, }, };
+ assert_se(tpm2_digest_init(TPM2_ALG_SHA256, &d) == 0);
+ assert_se(digest_check(&d, "0000000000000000000000000000000000000000000000000000000000000000"));
+ assert_se(tpm2_digest_rehash(TPM2_ALG_SHA256, &d) == 0);
+ assert_se(digest_check(&d, "66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925"));
+
+ d = d1;
+ assert_se(tpm2_digest_rehash(TPM2_ALG_SHA256, &d) == 0);
+ assert_se(digest_check(&d, "ab55014b5ace12ba70c3acc887db571585a83539aad3633d252a710f268f405c"));
+ assert_se(tpm2_digest_init(TPM2_ALG_SHA256, &d) == 0);
+ assert_se(digest_check(&d, "0000000000000000000000000000000000000000000000000000000000000000"));
+
+ /* tpm2_digest_many_digests */
+ assert_se(tpm2_digest_many_digests(TPM2_ALG_SHA256, &d, &d2, 1, false) == 0);
+ assert_se(digest_check(&d, "56571a1be3fbeab18d215f549095915a004b5788ca0d535be668559129a76f25"));
+ assert_se(tpm2_digest_many_digests(TPM2_ALG_SHA256, &d, &d2, 1, true) == 0);
+ assert_se(digest_check(&d, "99dedaee8f4d8d10a8be184399fde8740d5e17ff783ee5c288a4486e4ce3a1fe"));
+
+ const TPM2B_DIGEST da1[] = { d2, d3, };
+ assert_se(tpm2_digest_many_digests(TPM2_ALG_SHA256, &d, da1, ELEMENTSOF(da1), false) == 0);
+ assert_se(digest_check(&d, "525aa13ef9a61827778ec3acf16fbb23b65ae8770b8fb2684d3a33f9457dd6d8"));
+ assert_se(tpm2_digest_many_digests(TPM2_ALG_SHA256, &d, da1, ELEMENTSOF(da1), true) == 0);
+ assert_se(digest_check(&d, "399ca2aa98963d1bd81a2b58a7e5cda24bba1be88fb4da9aa73d97706846566b"));
+
+ const TPM2B_DIGEST da2[] = { d3, d2, d0 };
+ assert_se(tpm2_digest_many_digests(TPM2_ALG_SHA256, &d, da2, ELEMENTSOF(da2), false) == 0);
+ assert_se(digest_check(&d, "b26fd22db74d4cd896bff01c61aa498a575e4a553a7fb5a322a5fee36954313e"));
+ assert_se(tpm2_digest_many_digests(TPM2_ALG_SHA256, &d, da2, ELEMENTSOF(da2), true) == 0);
+ assert_se(digest_check(&d, "091e79a5b09d4048df49a680f966f3ff67910afe185c3baf9704c9ca45bcf259"));
+
+ const TPM2B_DIGEST da3[] = { d4, d4, d4, d4, d3, d4, d4, d4, d4, };
+ assert_se(tpm2_digest_many_digests(TPM2_ALG_SHA256, &d, da3, ELEMENTSOF(da3), false) == 0);
+ assert_se(digest_check(&d, "8eca947641b6002df79dfb571a7f78b7d0a61370a366f722386dfbe444d18830"));
+ assert_se(tpm2_digest_many_digests(TPM2_ALG_SHA256, &d, da3, ELEMENTSOF(da3), true) == 0);
+ assert_se(digest_check(&d, "f9ba17bc0bbe8794e9bcbf112e4d59a11eb68fffbcd5516a746e4857829dff04"));
+
+ /* tpm2_digest_buffer */
+ const uint8_t b1[] = { 1, 2, 3, 4, };
+ assert_se(tpm2_digest_buffer(TPM2_ALG_SHA256, &d, b1, ELEMENTSOF(b1), false) == 0);
+ assert_se(digest_check(&d, "9f64a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a"));
+ assert_se(tpm2_digest_buffer(TPM2_ALG_SHA256, &d, b1, ELEMENTSOF(b1), true) == 0);
+ assert_se(digest_check(&d, "ff3bd307b287e9b29bb572f6ccfd19deb0106d0c4c3c5cfe8a1d03a396092ed4"));
+
+ const void *b2 = d2.buffer;
+ assert_se(tpm2_digest_buffer(TPM2_ALG_SHA256, &d, b2, d2.size, false) == 0);
+ assert_se(digest_check(&d, "56571a1be3fbeab18d215f549095915a004b5788ca0d535be668559129a76f25"));
+ assert_se(tpm2_digest_buffer(TPM2_ALG_SHA256, &d, b2, d2.size, true) == 0);
+ assert_se(digest_check(&d, "99dedaee8f4d8d10a8be184399fde8740d5e17ff783ee5c288a4486e4ce3a1fe"));
+
+ /* tpm2_digest_many */
+ const struct iovec iov1[] = {
+ IOVEC_MAKE((void*) b1, ELEMENTSOF(b1)),
+ IOVEC_MAKE(d2.buffer, d2.size),
+ IOVEC_MAKE(d3.buffer, d3.size),
+ };
+ assert_se(tpm2_digest_many(TPM2_ALG_SHA256, &d, iov1, ELEMENTSOF(iov1), false) == 0);
+ assert_se(digest_check(&d, "cd7bde4a047af976b6f1b282309976229be59f96a78aa186de32a1aee488ab09"));
+ assert_se(tpm2_digest_many(TPM2_ALG_SHA256, &d, iov1, ELEMENTSOF(iov1), true) == 0);
+ assert_se(digest_check(&d, "02ecb0628264235111e0053e271092981c8b15d59cd46617836bee3149a4ecb0"));
+}
+
+static void tpm2b_public_init(TPM2B_PUBLIC *public) {
+ TPMT_PUBLIC tpmt = {
+ .type = TPM2_ALG_RSA,
+ .nameAlg = TPM2_ALG_SHA256,
+ .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
+ .parameters.rsaDetail = {
+ .symmetric = {
+ .algorithm = TPM2_ALG_AES,
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB,
+ },
+ .scheme.scheme = TPM2_ALG_NULL,
+ .keyBits = 2048,
+ },
+ };
+
+ const char *key = "9ec7341c52093ac40a1965a5df10432513c539adcf905e30577ab6ebc88ffe53cd08cef12ed9bec6125432f4fada3629b8b96d31b8f507aa35029188fe396da823fcb236027f7fbb01b0da3d87be7f999390449ced604bdf7e26c48657cc0671000f1147da195c3861c96642e54427cb7a11572e07567ec3fd6316978abc4bd92b27bb0a0e4958e599804eeb41d682b3b7fc1f960209f80a4fb8a1b64abfd96bf5d554e73cdd6ad1c8becb4fcf5e8f0c3e621d210e5e2f308f6520ad9a966779231b99f06c5989e5a23a9415c8808ab89ce81117632e2f8461cd4428bded40979236aeadafe8de3f51660a45e1dbc87694e6a36360201cca3ff9e7263e712727";
+ _cleanup_free_ void *mem = NULL;
+ size_t len = 0;
+ assert_se(unhexmem(key, strlen(key), &mem, &len) == 0);
+ assert_se(len <= sizeof(tpmt.unique.rsa.buffer));
+ memcpy_safe(tpmt.unique.rsa.buffer, mem, len);
+ tpmt.unique.rsa.size = len;
+
+ public->publicArea = tpmt;
+}
+
+TEST(calculate_name) {
+ TPM2B_PUBLIC public;
+ TPM2B_NAME name;
+
+ tpm2b_public_init(&public);
+ assert_se(tpm2_calculate_name(&public.publicArea, &name) == 0);
+ assert_se(name.size == SHA256_DIGEST_SIZE + 2);
+
+ const char *expect = "000be78f74a470dd92e979ca067cdb2293a35f075e8560b436bd2ccea5da21486a07";
+ _cleanup_free_ char *h = hexmem(name.name, name.size);
+ assert_se(h);
+
+ assert_se(strlen(expect) == strlen(h));
+ assert_se(streq(expect, h));
+}
+
+TEST(calculate_policy_auth_value) {
+ TPM2B_DIGEST d;
+
+ digest_init_sha256(&d, "0000000000000000000000000000000000000000000000000000000000000000");
+ assert_se(tpm2_calculate_policy_auth_value(&d) == 0);
+ assert_se(digest_check(&d, "8fcd2169ab92694e0c633f1ab772842b8241bbc20288981fc7ac1eddc1fddb0e"));
+ assert_se(tpm2_calculate_policy_auth_value(&d) == 0);
+ 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];
+ TPM2B_DIGEST d;
+ uint32_t pcr_mask;
+
+ digest_init_sha256(&d, "0000000000000000000000000000000000000000000000000000000000000000");
+ pcr_mask = (1<<4) | (1<<7) | (1<<8);
+ tpm2_tpml_pcr_selection_from_mask(pcr_mask, TPM2_ALG_SHA256, &pcr_selection);
+ digest_init_sha256(&pcr_values[0], "368f85b3013041dfe203faaa364f00b07c5da7b1e5f1dbf2efb06fa6b9bd92de");
+ digest_init_sha256(&pcr_values[1], "aa1154c9e0a774854ccbed4c8ce7e9b906b3d700a1a8db1772d0341a62dbe51b");
+ digest_init_sha256(&pcr_values[2], "cfde439a2c06af3479ca6bdc60429b90553d65300c5cfcc40004a08c6b5ad81a");
+ assert_se(tpm2_calculate_policy_pcr(&pcr_selection, pcr_values, 3, &d) == 0);
+ assert_se(digest_check(&d, "76532a0e16f7e6bf6b02918c11f75d99d729fab0cc81d0df2c4284a2c4fe6e05"));
+
+ pcr_mask = (1<<4) | (1<<7) | (1<<8);
+ tpm2_tpml_pcr_selection_from_mask(pcr_mask, TPM2_ALG_SHA256, &pcr_selection);
+ digest_init_sha256(&pcr_values[0], "368f85b3013041dfe203faaa364f00b07c5da7b1e5f1dbf2efb06fa6b9bd92de");
+ digest_init_sha256(&pcr_values[1], "aa1154c9e0a774854ccbed4c8ce7e9b906b3d700a1a8db1772d0341a62dbe51b");
+ digest_init_sha256(&pcr_values[2], "cfde439a2c06af3479ca6bdc60429b90553d65300c5cfcc40004a08c6b5ad81a");
+ assert_se(tpm2_calculate_policy_pcr(&pcr_selection, pcr_values, 3, &d) == 0);
+ assert_se(digest_check(&d, "97e64bcabb64c1fa4b726528644926c8029f5b4458b0575c98c04fe225629a0b"));
+
+ digest_init_sha256(&d, "0000000000000000000000000000000000000000000000000000000000000000");
+ pcr_mask = 0xffff;
+ tpm2_tpml_pcr_selection_from_mask(pcr_mask, TPM2_ALG_SHA256, &pcr_selection);
+ digest_init_sha256(&pcr_values[ 0], "2124793cbbe60c3a8637d3b84a5d054e87c351e1469a285acc04755e8b204dec");
+ digest_init_sha256(&pcr_values[ 1], "bf7592f18adcfdc549fc0b94939f5069a24697f9cff4a0dca29014767b97559d");
+ digest_init_sha256(&pcr_values[ 2], "4b00cff9dee3a364979b2dc241b34568a8ad49fcf2713df259e47dff8875feed");
+ digest_init_sha256(&pcr_values[ 3], "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969");
+ digest_init_sha256(&pcr_values[ 4], "368f85b3013041dfe203faaa364f00b07c5da7b1e5f1dbf2efb06fa6b9bd92de");
+ digest_init_sha256(&pcr_values[ 5], "c97c40369691c8e4aa78fb3a52655cd193b780a838b8e23f5f476576919db5e5");
+ digest_init_sha256(&pcr_values[ 6], "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969");
+ digest_init_sha256(&pcr_values[ 7], "aa1154c9e0a774854ccbed4c8ce7e9b906b3d700a1a8db1772d0341a62dbe51b");
+ digest_init_sha256(&pcr_values[ 8], "cfde439a2c06af3479ca6bdc60429b90553d65300c5cfcc40004a08c6b5ad81a");
+ digest_init_sha256(&pcr_values[ 9], "9c2bac22ef5ec84fcdb71c3ebf776cba1247e5da980e5ee08e45666a2edf0b8b");
+ digest_init_sha256(&pcr_values[10], "9885873f4d7348199ad286f8f2476d4f866940950f6f9fb9f945ed352dbdcbd2");
+ digest_init_sha256(&pcr_values[11], "42400ab950d21aa79d12cc4fdef67d1087a39ad64900619831c0974dbae54e44");
+ digest_init_sha256(&pcr_values[12], "767d064382e56ca1ad3bdcc6bc596112e6c2008b593d3570d24c2bfa64c4628c");
+ digest_init_sha256(&pcr_values[13], "30c16133175959408c9745d8dafadef5daf4b39cb2be04df0d60089bd46d3cc4");
+ digest_init_sha256(&pcr_values[14], "e3991b7ddd47be7e92726a832d6874c5349b52b789fa0db8b558c69fea29574e");
+ digest_init_sha256(&pcr_values[15], "852dae3ecb992bdeb13d6002fefeeffdd90feca8b378d56681ef2c885d0e5137");
+ assert_se(tpm2_calculate_policy_pcr(&pcr_selection, pcr_values, 16, &d) == 0);
+ assert_se(digest_check(&d, "22be4f1674f792d6345cea9427701068f0e8d9f42755dcc0e927e545a68f9c13"));
+ assert_se(tpm2_calculate_policy_pcr(&pcr_selection, pcr_values, 16, &d) == 0);
+ assert_se(digest_check(&d, "7481fd1b116078eb3ac2456e4ad542c9b46b9b8eb891335771ca8e7c8f8e4415"));
+}
+
#endif /* HAVE_TPM2 */
DEFINE_TEST_MAIN(LOG_DEBUG);