1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-10 05:18:17 +03:00

Merge pull request #34177 from poettering/pcrlock-and-signed

cryptenroll/cryptsetup: support combined signed PCR + pcrlock policies
This commit is contained in:
Lennart Poettering 2024-09-06 18:11:22 +02:00 committed by GitHub
commit 456cd065bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 741 additions and 279 deletions

View File

@ -82,3 +82,15 @@ void iovec_array_free(struct iovec *iovec, size_t n_iovec) {
free(iovec);
}
struct iovec* iovec_append(struct iovec *iovec, const struct iovec *append) {
assert(iovec_is_valid(iovec));
if (!iovec_is_set(append))
return iovec;
if (!greedy_realloc_append(&iovec->iov_base, &iovec->iov_len, append->iov_base, append->iov_len, 1))
return NULL;
return iovec;
}

View File

@ -70,3 +70,5 @@ static inline struct iovec *iovec_memdup(const struct iovec *source, struct iove
return ret;
}
struct iovec* iovec_append(struct iovec *iovec, const struct iovec *append);

View File

@ -10,6 +10,7 @@
#include "errno-util.h"
#include "fileio.h"
#include "hexdecoct.h"
#include "json-util.h"
#include "log.h"
#include "memory-util.h"
#include "random-util.h"
@ -18,20 +19,20 @@
static int search_policy_hash(
struct crypt_device *cd,
const struct iovec *hash) {
const struct iovec policy_hash[],
size_t n_policy_hash) {
int r;
assert(cd);
assert(iovec_is_valid(hash));
if (!iovec_is_set(hash))
/* Searches among the already enrolled TPM2 tokens for one that matches the exact set of policies specified */
if (n_policy_hash == 0)
return -ENOENT;
for (int token = 0; token < sym_crypt_token_max(CRYPT_LUKS2); token++) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
_cleanup_free_ void *thash = NULL;
size_t thash_size = 0;
int keyslot;
sd_json_variant *w;
@ -54,12 +55,45 @@ static int search_policy_hash(
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"TPM2 token data lacks 'tpm2-policy-hash' field.");
r = sd_json_variant_unhex(w, &thash, &thash_size);
if (r < 0)
return log_error_errno(r, "Invalid base64 data in 'tpm2-policy-hash' field: %m");
/* This is either an array of strings (for sharded enrollments), or a single string */
if (sd_json_variant_is_array(w)) {
if (memcmp_nn(hash->iov_base, hash->iov_len, thash, thash_size) == 0)
return keyslot; /* Found entry with same hash. */
if (sd_json_variant_elements(w) == n_policy_hash) {
sd_json_variant *i;
bool match = true;
size_t j = 0;
JSON_VARIANT_ARRAY_FOREACH(i, w) {
_cleanup_(iovec_done) struct iovec thash = {};
r = sd_json_variant_unhex(i, &thash.iov_base, &thash.iov_len);
if (r < 0)
return log_error_errno(r, "Invalid hex data in 'tpm2-policy-hash' field item : %m");
if (iovec_memcmp(policy_hash + j, &thash) != 0) {
match = false;
break;
}
j++;
}
assert(j == n_policy_hash);
if (match) /* Found entry with the exact same set of hashes */
return keyslot;
}
} else if (n_policy_hash == 1) {
_cleanup_(iovec_done) struct iovec thash = {};
r = sd_json_variant_unhex(w, &thash.iov_base, &thash.iov_len);
if (r < 0)
return log_error_errno(r, "Invalid hex data in 'tpm2-policy-hash' field: %m");
if (iovec_memcmp(policy_hash + 0, &thash) == 0)
return keyslot; /* Found entry with same hash. */
}
}
return -ENOENT; /* Not found */
@ -154,12 +188,16 @@ int load_volume_key_tpm2(
for (;;) {
_cleanup_(iovec_done) struct iovec pubkey = {}, salt = {}, srk = {}, pcrlock_nv = {};
_cleanup_(iovec_done) struct iovec blob = {}, policy_hash = {};
struct iovec *blobs = NULL, *policy_hash = NULL;
size_t n_blobs = 0, n_policy_hash = 0;
uint32_t hash_pcr_mask, pubkey_pcr_mask;
uint16_t pcr_bank, primary_alg;
TPM2Flags tpm2_flags;
int keyslot;
CLEANUP_ARRAY(policy_hash, n_policy_hash, iovec_array_free);
CLEANUP_ARRAY(blobs, n_blobs, iovec_array_free);
r = find_tpm2_auto_data(
cd,
UINT32_MAX,
@ -169,8 +207,10 @@ int load_volume_key_tpm2(
&pubkey,
&pubkey_pcr_mask,
&primary_alg,
&blob,
&blobs,
&n_blobs,
&policy_hash,
&n_policy_hash,
&salt,
&srk,
&pcrlock_nv,
@ -202,8 +242,10 @@ int load_volume_key_tpm2(
/* pcrlock_path= */ NULL,
primary_alg,
/* key_file= */ NULL, /* key_file_size= */ 0, /* key_file_offset= */ 0, /* no key file */
&blob,
&policy_hash,
blobs,
n_blobs,
policy_hash,
n_policy_hash,
&salt,
&srk,
&pcrlock_nv,
@ -257,7 +299,7 @@ int enroll_tpm2(struct crypt_device *cd,
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *signature_json = NULL;
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
_cleanup_(iovec_done) struct iovec srk = {}, blob = {}, pubkey = {};
_cleanup_(iovec_done) struct iovec srk = {}, pubkey = {};
_cleanup_(iovec_done_erase) struct iovec secret = {};
const char *node;
_cleanup_(erase_and_freep) char *pin_str = NULL;
@ -304,10 +346,7 @@ int enroll_tpm2(struct crypt_device *cd,
}
TPM2B_PUBLIC public = {};
/* Load the PCR public key if specified explicitly, or if no pcrlock policy was specified and
* automatic loading of PCR public keys wasn't disabled explicitly. The reason we turn this off when
* pcrlock is configured is simply that we currently not support both in combination. */
if (pcr_pubkey_path || (load_pcr_pubkey && !pcrlock_path)) {
if (pcr_pubkey_path || load_pcr_pubkey) {
r = tpm2_load_pcr_public_key(pcr_pubkey_path, &pubkey.iov_base, &pubkey.iov_len);
if (r < 0) {
if (pcr_pubkey_path || signature_path || r != -ENOENT)
@ -398,42 +437,88 @@ int enroll_tpm2(struct crypt_device *cd,
return log_error_errno(r, "Failed to determine best PCR bank: %m");
}
TPM2B_DIGEST policy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
/* Unfortunately TPM2 policy semantics make it very hard to combine PolicyAuthorize (which we need
* for signed PCR policies) and PolicyAuthorizeNV (which we need for pcrlock policies). Hence, let's
* use a "sharded" secret, and lock the first shard to the signed PCR policy, and the 2nd to the
* pcrlock if both are requested. */
TPM2B_DIGEST policy_hash[2] = {
TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE),
TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE),
};
size_t n_policy_hash = 1;
/* If both PCR public key unlock and pcrlock unlock is selected, then we create the one for PCR public key unlock first. */
r = tpm2_calculate_sealing_policy(
hash_pcr_values,
n_hash_pcr_values,
iovec_is_set(&pubkey) ? &public : NULL,
use_pin,
pcrlock_path ? &pcrlock_policy : NULL,
&policy);
pcrlock_path && !iovec_is_set(&pubkey) ? &pcrlock_policy : NULL,
policy_hash + 0);
if (r < 0)
return r;
if (device_key)
if (pcrlock_path && iovec_is_set(&pubkey)) {
r = tpm2_calculate_sealing_policy(
hash_pcr_values,
n_hash_pcr_values,
/* public= */ NULL, /* This one is off now */
use_pin,
&pcrlock_policy, /* And this one on instead. */
policy_hash + 1);
if (r < 0)
return r;
n_policy_hash ++;
}
struct iovec *blobs = NULL;
size_t n_blobs = 0;
CLEANUP_ARRAY(blobs, n_blobs, iovec_array_free);
if (device_key) {
if (n_policy_hash > 1)
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Combined signed PCR policies and pcrlock policies cannot be calculated offline, currently.");
blobs = new0(struct iovec, 1);
if (!blobs)
return log_oom();
n_blobs = 1;
r = tpm2_calculate_seal(
seal_key_handle,
&device_key_public,
/* attributes= */ NULL,
/* secret= */ NULL,
&policy,
policy_hash + 0,
pin_str,
&secret,
&blob,
blobs + 0,
&srk);
else
} else
r = tpm2_seal(tpm2_context,
seal_key_handle,
&policy,
policy_hash,
n_policy_hash,
pin_str,
&secret,
&blob,
&blobs,
&n_blobs,
/* ret_primary_alg= */ NULL,
&srk);
if (r < 0)
return log_error_errno(r, "Failed to seal to TPM2: %m");
struct iovec policy_hash_as_iovec[2] = {
IOVEC_MAKE(policy_hash[0].buffer, policy_hash[0].size),
IOVEC_MAKE(policy_hash[1].buffer, policy_hash[1].size),
};
/* Let's see if we already have this specific PCR policy hash enrolled, if so, exit early. */
r = search_policy_hash(cd, &IOVEC_MAKE(policy.buffer, policy.size));
r = search_policy_hash(cd, policy_hash_as_iovec, n_policy_hash);
if (r == -ENOENT)
log_debug_errno(r, "PCR policy hash not yet enrolled, enrolling now.");
else if (r < 0)
@ -461,8 +546,10 @@ int enroll_tpm2(struct crypt_device *cd,
pin_str,
pcrlock_path ? &pcrlock_policy : NULL,
/* primary_alg= */ 0,
&blob,
&IOVEC_MAKE(policy.buffer, policy.size),
blobs,
n_blobs,
policy_hash_as_iovec,
n_policy_hash,
&srk,
&secret2);
if (r < 0)
@ -498,8 +585,10 @@ int enroll_tpm2(struct crypt_device *cd,
&pubkey,
pubkey_pcr_mask,
/* primary_alg= */ 0,
&blob,
&IOVEC_MAKE(policy.buffer, policy.size),
blobs,
n_blobs,
policy_hash_as_iovec,
n_policy_hash,
use_pin ? &IOVEC_MAKE(binary_salt, sizeof(binary_salt)) : NULL,
&srk,
pcrlock_path ? &pcrlock_policy.nv_handle : NULL,

View File

@ -42,7 +42,7 @@ _public_ int cryptsetup_token_open_pin(
void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
_cleanup_(erase_and_freep) char *base64_encoded = NULL, *pin_string = NULL;
_cleanup_(iovec_done) struct iovec blob = {}, pubkey = {}, policy_hash = {}, salt = {}, srk = {}, pcrlock_nv = {};
_cleanup_(iovec_done) struct iovec pubkey = {}, salt = {}, srk = {}, pcrlock_nv = {};
_cleanup_(iovec_done_erase) struct iovec decrypted_key = {};
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
uint32_t hash_pcr_mask, pubkey_pcr_mask;
@ -76,6 +76,11 @@ _public_ int cryptsetup_token_open_pin(
if (r < 0)
return crypt_log_debug_errno(cd, r, "Failed to parse token JSON data: %m");
struct iovec *blobs = NULL, *policy_hash = NULL;
size_t n_blobs = 0, n_policy_hash = 0;
CLEANUP_ARRAY(blobs, n_blobs, iovec_array_free);
CLEANUP_ARRAY(policy_hash, n_policy_hash, iovec_array_free);
r = tpm2_parse_luks2_json(
v,
/* ret_keyslot= */ NULL,
@ -84,8 +89,10 @@ _public_ int cryptsetup_token_open_pin(
&pubkey,
&pubkey_pcr_mask,
&primary_alg,
&blob,
&blobs,
&n_blobs,
&policy_hash,
&n_policy_hash,
&salt,
&srk,
&pcrlock_nv,
@ -106,8 +113,10 @@ _public_ int cryptsetup_token_open_pin(
pin_string,
params.pcrlock_path,
primary_alg,
&blob,
&policy_hash,
blobs,
n_blobs,
policy_hash,
n_policy_hash,
&salt,
&srk,
&pcrlock_nv,
@ -167,8 +176,8 @@ _public_ void cryptsetup_token_dump(
struct crypt_device *cd /* is always LUKS2 context */,
const char *json /* validated 'systemd-tpm2' token if cryptsetup_token_validate is defined */) {
_cleanup_free_ char *hash_pcrs_str = NULL, *pubkey_pcrs_str = NULL, *blob_str = NULL, *policy_hash_str = NULL, *pubkey_str = NULL;
_cleanup_(iovec_done) struct iovec blob = {}, pubkey = {}, policy_hash = {}, salt = {}, srk = {}, pcrlock_nv = {};
_cleanup_free_ char *hash_pcrs_str = NULL, *pubkey_pcrs_str = NULL, *pubkey_str = NULL;
_cleanup_(iovec_done) struct iovec pubkey = {}, salt = {}, srk = {}, pcrlock_nv = {};
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
uint32_t hash_pcr_mask, pubkey_pcr_mask;
uint16_t pcr_bank, primary_alg;
@ -181,6 +190,11 @@ _public_ void cryptsetup_token_dump(
if (r < 0)
return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " JSON object: %m");
struct iovec *blobs = NULL, *policy_hash = NULL;
size_t n_blobs = 0, n_policy_hash = 0;
CLEANUP_ARRAY(blobs, n_blobs, iovec_array_free);
CLEANUP_ARRAY(policy_hash, n_policy_hash, iovec_array_free);
r = tpm2_parse_luks2_json(
v,
NULL,
@ -189,8 +203,10 @@ _public_ void cryptsetup_token_dump(
&pubkey,
&pubkey_pcr_mask,
&primary_alg,
&blob,
&blobs,
&n_blobs,
&policy_hash,
&n_policy_hash,
&salt,
&srk,
&pcrlock_nv,
@ -206,30 +222,40 @@ _public_ void cryptsetup_token_dump(
if (!pubkey_pcrs_str)
return (void) crypt_log_debug_errno(cd, ENOMEM, "Cannot format PCR hash mask: %m");
r = crypt_dump_buffer_to_hex_string(blob.iov_base, blob.iov_len, &blob_str);
if (r < 0)
return (void) crypt_log_debug_errno(cd, r, "Cannot dump " TOKEN_NAME " content: %m");
r = crypt_dump_buffer_to_hex_string(pubkey.iov_base, pubkey.iov_len, &pubkey_str);
if (r < 0)
return (void) crypt_log_debug_errno(cd, r, "Cannot dump " TOKEN_NAME " content: %m");
r = crypt_dump_buffer_to_hex_string(policy_hash.iov_base, policy_hash.iov_len, &policy_hash_str);
if (r < 0)
return (void) crypt_log_debug_errno(cd, r, "Cannot dump " TOKEN_NAME " content: %m");
crypt_log(cd, "\ttpm2-hash-pcrs: %s\n", strna(hash_pcrs_str));
crypt_log(cd, "\ttpm2-pcr-bank: %s\n", strna(tpm2_hash_alg_to_string(pcr_bank)));
crypt_log(cd, "\ttpm2-pubkey:" CRYPT_DUMP_LINE_SEP "%s\n", pubkey_str);
crypt_log(cd, "\ttpm2-pubkey-pcrs: %s\n", strna(pubkey_pcrs_str));
crypt_log(cd, "\ttpm2-primary-alg: %s\n", strna(tpm2_asym_alg_to_string(primary_alg)));
crypt_log(cd, "\ttpm2-blob: %s\n", blob_str);
crypt_log(cd, "\ttpm2-policy-hash:" CRYPT_DUMP_LINE_SEP "%s\n", policy_hash_str);
crypt_log(cd, "\ttpm2-pin: %s\n", true_false(flags & TPM2_FLAGS_USE_PIN));
crypt_log(cd, "\ttpm2-pcrlock: %s\n", true_false(flags & TPM2_FLAGS_USE_PCRLOCK));
crypt_log(cd, "\ttpm2-salt: %s\n", true_false(iovec_is_set(&salt)));
crypt_log(cd, "\ttpm2-srk: %s\n", true_false(iovec_is_set(&srk)));
crypt_log(cd, "\ttpm2-pcrlock-nv: %s\n", true_false(iovec_is_set(&pcrlock_nv)));
FOREACH_ARRAY(p, policy_hash, n_policy_hash) {
_cleanup_free_ char *policy_hash_str = NULL;
r = crypt_dump_buffer_to_hex_string(p->iov_base, p->iov_len, &policy_hash_str);
if (r < 0)
return (void) crypt_log_debug_errno(cd, r, "Cannot dump " TOKEN_NAME " content: %m");
crypt_log(cd, "\ttpm2-policy-hash:" CRYPT_DUMP_LINE_SEP "%s\n", policy_hash_str);
}
FOREACH_ARRAY(b, blobs, n_blobs) {
_cleanup_free_ char *blob_str = NULL;
r = crypt_dump_buffer_to_hex_string(b->iov_base, b->iov_len, &blob_str);
if (r < 0)
return (void) crypt_log_debug_errno(cd, r, "Cannot dump " TOKEN_NAME " content: %m");
crypt_log(cd, "\ttpm2-blob: %s\n", blob_str);
}
}
/*
@ -313,9 +339,18 @@ _public_ int cryptsetup_token_validate(
return 1;
}
r = sd_json_variant_unbase64(w, NULL, NULL);
if (r < 0)
return crypt_log_debug_errno(cd, r, "Invalid base64 data in 'tpm2-blob' field: %m");
if (sd_json_variant_is_array(w)) {
sd_json_variant *i;
JSON_VARIANT_ARRAY_FOREACH(i, w) {
r = sd_json_variant_unbase64(i, /* ret= */ NULL, /* ret_size= */ NULL);
if (r < 0)
return crypt_log_debug_errno(cd, r, "Invalid base64 data in 'tpm2-blob' field: %m");
}
} else {
r = sd_json_variant_unbase64(w, /* ret= */ NULL, /* ret_size= */ NULL);
if (r < 0)
return crypt_log_debug_errno(cd, r, "Invalid base64 data in 'tpm2-blob' field: %m");
}
w = sd_json_variant_by_key(v, "tpm2-policy-hash");
if (!w) {
@ -323,9 +358,18 @@ _public_ int cryptsetup_token_validate(
return 1;
}
r = sd_json_variant_unhex(w, NULL, NULL);
if (r < 0)
return crypt_log_debug_errno(cd, r, "Invalid base64 data in 'tpm2-policy-hash' field: %m");
if (sd_json_variant_is_array(w)) {
sd_json_variant *i;
JSON_VARIANT_ARRAY_FOREACH(i, w) {
r = sd_json_variant_unhex(i, /* ret= */ NULL, /* ret_size= */ NULL);
if (r < 0)
return crypt_log_debug_errno(cd, r, "Invalid hex data in 'tpm2-policy-hash' field: %m");
}
} else {
r = sd_json_variant_unhex(w, /* ret= */ NULL, /* ret_size= */ NULL);
if (r < 0)
return crypt_log_debug_errno(cd, r, "Invalid hex data in 'tpm2-policy-hash' field: %m");
}
w = sd_json_variant_by_key(v, "tpm2-pin");
if (w) {

View File

@ -24,8 +24,10 @@ int acquire_luks2_key(
const char *pin,
const char *pcrlock_path,
uint16_t primary_alg,
const struct iovec *blob,
const struct iovec *policy_hash,
const struct iovec blobs[],
size_t n_blobs,
const struct iovec policy_hash[],
size_t n_policy_hash,
const struct iovec *salt,
const struct iovec *srk,
const struct iovec *pcrlock_nv,
@ -101,8 +103,10 @@ int acquire_luks2_key(
pin,
FLAGS_SET(flags, TPM2_FLAGS_USE_PCRLOCK) ? &pcrlock_policy : NULL,
primary_alg,
blob,
blobs,
n_blobs,
policy_hash,
n_policy_hash,
srk,
ret_decrypted_key);
if (r < 0)

View File

@ -16,8 +16,10 @@ int acquire_luks2_key(
const char *pin,
const char *pcrlock_path,
uint16_t primary_alg,
const struct iovec *key_data,
const struct iovec *policy_hash,
const struct iovec blobs[],
size_t n_blobs,
const struct iovec policy_hash[],
size_t n_policy_hash,
const struct iovec *salt,
const struct iovec *srk,
const struct iovec *pcrlock_nv,

View File

@ -1862,8 +1862,9 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
/* pcrlock_path= */ NULL,
/* primary_alg= */ 0,
key_file, arg_keyfile_size, arg_keyfile_offset,
key_data,
key_data, /* n_blobs= */ 1,
/* policy_hash= */ NULL, /* we don't know the policy hash */
/* n_policy_hash= */ 0,
/* salt= */ NULL,
/* srk= */ NULL,
/* pcrlock_nv= */ NULL,
@ -1911,11 +1912,15 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
for (;;) {
_cleanup_(iovec_done) struct iovec pubkey = {}, salt = {}, srk = {}, pcrlock_nv = {};
_cleanup_(iovec_done) struct iovec blob = {}, policy_hash = {};
struct iovec *blobs = NULL, *policy_hash = NULL;
uint32_t hash_pcr_mask, pubkey_pcr_mask;
size_t n_blobs = 0, n_policy_hash = 0;
uint16_t pcr_bank, primary_alg;
TPM2Flags tpm2_flags;
CLEANUP_ARRAY(blobs, n_blobs, iovec_array_free);
CLEANUP_ARRAY(policy_hash, n_policy_hash, iovec_array_free);
r = find_tpm2_auto_data(
cd,
arg_tpm2_pcr_mask, /* if != UINT32_MAX we'll only look for tokens with this PCR mask */
@ -1925,8 +1930,10 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
&pubkey,
&pubkey_pcr_mask,
&primary_alg,
&blob,
&blobs,
&n_blobs,
&policy_hash,
&n_policy_hash,
&salt,
&srk,
&pcrlock_nv,
@ -1960,8 +1967,10 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
arg_tpm2_pcrlock,
primary_alg,
/* key_file= */ NULL, /* key_file_size= */ 0, /* key_file_offset= */ 0, /* no key file */
&blob,
&policy_hash,
blobs,
n_blobs,
policy_hash,
n_policy_hash,
&salt,
&srk,
&pcrlock_nv,

View File

@ -4236,7 +4236,7 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
if (IN_SET(p->encrypt, ENCRYPT_TPM2, ENCRYPT_KEY_FILE_TPM2)) {
#if HAVE_TPM2
_cleanup_(iovec_done) struct iovec pubkey = {}, blob = {}, srk = {};
_cleanup_(iovec_done) struct iovec pubkey = {}, srk = {};
_cleanup_(iovec_done_erase) struct iovec secret = {};
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
ssize_t base64_encoded_size;
@ -4309,35 +4309,71 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
return log_error_errno(r, "Could not get hash mask: %m");
}
TPM2B_DIGEST policy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
TPM2B_DIGEST policy_hash[2] = {
TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE),
TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE),
};
size_t n_policy_hash = 1;
/* If both PCR public key unlock and pcrlock unlock is selected, then shard the encryption key. */
r = tpm2_calculate_sealing_policy(
arg_tpm2_hash_pcr_values,
arg_tpm2_n_hash_pcr_values,
iovec_is_set(&pubkey) ? &public : NULL,
/* use_pin= */ false,
arg_tpm2_pcrlock ? &pcrlock_policy : NULL,
&policy);
arg_tpm2_pcrlock && !iovec_is_set(&pubkey) ? &pcrlock_policy : NULL,
policy_hash + 0);
if (r < 0)
return log_error_errno(r, "Could not calculate sealing policy digest: %m");
return log_error_errno(r, "Could not calculate sealing policy digest for shard 0: %m");
if (arg_tpm2_pcrlock && iovec_is_set(&pubkey)) {
r = tpm2_calculate_sealing_policy(
arg_tpm2_hash_pcr_values,
arg_tpm2_n_hash_pcr_values,
/* pubkey= */ NULL, /* Turn this one off for the 2nd shard */
/* use_pin= */ false,
&pcrlock_policy, /* But turn this one on */
policy_hash + 1);
if (r < 0)
return log_error_errno(r, "Could not calculate sealing policy digest for shard 1: %m");
n_policy_hash++;
}
struct iovec *blobs = NULL;
size_t n_blobs = 0;
CLEANUP_ARRAY(blobs, n_blobs, iovec_array_free);
if (arg_tpm2_device_key) {
if (n_policy_hash > 1)
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Combined signed PCR policies and pcrlock policies cannot be calculated offline, currently.");
blobs = new0(struct iovec, 1);
if (!blobs)
return log_oom();
n_blobs = 1;
if (arg_tpm2_device_key)
r = tpm2_calculate_seal(
arg_tpm2_seal_key_handle,
&device_key_public,
/* attributes= */ NULL,
/* secret= */ NULL,
&policy,
policy_hash + 0,
/* pin= */ NULL,
&secret,
&blob,
blobs + 0,
&srk);
else
} else
r = tpm2_seal(tpm2_context,
arg_tpm2_seal_key_handle,
&policy,
policy_hash,
n_policy_hash,
/* pin= */ NULL,
&secret,
&blob,
&blobs,
&n_blobs,
/* ret_primary_alg= */ NULL,
&srk);
if (r < 0)
@ -4361,6 +4397,11 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
if (keyslot < 0)
return log_error_errno(keyslot, "Failed to add new TPM2 key: %m");
struct iovec policy_hash_as_iovec[2] = {
IOVEC_MAKE(policy_hash[0].buffer, policy_hash[0].size),
IOVEC_MAKE(policy_hash[1].buffer, policy_hash[1].size),
};
r = tpm2_make_luks2_json(
keyslot,
hash_pcr_mask,
@ -4368,8 +4409,10 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
&pubkey,
arg_tpm2_public_key_pcr_mask,
/* primary_alg= */ 0,
&blob,
&IOVEC_MAKE(policy.buffer, policy.size),
blobs,
n_blobs,
policy_hash_as_iovec,
n_policy_hash,
/* salt= */ NULL, /* no salt because tpm2_seal has no pin */
&srk,
&pcrlock_policy.nv_handle,

View File

@ -957,12 +957,18 @@ int encrypt_credential_and_warn(
if (r < 0)
return log_error_errno(r, "Could not calculate sealing policy digest: %m");
struct iovec *blobs = NULL;
size_t n_blobs = 0;
CLEANUP_ARRAY(blobs, n_blobs, iovec_array_free);
r = tpm2_seal(tpm2_context,
/* seal_key_handle= */ 0,
&tpm2_policy,
/* n_policy_hash= */ 1,
/* pin= */ NULL,
&tpm2_key,
&tpm2_blob,
&blobs,
&n_blobs,
&tpm2_primary_alg,
/* ret_srk= */ NULL);
if (r < 0) {
@ -977,6 +983,9 @@ int encrypt_credential_and_warn(
if (!iovec_memdup(&IOVEC_MAKE(tpm2_policy.buffer, tpm2_policy.size), &tpm2_policy_hash))
return log_oom();
assert(n_blobs == 1);
tpm2_blob = TAKE_STRUCT(blobs[0]);
assert(tpm2_blob.iov_len <= CREDENTIAL_FIELD_SIZE_MAX);
assert(tpm2_policy_hash.iov_len <= CREDENTIAL_FIELD_SIZE_MAX);
}
@ -1341,7 +1350,9 @@ int decrypt_credential_and_warn(
/* pcrlock_policy= */ NULL,
le16toh(t->primary_alg),
&IOVEC_MAKE(t->policy_hash_and_blob, le32toh(t->blob_size)),
/* n_blobs= */ 1,
&IOVEC_MAKE(t->policy_hash_and_blob + le32toh(t->blob_size), le32toh(t->policy_hash_size)),
/* n_policy_hash= */ 1,
/* srk= */ NULL,
&tpm2_key);
if (r < 0)

View File

@ -70,8 +70,10 @@ int acquire_tpm2_key(
const char *key_file,
size_t key_file_size,
uint64_t key_file_offset,
const struct iovec *key_data,
const struct iovec *policy_hash,
const struct iovec blobs[],
size_t n_blobs,
const struct iovec policy_hash[],
size_t n_policy_hash,
const struct iovec *salt,
const struct iovec *srk,
const struct iovec *pcrlock_nv,
@ -82,9 +84,8 @@ int acquire_tpm2_key(
struct iovec *ret_decrypted_key) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *signature_json = NULL;
_cleanup_free_ void *loaded_blob = NULL;
_cleanup_(iovec_done) struct iovec loaded_blob = {};
_cleanup_free_ char *auto_device = NULL;
struct iovec blob;
int r;
assert(iovec_is_valid(salt));
@ -99,9 +100,7 @@ int acquire_tpm2_key(
device = auto_device;
}
if (iovec_is_set(key_data))
blob = *key_data;
else {
if (n_blobs == 0) {
_cleanup_free_ char *bindname = NULL;
/* If we read the salt via AF_UNIX, make this client recognizable */
@ -114,11 +113,12 @@ int acquire_tpm2_key(
key_file_size == 0 ? SIZE_MAX : key_file_size,
READ_FULL_FILE_CONNECT_SOCKET,
bindname,
(char**) &loaded_blob, &blob.iov_len);
(char**) &loaded_blob.iov_base, &loaded_blob.iov_len);
if (r < 0)
return r;
blob.iov_base = loaded_blob;
blobs = &loaded_blob;
n_blobs = 1;
}
if (pubkey_pcr_mask != 0) {
@ -158,8 +158,10 @@ int acquire_tpm2_key(
/* pin= */ NULL,
FLAGS_SET(flags, TPM2_FLAGS_USE_PCRLOCK) ? &pcrlock_policy : NULL,
primary_alg,
&blob,
blobs,
n_blobs,
policy_hash,
n_policy_hash,
srk,
ret_decrypted_key);
if (r < 0)
@ -204,8 +206,10 @@ int acquire_tpm2_key(
b64_salted_pin,
FLAGS_SET(flags, TPM2_FLAGS_USE_PCRLOCK) ? &pcrlock_policy : NULL,
primary_alg,
&blob,
blobs,
n_blobs,
policy_hash,
n_policy_hash,
srk,
ret_decrypted_key);
if (r < 0) {
@ -230,8 +234,10 @@ int find_tpm2_auto_data(
struct iovec *ret_pubkey,
uint32_t *ret_pubkey_pcr_mask,
uint16_t *ret_primary_alg,
struct iovec *ret_blob,
struct iovec *ret_policy_hash,
struct iovec **ret_blobs,
size_t *ret_n_blobs,
struct iovec **ret_policy_hash,
size_t *ret_n_policy_hash,
struct iovec *ret_salt,
struct iovec *ret_srk,
struct iovec *ret_pcrlock_nv,
@ -242,15 +248,35 @@ int find_tpm2_auto_data(
int r, token;
assert(cd);
assert(ret_hash_pcr_mask);
assert(ret_pcrlock_nv);
assert(ret_pubkey);
assert(ret_pubkey_pcr_mask);
assert(ret_primary_alg);
assert(ret_blobs);
assert(ret_n_blobs);
assert(ret_policy_hash);
assert(ret_n_policy_hash);
assert(ret_salt);
assert(ret_srk);
assert(ret_pcrlock_nv);
assert(ret_flags);
assert(ret_keyslot);
assert(ret_token);
for (token = start_token; token < sym_crypt_token_max(CRYPT_LUKS2); token++) {
_cleanup_(iovec_done) struct iovec blob = {}, policy_hash = {}, pubkey = {}, salt = {}, srk = {}, pcrlock_nv = {};
_cleanup_(iovec_done) struct iovec pubkey = {}, salt = {}, srk = {}, pcrlock_nv = {};
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
struct iovec *blobs = NULL, *policy_hash = NULL;
size_t n_blobs = 0, n_policy_hash = 0;
uint32_t hash_pcr_mask, pubkey_pcr_mask;
uint16_t pcr_bank, primary_alg;
TPM2Flags flags;
int keyslot;
CLEANUP_ARRAY(blobs, n_blobs, iovec_array_free);
CLEANUP_ARRAY(policy_hash, n_policy_hash, iovec_array_free);
r = cryptsetup_get_token_as_json(cd, token, "systemd-tpm2", &v);
if (IN_SET(r, -ENOENT, -EINVAL, -EMEDIUMTYPE))
continue;
@ -265,8 +291,10 @@ int find_tpm2_auto_data(
&pubkey,
&pubkey_pcr_mask,
&primary_alg,
&blob,
&blobs,
&n_blobs,
&policy_hash,
&n_policy_hash,
&salt,
&srk,
&pcrlock_nv,
@ -287,8 +315,10 @@ int find_tpm2_auto_data(
*ret_pubkey = TAKE_STRUCT(pubkey);
*ret_pubkey_pcr_mask = pubkey_pcr_mask;
*ret_primary_alg = primary_alg;
*ret_blob = TAKE_STRUCT(blob);
*ret_policy_hash = TAKE_STRUCT(policy_hash);
*ret_blobs = TAKE_PTR(blobs);
*ret_n_blobs = n_blobs;
*ret_policy_hash = TAKE_PTR(policy_hash);
*ret_n_policy_hash = n_policy_hash;
*ret_salt = TAKE_STRUCT(salt);
*ret_keyslot = keyslot;
*ret_token = token;

View File

@ -24,8 +24,10 @@ int acquire_tpm2_key(
const char *key_file,
size_t key_file_size,
uint64_t key_file_offset,
const struct iovec *key_data,
const struct iovec *policy_hash,
const struct iovec blobs[],
size_t n_blobs,
const struct iovec policy_hash[],
size_t n_policy_hash,
const struct iovec *salt,
const struct iovec *srk,
const struct iovec *pcrlock_nv,
@ -44,8 +46,10 @@ int find_tpm2_auto_data(
struct iovec *ret_pubkey,
uint32_t *ret_pubkey_pcr_mask,
uint16_t *ret_primary_alg,
struct iovec *ret_blob,
struct iovec *ret_policy_hash,
struct iovec **ret_blobs,
size_t *ret_n_blobs,
struct iovec **ret_policy_hash,
size_t *ret_n_policy_hash,
struct iovec *ret_salt,
struct iovec *ret_srk,
struct iovec *ret_pcrlock_nv,
@ -68,8 +72,10 @@ static inline int acquire_tpm2_key(
const char *key_file,
size_t key_file_size,
uint64_t key_file_offset,
const struct iovec *key_data,
const struct iovec *policy_hash,
const struct iovec blobs[],
size_t n_blobs,
const struct iovec policy_hash[],
size_t n_policy_hash,
const struct iovec *salt,
const struct iovec *srk,
const struct iovec *pcrlock_nv,
@ -92,8 +98,10 @@ static inline int find_tpm2_auto_data(
struct iovec *ret_pubkey,
uint32_t *ret_pubkey_pcr_mask,
uint16_t *ret_primary_alg,
struct iovec *ret_blob,
struct iovec *ret_policy_hash,
struct iovec **ret_blobs,
size_t *ret_n_blobs,
struct iovec **ret_policy_hash,
size_t *ret_n_policy_hash,
struct iovec *ret_salt,
struct iovec *ret_srk,
struct iovec *ret_pcrlock_nv,

View File

@ -210,10 +210,22 @@ int dlopen_tpm2(void) {
}
void Esys_Freep(void *p) {
assert(p);
if (*(void**) p)
sym_Esys_Free(*(void**) p);
}
static void tpm2b_sensitive_data_erase_and_esys_freep(TPM2B_SENSITIVE_DATA **p) {
assert(p);
if (!*p)
return;
explicit_bzero_safe((*p)->buffer, (*p)->size);
sym_Esys_Free(*p);
}
/* Get a specific TPM capability (or capabilities).
*
* Returns 0 if there are no more capability properties of the requested type, or 1 if there are more, or < 0
@ -4179,8 +4191,11 @@ int tpm2_calculate_sealing_policy(
assert(pcr_values || n_pcr_values == 0);
assert(digest);
/* The combination of signed PCR policies and pcrlock is not supported (because we cannot combine
* PolicyAuthorize and PolicyAuthorizeNV in one policy). Callers need to use "sharding" of the
* symmetric FDE unlock key to make policies like that work. */
if (public && pcrlock_policy)
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Policies with both signed PCR and pcrlock are currently not supported.");
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Policies that combined signed PCR and pcrlock are not supported.");
if (public) {
r = tpm2_calculate_policy_authorize(public, NULL, digest);
@ -5375,10 +5390,12 @@ int tpm2_calculate_seal(
int tpm2_seal(Tpm2Context *c,
uint32_t seal_key_handle,
const TPM2B_DIGEST *policy,
const TPM2B_DIGEST policy[],
size_t n_policy,
const char *pin,
struct iovec *ret_secret,
struct iovec *ret_blob,
struct iovec **ret_blobs,
size_t *ret_n_blobs,
uint16_t *ret_primary_alg,
struct iovec *ret_srk) {
@ -5386,7 +5403,8 @@ int tpm2_seal(Tpm2Context *c,
int r;
assert(ret_secret);
assert(ret_blob);
assert(ret_blobs);
assert(ret_n_blobs);
/* So here's what we do here: we connect to the TPM2 chip. It persistently contains a "seed" key that
* is randomized when the TPM2 is first initialized or reset and remains stable across boots. We
@ -5424,7 +5442,6 @@ int tpm2_seal(Tpm2Context *c,
.objectAttributes = hmac_attributes,
.parameters.keyedHashDetail.scheme.scheme = TPM2_ALG_NULL,
.unique.keyedHash.size = SHA256_DIGEST_SIZE,
.authPolicy = policy ? *policy : TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE),
};
TPMS_SENSITIVE_CREATE hmac_sensitive = {
@ -5443,12 +5460,6 @@ int tpm2_seal(Tpm2Context *c,
(void) tpm2_credit_random(c);
log_debug("Generating secret key data.");
r = crypto_random_bytes(hmac_sensitive.data.buffer, hmac_sensitive.data.size);
if (r < 0)
return log_debug_errno(r, "Failed to generate secret key: %m");
_cleanup_(tpm2_handle_freep) Tpm2Handle *primary_handle = NULL;
if (ret_srk) {
_cleanup_(Esys_Freep) TPM2B_PUBLIC *primary_public = NULL;
@ -5526,24 +5537,51 @@ int tpm2_seal(Tpm2Context *c,
if (r < 0)
return r;
_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;
log_debug("Generating secret key data.");
/* At least one shard, and if we have multiple policies, then we need one shard for each */
size_t n_shards = MAX(n_policy, 1U);
/* Create a large secret which covers all shards we need */
_cleanup_(iovec_done_erase) struct iovec secret = {};
secret.iov_base = memdup(hmac_sensitive.data.buffer, hmac_sensitive.data.size);
if (!secret.iov_base)
return log_oom_debug();
secret.iov_len = hmac_sensitive.data.size;
log_debug("Marshalling private and public part of HMAC key.");
_cleanup_(iovec_done) struct iovec blob = {};
r = tpm2_marshal_blob(public, private, /* seed= */ NULL, &blob.iov_base, &blob.iov_len);
r = crypto_random_bytes_allocate_iovec(hmac_sensitive.data.size * n_shards, &secret);
if (r < 0)
return log_debug_errno(r, "Could not create sealed blob: %m");
return log_debug_errno(r, "Failed to generate secret key: %m");
struct iovec *blobs = new0(struct iovec, n_shards);
size_t n_blobs = 0;
CLEANUP_ARRAY(blobs, n_blobs, iovec_array_free);
for (size_t shard = 0; shard < n_shards; shard++) {
/* Patch this shard's policy into the template */
if (shard < n_policy)
hmac_template.authPolicy = policy[shard];
else
hmac_template.authPolicy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
/* Copy in this shard's secret key */
memcpy(hmac_sensitive.data.buffer,
(const uint8_t*) secret.iov_base + (hmac_sensitive.data.size * shard),
hmac_sensitive.data.size);
log_debug("Creating HMAC key on TPM for shard %zu.", shard);
_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;
log_debug("Marshalling private and public part of HMAC key for shard %zu.", shard);
r = tpm2_marshal_blob(public, private, /* seed= */ NULL, &blobs[n_blobs].iov_base, &blobs[n_blobs].iov_len);
if (r < 0)
return log_debug_errno(r, "Could not create sealed blob: %m");
n_blobs++;
}
if (DEBUG_LOGGING)
log_debug("Completed TPM2 key sealing in %s.", FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - start, 1));
@ -5571,7 +5609,8 @@ int tpm2_seal(Tpm2Context *c,
}
*ret_secret = TAKE_STRUCT(secret);
*ret_blob = TAKE_STRUCT(blob);
*ret_blobs = TAKE_PTR(blobs);
*ret_n_blobs = n_blobs;
if (ret_primary_alg)
*ret_primary_alg = primary_alg;
@ -5590,16 +5629,17 @@ int tpm2_unseal(Tpm2Context *c,
const char *pin,
const Tpm2PCRLockPolicy *pcrlock_policy,
uint16_t primary_alg,
const struct iovec *blob,
const struct iovec *known_policy_hash,
const struct iovec blobs[],
size_t n_blobs,
const struct iovec known_policy_hash[],
size_t n_known_policy_hash,
const struct iovec *srk,
struct iovec *ret_secret) {
TSS2_RC rc;
int r;
assert(iovec_is_set(blob));
assert(iovec_is_valid(known_policy_hash));
assert(n_blobs > 0);
assert(iovec_is_valid(pubkey));
assert(ret_secret);
@ -5616,12 +5656,11 @@ int tpm2_unseal(Tpm2Context *c,
usec_t start = now(CLOCK_MONOTONIC);
TPM2B_PUBLIC public;
TPM2B_PRIVATE private;
TPM2B_ENCRYPTED_SECRET seed = {};
r = tpm2_unmarshal_blob(blob->iov_base, blob->iov_len, &public, &private, &seed);
if (r < 0)
return log_debug_errno(r, "Could not extract parts from blob: %m");
size_t n_shards = pcrlock_policy && iovec_is_set(pubkey) ? 2 : 1;
if (n_blobs != n_shards)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Number of provided key blobs (%zu) does not match policy requirements (%zu).", n_blobs, n_shards);
if (n_known_policy_hash > 0 && n_known_policy_hash != n_shards)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Number of provided known policy hashes (%zu) does not match policy requirements (%zu or 0).", n_known_policy_hash, n_shards);
/* Older code did not save the pcr_bank, and unsealing needed to detect the best pcr bank to use,
* so we need to handle that legacy situation. */
@ -5657,37 +5696,6 @@ int tpm2_unseal(Tpm2Context *c,
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"No SRK or primary alg provided.");
if (seed.size > 0) {
/* This is a calculated (or duplicated) sealed object, and must be imported. */
_cleanup_free_ TPM2B_PRIVATE *imported_private = NULL;
r = tpm2_import(c,
primary_handle,
/* session= */ NULL,
&public,
&private,
&seed,
/* encryption_key= */ NULL,
/* symmetric= */ NULL,
&imported_private);
if (r < 0)
return r;
private = *imported_private;
}
log_debug("Loading HMAC key into TPM.");
/*
* Nothing sensitive on the bus, no need for encryption. Even if an attacker
* gives you back a different key, the session initiation will fail. In the
* SRK model, the tpmKey is verified. In the non-srk model, with pin, the bindKey
* provides protections.
*/
_cleanup_(tpm2_handle_freep) Tpm2Handle *hmac_key = NULL;
r = tpm2_load(c, primary_handle, NULL, &public, &private, &hmac_key);
if (r < 0)
return r;
TPM2B_PUBLIC pubkey_tpm2b;
_cleanup_(iovec_done) struct iovec fp = {};
if (iovec_is_set(pubkey)) {
@ -5700,100 +5708,143 @@ int tpm2_unseal(Tpm2Context *c,
return log_debug_errno(r, "Could not get key fingerprint: %m");
}
/*
* 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_(Esys_Freep) TPM2B_SENSITIVE_DATA* unsealed = NULL;
_cleanup_(iovec_done_erase) struct iovec secret = {};
for (unsigned i = RETRY_UNSEAL_MAX;; i--) {
_cleanup_(tpm2_handle_freep) Tpm2Handle *encryption_session = NULL;
r = tpm2_make_encryption_session(c, primary_handle, hmac_key, &encryption_session);
if (r < 0)
return r;
bool retry = false;
iovec_done_erase(&secret); /* clear data from previous unseal attempt */
_cleanup_(tpm2_handle_freep) Tpm2Handle *policy_session = NULL;
_cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
r = tpm2_make_policy_session(
c,
primary_handle,
encryption_session,
&policy_session);
if (r < 0)
return r;
for (size_t shard = 0; shard < n_blobs; shard++) {
TPM2B_PUBLIC public;
TPM2B_PRIVATE private;
TPM2B_ENCRYPTED_SECRET seed = {};
r = tpm2_unmarshal_blob(blobs[shard].iov_base, blobs[shard].iov_len, &public, &private, &seed);
if (r < 0)
return log_debug_errno(r, "Could not extract parts from blob: %m");
r = tpm2_build_sealing_policy(
c,
policy_session,
hash_pcr_mask,
pcr_bank,
iovec_is_set(pubkey) ? &pubkey_tpm2b : NULL,
fp.iov_base, fp.iov_len,
pubkey_pcr_mask,
signature,
!!pin,
pcrlock_policy,
&policy_digest);
if (r < 0)
return r;
if (seed.size > 0) {
/* This is a calculated (or duplicated) sealed object, and must be imported. */
_cleanup_free_ TPM2B_PRIVATE *imported_private = NULL;
r = tpm2_import(c,
primary_handle,
/* session= */ NULL,
&public,
&private,
&seed,
/* encryption_key= */ NULL,
/* symmetric= */ NULL,
&imported_private);
if (r < 0)
return r;
/* If we know the policy hash to expect, and it doesn't match, we can shortcut things here, and not
* wait until the TPM2 tells us to go away. */
if (iovec_is_set(known_policy_hash) && memcmp_nn(policy_digest->buffer,
private = *imported_private;
}
log_debug("Loading HMAC key into TPM for shard %zu.", shard);
/* Nothing sensitive on the bus, no need for encryption. Even if an attacker gives
* you back a different key, the session initiation will fail. In the SRK model, the
* tpmKey is verified. In the non-srk model, with pin, the bindKey provides
* protections. */
_cleanup_(tpm2_handle_freep) Tpm2Handle *hmac_key = NULL;
r = tpm2_load(c, primary_handle, NULL, &public, &private, &hmac_key);
if (r < 0)
return r;
/* 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_freep) Tpm2Handle *encryption_session = NULL;
r = tpm2_make_encryption_session(c, primary_handle, hmac_key, &encryption_session);
if (r < 0)
return r;
_cleanup_(tpm2_handle_freep) Tpm2Handle *policy_session = NULL;
_cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
r = tpm2_make_policy_session(
c,
primary_handle,
encryption_session,
&policy_session);
if (r < 0)
return r;
/* If both public PCR key and pcrlock policies are requested, then generate the
* public PCR policy for the first shared, and the pcrlock policy for the 2nd */
r = tpm2_build_sealing_policy(
c,
policy_session,
hash_pcr_mask,
pcr_bank,
shard == 0 && iovec_is_set(pubkey) ? &pubkey_tpm2b : NULL,
fp.iov_base, fp.iov_len,
shard == 0 ? pubkey_pcr_mask : 0,
signature,
!!pin,
(shard == 1 || !iovec_is_set(pubkey)) ? pcrlock_policy : NULL,
&policy_digest);
if (r < 0)
return r;
/* If we know the policy hash to expect, and it doesn't match, we can shortcut things here, and not
* wait until the TPM2 tells us to go away. */
if (n_known_policy_hash > 0 && memcmp_nn(policy_digest->buffer,
policy_digest->size,
known_policy_hash->iov_base,
known_policy_hash->iov_len) != 0) {
known_policy_hash[shard].iov_base,
known_policy_hash[shard].iov_len) != 0) {
#if HAVE_OPENSSL
if (iovec_is_set(pubkey) &&
pubkey_tpm2b.publicArea.type == TPM2_ALG_RSA &&
pubkey_tpm2b.publicArea.parameters.rsaDetail.exponent == TPM2_RSA_DEFAULT_EXPONENT) {
/* Due to bug #30546, if using RSA pubkey with the default exponent, we may
* need to set the exponent to the TPM special-case value of 0 and retry. */
log_debug("Policy hash mismatch, retrying with RSA pubkey exponent set to 0.");
pubkey_tpm2b.publicArea.parameters.rsaDetail.exponent = 0;
continue;
} else
if (shard == 0 &&
iovec_is_set(pubkey) &&
pubkey_tpm2b.publicArea.type == TPM2_ALG_RSA &&
pubkey_tpm2b.publicArea.parameters.rsaDetail.exponent == TPM2_RSA_DEFAULT_EXPONENT) {
/* Due to bug #30546, if using RSA pubkey with the default exponent, we may
* need to set the exponent to the TPM special-case value of 0 and retry. */
log_debug("Policy hash mismatch, retrying with RSA pubkey exponent set to 0.");
pubkey_tpm2b.publicArea.parameters.rsaDetail.exponent = 0;
retry = true;
break;
}
#endif
return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
"Current policy digest does not match stored policy digest, cancelling "
"TPM2 authentication attempt.");
}
log_debug("Unsealing HMAC key for shard %zu.", shard);
_cleanup_(tpm2b_sensitive_data_erase_and_esys_freep) TPM2B_SENSITIVE_DATA* unsealed = NULL;
rc = sym_Esys_Unseal(
c->esys_context,
hmac_key->esys_handle,
policy_session->esys_handle,
encryption_session->esys_handle, /* use HMAC session to enable parameter encryption */
ESYS_TR_NONE,
&unsealed);
if (rc == TPM2_RC_PCR_CHANGED && i > 0) {
log_debug("A PCR value changed during the TPM2 policy session, restarting HMAC key unsealing (%u tries left).", i);
retry = true;
break;
}
if (rc != TPM2_RC_SUCCESS)
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to unseal HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
if (!iovec_append(&secret, &IOVEC_MAKE(unsealed->buffer, unsealed->size)))
return log_oom_debug();
}
log_debug("Unsealing HMAC key.");
rc = sym_Esys_Unseal(
c->esys_context,
hmac_key->esys_handle,
policy_session->esys_handle,
encryption_session->esys_handle, /* use HMAC session to enable parameter encryption */
ESYS_TR_NONE,
&unsealed);
if (rc == TSS2_RC_SUCCESS)
if (!retry)
break;
if (rc != TPM2_RC_PCR_CHANGED || i == 0)
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to unseal HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
log_debug("A PCR value changed during the TPM2 policy session, restarting HMAC key unsealing (%u tries left).", i);
}
_cleanup_(iovec_done_erase) struct iovec secret = {};
secret.iov_base = memdup(unsealed->buffer, unsealed->size);
explicit_bzero_safe(unsealed->buffer, unsealed->size);
if (!secret.iov_base)
return log_oom_debug();
secret.iov_len = unsealed->size;
if (DEBUG_LOGGING)
log_debug("Completed TPM2 key unsealing in %s.", FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - start, 1));
*ret_secret = TAKE_STRUCT(secret);
return 0;
}
@ -6064,7 +6115,7 @@ int tpm2_unseal_data(
if (r < 0)
return r;
_cleanup_(Esys_Freep) TPM2B_SENSITIVE_DATA* unsealed = NULL;
_cleanup_(tpm2b_sensitive_data_erase_and_esys_freep) TPM2B_SENSITIVE_DATA* unsealed = NULL;
rc = sym_Esys_Unseal(
c->esys_context,
what->esys_handle,
@ -6079,11 +6130,10 @@ int tpm2_unseal_data(
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to unseal data: %s", sym_Tss2_RC_Decode(rc));
_cleanup_(iovec_done) struct iovec d = {};
d = IOVEC_MAKE(memdup(unsealed->buffer, unsealed->size), unsealed->size);
explicit_bzero_safe(unsealed->buffer, unsealed->size);
_cleanup_(iovec_done) struct iovec d = {
.iov_base = memdup(unsealed->buffer, unsealed->size),
.iov_len = unsealed->size,
};
if (!d.iov_base)
return log_oom_debug();
@ -7344,6 +7394,46 @@ int tpm2_parse_pcr_json_array(sd_json_variant *v, uint32_t *ret) {
return 0;
}
static int tpm2_make_shard_array(
const struct iovec data[],
size_t n_data,
int (*encode_iovec)(sd_json_variant **, const void*, size_t n), /* pass sd_json_variant_new_base64() or sd_json_variant_new_hex() */
sd_json_variant **ret) {
int r;
/* Turns a series of struct iovec into either an array of base64/hex strings, or a single string
* thereof. Used for generated "tpm2-blob" or "tpm2-policy-hash" fields. */
assert(data);
assert(n_data > 0);
assert(encode_iovec);
assert(ret);
/* Only one item? Then create only one encoded string, for compatibility with older versions which
* didn't support the "sharding" scheme */
if (n_data == 1)
return encode_iovec(ret, data[0].iov_base, data[0].iov_len);
/* Multiple items? Then generate an array of encoded strings */
_cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL;
FOREACH_ARRAY(d, data, n_data) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *item = NULL;
r = encode_iovec(&item, d->iov_base, d->iov_len);
if (r < 0)
return r;
r = sd_json_variant_append_array(&j, item);
if (r < 0)
return r;
}
*ret = TAKE_PTR(j);
return 0;
}
int tpm2_make_luks2_json(
int keyslot,
uint32_t hash_pcr_mask,
@ -7351,8 +7441,10 @@ int tpm2_make_luks2_json(
const struct iovec *pubkey,
uint32_t pubkey_pcr_mask,
uint16_t primary_alg,
const struct iovec *blob,
const struct iovec *policy_hash,
const struct iovec blobs[],
size_t n_blobs,
const struct iovec policy_hash[],
size_t n_policy_hash,
const struct iovec *salt,
const struct iovec *srk,
const struct iovec *pcrlock_nv,
@ -7364,8 +7456,8 @@ int tpm2_make_luks2_json(
int r;
assert(iovec_is_valid(pubkey));
assert(iovec_is_valid(blob));
assert(iovec_is_valid(policy_hash));
assert(n_blobs >= 1);
assert(n_policy_hash >= 1);
if (asprintf(&keyslot_as_string, "%i", keyslot) < 0)
return -ENOMEM;
@ -7380,6 +7472,16 @@ int tpm2_make_luks2_json(
return r;
}
_cleanup_(sd_json_variant_unrefp) sd_json_variant *phj = NULL;
r = tpm2_make_shard_array(policy_hash, n_policy_hash, sd_json_variant_new_hex, &phj);
if (r < 0)
return r;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *bj = NULL;
r = tpm2_make_shard_array(blobs, n_blobs, sd_json_variant_new_base64, &bj);
if (r < 0)
return r;
/* Note: We made the mistake of using "-" in the field names, which isn't particular compatible with
* other programming languages. Let's not make things worse though, i.e. future additions to the JSON
* object should use "_" rather than "-" in field names. */
@ -7388,11 +7490,11 @@ int tpm2_make_luks2_json(
&v,
SD_JSON_BUILD_PAIR("type", JSON_BUILD_CONST_STRING("systemd-tpm2")),
SD_JSON_BUILD_PAIR("keyslots", SD_JSON_BUILD_ARRAY(SD_JSON_BUILD_STRING(keyslot_as_string))),
SD_JSON_BUILD_PAIR("tpm2-blob", JSON_BUILD_IOVEC_BASE64(blob)),
SD_JSON_BUILD_PAIR("tpm2-blob", SD_JSON_BUILD_VARIANT(bj)),
SD_JSON_BUILD_PAIR("tpm2-pcrs", SD_JSON_BUILD_VARIANT(hmj)),
SD_JSON_BUILD_PAIR_CONDITION(pcr_bank != 0 && tpm2_hash_alg_to_string(pcr_bank), "tpm2-pcr-bank", SD_JSON_BUILD_STRING(tpm2_hash_alg_to_string(pcr_bank))),
SD_JSON_BUILD_PAIR_CONDITION(primary_alg != 0 && tpm2_asym_alg_to_string(primary_alg), "tpm2-primary-alg", SD_JSON_BUILD_STRING(tpm2_asym_alg_to_string(primary_alg))),
SD_JSON_BUILD_PAIR("tpm2-policy-hash", JSON_BUILD_IOVEC_HEX(policy_hash)),
SD_JSON_BUILD_PAIR("tpm2-policy-hash", SD_JSON_BUILD_VARIANT(phj)),
SD_JSON_BUILD_PAIR_CONDITION(FLAGS_SET(flags, TPM2_FLAGS_USE_PIN), "tpm2-pin", SD_JSON_BUILD_BOOLEAN(true)),
SD_JSON_BUILD_PAIR_CONDITION(FLAGS_SET(flags, TPM2_FLAGS_USE_PCRLOCK), "tpm2_pcrlock", SD_JSON_BUILD_BOOLEAN(true)),
SD_JSON_BUILD_PAIR_CONDITION(pubkey_pcr_mask != 0, "tpm2_pubkey_pcrs", SD_JSON_BUILD_VARIANT(pkmj)),
@ -7409,6 +7511,63 @@ int tpm2_make_luks2_json(
return keyslot;
}
static int tpm2_parse_shard_array(
sd_json_variant *v,
const char *name,
int (*decode_iovec)(sd_json_variant*, struct iovec *ret), /* pass json_variant_unbase64_iovec() or json_variant_unhex_iovec() */
struct iovec **ret_data,
size_t *ret_n_data) {
int r;
assert(v);
assert(name);
assert(decode_iovec);
assert(ret_data);
assert(ret_n_data);
/* Parses the "tpm2-blob" or "tpm2-policy-hash" fields of our LUKS JSON serialization. This can
* either be an array of base64/hex strings, or a single such string. The former to allow for sharded
* keys. The latter mostly for compatibility with older versions where we didn't support sharded
* keys. */
struct iovec *data = NULL;
size_t n_data = 0;
CLEANUP_ARRAY(data, n_data, iovec_array_free);
if (sd_json_variant_is_array(v)) {
if (sd_json_variant_elements(v) == 0)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "TPM2 token data contains empty '%s' array.", name);
data = new0(struct iovec, sd_json_variant_elements(v));
if (!data)
return log_oom_debug();
sd_json_variant *i;
JSON_VARIANT_ARRAY_FOREACH(i, v) {
r = decode_iovec(i, data + n_data);
if (r < 0)
return log_debug_errno(r, "Invalid data in '%s' field.", name);
n_data++;
}
} else {
data = new0(struct iovec, 1);
if (!data)
return log_oom_debug();
r = decode_iovec(v, data + 0);
if (r < 0)
return log_debug_errno(r, "Invalid data in '%s' field.", name);
n_data = 1;
}
*ret_data = TAKE_PTR(data);
*ret_n_data = n_data;
return 0;
}
int tpm2_parse_luks2_json(
sd_json_variant *v,
int *ret_keyslot,
@ -7417,14 +7576,16 @@ int tpm2_parse_luks2_json(
struct iovec *ret_pubkey,
uint32_t *ret_pubkey_pcr_mask,
uint16_t *ret_primary_alg,
struct iovec *ret_blob,
struct iovec *ret_policy_hash,
struct iovec **ret_blobs,
size_t *ret_n_blobs,
struct iovec **ret_policy_hash,
size_t *ret_n_policy_hash,
struct iovec *ret_salt,
struct iovec *ret_srk,
struct iovec *ret_pcrlock_nv,
TPM2Flags *ret_flags) {
_cleanup_(iovec_done) struct iovec blob = {}, policy_hash = {}, pubkey = {}, salt = {}, srk = {}, pcrlock_nv = {};
_cleanup_(iovec_done) struct iovec pubkey = {}, salt = {}, srk = {}, pcrlock_nv = {};
uint32_t hash_pcr_mask = 0, pubkey_pcr_mask = 0;
uint16_t primary_alg = TPM2_ALG_ECC; /* ECC was the only supported algorithm in systemd < 250, use that as implied default, for compatibility */
uint16_t pcr_bank = UINT16_MAX; /* default: pick automatically */
@ -7489,17 +7650,25 @@ int tpm2_parse_luks2_json(
if (!w)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "TPM2 token data lacks 'tpm2-blob' field.");
r = json_variant_unbase64_iovec(w, &blob);
struct iovec *blobs = NULL;
size_t n_blobs = 0;
CLEANUP_ARRAY(blobs, n_blobs, iovec_array_free);
r = tpm2_parse_shard_array(w, "tpm2-blob", json_variant_unbase64_iovec, &blobs, &n_blobs);
if (r < 0)
return log_debug_errno(r, "Invalid base64 data in 'tpm2-blob' field.");
return r;
w = sd_json_variant_by_key(v, "tpm2-policy-hash");
if (!w)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "TPM2 token data lacks 'tpm2-policy-hash' field.");
r = json_variant_unhex_iovec(w, &policy_hash);
struct iovec *policy_hash = NULL;
size_t n_policy_hash = 0;
CLEANUP_ARRAY(policy_hash, n_policy_hash, iovec_array_free);
r = tpm2_parse_shard_array(w, "tpm2-policy-hash", json_variant_unhex_iovec, &policy_hash, &n_policy_hash);
if (r < 0)
return log_debug_errno(r, "Invalid base64 data in 'tpm2-policy-hash' field.");
return r;
w = sd_json_variant_by_key(v, "tpm2-pin");
if (w) {
@ -7565,10 +7734,14 @@ int tpm2_parse_luks2_json(
*ret_pubkey_pcr_mask = pubkey_pcr_mask;
if (ret_primary_alg)
*ret_primary_alg = primary_alg;
if (ret_blob)
*ret_blob = TAKE_STRUCT(blob);
if (ret_blobs)
*ret_blobs = TAKE_PTR(blobs);
if (ret_n_blobs)
*ret_n_blobs = n_blobs;
if (ret_policy_hash)
*ret_policy_hash = TAKE_STRUCT(policy_hash);
*ret_policy_hash = TAKE_PTR(policy_hash);
if (ret_n_policy_hash)
*ret_n_policy_hash = n_policy_hash;
if (ret_salt)
*ret_salt = TAKE_STRUCT(salt);
if (ret_srk)

View File

@ -291,8 +291,8 @@ int tpm2_get_best_srk_template(Tpm2Context *c, TPMT_PUBLIC *ret_template);
int tpm2_get_srk(Tpm2Context *c, const Tpm2Handle *session, TPM2B_PUBLIC **ret_public, TPM2B_NAME **ret_name, TPM2B_NAME **ret_qname, Tpm2Handle **ret_handle);
int tpm2_get_or_create_srk(Tpm2Context *c, const Tpm2Handle *session, TPM2B_PUBLIC **ret_public, TPM2B_NAME **ret_name, TPM2B_NAME **ret_qname, Tpm2Handle **ret_handle);
int tpm2_seal(Tpm2Context *c, uint32_t seal_key_handle, const TPM2B_DIGEST *policy, const char *pin, struct iovec *ret_secret, struct iovec *ret_blob, uint16_t *ret_primary_alg, struct iovec *ret_srk);
int tpm2_unseal(Tpm2Context *c, uint32_t hash_pcr_mask, uint16_t pcr_bank, const struct iovec *pubkey, uint32_t pubkey_pcr_mask, sd_json_variant *signature, const char *pin, const Tpm2PCRLockPolicy *pcrlock_policy, uint16_t primary_alg, const struct iovec *blob, const struct iovec *policy_hash, const struct iovec *srk, struct iovec *ret_secret);
int tpm2_seal(Tpm2Context *c, uint32_t seal_key_handle, const TPM2B_DIGEST policy_hash[], size_t n_policy, const char *pin, struct iovec *ret_secret, struct iovec **ret_blobs, size_t *ret_n_blobs, uint16_t *ret_primary_alg, struct iovec *ret_srk);
int tpm2_unseal(Tpm2Context *c, uint32_t hash_pcr_mask, uint16_t pcr_bank, const struct iovec *pubkey, uint32_t pubkey_pcr_mask, sd_json_variant *signature, const char *pin, const Tpm2PCRLockPolicy *pcrlock_policy, uint16_t primary_alg, const struct iovec blobs[], size_t n_blobs, const struct iovec policy_hash[], size_t n_policy_hash, const struct iovec *srk, struct iovec *ret_secret);
#if HAVE_OPENSSL
int tpm2_tpm2b_public_to_openssl_pkey(const TPM2B_PUBLIC *public, EVP_PKEY **ret);
@ -391,8 +391,8 @@ int tpm2_find_device_auto(char **ret);
int tpm2_make_pcr_json_array(uint32_t pcr_mask, sd_json_variant **ret);
int tpm2_parse_pcr_json_array(sd_json_variant *v, uint32_t *ret);
int tpm2_make_luks2_json(int keyslot, uint32_t hash_pcr_mask, uint16_t pcr_bank, const struct iovec *pubkey, uint32_t pubkey_pcr_mask, uint16_t primary_alg, const struct iovec *blob, const struct iovec *policy_hash, const struct iovec *salt, const struct iovec *srk, const struct iovec *pcrlock_nv, TPM2Flags flags, sd_json_variant **ret);
int tpm2_parse_luks2_json(sd_json_variant *v, int *ret_keyslot, uint32_t *ret_hash_pcr_mask, uint16_t *ret_pcr_bank, struct iovec *ret_pubkey, uint32_t *ret_pubkey_pcr_mask, uint16_t *ret_primary_alg, struct iovec *ret_blob, struct iovec *ret_policy_hash, struct iovec *ret_salt, struct iovec *ret_srk, struct iovec *pcrlock_nv, TPM2Flags *ret_flags);
int tpm2_make_luks2_json(int keyslot, uint32_t hash_pcr_mask, uint16_t pcr_bank, const struct iovec *pubkey, uint32_t pubkey_pcr_mask, uint16_t primary_alg, const struct iovec blobs[], size_t n_blobs, const struct iovec policy_hash[], size_t n_policy_hash, const struct iovec *salt, const struct iovec *srk, const struct iovec *pcrlock_nv, TPM2Flags flags, sd_json_variant **ret);
int tpm2_parse_luks2_json(sd_json_variant *v, int *ret_keyslot, uint32_t *ret_hash_pcr_mask, uint16_t *ret_pcr_bank, struct iovec *ret_pubkey, uint32_t *ret_pubkey_pcr_mask, uint16_t *ret_primary_alg, struct iovec **ret_blobs, size_t *ret_n_blobs, struct iovec **ret_policy_hash, size_t *ret_n_policy_hash, struct iovec *ret_salt, struct iovec *ret_srk, struct iovec *pcrlock_nv, TPM2Flags *ret_flags);
/* Default to PCR 7 only */
#define TPM2_PCR_INDEX_DEFAULT UINT32_C(7)

View File

@ -54,4 +54,17 @@ TEST(iovec_set_and_valid) {
assert_se(!iovec_is_valid(&invalid));
}
TEST(iovec_append) {
_cleanup_(iovec_done) struct iovec iov = {};
assert_se(iovec_append(&iov, &IOVEC_MAKE_STRING("")) == &iov);
assert_se(iovec_append(&iov, &IOVEC_MAKE_STRING("waldo")) == &iov);
assert_se(iovec_append(&iov, &IOVEC_MAKE_STRING("quux")) == &iov);
assert_se(iovec_append(&iov, &IOVEC_MAKE_STRING("")) == &iov);
assert_se(iovec_append(&iov, &IOVEC_MAKE_STRING("p")) == &iov);
assert_se(iovec_append(&iov, &IOVEC_MAKE_STRING("")) == &iov);
assert_se(iovec_memcmp(&iov, &IOVEC_MAKE_STRING("waldoquuxp")) == 0);
}
DEFINE_TEST_MAIN(LOG_INFO);

View File

@ -1163,7 +1163,9 @@ static void calculate_seal_and_unseal(
/* pcrlock_policy= */ NULL,
/* primary_alg= */ 0,
&blob,
/* n_blobs= */ 1,
/* known_policy_hash= */ NULL,
/* n_known_policy_hash= */ 0,
&serialized_parent,
&unsealed_secret) >= 0);
@ -1222,14 +1224,20 @@ static void check_seal_unseal_for_handle(Tpm2Context *c, TPM2_HANDLE handle) {
log_debug("Check seal/unseal for handle 0x%" PRIx32, handle);
_cleanup_(iovec_done) struct iovec secret = {}, blob = {}, srk = {}, unsealed_secret = {};
_cleanup_(iovec_done) struct iovec secret = {}, srk = {}, unsealed_secret = {};
struct iovec *blobs = NULL;
size_t n_blobs = 0;
CLEANUP_ARRAY(blobs, n_blobs, iovec_array_free);
assert_se(tpm2_seal(
c,
handle,
&policy,
1,
/* pin= */ NULL,
&secret,
&blob,
&blobs,
&n_blobs,
/* ret_primary_alg= */ NULL,
&srk) >= 0);
@ -1243,8 +1251,10 @@ static void check_seal_unseal_for_handle(Tpm2Context *c, TPM2_HANDLE handle) {
/* pin= */ NULL,
/* pcrlock_policy= */ NULL,
/* primary_alg= */ 0,
&blob,
blobs,
n_blobs,
/* policy_hash= */ NULL,
/* n_policy_hash= */ 0,
&srk,
&unsealed_secret) >= 0);

View File

@ -10,9 +10,10 @@ export SYSTEMD_LOG_LEVEL=debug
export PAGER=
SD_PCREXTEND="/usr/lib/systemd/systemd-pcrextend"
SD_PCRLOCK="/usr/lib/systemd/systemd-pcrlock"
SD_MEASURE="/usr/lib/systemd/systemd-measure"
if [[ ! -x "${SD_PCREXTEND:?}" ]] || [[ ! -x "${SD_PCRLOCK:?}" ]] ; then
echo "$SD_PCREXTEND or $SD_PCRLOCK not found, skipping pcrlock tests"
if [[ ! -x "${SD_PCREXTEND:?}" ]] || [[ ! -x "${SD_PCRLOCK:?}" ]] || [[ ! -x "${SD_MEASURE:?}" ]] ; then
echo "$SD_PCREXTEND or $SD_PCRLOCK or $SD_MEASURE not found, skipping pcrlock tests"
exit 0
fi
@ -127,6 +128,17 @@ echo -n test70-take-two | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/92
systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless
systemd-cryptsetup detach pcrlock
# Now combined pcrlock and signed PCR
# Generate key pair
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out "$img".private.pem
openssl rsa -pubout -in "$img".private.pem -out "$img".public.pem
systemd-cryptenroll --unlock-tpm2-device=auto --tpm2-device=auto --tpm2-pcrlock=/var/lib/systemd/pcrlock.json --tpm2-public-key="$img".public.pem --wipe-slot=tpm2 "$img"
"$SD_MEASURE" sign --current --bank=sha256 --private-key="$img".private.pem --public-key="$img".public.pem --phase=: | tee "$img".pcrsign
SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach pcrlock "$img" - "tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,tpm2-signature=$img.pcrsign,headless"
systemd-cryptsetup detach pcrlock
systemd-cryptenroll --unlock-key-file=/tmp/pcrlockpwd --tpm2-device=auto --tpm2-pcrlock=/var/lib/systemd/pcrlock.json --wipe-slot=tpm2 "$img"
rm "$img".public.pem "$img".private.pem "$img".pcrsign
# Now use the root fs support, i.e. make the tool write a copy of the pcrlock
# file as service credential to some temporary dir and remove the local copy, so that
# it has to use the credential version.