diff --git a/man/systemd-cryptenroll.xml b/man/systemd-cryptenroll.xml
index 6ca850e0ef..cea2072c47 100644
--- a/man/systemd-cryptenroll.xml
+++ b/man/systemd-cryptenroll.xml
@@ -391,13 +391,22 @@
PCR
Configures the TPM2 PCRs (Platform Configuration Registers) to bind to when
- enrollment is requested via . Takes a list of PCR names or numeric
- indices in the range 0…23. Multiple PCR indexes are separated by +. If not
- specified, the default is to use PCR 7 only. If an empty string is specified, binds the enrollment to
- no PCRs at all. See the table above for a list of available PCRs.
+ enrollment is requested via . Takes a list of PCR entries, where each
+ entry starts with a name or numeric index in the range 0…23, optionally followed by
+ : and a hash algorithm name (specifying the PCR bank), optionally followed by
+ = and a hash digest value. Multiple PCR entries are separated by
+ +. If not specified, the default is to use PCR 7 only. If an empty string is
+ specified, binds the enrollment to no PCRs at all. See the table above for a list of available
+ PCRs.
Example:
specifies that PCR registers 4, 1, and 5 should be used.
+ Example: specifies that PCR register 7 from the SHA256
+ bank should be used.
+ Example:
+ specifies that PCR register 4 from the SHA1 bank should be used, and a hash digest value of
+ 0x3A3F780F11A4B49969FCAA80CD6E3957C33B2275 will be used instead of reading the current PCR
+ value.
diff --git a/src/basic/macro.h b/src/basic/macro.h
index 13620a60bc..d9df3b7365 100644
--- a/src/basic/macro.h
+++ b/src/basic/macro.h
@@ -335,6 +335,15 @@ static inline int __coverity_check_and_return__(int condition) {
} \
}
+/* When func() doesn't return the appropriate type, and is also a macro, set variable to empty afterwards. */
+#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO(type, func, empty) \
+ static inline void func##p(type *p) { \
+ if (*p != (empty)) { \
+ func(*p); \
+ *p = (empty); \
+ } \
+ }
+
#define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope) \
scope type *name##_ref(type *p) { \
if (!p) \
diff --git a/src/boot/measure.c b/src/boot/measure.c
index e388fb666e..518c41f65c 100644
--- a/src/boot/measure.c
+++ b/src/boot/measure.c
@@ -831,23 +831,13 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
if (tpmalg < 0)
return log_error_errno(tpmalg, "Unsupported PCR bank");
- TPML_PCR_SELECTION pcr_selection;
- tpm2_tpml_pcr_selection_from_mask(1 << TPM_PCR_INDEX_KERNEL_IMAGE,
- tpmalg,
- &pcr_selection);
+ Tpm2PCRValue pcr_value = TPM2_PCR_VALUE_MAKE(TPM_PCR_INDEX_KERNEL_IMAGE,
+ tpmalg,
+ TPM2B_DIGEST_MAKE(p->value, p->value_size));
- TPM2B_DIGEST pcr_values = {
- .size = p->value_size,
- };
- assert(sizeof(pcr_values.buffer) >= p->value_size);
- memcpy_safe(pcr_values.buffer, p->value, p->value_size);
+ TPM2B_DIGEST pcr_policy_digest = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
- TPM2B_DIGEST pcr_policy_digest;
- r = tpm2_digest_init(TPM2_ALG_SHA256, &pcr_policy_digest);
- if (r < 0)
- return r;
-
- r = tpm2_calculate_policy_pcr(&pcr_selection, &pcr_values, 1, &pcr_policy_digest);
+ r = tpm2_calculate_policy_pcr(&pcr_value, 1, &pcr_policy_digest);
if (r < 0)
return r;
diff --git a/src/creds/creds.c b/src/creds/creds.c
index 679b0750de..36531d7d18 100644
--- a/src/creds/creds.c
+++ b/src/creds/creds.c
@@ -860,7 +860,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_TPM2_PCRS: /* For fixed hash PCR policies only */
- r = tpm2_parse_pcr_argument(optarg, &arg_tpm2_pcr_mask);
+ r = tpm2_parse_pcr_argument_to_mask(optarg, &arg_tpm2_pcr_mask);
if (r < 0)
return r;
@@ -874,7 +874,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_TPM2_PUBLIC_KEY_PCRS: /* For public key PCR policies only */
- r = tpm2_parse_pcr_argument(optarg, &arg_tpm2_public_key_pcr_mask);
+ r = tpm2_parse_pcr_argument_to_mask(optarg, &arg_tpm2_public_key_pcr_mask);
if (r < 0)
return r;
diff --git a/src/cryptenroll/cryptenroll-tpm2.c b/src/cryptenroll/cryptenroll-tpm2.c
index 4dc3c1794d..86aed31838 100644
--- a/src/cryptenroll/cryptenroll-tpm2.c
+++ b/src/cryptenroll/cryptenroll-tpm2.c
@@ -133,7 +133,8 @@ int enroll_tpm2(struct crypt_device *cd,
const void *volume_key,
size_t volume_key_size,
const char *device,
- uint32_t hash_pcr_mask,
+ Tpm2PCRValue *hash_pcr_values,
+ size_t n_hash_pcr_values,
const char *pubkey_path,
uint32_t pubkey_pcr_mask,
const char *signature_path,
@@ -143,9 +144,8 @@ int enroll_tpm2(struct crypt_device *cd,
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *signature_json = NULL;
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
_cleanup_free_ void *srk_buf = NULL;
- size_t secret_size, blob_size, hash_size, pubkey_size = 0, srk_buf_size = 0;
- _cleanup_free_ void *blob = NULL, *hash = NULL, *pubkey = NULL;
- uint16_t pcr_bank, primary_alg;
+ size_t secret_size, blob_size, pubkey_size = 0, srk_buf_size = 0;
+ _cleanup_free_ void *blob = NULL, *pubkey = NULL;
const char *node;
_cleanup_(erase_and_freep) char *pin_str = NULL;
ssize_t base64_encoded_size;
@@ -163,7 +163,7 @@ int enroll_tpm2(struct crypt_device *cd,
assert(cd);
assert(volume_key);
assert(volume_key_size > 0);
- assert(TPM2_PCR_MASK_VALID(hash_pcr_mask));
+ assert(TPM2_PCR_VALUES_VALID(hash_pcr_values, n_hash_pcr_values));
assert(TPM2_PCR_MASK_VALID(pubkey_pcr_mask));
assert_se(node = crypt_get_device_name(cd));
@@ -206,23 +206,69 @@ int enroll_tpm2(struct crypt_device *cd,
return log_debug_errno(r, "Failed to read TPM PCR signature: %m");
}
- r = tpm2_seal(device,
- hash_pcr_mask,
- pubkey, pubkey_size,
- pubkey_pcr_mask,
+ _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
+ r = tpm2_context_new(device, &tpm2_context);
+ if (r < 0)
+ return r;
+
+ bool pcr_value_specified = false;
+ for (size_t i = 0; i < n_hash_pcr_values; i++)
+ if (hash_pcr_values[i].value.size > 0) {
+ pcr_value_specified = true;
+ break;
+ }
+
+ r = tpm2_pcr_read_missing_values(tpm2_context, hash_pcr_values, n_hash_pcr_values);
+ if (r < 0)
+ return r;
+
+ uint16_t hash_pcr_bank = 0;
+ uint32_t hash_pcr_mask = 0;
+ if (n_hash_pcr_values > 0) {
+ size_t hash_count;
+ r = tpm2_pcr_values_hash_count(hash_pcr_values, n_hash_pcr_values, &hash_count);
+ if (r < 0)
+ return log_error_errno(r, "Could not get hash count: %m");
+
+ if (hash_count > 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Multiple PCR banks selected.");
+
+ hash_pcr_bank = hash_pcr_values[0].hash;
+ r = tpm2_pcr_values_to_mask(hash_pcr_values, n_hash_pcr_values, hash_pcr_bank, &hash_pcr_mask);
+ if (r < 0)
+ return log_error_errno(r, "Could not get hash mask: %m");
+ }
+
+ TPM2B_PUBLIC public;
+ if (pubkey) {
+ r = tpm2_tpm2b_public_from_pem(pubkey, pubkey_size, &public);
+ if (r < 0)
+ return log_error_errno(r, "Could not convert public key to TPM2B_PUBLIC: %m");
+ }
+
+ TPM2B_DIGEST policy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
+ r = tpm2_calculate_sealing_policy(
+ hash_pcr_values,
+ n_hash_pcr_values,
+ pubkey ? &public : NULL,
+ use_pin,
+ &policy);
+ if (r < 0)
+ return r;
+
+ r = tpm2_seal(tpm2_context,
+ &policy,
pin_str,
&secret, &secret_size,
&blob, &blob_size,
- &hash, &hash_size,
- &pcr_bank,
- &primary_alg,
+ /* ret_primary_alg= */ NULL,
&srk_buf,
&srk_buf_size);
if (r < 0)
return r;
/* Let's see if we already have this specific PCR policy hash enrolled, if so, exit early. */
- r = search_policy_hash(cd, hash, hash_size);
+ r = search_policy_hash(cd, policy.buffer, policy.size);
if (r == -ENOENT)
log_debug_errno(r, "PCR policy hash not yet enrolled, enrolling now.");
else if (r < 0)
@@ -233,21 +279,21 @@ int enroll_tpm2(struct crypt_device *cd,
}
/* Quick verification that everything is in order, we are not in a hurry after all. */
- if (!pubkey || signature_json) {
+ if ((!pubkey || signature_json) && !pcr_value_specified) {
_cleanup_(erase_and_freep) void *secret2 = NULL;
size_t secret2_size;
log_debug("Unsealing for verification...");
r = tpm2_unseal(device,
hash_pcr_mask,
- pcr_bank,
+ hash_pcr_bank,
pubkey, pubkey_size,
pubkey_pcr_mask,
signature_json,
pin_str,
- primary_alg,
+ /* primary_alg= */ 0,
blob, blob_size,
- hash, hash_size,
+ policy.buffer, policy.size,
srk_buf, srk_buf_size,
&secret2, &secret2_size);
if (r < 0)
@@ -279,12 +325,12 @@ int enroll_tpm2(struct crypt_device *cd,
r = tpm2_make_luks2_json(
keyslot,
hash_pcr_mask,
- pcr_bank,
+ hash_pcr_bank,
pubkey, pubkey_size,
pubkey_pcr_mask,
- primary_alg,
+ /* primary_alg= */ 0,
blob, blob_size,
- hash, hash_size,
+ policy.buffer, policy.size,
use_pin ? binary_salt : NULL,
use_pin ? sizeof(binary_salt) : 0,
srk_buf, srk_buf_size,
diff --git a/src/cryptenroll/cryptenroll-tpm2.h b/src/cryptenroll/cryptenroll-tpm2.h
index b6e0c2808e..d43a9a8ffe 100644
--- a/src/cryptenroll/cryptenroll-tpm2.h
+++ b/src/cryptenroll/cryptenroll-tpm2.h
@@ -5,11 +5,12 @@
#include "cryptsetup-util.h"
#include "log.h"
+#include "tpm2-util.h"
#if HAVE_TPM2
-int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t hash_pcr_mask, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin);
+int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin);
#else
-static inline int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t hash_pcr_mask, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin) {
+static inline int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin) {
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"TPM2 key enrollment not supported.");
}
diff --git a/src/cryptenroll/cryptenroll.c b/src/cryptenroll/cryptenroll.c
index fe25619d85..1ac2c6951e 100644
--- a/src/cryptenroll/cryptenroll.c
+++ b/src/cryptenroll/cryptenroll.c
@@ -28,7 +28,6 @@
#include "strv.h"
#include "terminal-util.h"
#include "tpm-pcr.h"
-#include "tpm2-util.h"
static EnrollType arg_enroll_type = _ENROLL_TYPE_INVALID;
static char *arg_unlock_keyfile = NULL;
@@ -37,10 +36,13 @@ static char *arg_unlock_fido2_device = NULL;
static char *arg_pkcs11_token_uri = NULL;
static char *arg_fido2_device = NULL;
static char *arg_tpm2_device = NULL;
-static uint32_t arg_tpm2_pcr_mask = UINT32_MAX;
+static Tpm2PCRValue *arg_tpm2_hash_pcr_values = NULL;
+static size_t arg_tpm2_n_hash_pcr_values = 0;
+static bool arg_tpm2_hash_pcr_values_use_default = true;
static bool arg_tpm2_pin = false;
static char *arg_tpm2_public_key = NULL;
-static uint32_t arg_tpm2_public_key_pcr_mask = UINT32_MAX;
+static uint32_t arg_tpm2_public_key_pcr_mask = 0;
+static bool arg_tpm2_public_key_pcr_mask_use_default = true;
static char *arg_tpm2_signature = NULL;
static char *arg_node = NULL;
static int *arg_wipe_slots = NULL;
@@ -61,6 +63,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_unlock_fido2_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri, freep);
STATIC_DESTRUCTOR_REGISTER(arg_fido2_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_tpm2_hash_pcr_values, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature, freep);
STATIC_DESTRUCTOR_REGISTER(arg_node, freep);
@@ -356,7 +359,8 @@ static int parse_argv(int argc, char *argv[]) {
}
case ARG_TPM2_PCRS:
- r = tpm2_parse_pcr_argument(optarg, &arg_tpm2_pcr_mask);
+ arg_tpm2_hash_pcr_values_use_default = false;
+ r = tpm2_parse_pcr_argument_append(optarg, &arg_tpm2_hash_pcr_values, &arg_tpm2_n_hash_pcr_values);
if (r < 0)
return r;
@@ -377,7 +381,8 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_TPM2_PUBLIC_KEY_PCRS:
- r = tpm2_parse_pcr_argument(optarg, &arg_tpm2_public_key_pcr_mask);
+ arg_tpm2_public_key_pcr_mask_use_default = false;
+ r = tpm2_parse_pcr_argument_to_mask(optarg, &arg_tpm2_public_key_pcr_mask);
if (r < 0)
return r;
@@ -476,10 +481,15 @@ static int parse_argv(int argc, char *argv[]) {
if (r < 0)
return r;
- if (arg_tpm2_pcr_mask == UINT32_MAX)
- arg_tpm2_pcr_mask = TPM2_PCR_MASK_DEFAULT;
- if (arg_tpm2_public_key_pcr_mask == UINT32_MAX)
- arg_tpm2_public_key_pcr_mask = UINT32_C(1) << TPM_PCR_INDEX_KERNEL_IMAGE;
+ if (arg_tpm2_public_key_pcr_mask_use_default && arg_tpm2_public_key)
+ arg_tpm2_public_key_pcr_mask = INDEX_TO_MASK(uint32_t, TPM_PCR_INDEX_KERNEL_IMAGE);
+
+ if (arg_tpm2_hash_pcr_values_use_default && !GREEDY_REALLOC_APPEND(
+ arg_tpm2_hash_pcr_values,
+ arg_tpm2_n_hash_pcr_values,
+ &TPM2_PCR_VALUE_MAKE(TPM2_PCR_INDEX_DEFAULT, /* hash= */ 0, /* value= */ {}),
+ 1))
+ return log_oom();
return 1;
}
@@ -655,7 +665,7 @@ static int run(int argc, char *argv[]) {
break;
case ENROLL_TPM2:
- slot = enroll_tpm2(cd, vk, vks, arg_tpm2_device, arg_tpm2_pcr_mask, arg_tpm2_public_key, arg_tpm2_public_key_pcr_mask, arg_tpm2_signature, arg_tpm2_pin);
+ slot = enroll_tpm2(cd, vk, vks, arg_tpm2_device, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, arg_tpm2_public_key, arg_tpm2_public_key_pcr_mask, arg_tpm2_signature, arg_tpm2_pin);
break;
case _ENROLL_TYPE_INVALID:
diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
index aab3a4b4c0..94d568c17f 100644
--- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
+++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
@@ -278,7 +278,7 @@ _public_ int cryptsetup_token_validate(
}
u = json_variant_unsigned(e);
- if (!TPM2_PCR_VALID(u)) {
+ if (!TPM2_PCR_INDEX_VALID(u)) {
crypt_log_debug(cd, "TPM2 PCR number out of range.");
return 1;
}
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index f62401b9bc..b5aa396cde 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -399,7 +399,7 @@ static int parse_one_option(const char *option) {
} else if ((val = startswith(option, "tpm2-pcrs="))) {
- r = tpm2_parse_pcr_argument(val, &arg_tpm2_pcr_mask);
+ r = tpm2_parse_pcr_argument_to_mask(val, &arg_tpm2_pcr_mask);
if (r < 0)
return r;
@@ -435,7 +435,7 @@ static int parse_one_option(const char *option) {
}
pcr = r ? TPM_PCR_INDEX_VOLUME_KEY : UINT_MAX;
- } else if (!TPM2_PCR_VALID(pcr)) {
+ } else if (!TPM2_PCR_INDEX_VALID(pcr)) {
log_warning("Selected TPM index for measurement %u outside of allowed range 0…%u, ignoring.", pcr, TPM2_PCRS_MAX-1);
return 0;
}
diff --git a/src/partition/repart.c b/src/partition/repart.c
index 4fe2c63a46..1a316012e7 100644
--- a/src/partition/repart.c
+++ b/src/partition/repart.c
@@ -140,9 +140,12 @@ static size_t arg_key_size = 0;
static EVP_PKEY *arg_private_key = NULL;
static X509 *arg_certificate = NULL;
static char *arg_tpm2_device = NULL;
-static uint32_t arg_tpm2_pcr_mask = UINT32_MAX;
+static Tpm2PCRValue *arg_tpm2_hash_pcr_values = NULL;
+static size_t arg_tpm2_n_hash_pcr_values = 0;
+static bool arg_tpm2_hash_pcr_values_use_default = true;
static char *arg_tpm2_public_key = NULL;
-static uint32_t arg_tpm2_public_key_pcr_mask = UINT32_MAX;
+static uint32_t arg_tpm2_public_key_pcr_mask = 0;
+static bool arg_tpm2_public_key_pcr_mask_use_default = true;
static bool arg_split = false;
static GptPartitionType *arg_filter_partitions = NULL;
static size_t arg_n_filter_partitions = 0;
@@ -162,6 +165,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_key, erase_and_freep);
STATIC_DESTRUCTOR_REGISTER(arg_private_key, EVP_PKEY_freep);
STATIC_DESTRUCTOR_REGISTER(arg_certificate, X509_freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_tpm2_hash_pcr_values, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
STATIC_DESTRUCTOR_REGISTER(arg_filter_partitions, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
@@ -3639,10 +3643,9 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
_cleanup_(erase_and_freep) void *secret = NULL;
_cleanup_free_ void *pubkey = NULL;
- _cleanup_free_ void *blob = NULL, *hash = NULL, *srk_buf = NULL;
- size_t secret_size, blob_size, hash_size, pubkey_size = 0, srk_buf_size = 0;
+ _cleanup_free_ void *blob = NULL, *srk_buf = NULL;
+ size_t secret_size, blob_size, pubkey_size = 0, srk_buf_size = 0;
ssize_t base64_encoded_size;
- uint16_t pcr_bank, primary_alg;
int keyslot;
if (arg_tpm2_public_key_pcr_mask != 0) {
@@ -3656,18 +3659,51 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
}
}
- r = tpm2_seal(arg_tpm2_device,
- arg_tpm2_pcr_mask,
- pubkey, pubkey_size,
- arg_tpm2_public_key_pcr_mask,
+ _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
+ r = tpm2_context_new(arg_tpm2_device, &tpm2_context);
+ if (r < 0)
+ return r;
+
+ TPM2B_PUBLIC public;
+ if (pubkey) {
+ r = tpm2_tpm2b_public_from_pem(pubkey, pubkey_size, &public);
+ if (r < 0)
+ return log_error_errno(r, "Could not convert public key to TPM2B_PUBLIC: %m");
+ }
+
+ r = tpm2_pcr_read_missing_values(tpm2_context, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values);
+ if (r < 0)
+ return r;
+
+ uint16_t hash_pcr_bank = 0;
+ uint32_t hash_pcr_mask = 0;
+ if (arg_tpm2_n_hash_pcr_values > 0) {
+ size_t hash_count;
+ r = tpm2_pcr_values_hash_count(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, &hash_count);
+ if (r < 0)
+ return log_error_errno(r, "Could not get hash count: %m");
+
+ if (hash_count > 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Multiple PCR banks selected.");
+
+ hash_pcr_bank = arg_tpm2_hash_pcr_values[0].hash;
+ r = tpm2_pcr_values_to_mask(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, hash_pcr_bank, &hash_pcr_mask);
+ if (r < 0)
+ return log_error_errno(r, "Could not get hash mask: %m");
+ }
+
+ TPM2B_DIGEST policy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
+ r = tpm2_calculate_sealing_policy(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, &public, /* use_pin= */ false, &policy);
+ if (r < 0)
+ return r;
+
+ r = tpm2_seal(tpm2_context,
+ &policy,
/* pin= */ NULL,
&secret, &secret_size,
&blob, &blob_size,
- &hash, &hash_size,
- &pcr_bank,
- &primary_alg,
- &srk_buf,
- &srk_buf_size);
+ /* ret_primary_alg= */ NULL,
+ &srk_buf, &srk_buf_size);
if (r < 0)
return log_error_errno(r, "Failed to seal to TPM2: %m");
@@ -3691,13 +3727,13 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
r = tpm2_make_luks2_json(
keyslot,
- arg_tpm2_pcr_mask,
- pcr_bank,
+ hash_pcr_mask,
+ hash_pcr_bank,
pubkey, pubkey_size,
arg_tpm2_public_key_pcr_mask,
- primary_alg,
+ /* primary_alg= */ 0,
blob, blob_size,
- hash, hash_size,
+ policy.buffer, policy.size,
NULL, 0, /* no salt because tpm2_seal has no pin */
srk_buf, srk_buf_size,
0,
@@ -6447,7 +6483,8 @@ static int parse_argv(int argc, char *argv[]) {
}
case ARG_TPM2_PCRS:
- r = tpm2_parse_pcr_argument(optarg, &arg_tpm2_pcr_mask);
+ arg_tpm2_hash_pcr_values_use_default = false;
+ r = tpm2_parse_pcr_argument_append(optarg, &arg_tpm2_hash_pcr_values, &arg_tpm2_n_hash_pcr_values);
if (r < 0)
return r;
@@ -6461,7 +6498,8 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_TPM2_PUBLIC_KEY_PCRS:
- r = tpm2_parse_pcr_argument(optarg, &arg_tpm2_public_key_pcr_mask);
+ arg_tpm2_public_key_pcr_mask_use_default = false;
+ r = tpm2_parse_pcr_argument_to_mask(optarg, &arg_tpm2_public_key_pcr_mask);
if (r < 0)
return r;
@@ -6597,10 +6635,15 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"A path to a loopback file must be specified when --split is used.");
- if (arg_tpm2_pcr_mask == UINT32_MAX)
- arg_tpm2_pcr_mask = TPM2_PCR_MASK_DEFAULT;
- if (arg_tpm2_public_key_pcr_mask == UINT32_MAX)
- arg_tpm2_public_key_pcr_mask = UINT32_C(1) << TPM_PCR_INDEX_KERNEL_IMAGE;
+ if (arg_tpm2_public_key_pcr_mask_use_default && arg_tpm2_public_key)
+ arg_tpm2_public_key_pcr_mask = INDEX_TO_MASK(uint32_t, TPM_PCR_INDEX_KERNEL_IMAGE);
+
+ if (arg_tpm2_hash_pcr_values_use_default && !GREEDY_REALLOC_APPEND(
+ arg_tpm2_hash_pcr_values,
+ arg_tpm2_n_hash_pcr_values,
+ &TPM2_PCR_VALUE_MAKE(TPM2_PCR_INDEX_DEFAULT, /* hash= */ 0, /* value= */ {}),
+ 1))
+ return log_oom();
if (arg_pretty < 0 && isatty(STDOUT_FILENO))
arg_pretty = true;
diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c
index 01f79331b9..2580c2333c 100644
--- a/src/resolve/resolved-dns-dnssec.c
+++ b/src/resolve/resolved-dns-dnssec.c
@@ -14,7 +14,7 @@
#include "sort-util.h"
#include "string-table.h"
-#if PREFER_OPENSSL
+#if PREFER_OPENSSL && OPENSSL_VERSION_MAJOR >= 3
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(RSA*, RSA_free, NULL);
diff --git a/src/shared/creds-util.c b/src/shared/creds-util.c
index 16df01b9ca..8a5240e5f1 100644
--- a/src/shared/creds-util.c
+++ b/src/shared/creds-util.c
@@ -825,18 +825,49 @@ int encrypt_credential_and_warn(
if (!pubkey)
tpm2_pubkey_pcr_mask = 0;
- r = tpm2_seal(tpm2_device,
- tpm2_hash_pcr_mask,
- pubkey, pubkey_size,
- tpm2_pubkey_pcr_mask,
+ _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
+ r = tpm2_context_new(tpm2_device, &tpm2_context);
+ if (r < 0)
+ return r;
+
+ r = tpm2_get_best_pcr_bank(tpm2_context, tpm2_hash_pcr_mask | tpm2_pubkey_pcr_mask, &tpm2_pcr_bank);
+ if (r < 0)
+ return r;
+
+ TPML_PCR_SELECTION tpm2_hash_pcr_selection;
+ tpm2_tpml_pcr_selection_from_mask(tpm2_hash_pcr_mask, tpm2_pcr_bank, &tpm2_hash_pcr_selection);
+
+ _cleanup_free_ Tpm2PCRValue *tpm2_hash_pcr_values = NULL;
+ size_t tpm2_n_hash_pcr_values;
+ r = tpm2_pcr_read(tpm2_context, &tpm2_hash_pcr_selection, &tpm2_hash_pcr_values, &tpm2_n_hash_pcr_values);
+ if (r < 0)
+ return r;
+
+ TPM2B_PUBLIC public;
+ if (pubkey) {
+ r = tpm2_tpm2b_public_from_pem(pubkey, pubkey_size, &public);
+ if (r < 0)
+ return log_error_errno(r, "Could not convert public key to TPM2B_PUBLIC: %m");
+ }
+
+ TPM2B_DIGEST tpm2_policy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
+ r = tpm2_calculate_sealing_policy(
+ tpm2_hash_pcr_values,
+ tpm2_n_hash_pcr_values,
+ pubkey ? &public : NULL,
+ /* use_pin= */ false,
+ &tpm2_policy);
+ if (r < 0)
+ return r;
+
+ r = tpm2_seal(tpm2_context,
+ &tpm2_policy,
/* pin= */ NULL,
&tpm2_key, &tpm2_key_size,
&tpm2_blob, &tpm2_blob_size,
- &tpm2_policy_hash, &tpm2_policy_hash_size,
- &tpm2_pcr_bank,
&tpm2_primary_alg,
/* ret_srk_buf= */ NULL,
- /* ret_srk_buf_size= */ 0);
+ /* ret_srk_buf_size= */ NULL);
if (r < 0) {
if (sd_id128_equal(with_key, _CRED_AUTO_INITRD))
log_warning("TPM2 present and used, but we didn't manage to talk to it. Credential will be refused if SecureBoot is enabled.");
@@ -846,6 +877,12 @@ int encrypt_credential_and_warn(
log_notice_errno(r, "TPM2 sealing didn't work, continuing without TPM2: %m");
}
+ tpm2_policy_hash_size = tpm2_policy.size;
+ tpm2_policy_hash = malloc(tpm2_policy_hash_size);
+ if (!tpm2_policy_hash)
+ return log_oom();
+ memcpy(tpm2_policy_hash, tpm2_policy.buffer, tpm2_policy_hash_size);
+
assert(tpm2_blob_size <= CREDENTIAL_FIELD_SIZE_MAX);
assert(tpm2_policy_hash_size <= CREDENTIAL_FIELD_SIZE_MAX);
}
diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c
index c7fcbd9ea4..c3f8f91eb3 100644
--- a/src/shared/openssl-util.c
+++ b/src/shared/openssl-util.c
@@ -1,10 +1,29 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "fd-util.h"
#include "openssl-util.h"
#include "alloc-util.h"
#include "hexdecoct.h"
#if HAVE_OPENSSL
+int openssl_pkey_from_pem(const void *pem, size_t pem_size, EVP_PKEY **ret) {
+ assert(pem);
+ assert(ret);
+
+ _cleanup_fclose_ FILE *f = NULL;
+ f = fmemopen((void*) pem, pem_size, "r");
+ if (!f)
+ return log_oom_debug();
+
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
+ if (!pkey)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse PEM.");
+
+ *ret = TAKE_PTR(pkey);
+
+ return 0;
+}
+
int openssl_hash(const EVP_MD *alg,
const void *msg,
size_t msg_len,
@@ -109,6 +128,342 @@ int rsa_pkey_to_suitable_key_size(
return 0;
}
+/* Generate RSA public key from provided "n" and "e" values. Note that if "e" is a number (e.g. uint32_t), it
+ * must be provided here big-endian, e.g. wrap it with htobe32(). */
+int rsa_pkey_from_n_e(const void *n, size_t n_size, const void *e, size_t e_size, EVP_PKEY **ret) {
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL;
+
+ assert(n);
+ assert(e);
+ assert(ret);
+
+ _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
+ if (!ctx)
+ return log_oom_debug();
+
+ _cleanup_(BN_freep) BIGNUM *bn_n = BN_bin2bn(n, n_size, NULL);
+ if (!bn_n)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to create BIGNUM for RSA n.");
+
+ _cleanup_(BN_freep) BIGNUM *bn_e = BN_bin2bn(e, e_size, NULL);
+ if (!bn_e)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to create BIGNUM for RSA e.");
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ if (EVP_PKEY_fromdata_init(ctx) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize EVP_PKEY_CTX.");
+
+ _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
+ if (!bld)
+ return log_oom_debug();
+
+ if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, bn_n))
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set RSA OSSL_PKEY_PARAM_RSA_N.");
+
+ if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, bn_e))
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set RSA OSSL_PKEY_PARAM_RSA_E.");
+
+ _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(bld);
+ if (!params)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to build RSA OSSL_PARAM.");
+
+ if (EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to create RSA EVP_PKEY.");
+#else
+ _cleanup_(RSA_freep) RSA *rsa_key = RSA_new();
+ if (!rsa_key)
+ return log_oom_debug();
+
+ if (!RSA_set0_key(rsa_key, bn_n, bn_e, NULL))
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set RSA n/e.");
+ /* rsa_key owns these now, don't free */
+ TAKE_PTR(bn_n);
+ TAKE_PTR(bn_e);
+
+ pkey = EVP_PKEY_new();
+ if (!pkey)
+ return log_oom_debug();
+
+ if (!EVP_PKEY_assign_RSA(pkey, rsa_key))
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to assign RSA key.");
+ /* pkey owns this now, don't free */
+ TAKE_PTR(rsa_key);
+#endif
+
+ *ret = TAKE_PTR(pkey);
+
+ return 0;
+}
+
+/* Get the "n" and "e" values from the pkey. The values are returned in "bin" format, i.e. BN_bn2bin(). */
+int rsa_pkey_to_n_e(
+ const EVP_PKEY *pkey,
+ void **ret_n,
+ size_t *ret_n_size,
+ void **ret_e,
+ size_t *ret_e_size) {
+
+ assert(pkey);
+ assert(ret_n);
+ assert(ret_n_size);
+ assert(ret_e);
+ assert(ret_e_size);
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ _cleanup_(BN_freep) BIGNUM *bn_n = NULL;
+ if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, &bn_n))
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get RSA n.");
+
+ _cleanup_(BN_freep) BIGNUM *bn_e = NULL;
+ if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_E, &bn_e))
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get RSA e.");
+#else
+ const RSA *rsa = EVP_PKEY_get0_RSA((EVP_PKEY*) pkey);
+ if (!rsa)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to get RSA key from public key.");
+
+ const BIGNUM *bn_n = RSA_get0_n(rsa);
+ if (!bn_n)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get RSA n.");
+
+ const BIGNUM *bn_e = RSA_get0_e(rsa);
+ if (!bn_e)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get RSA e.");
+#endif
+
+ size_t n_size = BN_num_bytes(bn_n), e_size = BN_num_bytes(bn_e);
+ _cleanup_free_ void *n = malloc(n_size), *e = malloc(e_size);
+ if (!n || !e)
+ return log_oom_debug();
+
+ assert(BN_bn2bin(bn_n, n) == (int) n_size);
+ assert(BN_bn2bin(bn_e, e) == (int) e_size);
+
+ *ret_n = TAKE_PTR(n);
+ *ret_n_size = n_size;
+ *ret_e = TAKE_PTR(e);
+ *ret_e_size = e_size;
+
+ return 0;
+}
+
+/* Generate a new RSA key with the specified number of bits. */
+int rsa_pkey_new(size_t bits, EVP_PKEY **ret) {
+ assert(ret);
+
+ _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
+ if (!ctx)
+ return log_oom_debug();
+
+ if (EVP_PKEY_keygen_init(ctx) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize EVP_PKEY_CTX.");
+
+ if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, (int) bits) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set RSA bits to %zu.", bits);
+
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL;
+ if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to generate ECC key.");
+
+ *ret = TAKE_PTR(pkey);
+
+ return 0;
+}
+
+/* Generate ECC public key from provided curve ID and x/y points. */
+int ecc_pkey_from_curve_x_y(
+ int curve_id,
+ const void *x,
+ size_t x_size,
+ const void *y,
+ size_t y_size,
+ EVP_PKEY **ret) {
+
+ assert(x);
+ assert(y);
+ assert(ret);
+
+ _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
+ if (!ctx)
+ return log_oom_debug();
+
+ _cleanup_(BN_freep) BIGNUM *bn_x = BN_bin2bn(x, x_size, NULL), *bn_y = BN_bin2bn(y, y_size, NULL);
+ if (!bn_x || !bn_y)
+ return log_oom_debug();
+
+ _cleanup_(EC_GROUP_freep) EC_GROUP *group = EC_GROUP_new_by_curve_name(curve_id);
+ if (!group)
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "ECC curve id %d not supported.", curve_id);
+
+ _cleanup_(EC_POINT_freep) EC_POINT *point = EC_POINT_new(group);
+ if (!point)
+ return log_oom_debug();
+
+ if (!EC_POINT_set_affine_coordinates(group, point, bn_x, bn_y, NULL))
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set ECC coordinates.");
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ if (EVP_PKEY_fromdata_init(ctx) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to initialize EVP_PKEY_CTX.");
+
+ _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
+ if (!bld)
+ return log_oom_debug();
+
+ if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME, (char*) OSSL_EC_curve_nid2name(curve_id), 0))
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to add ECC OSSL_PKEY_PARAM_GROUP_NAME.");
+
+ _cleanup_(OPENSSL_freep) void *pbuf = NULL;
+ size_t pbuf_len = 0;
+ pbuf_len = EC_POINT_point2buf(group, point, POINT_CONVERSION_UNCOMPRESSED, (unsigned char**) &pbuf, NULL);
+ if (pbuf_len == 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert ECC point to buffer.");
+
+ if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, pbuf, pbuf_len))
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to add ECC OSSL_PKEY_PARAM_PUB_KEY.");
+
+ _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(bld);
+ if (!params)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to build ECC OSSL_PARAM.");
+
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL;
+ if (EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Failed to create ECC EVP_PKEY.");
+#else
+ _cleanup_(EC_KEY_freep) EC_KEY *eckey = EC_KEY_new();
+ if (!eckey)
+ return log_oom_debug();
+
+ if (!EC_KEY_set_group(eckey, group))
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set ECC group.");
+
+ if (!EC_KEY_set_public_key(eckey, point))
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set ECC point.");
+
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = EVP_PKEY_new();
+ if (!pkey)
+ return log_oom_debug();
+
+ if (!EVP_PKEY_assign_EC_KEY(pkey, eckey))
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to assign ECC key.");
+ /* pkey owns this now, don't free */
+ TAKE_PTR(eckey);
+#endif
+
+ *ret = TAKE_PTR(pkey);
+
+ return 0;
+}
+
+int ecc_pkey_to_curve_x_y(
+ const EVP_PKEY *pkey,
+ int *ret_curve_id,
+ void **ret_x,
+ size_t *ret_x_size,
+ void **ret_y,
+ size_t *ret_y_size) {
+
+ _cleanup_(BN_freep) BIGNUM *bn_x = NULL, *bn_y = NULL;
+ int curve_id;
+
+ assert(pkey);
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ size_t name_size;
+ if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0, &name_size))
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get ECC group name size.");
+
+ _cleanup_free_ char *name = malloc(name_size + 1);
+ if (!name)
+ return log_oom_debug();
+
+ if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, name, name_size + 1, NULL))
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get ECC group name.");
+
+ curve_id = OBJ_sn2nid(name);
+ if (curve_id == NID_undef)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get ECC curve id.");
+
+ if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_X, &bn_x))
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get ECC point x.");
+
+ if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, &bn_y))
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get ECC point y.");
+#else
+ const EC_KEY *eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY*) pkey);
+ if (!eckey)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get EC_KEY.");
+
+ const EC_GROUP *group = EC_KEY_get0_group(eckey);
+ if (!group)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get EC_GROUP.");
+
+ curve_id = EC_GROUP_get_curve_name(group);
+ if (curve_id == NID_undef)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get ECC curve id.");
+
+ const EC_POINT *point = EC_KEY_get0_public_key(eckey);
+ if (!point)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get EC_POINT.");
+
+ bn_x = BN_new();
+ bn_y = BN_new();
+ if (!bn_x || !bn_y)
+ return log_oom_debug();
+
+ if (!EC_POINT_get_affine_coordinates(group, point, bn_x, bn_y, NULL))
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get ECC x/y.");
+#endif
+
+ size_t x_size = BN_num_bytes(bn_x), y_size = BN_num_bytes(bn_y);
+ _cleanup_free_ void *x = malloc(x_size), *y = malloc(y_size);
+ if (!x || !y)
+ return log_oom_debug();
+
+ assert(BN_bn2bin(bn_x, x) == (int) x_size);
+ assert(BN_bn2bin(bn_y, y) == (int) y_size);
+
+ if (ret_curve_id)
+ *ret_curve_id = curve_id;
+ if (ret_x)
+ *ret_x = TAKE_PTR(x);
+ if (ret_x_size)
+ *ret_x_size = x_size;
+ if (ret_y)
+ *ret_y = TAKE_PTR(y);
+ if (ret_y_size)
+ *ret_y_size = y_size;
+
+ return 0;
+}
+
+/* Generate a new ECC key for the specified ECC curve id. */
+int ecc_pkey_new(int curve_id, EVP_PKEY **ret) {
+ assert(ret);
+
+ _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
+ if (!ctx)
+ return log_oom_debug();
+
+ if (EVP_PKEY_keygen_init(ctx) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize EVP_PKEY_CTX.");
+
+ if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, curve_id) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set ECC curve %d.", curve_id);
+
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL;
+ if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to generate ECC key.");
+
+ *ret = TAKE_PTR(pkey);
+
+ return 0;
+}
+
int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size) {
_cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* m = NULL;
_cleanup_free_ void *d = NULL, *h = NULL;
diff --git a/src/shared/openssl-util.h b/src/shared/openssl-util.h
index 4fa0a95966..90158f589b 100644
--- a/src/shared/openssl-util.h
+++ b/src/shared/openssl-util.h
@@ -9,6 +9,7 @@
#if HAVE_OPENSSL
# include
# include
+# include
# include
# include
# include
@@ -21,8 +22,10 @@
# endif
# if OPENSSL_VERSION_MAJOR >= 3
# include
+# include
# endif
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO(void*, OPENSSL_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(X509_NAME*, X509_NAME_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_PKEY_CTX*, EVP_PKEY_CTX_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_CIPHER_CTX*, EVP_CIPHER_CTX_free, NULL);
@@ -35,6 +38,13 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(PKCS7*, PKCS7_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(SSL*, SSL_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(BIO*, BIO_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_MD_CTX*, EVP_MD_CTX_free, NULL);
+#if OPENSSL_VERSION_MAJOR >= 3
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(OSSL_PARAM*, OSSL_PARAM_free, NULL);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(OSSL_PARAM_BLD*, OSSL_PARAM_BLD_free, NULL);
+#else
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EC_KEY*, EC_KEY_free, NULL);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(RSA*, RSA_free, NULL);
+#endif
static inline void sk_X509_free_allp(STACK_OF(X509) **sk) {
if (!sk || !*sk)
@@ -43,12 +53,26 @@ static inline void sk_X509_free_allp(STACK_OF(X509) **sk) {
sk_X509_pop_free(*sk, X509_free);
}
+int openssl_pkey_from_pem(const void *pem, size_t pem_size, EVP_PKEY **ret);
+
int openssl_hash(const EVP_MD *alg, const void *msg, size_t msg_len, uint8_t *ret_hash, size_t *ret_hash_len);
int rsa_encrypt_bytes(EVP_PKEY *pkey, const void *decrypted_key, size_t decrypted_key_size, void **ret_encrypt_key, size_t *ret_encrypt_key_size);
int rsa_pkey_to_suitable_key_size(EVP_PKEY *pkey, size_t *ret_suitable_key_size);
+int rsa_pkey_new(size_t bits, EVP_PKEY **ret);
+
+int rsa_pkey_from_n_e(const void *n, size_t n_size, const void *e, size_t e_size, EVP_PKEY **ret);
+
+int rsa_pkey_to_n_e(const EVP_PKEY *pkey, void **ret_n, size_t *ret_n_size, void **ret_e, size_t *ret_e_size);
+
+int ecc_pkey_from_curve_x_y(int curve_id, const void *x, size_t x_size, const void *y, size_t y_size, EVP_PKEY **ret);
+
+int ecc_pkey_to_curve_x_y(const EVP_PKEY *pkey, int *ret_curve_id, void **ret_x, size_t *ret_x_size, void **ret_y, size_t *ret_y_size);
+
+int ecc_pkey_new(int curve_id, EVP_PKEY **ret);
+
int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size);
#else
diff --git a/src/shared/tests.h b/src/shared/tests.h
index 6b87026661..27467fabb6 100644
--- a/src/shared/tests.h
+++ b/src/shared/tests.h
@@ -39,6 +39,12 @@ bool have_namespaces(void);
#define CAN_MEMLOCK_SIZE (512 * 1024U)
bool can_memlock(void);
+/* Define void* buffer and size_t length variables from a hex string. */
+#define DEFINE_HEX_PTR(name, hex) \
+ _cleanup_free_ void *name = NULL; \
+ size_t name##_len = 0; \
+ assert_se(unhexmem(hex, strlen(hex), &name, &name##_len) >= 0);
+
#define TEST_REQ_RUNNING_SYSTEMD(x) \
if (sd_booted() > 0) { \
x; \
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index bc4f7f3c90..21ee4dc8e0 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -19,10 +19,10 @@
#include "logarithm.h"
#include "memory-util.h"
#include "nulstr-util.h"
-#include "openssl-util.h"
#include "parse-util.h"
#include "random-util.h"
#include "sha256.h"
+#include "sort-util.h"
#include "stat-util.h"
#include "string-table.h"
#include "time-util.h"
@@ -1204,15 +1204,14 @@ static int tpm2_get_or_create_srk(
/* Utility functions for TPMS_PCR_SELECTION. */
/* Convert a TPMS_PCR_SELECTION object to a mask. */
-void tpm2_tpms_pcr_selection_to_mask(const TPMS_PCR_SELECTION *s, uint32_t *ret) {
+uint32_t tpm2_tpms_pcr_selection_to_mask(const TPMS_PCR_SELECTION *s) {
assert(s);
assert(s->sizeofSelect <= sizeof(s->pcrSelect));
- assert(ret);
uint32_t mask = 0;
for (unsigned i = 0; i < s->sizeofSelect; i++)
SET_FLAG(mask, (uint32_t)s->pcrSelect[i] << (i * 8), true);
- *ret = mask;
+ return mask;
}
/* Convert a mask and hash alg to a TPMS_PCR_SELECTION object. */
@@ -1233,16 +1232,36 @@ void tpm2_tpms_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash_alg, TP
};
}
+/* Test if all bits in the mask are set in the TPMS_PCR_SELECTION. */
+bool tpm2_tpms_pcr_selection_has_mask(const TPMS_PCR_SELECTION *s, uint32_t mask) {
+ assert(s);
+
+ return FLAGS_SET(tpm2_tpms_pcr_selection_to_mask(s), mask);
+}
+
+static void tpm2_tpms_pcr_selection_update_mask(TPMS_PCR_SELECTION *s, uint32_t mask, bool b) {
+ assert(s);
+
+ tpm2_tpms_pcr_selection_from_mask(UPDATE_FLAG(tpm2_tpms_pcr_selection_to_mask(s), mask, b), s->hash, s);
+}
+
+/* Add all PCR selections in the mask. */
+void tpm2_tpms_pcr_selection_add_mask(TPMS_PCR_SELECTION *s, uint32_t mask) {
+ tpm2_tpms_pcr_selection_update_mask(s, mask, 1);
+}
+
+/* Remove all PCR selections in the mask. */
+void tpm2_tpms_pcr_selection_sub_mask(TPMS_PCR_SELECTION *s, uint32_t mask) {
+ tpm2_tpms_pcr_selection_update_mask(s, mask, 0);
+}
+
/* Add all PCR selections in 'b' to 'a'. Both must have the same hash alg. */
void tpm2_tpms_pcr_selection_add(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b) {
assert(a);
assert(b);
assert(a->hash == b->hash);
- uint32_t maska, maskb;
- tpm2_tpms_pcr_selection_to_mask(a, &maska);
- tpm2_tpms_pcr_selection_to_mask(b, &maskb);
- tpm2_tpms_pcr_selection_from_mask(maska | maskb, a->hash, a);
+ tpm2_tpms_pcr_selection_add_mask(a, tpm2_tpms_pcr_selection_to_mask(b));
}
/* Remove all PCR selections in 'b' from 'a'. Both must have the same hash alg. */
@@ -1251,10 +1270,7 @@ void tpm2_tpms_pcr_selection_sub(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION
assert(b);
assert(a->hash == b->hash);
- uint32_t maska, maskb;
- tpm2_tpms_pcr_selection_to_mask(a, &maska);
- tpm2_tpms_pcr_selection_to_mask(b, &maskb);
- tpm2_tpms_pcr_selection_from_mask(maska & ~maskb, a->hash, a);
+ tpm2_tpms_pcr_selection_sub_mask(a, tpm2_tpms_pcr_selection_to_mask(b));
}
/* Move all PCR selections in 'b' to 'a'. Both must have the same hash alg. */
@@ -1269,11 +1285,7 @@ void tpm2_tpms_pcr_selection_move(TPMS_PCR_SELECTION *a, TPMS_PCR_SELECTION *b)
#define FOREACH_PCR_IN_TPMS_PCR_SELECTION(pcr, tpms) \
_FOREACH_PCR_IN_TPMS_PCR_SELECTION(pcr, tpms, UNIQ)
#define _FOREACH_PCR_IN_TPMS_PCR_SELECTION(pcr, tpms, uniq) \
- FOREACH_PCR_IN_MASK(pcr, \
- ({ uint32_t UNIQ_T(_mask, uniq); \
- tpm2_tpms_pcr_selection_to_mask(tpms, &UNIQ_T(_mask, uniq)); \
- UNIQ_T(_mask, uniq); \
- }))
+ FOREACH_PCR_IN_MASK(pcr, tpm2_tpms_pcr_selection_to_mask(tpms))
#define FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, tpml) \
UNIQ_FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, tpml, UNIQ)
@@ -1295,21 +1307,17 @@ char *tpm2_tpms_pcr_selection_to_string(const TPMS_PCR_SELECTION *s) {
const char *algstr = strna(tpm2_hash_alg_to_string(s->hash));
- uint32_t mask;
- tpm2_tpms_pcr_selection_to_mask(s, &mask);
- _cleanup_free_ char *maskstr = tpm2_pcr_mask_to_string(mask);
- if (!maskstr)
+ _cleanup_free_ char *mask = tpm2_pcr_mask_to_string(tpm2_tpms_pcr_selection_to_mask(s));
+ if (!mask)
return NULL;
- return strjoin(algstr, "(", maskstr, ")");
+ return strjoin(algstr, "(", mask, ")");
}
size_t tpm2_tpms_pcr_selection_weight(const TPMS_PCR_SELECTION *s) {
assert(s);
- uint32_t mask;
- tpm2_tpms_pcr_selection_to_mask(s, &mask);
- return popcount(mask);
+ return popcount(tpm2_tpms_pcr_selection_to_mask(s));
}
/* Utility functions for TPML_PCR_SELECTION. */
@@ -1360,10 +1368,9 @@ static TPMS_PCR_SELECTION *tpm2_tpml_pcr_selection_get_tpms_pcr_selection(
return selection;
}
-/* Convert a TPML_PCR_SELECTION object to a mask. Returns -ENOENT if 'hash_alg' is not in the object. */
-int tpm2_tpml_pcr_selection_to_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash_alg, uint32_t *ret) {
+/* Convert a TPML_PCR_SELECTION object to a mask. Returns empty mask (i.e. 0) if 'hash_alg' is not in the object. */
+uint32_t tpm2_tpml_pcr_selection_to_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash_alg) {
assert(l);
- assert(ret);
/* Make a copy, as tpm2_tpml_pcr_selection_get_tpms_pcr_selection() will modify the object if there
* are multiple entries with the requested hash alg. */
@@ -1372,10 +1379,9 @@ int tpm2_tpml_pcr_selection_to_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH h
TPMS_PCR_SELECTION *s;
s = tpm2_tpml_pcr_selection_get_tpms_pcr_selection(&lcopy, hash_alg);
if (!s)
- return SYNTHETIC_ERRNO(ENOENT);
+ return 0;
- tpm2_tpms_pcr_selection_to_mask(s, ret);
- return 0;
+ return tpm2_tpms_pcr_selection_to_mask(s);
}
/* Convert a mask and hash alg to a TPML_PCR_SELECTION object. */
@@ -1439,6 +1445,33 @@ void tpm2_tpml_pcr_selection_sub_tpms_pcr_selection(TPML_PCR_SELECTION *l, const
tpm2_tpms_pcr_selection_sub(selection, s);
}
+/* Test if all bits in the mask for the hash are set in the TPML_PCR_SELECTION. */
+bool tpm2_tpml_pcr_selection_has_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t mask) {
+ assert(l);
+
+ return FLAGS_SET(tpm2_tpml_pcr_selection_to_mask(l, hash), mask);
+}
+
+/* Add the PCR selections in the mask, with the provided hash. */
+void tpm2_tpml_pcr_selection_add_mask(TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t mask) {
+ TPMS_PCR_SELECTION tpms;
+
+ assert(l);
+
+ tpm2_tpms_pcr_selection_from_mask(mask, hash, &tpms);
+ tpm2_tpml_pcr_selection_add_tpms_pcr_selection(l, &tpms);
+}
+
+/* Remove the PCR selections in the mask, with the provided hash. */
+void tpm2_tpml_pcr_selection_sub_mask(TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t mask) {
+ TPMS_PCR_SELECTION tpms;
+
+ assert(l);
+
+ tpm2_tpms_pcr_selection_from_mask(mask, hash, &tpms);
+ tpm2_tpml_pcr_selection_sub_tpms_pcr_selection(l, &tpms);
+}
+
/* Add all PCR selections in 'b' to 'a'. */
void tpm2_tpml_pcr_selection_add(TPML_PCR_SELECTION *a, const TPML_PCR_SELECTION *b) {
assert(a);
@@ -1487,7 +1520,321 @@ size_t tpm2_tpml_pcr_selection_weight(const TPML_PCR_SELECTION *l) {
return weight;
}
-static void tpm2_log_debug_tpml_pcr_selection(const TPML_PCR_SELECTION *l, const char *msg) {
+bool TPM2_PCR_VALUE_VALID(const Tpm2PCRValue *pcr_value) {
+ int r;
+
+ assert(pcr_value);
+
+ if (!TPM2_PCR_INDEX_VALID(pcr_value->index)) {
+ log_debug("PCR index %u invalid.", pcr_value->index);
+ return false;
+ }
+
+ /* If it contains a value, the value size must match the hash size. */
+ if (pcr_value->value.size > 0) {
+ r = tpm2_hash_alg_to_size(pcr_value->hash);
+ if (r < 0)
+ return false;
+
+ if ((int) pcr_value->value.size != r) {
+ log_debug("PCR hash 0x%" PRIx16 " expected size %d does not match actual size %" PRIu16 ".",
+ pcr_value->hash, r, pcr_value->value.size);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* Verify all entries are valid, and consistent with each other. The requirements for consistency are:
+ *
+ * 1) all entries must be sorted in ascending order (e.g. using tpm2_sort_pcr_values())
+ * 2) all entries must be unique, i.e. there cannot be 2 entries with the same hash and index
+ */
+bool TPM2_PCR_VALUES_VALID(const Tpm2PCRValue *pcr_values, size_t n_pcr_values) {
+ assert(pcr_values || n_pcr_values == 0);
+
+ for (size_t i = 0; i < n_pcr_values; i++) {
+ const Tpm2PCRValue *v = &pcr_values[i];
+
+ if (!TPM2_PCR_VALUE_VALID(v))
+ return false;
+
+ if (i == 0)
+ continue;
+
+ const Tpm2PCRValue *l = &pcr_values[i - 1];
+
+ /* Hashes must be sorted in ascending order */
+ if (v->hash < l->hash) {
+ log_debug("PCR values not in ascending order, hash %" PRIu16 " is after %" PRIu16 ".",
+ v->hash, l->hash);
+ return false;
+ }
+
+ if (v->hash == l->hash) {
+ /* Indexes (for the same hash) must be sorted in ascending order */
+ if (v->index < l->index) {
+ log_debug("PCR values not in ascending order, hash %" PRIu16 " index %u is after %u.",
+ v->hash, v->index, l->index);
+ return false;
+ }
+
+ /* Indexes (for the same hash) must not be duplicates */
+ if (v->index == l->index) {
+ log_debug("PCR values contain duplicates for hash %" PRIu16 " index %u.",
+ v->hash, v->index);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static int cmp_pcr_values(const Tpm2PCRValue *a, const Tpm2PCRValue *b) {
+ assert(a);
+ assert(b);
+
+ return CMP(a->hash, b->hash) ?: CMP(a->index, b->index);
+}
+
+/* Sort the array of Tpm2PCRValue entries in-place. This sorts first in ascending order of hash algorithm
+ * (sorting simply by the TPM2 hash algorithm number), and then sorting by pcr index. */
+void tpm2_sort_pcr_values(Tpm2PCRValue *pcr_values, size_t n_pcr_values) {
+ typesafe_qsort(pcr_values, n_pcr_values, cmp_pcr_values);
+}
+
+int tpm2_pcr_values_from_mask(uint32_t mask, TPMI_ALG_HASH hash, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values) {
+ _cleanup_free_ Tpm2PCRValue *pcr_values = NULL;
+ size_t n_pcr_values = 0;
+
+ assert(ret_pcr_values);
+ assert(ret_n_pcr_values);
+
+ FOREACH_PCR_IN_MASK(index, mask)
+ if (!GREEDY_REALLOC_APPEND(
+ pcr_values,
+ n_pcr_values,
+ &TPM2_PCR_VALUE_MAKE(index, hash, {}),
+ 1))
+ return log_oom_debug();
+
+ *ret_pcr_values = TAKE_PTR(pcr_values);
+ *ret_n_pcr_values = n_pcr_values;
+
+ return 0;
+}
+
+int tpm2_pcr_values_to_mask(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPMI_ALG_HASH hash, uint32_t *ret_mask) {
+ uint32_t mask = 0;
+
+ assert(pcr_values || n_pcr_values == 0);
+ assert(ret_mask);
+
+ if (!TPM2_PCR_VALUES_VALID(pcr_values, n_pcr_values))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PCR values.");
+
+ for (size_t i = 0; i < n_pcr_values; i++)
+ if (pcr_values[i].hash == hash)
+ SET_BIT(mask, pcr_values[i].index);
+
+ *ret_mask = mask;
+
+ return 0;
+}
+
+int tpm2_tpml_pcr_selection_from_pcr_values(
+ const Tpm2PCRValue *pcr_values,
+ size_t n_pcr_values,
+ TPML_PCR_SELECTION *ret_selection,
+ TPM2B_DIGEST **ret_values,
+ size_t *ret_n_values) {
+
+ TPML_PCR_SELECTION selection = {};
+ _cleanup_free_ TPM2B_DIGEST *values = NULL;
+ size_t n_values = 0;
+
+ assert(pcr_values || n_pcr_values == 0);
+
+ if (!TPM2_PCR_VALUES_VALID(pcr_values, n_pcr_values))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "PCR values are not valid.");
+
+ for (size_t i = 0; i < n_pcr_values; i++) {
+ unsigned index = pcr_values[i].index;
+ TPMI_ALG_HASH hash = pcr_values[i].hash;
+ const TPM2B_DIGEST *digest = &pcr_values[i].value;
+
+ tpm2_tpml_pcr_selection_add_mask(&selection, hash, INDEX_TO_MASK(uint32_t, index));
+
+ if (!GREEDY_REALLOC_APPEND(values, n_values, digest, 1))
+ return log_oom_debug();
+ }
+
+ if (ret_selection)
+ *ret_selection = selection;
+ if (ret_values)
+ *ret_values = TAKE_PTR(values);
+ if (ret_n_values)
+ *ret_n_values = n_values;
+
+ return 0;
+}
+
+/* Count the number of different hash algorithms for all the entries. */
+int tpm2_pcr_values_hash_count(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, size_t *ret_count) {
+ TPML_PCR_SELECTION selection;
+ int r;
+
+ assert(pcr_values);
+ assert(ret_count);
+
+ r = tpm2_tpml_pcr_selection_from_pcr_values(
+ pcr_values,
+ n_pcr_values,
+ &selection,
+ /* ret_values= */ NULL,
+ /* ret_n_values= */ NULL);
+ if (r < 0)
+ return r;
+
+ *ret_count = selection.count;
+
+ return 0;
+}
+
+/* Parse a string argument into a Tpm2PCRValue object.
+ *
+ * The format is [:hash[=value]] where index is the index number (or name) of the PCR, e.g. 0 (or
+ * platform-code), hash is the name of the hash algorithm (e.g. sha256) and value is the hex hash digest
+ * value, optionally with a leading 0x. This does not check for validity of the fields. */
+int tpm2_pcr_value_from_string(const char *arg, Tpm2PCRValue *ret_pcr_value) {
+ Tpm2PCRValue pcr_value = {};
+ const char *p = arg;
+ int r;
+
+ assert(arg);
+ assert(ret_pcr_value);
+
+ _cleanup_free_ char *index = NULL;
+ r = extract_first_word(&p, &index, ":", /* flags= */ 0);
+ if (r < 1)
+ return log_error_errno(r, "Could not parse pcr value '%s': %m", p);
+
+ r = pcr_index_from_string(index);
+ if (r < 0)
+ return log_error_errno(r, "Invalid pcr index '%s': %m", index);
+ pcr_value.index = (unsigned) r;
+
+ if (!isempty(p)) {
+ _cleanup_free_ char *hash = NULL;
+ r = extract_first_word(&p, &hash, "=", /* flags= */ 0);
+ if (r < 1)
+ return log_error_errno(r, "Could not parse pcr hash algorithm '%s': %m", p);
+
+ r = tpm2_hash_alg_from_string(hash);
+ if (r < 0)
+ return log_error_errno(r, "Invalid pcr hash algorithm '%s': %m", hash);
+ pcr_value.hash = (TPMI_ALG_HASH) r;
+ }
+
+ if (!isempty(p)) {
+ /* Remove leading 0x if present */
+ p = startswith_no_case(p, "0x") ?: p;
+
+ _cleanup_free_ void *buf = NULL;
+ size_t buf_size = 0;
+ r = unhexmem(p, strlen(p), &buf, &buf_size);
+ if (r < 0)
+ return log_error_errno(r, "Invalid pcr hash value '%s': %m", p);
+
+ r = TPM2B_DIGEST_CHECK_SIZE(buf_size);
+ if (r < 0)
+ return log_error_errno(r, "PCR hash value size %zu too large.", buf_size);
+
+ pcr_value.value = TPM2B_DIGEST_MAKE(buf, buf_size);
+ }
+
+ *ret_pcr_value = pcr_value;
+
+ return 0;
+}
+
+/* Return a string for the PCR value. The format is described in tpm2_pcr_value_from_string(). Note that if
+ * the hash algorithm is not recognized, neither hash name nor hash digest value is included in the
+ * string. This does not check for validity. */
+char *tpm2_pcr_value_to_string(const Tpm2PCRValue *pcr_value) {
+ _cleanup_free_ char *index = NULL, *value = NULL;
+ int r;
+
+ r = asprintf(&index, "%u", pcr_value->index);
+ if (r < 0)
+ return NULL;
+
+ const char *hash = tpm2_hash_alg_to_string(pcr_value->hash);
+
+ if (hash && pcr_value->value.size > 0) {
+ value = hexmem(pcr_value->value.buffer, pcr_value->value.size);
+ if (!value)
+ return NULL;
+ }
+
+ return strjoin(index, hash ? ":" : "", hash ?: "", value ? "=" : "", value ?: "");
+}
+
+/* Parse a string argument into an array of Tpm2PCRValue objects.
+ *
+ * The format is zero or more entries separated by ',' or '+'. The format of each entry is described in
+ * tpm2_pcr_value_from_string(). This does not check for validity of the entries. */
+int tpm2_pcr_values_from_string(const char *arg, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values) {
+ const char *p = arg;
+ int r;
+
+ assert(arg);
+ assert(ret_pcr_values);
+ assert(ret_n_pcr_values);
+
+ _cleanup_free_ Tpm2PCRValue *pcr_values = NULL;
+ size_t n_pcr_values = 0;
+
+ for (;;) {
+ _cleanup_free_ char *pcr_arg = NULL;
+ r = extract_first_word(&p, &pcr_arg, ",+", /* flags= */ 0);
+ if (r < 0)
+ return log_error_errno(r, "Could not parse pcr values '%s': %m", p);
+ if (r == 0)
+ break;
+
+ Tpm2PCRValue pcr_value;
+ r = tpm2_pcr_value_from_string(pcr_arg, &pcr_value);
+ if (r < 0)
+ return r;
+
+ if (!GREEDY_REALLOC_APPEND(pcr_values, n_pcr_values, &pcr_value, 1))
+ return log_oom();
+ }
+
+ *ret_pcr_values = TAKE_PTR(pcr_values);
+ *ret_n_pcr_values = n_pcr_values;
+
+ return 0;
+}
+
+/* Return a string representing the array of PCR values. The format is as described in
+ * tpm2_pcr_values_from_string(). This does not check for validity. */
+char *tpm2_pcr_values_to_string(const Tpm2PCRValue *pcr_values, size_t n_pcr_values) {
+ _cleanup_free_ char *s = NULL;
+
+ for (size_t i = 0; i < n_pcr_values; i++) {
+ _cleanup_free_ char *pcrstr = tpm2_pcr_value_to_string(&pcr_values[i]);
+ if (!pcrstr || !strextend_with_separator(&s, "+", pcrstr))
+ return NULL;
+ }
+
+ return s ? TAKE_PTR(s) : strdup("");
+}
+
+void tpm2_log_debug_tpml_pcr_selection(const TPML_PCR_SELECTION *l, const char *msg) {
if (!DEBUG_LOGGING || !l)
return;
@@ -1495,7 +1842,15 @@ static void tpm2_log_debug_tpml_pcr_selection(const TPML_PCR_SELECTION *l, const
log_debug("%s: %s", msg ?: "PCR selection", strna(s));
}
-static void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *msg) {
+void tpm2_log_debug_pcr_value(const Tpm2PCRValue *pcr_value, const char *msg) {
+ if (!DEBUG_LOGGING || !pcr_value)
+ return;
+
+ _cleanup_free_ char *s = tpm2_pcr_value_to_string(pcr_value);
+ log_debug("%s: %s", msg ?: "PCR value", strna(s));
+}
+
+void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *msg) {
if (!DEBUG_LOGGING || !buffer || size == 0)
return;
@@ -1503,12 +1858,12 @@ static void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *m
log_debug("%s: %s", msg ?: "Buffer", strna(h));
}
-static void tpm2_log_debug_digest(const TPM2B_DIGEST *digest, const char *msg) {
+void tpm2_log_debug_digest(const TPM2B_DIGEST *digest, const char *msg) {
if (digest)
tpm2_log_debug_buffer(digest->buffer, digest->size, msg ?: "Digest");
}
-static void tpm2_log_debug_name(const TPM2B_NAME *name, const char *msg) {
+void tpm2_log_debug_name(const TPM2B_NAME *name, const char *msg) {
if (name)
tpm2_log_debug_buffer(name->name, name->size, msg ?: "Name");
}
@@ -1890,22 +2245,27 @@ int tpm2_create_loaded(
return 0;
}
-static int tpm2_pcr_read(
+/* Read hash values from the specified PCR selection. Provides a Tpm2PCRValue array that contains all
+ * requested PCR values, in the order provided by the TPM. Normally, the provided pcr values will match
+ * exactly what is in the provided selection, but the TPM may ignore some selected PCRs (for example, if an
+ * unimplemented PCR index is requested), in which case those PCRs will be absent from the provided pcr
+ * values. */
+int tpm2_pcr_read(
Tpm2Context *c,
const TPML_PCR_SELECTION *pcr_selection,
- TPML_PCR_SELECTION *ret_pcr_selection,
- TPM2B_DIGEST **ret_pcr_values,
+ Tpm2PCRValue **ret_pcr_values,
size_t *ret_n_pcr_values) {
- _cleanup_free_ TPM2B_DIGEST *pcr_values = NULL;
- TPML_PCR_SELECTION remaining, total_read = {};
+ _cleanup_free_ Tpm2PCRValue *pcr_values = NULL;
size_t n_pcr_values = 0;
TSS2_RC rc;
assert(c);
assert(pcr_selection);
+ assert(ret_pcr_values);
+ assert(ret_n_pcr_values);
- remaining = *pcr_selection;
+ TPML_PCR_SELECTION remaining = *pcr_selection;
while (!tpm2_tpml_pcr_selection_is_empty(&remaining)) {
_cleanup_(Esys_Freep) TPML_PCR_SELECTION *current_read = NULL;
_cleanup_(Esys_Freep) TPML_DIGEST *current_values = NULL;
@@ -1926,44 +2286,103 @@ static int tpm2_pcr_read(
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to read TPM2 PCRs: %s", sym_Tss2_RC_Decode(rc));
+ tpm2_log_debug_tpml_pcr_selection(current_read, "Read PCR selection");
+
if (tpm2_tpml_pcr_selection_is_empty(current_read)) {
log_warning("TPM2 refused to read possibly unimplemented PCRs, ignoring.");
break;
}
+ unsigned i = 0;
+ FOREACH_PCR_IN_TPML_PCR_SELECTION(index, tpms, current_read) {
+ assert(i < current_values->count);
+ Tpm2PCRValue pcr_value = {
+ .index = index,
+ .hash = tpms->hash,
+ .value = current_values->digests[i++],
+ };
+
+ tpm2_log_debug_pcr_value(&pcr_value, /* msg= */ NULL);
+
+ if (!GREEDY_REALLOC_APPEND(pcr_values, n_pcr_values, &pcr_value, 1))
+ return log_oom();
+ }
+ assert(i == current_values->count);
+
tpm2_tpml_pcr_selection_sub(&remaining, current_read);
- tpm2_tpml_pcr_selection_add(&total_read, current_read);
+ }
- if (!GREEDY_REALLOC(pcr_values, n_pcr_values + current_values->count))
- return log_oom();
+ tpm2_sort_pcr_values(pcr_values, n_pcr_values);
- memcpy_safe(&pcr_values[n_pcr_values], current_values->digests,
- current_values->count * sizeof(TPM2B_DIGEST));
- n_pcr_values += current_values->count;
+ if (!TPM2_PCR_VALUES_VALID(pcr_values, n_pcr_values))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PCR values read from TPM are not valid.");
- if (DEBUG_LOGGING) {
- unsigned i = 0;
- FOREACH_PCR_IN_TPML_PCR_SELECTION(pcr, s, current_read) {
- assert(i < current_values->count);
+ *ret_pcr_values = TAKE_PTR(pcr_values);
+ *ret_n_pcr_values = n_pcr_values;
- TPM2B_DIGEST *d = ¤t_values->digests[i];
- i++;
+ return 0;
+}
- TPML_PCR_SELECTION l;
- tpm2_tpml_pcr_selection_from_mask(INDEX_TO_MASK(uint32_t, pcr), s->hash, &l);
+/* Read the PCR value for each TPM2PCRValue entry in the array that does not have a value set. If all entries
+ * have an unset hash (i.e. hash == 0), this first detects the "best" PCR bank to use; otherwise, all entries
+ * must have a valid hash set. All entries must have a valid index. If this cannot read a PCR value for all
+ * appropriate entries, this returns an error. This does not check the array for validity. */
+int tpm2_pcr_read_missing_values(Tpm2Context *c, Tpm2PCRValue *pcr_values, size_t n_pcr_values) {
+ TPMI_ALG_HASH pcr_bank = 0;
+ int r;
- _cleanup_free_ char *desc = tpm2_tpml_pcr_selection_to_string(&l);
- tpm2_log_debug_digest(d, strna(desc));
- }
+ assert(c);
+ assert(pcr_values || n_pcr_values == 0);
+
+ if (n_pcr_values > 0) {
+ size_t hash_count;
+ r = tpm2_pcr_values_hash_count(pcr_values, n_pcr_values, &hash_count);
+ if (r < 0)
+ return log_error_errno(r, "Could not get hash count from pcr values: %m");
+
+ if (hash_count == 1 && pcr_values[0].hash == 0) {
+ uint32_t mask;
+ r = tpm2_pcr_values_to_mask(pcr_values, n_pcr_values, 0, &mask);
+ if (r < 0)
+ return r;
+
+ r = tpm2_get_best_pcr_bank(c, mask, &pcr_bank);
+ if (r < 0)
+ return r;
}
}
- if (ret_pcr_selection)
- *ret_pcr_selection = total_read;
- if (ret_pcr_values)
- *ret_pcr_values = TAKE_PTR(pcr_values);
- if (ret_n_pcr_values)
- *ret_n_pcr_values = n_pcr_values;
+ for (size_t i = 0; i < n_pcr_values; i++) {
+ Tpm2PCRValue *v = &pcr_values[i];
+
+ if (v->hash == 0)
+ v->hash = pcr_bank;
+
+ if (v->value.size > 0)
+ continue;
+
+ TPML_PCR_SELECTION selection;
+ r = tpm2_tpml_pcr_selection_from_pcr_values(v, 1, &selection, NULL, NULL);
+ if (r < 0)
+ return r;
+
+ _cleanup_free_ Tpm2PCRValue *read_values = NULL;
+ size_t n_read_values;
+ r = tpm2_pcr_read(c, &selection, &read_values, &n_read_values);
+ if (r < 0)
+ return r;
+
+ if (n_read_values == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Could not read PCR hash 0x%" PRIu16 " index %u",
+ v->hash, v->index);
+
+ assert(n_read_values == 1);
+ assert(read_values[0].hash == v->hash);
+ assert(read_values[0].index == v->index);
+
+ v->value = read_values[0].value;
+ }
return 0;
}
@@ -1973,9 +2392,7 @@ static int tpm2_pcr_mask_good(
TPMI_ALG_HASH bank,
uint32_t mask) {
- _cleanup_free_ TPM2B_DIGEST *pcr_values = NULL;
TPML_PCR_SELECTION selection;
- size_t n_pcr_values = 0;
int r;
assert(c);
@@ -1986,22 +2403,18 @@ static int tpm2_pcr_mask_good(
tpm2_tpml_pcr_selection_from_mask(mask, bank, &selection);
- r = tpm2_pcr_read(c, &selection, &selection, &pcr_values, &n_pcr_values);
+ _cleanup_free_ Tpm2PCRValue *pcr_values = NULL;
+ size_t n_pcr_values;
+ r = tpm2_pcr_read(c, &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 < n_pcr_values);
-
- if (!memeqbyte(0x00, pcr_values[i].buffer, pcr_values[i].size) &&
- !memeqbyte(0xFF, pcr_values[i].buffer, pcr_values[i].size))
+ for (unsigned i = 0; i < n_pcr_values; i++)
+ if (!memeqbyte(0x00, pcr_values[i].value.buffer, pcr_values[i].value.size) &&
+ !memeqbyte(0xFF, pcr_values[i].value.buffer, pcr_values[i].value.size))
return true;
- i++;
- }
-
return false;
}
@@ -2035,7 +2448,7 @@ static int tpm2_bank_has24(const TPMS_PCR_SELECTION *selection) {
return valid;
}
-static int tpm2_get_best_pcr_bank(
+int tpm2_get_best_pcr_bank(
Tpm2Context *c,
uint32_t pcr_mask,
TPMI_ALG_HASH *ret) {
@@ -2494,124 +2907,6 @@ static int tpm2_make_policy_session(
return 0;
}
-static int openssl_pubkey_to_tpm2_pubkey(
- const void *pubkey,
- size_t pubkey_size,
- TPM2B_PUBLIC *output,
- void **ret_fp,
- size_t *ret_fp_size) {
-
-#if HAVE_OPENSSL
-#if OPENSSL_VERSION_MAJOR >= 3
- _cleanup_(BN_freep) BIGNUM *n = NULL, *e = NULL;
-#else
- const BIGNUM *n = NULL, *e = NULL;
- const RSA *rsa = NULL;
-#endif
- int r, n_bytes, e_bytes;
-
- assert(pubkey);
- assert(pubkey_size > 0);
- assert(output);
-
- /* Converts an OpenSSL public key to a structure that the TPM chip can process. */
-
- _cleanup_fclose_ FILE *f = NULL;
- f = fmemopen((void*) pubkey, pubkey_size, "r");
- if (!f)
- return log_oom();
-
- _cleanup_(EVP_PKEY_freep) EVP_PKEY *input = NULL;
- input = PEM_read_PUBKEY(f, NULL, NULL, NULL);
- if (!input)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse PEM public key.");
-
- if (EVP_PKEY_base_id(input) != EVP_PKEY_RSA)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Provided public key is not an RSA key.");
-
-#if OPENSSL_VERSION_MAJOR >= 3
- if (!EVP_PKEY_get_bn_param(input, OSSL_PKEY_PARAM_RSA_N, &n))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to get RSA modulus from public key.");
-#else
- rsa = EVP_PKEY_get0_RSA(input);
- if (!rsa)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to extract RSA key from public key.");
-
- n = RSA_get0_n(rsa);
- if (!n)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to get RSA modulus from public key.");
-#endif
-
- n_bytes = BN_num_bytes(n);
- assert_se(n_bytes > 0);
- if ((size_t) n_bytes > sizeof_field(TPM2B_PUBLIC, publicArea.unique.rsa.buffer))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "RSA modulus too large for TPM2 public key object.");
-
-#if OPENSSL_VERSION_MAJOR >= 3
- if (!EVP_PKEY_get_bn_param(input, OSSL_PKEY_PARAM_RSA_E, &e))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to get RSA exponent from public key.");
-#else
- e = RSA_get0_e(rsa);
- if (!e)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to get RSA exponent from public key.");
-#endif
-
- e_bytes = BN_num_bytes(e);
- assert_se(e_bytes > 0);
- if ((size_t) e_bytes > sizeof_field(TPM2B_PUBLIC, publicArea.parameters.rsaDetail.exponent))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "RSA exponent too large for TPM2 public key object.");
-
- *output = (TPM2B_PUBLIC) {
- .size = sizeof(TPMT_PUBLIC),
- .publicArea = {
- .type = TPM2_ALG_RSA,
- .nameAlg = TPM2_ALG_SHA256,
- .objectAttributes = TPMA_OBJECT_DECRYPT | TPMA_OBJECT_SIGN_ENCRYPT | TPMA_OBJECT_USERWITHAUTH,
- .parameters.rsaDetail = {
- .scheme = {
- .scheme = TPM2_ALG_NULL,
- .details.anySig.hashAlg = TPM2_ALG_NULL,
- },
- .symmetric = {
- .algorithm = TPM2_ALG_NULL,
- .mode.sym = TPM2_ALG_NULL,
- },
- .keyBits = n_bytes * 8,
- /* .exponent will be filled in below. */
- },
- .unique = {
- .rsa.size = n_bytes,
- /* .rsa.buffer will be filled in below. */
- },
- },
- };
-
- if (BN_bn2bin(n, output->publicArea.unique.rsa.buffer) <= 0)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to convert RSA modulus.");
-
- if (BN_bn2bin(e, (unsigned char*) &output->publicArea.parameters.rsaDetail.exponent) <= 0)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to convert RSA exponent.");
-
- if (ret_fp) {
- _cleanup_free_ void *fp = NULL;
- size_t fp_size;
-
- assert(ret_fp_size);
-
- r = pubkey_fingerprint(input, EVP_sha256(), &fp, &fp_size);
- if (r < 0)
- return log_error_errno(r, "Failed to calculate public key fingerprint: %m");
-
- *ret_fp = TAKE_PTR(fp);
- *ret_fp_size = fp_size;
- }
-
- return 0;
-#else /* HAVE_OPENSSL */
- return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL support is disabled.");
-#endif
-}
-
static int find_signature(
JsonVariant *v,
const TPML_PCR_SELECTION *pcr_selection,
@@ -2634,10 +2929,7 @@ static int find_signature(
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Signature is not a JSON object.");
uint16_t pcr_bank = pcr_selection->pcrSelections[0].hash;
- uint32_t pcr_mask;
- r = tpm2_tpml_pcr_selection_to_mask(pcr_selection, pcr_bank, &pcr_mask);
- if (r < 0)
- return r;
+ uint32_t pcr_mask = tpm2_tpml_pcr_selection_to_mask(pcr_selection, pcr_bank);
k = tpm2_hash_alg_to_string(pcr_bank);
if (!k)
@@ -2867,8 +3159,7 @@ static int tpm2_policy_auth_value(
/* Extend 'digest' with the PolicyPCR calculated hash. */
int tpm2_calculate_policy_pcr(
- const TPML_PCR_SELECTION *pcr_selection,
- const TPM2B_DIGEST pcr_values[],
+ const Tpm2PCRValue *pcr_values,
size_t n_pcr_values,
TPM2B_DIGEST *digest) {
@@ -2876,7 +3167,6 @@ int tpm2_calculate_policy_pcr(
TSS2_RC rc;
int r;
- assert(pcr_selection);
assert(pcr_values || n_pcr_values == 0);
assert(digest);
assert(digest->size == SHA256_DIGEST_SIZE);
@@ -2885,13 +3175,20 @@ int tpm2_calculate_policy_pcr(
if (r < 0)
return log_error_errno(r, "TPM2 support not installed: %m");
+ TPML_PCR_SELECTION pcr_selection;
+ _cleanup_free_ TPM2B_DIGEST *values = NULL;
+ size_t n_values;
+ r = tpm2_tpml_pcr_selection_from_pcr_values(pcr_values, n_pcr_values, &pcr_selection, &values, &n_values);
+ if (r < 0)
+ return log_error_errno(r, "Could not convert PCR values to TPML_PCR_SELECTION: %m");
+
TPM2B_DIGEST hash = {};
- r = tpm2_digest_many_digests(TPM2_ALG_SHA256, &hash, pcr_values, n_pcr_values, /* extend= */ false);
+ r = tpm2_digest_many_digests(TPM2_ALG_SHA256, &hash, values, n_values, /* extend= */ false);
if (r < 0)
return r;
_cleanup_free_ uint8_t *buf = NULL;
- size_t size = 0, maxsize = sizeof(command) + sizeof(*pcr_selection);
+ size_t size = 0, maxsize = sizeof(command) + sizeof(pcr_selection);
buf = malloc(maxsize);
if (!buf)
@@ -2902,7 +3199,7 @@ int tpm2_calculate_policy_pcr(
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);
+ 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));
@@ -3074,16 +3371,17 @@ static int tpm2_policy_authorize(
if (r < 0)
return r;
+ r = TPM2B_PUBLIC_KEY_RSA_CHECK_SIZE(signature_size);
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Signature larger than buffer.");
+
TPMT_SIGNATURE policy_signature = {
.sigAlg = TPM2_ALG_RSASSA,
.signature.rsassa = {
.hash = TPM2_ALG_SHA256,
- .sig.size = signature_size,
+ .sig = TPM2B_PUBLIC_KEY_RSA_MAKE(signature_raw, 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,
@@ -3127,16 +3425,16 @@ static int tpm2_policy_authorize(
}
/* 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,
+int tpm2_calculate_sealing_policy(
+ const Tpm2PCRValue *pcr_values,
+ size_t n_pcr_values,
const TPM2B_PUBLIC *public,
- const char *pin,
+ bool use_pin,
TPM2B_DIGEST *digest) {
int r;
+ assert(pcr_values || n_pcr_values == 0);
assert(digest);
if (public) {
@@ -3145,13 +3443,13 @@ static int tpm2_calculate_sealing_policy(
return r;
}
- if (hash_pcr_selection && !tpm2_tpml_pcr_selection_is_empty(hash_pcr_selection)) {
- r = tpm2_calculate_policy_pcr(hash_pcr_selection, hash_pcr_values, n_hash_pcr_values, digest);
+ if (n_pcr_values > 0) {
+ r = tpm2_calculate_policy_pcr(pcr_values, n_pcr_values, digest);
if (r < 0)
return r;
}
- if (pin) {
+ if (use_pin) {
r = tpm2_calculate_policy_auth_value(digest);
if (r < 0)
return r;
@@ -3218,38 +3516,231 @@ static int tpm2_build_sealing_policy(
return 0;
}
-int tpm2_seal(const char *device,
- uint32_t hash_pcr_mask,
- const void *pubkey,
- const size_t pubkey_size,
- uint32_t pubkey_pcr_mask,
+#if HAVE_OPENSSL
+static int tpm2_ecc_curve_from_openssl_curve_id(int curve_id, TPM2_ECC_CURVE *ret) {
+ assert(ret);
+
+ switch (curve_id) {
+ case NID_X9_62_prime192v1: *ret = TPM2_ECC_NIST_P192; return 0;
+ case NID_secp224r1: *ret = TPM2_ECC_NIST_P192; return 0;
+ case NID_X9_62_prime256v1: *ret = TPM2_ECC_NIST_P256; return 0;
+ case NID_secp384r1: *ret = TPM2_ECC_NIST_P384; return 0;
+ case NID_secp521r1: *ret = TPM2_ECC_NIST_P521; return 0;
+ case NID_sm2: *ret = TPM2_ECC_SM2_P256; return 0;
+ }
+
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Openssl ECC curve id %d not supported.", curve_id);
+}
+
+static int tpm2_ecc_curve_to_openssl_curve_id(TPM2_ECC_CURVE curve, int *ret) {
+ assert(ret);
+
+ switch (curve) {
+ case TPM2_ECC_NIST_P192: *ret = NID_X9_62_prime192v1; return 0;
+ case TPM2_ECC_NIST_P224: *ret = NID_secp224r1; return 0;
+ case TPM2_ECC_NIST_P256: *ret = NID_X9_62_prime256v1; return 0;
+ case TPM2_ECC_NIST_P384: *ret = NID_secp384r1; return 0;
+ case TPM2_ECC_NIST_P521: *ret = NID_secp521r1; return 0;
+ case TPM2_ECC_SM2_P256: *ret = NID_sm2; return 0;
+ }
+
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "TPM2 ECC curve %u not supported.", curve);
+}
+
+#define TPM2_RSA_DEFAULT_EXPONENT UINT32_C(0x10001)
+
+int tpm2_tpm2b_public_to_openssl_pkey(const TPM2B_PUBLIC *public, EVP_PKEY **ret) {
+ int r;
+
+ assert(public);
+ assert(ret);
+
+ const TPMT_PUBLIC *p = &public->publicArea;
+ if (p->type == TPM2_ALG_ECC) {
+ int curve_id;
+ r = tpm2_ecc_curve_to_openssl_curve_id(p->parameters.eccDetail.curveID, &curve_id);
+ if (r < 0)
+ return r;
+
+ const TPMS_ECC_POINT *point = &p->unique.ecc;
+ return ecc_pkey_from_curve_x_y(
+ curve_id,
+ point->x.buffer,
+ point->x.size,
+ point->y.buffer,
+ point->y.size,
+ ret);
+ }
+
+ if (p->type == TPM2_ALG_RSA) {
+ /* TPM specification Part 2 ("Structures") section for TPMS_RSA_PARAMS states "An exponent of
+ * zero indicates that the exponent is the default of 2^16 + 1". */
+ uint32_t exponent = htobe32(p->parameters.rsaDetail.exponent ?: TPM2_RSA_DEFAULT_EXPONENT);
+ return rsa_pkey_from_n_e(
+ p->unique.rsa.buffer,
+ p->unique.rsa.size,
+ &exponent,
+ sizeof(exponent),
+ ret);
+ }
+
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "TPM2 asymmetric algorithm 0x%" PRIx16 " not supported.", p->type);
+}
+
+int tpm2_tpm2b_public_from_openssl_pkey(const EVP_PKEY *pkey, TPM2B_PUBLIC *ret) {
+ int key_id, r;
+
+ assert(pkey);
+ assert(ret);
+
+ TPMT_PUBLIC public = {
+ .nameAlg = TPM2_ALG_SHA256,
+ .objectAttributes = TPMA_OBJECT_DECRYPT | TPMA_OBJECT_SIGN_ENCRYPT | TPMA_OBJECT_USERWITHAUTH,
+ .parameters.asymDetail = {
+ .symmetric.algorithm = TPM2_ALG_NULL,
+ .scheme.scheme = TPM2_ALG_NULL,
+ },
+ };
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ key_id = EVP_PKEY_get_id(pkey);
+#else
+ key_id = EVP_PKEY_id(pkey);
+#endif
+
+ if (key_id == EVP_PKEY_EC) {
+ public.type = TPM2_ALG_ECC;
+
+ int curve_id;
+ _cleanup_free_ void *x = NULL, *y = NULL;
+ size_t x_size, y_size;
+ r = ecc_pkey_to_curve_x_y(pkey, &curve_id, &x, &x_size, &y, &y_size);
+ if (r < 0)
+ return log_error_errno(r, "Could not get ECC key curve/x/y: %m");
+
+ TPM2_ECC_CURVE curve;
+ r = tpm2_ecc_curve_from_openssl_curve_id(curve_id, &curve);
+ if (r < 0)
+ return r;
+
+ public.parameters.eccDetail.curveID = curve;
+
+ public.parameters.eccDetail.kdf.scheme = TPM2_ALG_NULL;
+
+ r = TPM2B_ECC_PARAMETER_CHECK_SIZE(x_size);
+ if (r < 0)
+ return log_error_errno(r, "ECC key x size %zu too large.", x_size);
+
+ public.unique.ecc.x = TPM2B_ECC_PARAMETER_MAKE(x, x_size);
+
+ r = TPM2B_ECC_PARAMETER_CHECK_SIZE(y_size);
+ if (r < 0)
+ return log_error_errno(r, "ECC key y size %zu too large.", y_size);
+
+ public.unique.ecc.y = TPM2B_ECC_PARAMETER_MAKE(y, y_size);
+ } else if (key_id == EVP_PKEY_RSA) {
+ public.type = TPM2_ALG_RSA;
+
+ _cleanup_free_ void *n = NULL, *e = NULL;
+ size_t n_size, e_size;
+ r = rsa_pkey_to_n_e(pkey, &n, &n_size, &e, &e_size);
+ if (r < 0)
+ return log_error_errno(r, "Could not get RSA key n/e: %m");
+
+ r = TPM2B_PUBLIC_KEY_RSA_CHECK_SIZE(n_size);
+ if (r < 0)
+ return log_error_errno(r, "RSA key n size %zu too large.", n_size);
+
+ public.unique.rsa = TPM2B_PUBLIC_KEY_RSA_MAKE(n, n_size);
+ public.parameters.rsaDetail.keyBits = n_size * 8;
+
+ if (sizeof(uint32_t) < e_size)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "RSA key e size %zu too large.", e_size);
+
+ uint32_t exponent = 0;
+ memcpy((void*) &exponent, e, e_size);
+ exponent = be32toh(exponent) >> (32 - e_size * 8);
+ if (exponent == TPM2_RSA_DEFAULT_EXPONENT)
+ exponent = 0;
+ public.parameters.rsaDetail.exponent = exponent;
+ } else
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "EVP_PKEY type %d not supported.", key_id);
+
+ *ret = (TPM2B_PUBLIC) {
+ .size = sizeof(public),
+ .publicArea = public,
+ };
+
+ return 0;
+}
+#endif
+
+int tpm2_tpm2b_public_to_fingerprint(
+ const TPM2B_PUBLIC *public,
+ void **ret_fingerprint,
+ size_t *ret_fingerprint_size) {
+
+#if HAVE_OPENSSL
+ int r;
+
+ assert(public);
+ assert(ret_fingerprint);
+ assert(ret_fingerprint_size);
+
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL;
+ r = tpm2_tpm2b_public_to_openssl_pkey(public, &pkey);
+ if (r < 0)
+ return r;
+
+ /* Hardcode fingerprint to SHA256 */
+ return pubkey_fingerprint(pkey, EVP_sha256(), ret_fingerprint, ret_fingerprint_size);
+#else
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL support is disabled.");
+#endif
+}
+
+int tpm2_tpm2b_public_from_pem(const void *pem, size_t pem_size, TPM2B_PUBLIC *ret) {
+#if HAVE_OPENSSL
+ int r;
+
+ assert(pem);
+ assert(ret);
+
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL;
+ r = openssl_pkey_from_pem(pem, pem_size, &pkey);
+ if (r < 0)
+ return r;
+
+ return tpm2_tpm2b_public_from_openssl_pkey(pkey, ret);
+#else
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL support is disabled.");
+#endif
+}
+
+int tpm2_seal(Tpm2Context *c,
+ const TPM2B_DIGEST *policy,
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) {
+ uint16_t primary_alg = 0;
TSS2_RC rc;
int r;
- assert(pubkey || pubkey_size == 0);
-
assert(ret_secret);
assert(ret_secret_size);
assert(ret_blob);
assert(ret_blob_size);
- assert(ret_pcr_hash);
- assert(ret_pcr_hash_size);
- assert(ret_pcr_bank);
-
- assert(TPM2_PCR_MASK_VALID(hash_pcr_mask));
- assert(TPM2_PCR_MASK_VALID(pubkey_pcr_mask));
/* 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
@@ -3269,54 +3760,6 @@ int tpm2_seal(const char *device,
usec_t start = now(CLOCK_MONOTONIC);
- _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
- r = tpm2_context_new(device, &c);
- if (r < 0)
- return r;
-
- TPMI_ALG_HASH pcr_bank = 0;
- if (hash_pcr_mask | pubkey_pcr_mask) {
- /* Some TPM2 devices only can do SHA1. Prefer SHA256 but allow SHA1. */
- r = tpm2_get_best_pcr_bank(c, hash_pcr_mask|pubkey_pcr_mask, &pcr_bank);
- 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;
- }
-
- 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;
- }
-
- TPM2B_DIGEST policy_digest;
- r = tpm2_digest_init(TPM2_ALG_SHA256, &policy_digest);
- if (r < 0)
- return r;
-
- 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;
-
/* We use a keyed hash object (i.e. HMAC) to store the secret key we want to use for unlocking the
* LUKS2 volume with. We don't ever use for HMAC/keyed hash operations however, we just use it
* because it's a key type that is universally supported and suitable for symmetric binary blobs. */
@@ -3326,7 +3769,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 ? *policy : TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE),
};
TPMS_SENSITIVE_CREATE hmac_sensitive = {
@@ -3351,21 +3794,33 @@ int tpm2_seal(const char *device,
if (r < 0)
return log_error_errno(r, "Failed to generate secret key: %m");
- _cleanup_(Esys_Freep) TPM2B_PUBLIC *primary_public = NULL;
_cleanup_(tpm2_handle_freep) Tpm2Handle *primary_handle = NULL;
if (ret_srk_buf) {
- r = tpm2_get_or_create_srk(c, NULL, &primary_public, NULL, NULL, &primary_handle);
+ _cleanup_(Esys_Freep) TPM2B_PUBLIC *primary_public = NULL;
+ r = tpm2_get_or_create_srk(
+ c,
+ /* session= */ NULL,
+ &primary_public,
+ /* ret_name= */ NULL,
+ /* ret_qname= */ NULL,
+ &primary_handle);
if (r < 0)
return r;
+
+ primary_alg = primary_public->publicArea.type;
} else {
/* TODO: force all callers to provide ret_srk_buf, so we can stop sealing with the legacy templates. */
+ primary_alg = TPM2_ALG_ECC;
+
TPM2B_PUBLIC template = { .size = sizeof(TPMT_PUBLIC), };
- r = tpm2_get_legacy_template(TPM2_ALG_ECC, &template.publicArea);
+ r = tpm2_get_legacy_template(primary_alg, &template.publicArea);
if (r < 0)
return log_error_errno(r, "Could not get legacy ECC template: %m");
if (!tpm2_supports_tpmt_public(c, &template.publicArea)) {
- r = tpm2_get_legacy_template(TPM2_ALG_RSA, &template.publicArea);
+ primary_alg = TPM2_ALG_RSA;
+
+ r = tpm2_get_legacy_template(primary_alg, &template.publicArea);
if (r < 0)
return log_error_errno(r, "Could not get legacy RSA template: %m");
@@ -3379,7 +3834,7 @@ int tpm2_seal(const char *device,
/* session= */ NULL,
&template,
/* sensitive= */ NULL,
- &primary_public,
+ /* ret_public= */ NULL,
&primary_handle);
if (r < 0)
return r;
@@ -3420,11 +3875,6 @@ int tpm2_seal(const char *device,
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to marshal public key: %s", sym_Tss2_RC_Decode(rc));
- _cleanup_free_ void *hash = NULL;
- hash = memdup(policy_digest.buffer, policy_digest.size);
- if (!hash)
- return log_oom();
-
/* serialize the key for storage in the LUKS header. A deserialized ESYS_TR provides both
* the raw TPM handle as well as the object name. The object name is used to verify that
* the key we use later is the key we expect to establish the session with.
@@ -3460,10 +3910,9 @@ int tpm2_seal(const char *device,
*ret_secret_size = hmac_sensitive.data.size;
*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_bank = pcr_bank;
- *ret_primary_alg = primary_public->publicArea.type;
+
+ if (ret_primary_alg)
+ *ret_primary_alg = primary_alg;
return 0;
}
@@ -3594,14 +4043,17 @@ int tpm2_unseal(const char *device,
if (r < 0)
return r;
- TPM2B_PUBLIC pubkey_tpm2, *authorize_key = NULL;
+ TPM2B_PUBLIC pubkey_tpm2b;
_cleanup_free_ void *fp = NULL;
size_t fp_size = 0;
if (pubkey) {
- r = openssl_pubkey_to_tpm2_pubkey(pubkey, pubkey_size, &pubkey_tpm2, &fp, &fp_size);
+ r = tpm2_tpm2b_public_from_pem(pubkey, pubkey_size, &pubkey_tpm2b);
if (r < 0)
- return r;
- authorize_key = &pubkey_tpm2;
+ return log_error_errno(r, "Could not create TPMT_PUBLIC: %m");
+
+ r = tpm2_tpm2b_public_to_fingerprint(&pubkey_tpm2b, &fp, &fp_size);
+ if (r < 0)
+ return log_error_errno(r, "Could not get key fingerprint: %m");
}
/*
@@ -3638,7 +4090,7 @@ int tpm2_unseal(const char *device,
policy_session,
hash_pcr_mask,
pcr_bank,
- authorize_key,
+ pubkey ? &pubkey_tpm2b : NULL,
fp, fp_size,
pubkey_pcr_mask,
signature,
@@ -3909,45 +4361,6 @@ char *tpm2_pcr_mask_to_string(uint32_t mask) {
return TAKE_PTR(s);
}
-int tpm2_pcr_mask_from_string(const char *arg, uint32_t *ret_mask) {
- uint32_t mask = 0;
- int r;
-
- assert(arg);
- assert(ret_mask);
-
- if (isempty(arg)) {
- *ret_mask = 0;
- return 0;
- }
-
- /* Parses a "," or "+" separated list of PCR indexes. We support "," since this is a list after all,
- * and most other tools expect comma separated PCR specifications. We also support "+" since in
- * /etc/crypttab the "," is already used to separate options, hence a different separator is nice to
- * avoid escaping. */
-
- const char *p = arg;
- for (;;) {
- _cleanup_free_ char *pcr = NULL;
- unsigned n;
-
- r = extract_first_word(&p, &pcr, ",+", EXTRACT_DONT_COALESCE_SEPARATORS);
- if (r == 0)
- break;
- if (r < 0)
- return log_error_errno(r, "Failed to parse PCR list: %s", arg);
-
- r = pcr_index_from_string(pcr);
- if (r < 0)
- return log_error_errno(r, "Failed to parse specified PCR or specified PCR is out of range: %s", pcr);
- n = r;
- SET_BIT(mask, n);
- }
-
- *ret_mask = mask;
- return 0;
-}
-
int tpm2_make_pcr_json_array(uint32_t pcr_mask, JsonVariant **ret) {
_cleanup_(json_variant_unrefp) JsonVariant *a = NULL;
int r;
@@ -4239,6 +4652,18 @@ int tpm2_parse_luks2_json(
return 0;
}
+int tpm2_hash_alg_to_size(uint16_t alg) {
+ if (alg == TPM2_ALG_SHA1)
+ return 20;
+ if (alg == TPM2_ALG_SHA256)
+ return 32;
+ if (alg == TPM2_ALG_SHA384)
+ return 48;
+ if (alg == TPM2_ALG_SHA512)
+ return 64;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown hash algorithm id 0x%" PRIx16, alg);
+}
+
const char *tpm2_hash_alg_to_string(uint16_t alg) {
if (alg == TPM2_ALG_SHA1)
return "sha1";
@@ -4248,6 +4673,7 @@ const char *tpm2_hash_alg_to_string(uint16_t alg) {
return "sha384";
if (alg == TPM2_ALG_SHA512)
return "sha512";
+ log_debug("Unknown hash algorithm id 0x%" PRIx16, alg);
return NULL;
}
@@ -4260,7 +4686,7 @@ int tpm2_hash_alg_from_string(const char *alg) {
return TPM2_ALG_SHA384;
if (strcaseeq_ptr(alg, "sha512"))
return TPM2_ALG_SHA512;
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown hash algorithm name '%s'", alg);
}
const char *tpm2_asym_alg_to_string(uint16_t alg) {
@@ -4268,6 +4694,7 @@ const char *tpm2_asym_alg_to_string(uint16_t alg) {
return "ecc";
if (alg == TPM2_ALG_RSA)
return "rsa";
+ log_debug("Unknown asymmetric algorithm id 0x%" PRIx16, alg);
return NULL;
}
@@ -4276,7 +4703,7 @@ int tpm2_asym_alg_from_string(const char *alg) {
return TPM2_ALG_ECC;
if (strcaseeq_ptr(alg, "rsa"))
return TPM2_ALG_RSA;
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown asymmetric algorithm name '%s'", alg);
}
Tpm2Support tpm2_support(void) {
@@ -4314,29 +4741,147 @@ Tpm2Support tpm2_support(void) {
return support;
}
-int tpm2_parse_pcr_argument(const char *arg, uint32_t *mask) {
- uint32_t m;
+#if HAVE_TPM2
+static void tpm2_pcr_values_apply_default_hash_alg(Tpm2PCRValue *pcr_values, size_t n_pcr_values) {
+ TPMI_ALG_HASH default_hash = 0;
+ for (size_t i = 0; i < n_pcr_values; i++)
+ if (pcr_values[i].hash != 0) {
+ default_hash = pcr_values[i].hash;
+ break;
+ }
+
+ if (default_hash != 0)
+ for (size_t i = 0; i < n_pcr_values; i++)
+ if (pcr_values[i].hash == 0)
+ pcr_values[i].hash = default_hash;
+}
+#endif
+
+/* Parse the PCR selection/value arg(s) and return a corresponding array of Tpm2PCRValue objects.
+ *
+ * The format is the same as tpm2_pcr_values_from_string(). The first provided entry with a hash algorithm
+ * set will be used as the 'default' hash algorithm. All entries with an unset hash algorithm will be updated
+ * with the 'default' hash algorithm. The resulting array will be sorted and checked for validity.
+ *
+ * This will replace *ret_pcr_values with the new array of pcr values; to append to an existing array, use
+ * tpm2_parse_pcr_argument_append(). */
+int tpm2_parse_pcr_argument(const char *arg, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values) {
+#if HAVE_TPM2
int r;
- assert(mask);
+ assert(arg);
+ assert(ret_pcr_values);
+ assert(ret_n_pcr_values);
- /* For use in getopt_long() command line parsers: merges masks specified on the command line */
-
- if (isempty(arg)) {
- *mask = 0;
- return 0;
- }
-
- r = tpm2_pcr_mask_from_string(arg, &m);
+ _cleanup_free_ Tpm2PCRValue *pcr_values = NULL;
+ size_t n_pcr_values = 0;
+ r = tpm2_pcr_values_from_string(arg, &pcr_values, &n_pcr_values);
if (r < 0)
return r;
- if (*mask == UINT32_MAX)
- *mask = m;
- else
- *mask |= m;
+ tpm2_pcr_values_apply_default_hash_alg(pcr_values, n_pcr_values);
+
+ tpm2_sort_pcr_values(pcr_values, n_pcr_values);
+
+ if (!TPM2_PCR_VALUES_VALID(pcr_values, n_pcr_values))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Parsed PCR values are not valid.");
+
+ *ret_pcr_values = TAKE_PTR(pcr_values);
+ *ret_n_pcr_values = n_pcr_values;
return 0;
+#else
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 support is disabled.");
+#endif
+}
+
+/* Same as tpm2_parse_pcr_argument(), but the pcr values array is appended to. If the provided pcr values
+ * array is not NULL, it must point to an allocated pcr values array and the provided number of pcr values
+ * must be correct.
+ *
+ * Note that 'arg' is parsed into a new array of pcr values independently of any previous pcr values,
+ * including application of the default hash algorithm. Then the two arrays are combined, the default hash
+ * algorithm check applied again (in case either the previous or current array had no default hash
+ * algorithm), and then the resulting array is sorted and rechecked for validity. */
+int tpm2_parse_pcr_argument_append(const char *arg, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values) {
+#if HAVE_TPM2
+ int r;
+
+ assert(arg);
+ assert(ret_pcr_values);
+ assert(ret_n_pcr_values);
+
+ _cleanup_free_ Tpm2PCRValue *pcr_values = NULL;
+ size_t n_pcr_values;
+ r = tpm2_parse_pcr_argument(arg, &pcr_values, &n_pcr_values);
+ if (r < 0)
+ return r;
+
+ /* If we got previous values, append them. */
+ if (*ret_pcr_values && !GREEDY_REALLOC_APPEND(pcr_values, n_pcr_values, *ret_pcr_values, *ret_n_pcr_values))
+ return log_oom();
+
+ tpm2_pcr_values_apply_default_hash_alg(pcr_values, n_pcr_values);
+
+ tpm2_sort_pcr_values(pcr_values, n_pcr_values);
+
+ if (!TPM2_PCR_VALUES_VALID(pcr_values, n_pcr_values))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Parsed PCR values are not valid.");
+
+ SWAP_TWO(*ret_pcr_values, pcr_values);
+ *ret_n_pcr_values = n_pcr_values;
+
+ return 0;
+#else
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 support is disabled.");
+#endif
+}
+
+/* Same as tpm2_parse_pcr_argument() but converts the pcr values to a pcr mask. If more than one hash
+ * algorithm is included in the pcr values array this results in error. This retains the previous behavior of
+ * tpm2_parse_pcr_argument() of clearing the mask if 'arg' is empty, replacing the mask if it is set to
+ * UINT32_MAX, and or-ing the mask otherwise. */
+int tpm2_parse_pcr_argument_to_mask(const char *arg, uint32_t *ret_mask) {
+#if HAVE_TPM2
+ _cleanup_free_ Tpm2PCRValue *pcr_values = NULL;
+ size_t n_pcr_values;
+ int r;
+
+ assert(arg);
+ assert(ret_mask);
+
+ r = tpm2_parse_pcr_argument(arg, &pcr_values, &n_pcr_values);
+ if (r < 0)
+ return r;
+
+ if (n_pcr_values == 0) {
+ /* This retains the previous behavior of clearing the mask if the arg is empty */
+ *ret_mask = 0;
+ return 0;
+ }
+
+ size_t hash_count;
+ r = tpm2_pcr_values_hash_count(pcr_values, n_pcr_values, &hash_count);
+ if (r < 0)
+ return log_error_errno(r, "Could not get hash count from pcr values: %m");
+
+ if (hash_count > 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Multiple PCR hash banks selected.");
+
+ uint32_t new_mask;
+ r = tpm2_pcr_values_to_mask(pcr_values, n_pcr_values, pcr_values[0].hash, &new_mask);
+ if (r < 0)
+ return log_error_errno(r, "Could not get pcr values mask: %m");
+
+ if (*ret_mask == UINT32_MAX)
+ *ret_mask = new_mask;
+ else
+ *ret_mask |= new_mask;
+
+ return 0;
+#else
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 support is disabled.");
+#endif
}
int tpm2_load_pcr_signature(const char *path, JsonVariant **ret) {
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 12f05b43f9..32da247c37 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -7,6 +7,7 @@
#include "io-util.h"
#include "json.h"
#include "macro.h"
+#include "openssl-util.h"
#include "sha256.h"
typedef enum TPM2Flags {
@@ -17,7 +18,7 @@ typedef enum TPM2Flags {
* TPM2 on a Client PC must have at least 24 PCRs. This hardcodes our expectation of 24. */
#define TPM2_PCRS_MAX 24U
#define TPM2_PCRS_MASK ((UINT32_C(1) << TPM2_PCRS_MAX) - 1)
-static inline bool TPM2_PCR_VALID(unsigned pcr) {
+static inline bool TPM2_PCR_INDEX_VALID(unsigned pcr) {
return pcr < TPM2_PCRS_MAX;
}
static inline bool TPM2_PCR_MASK_VALID(uint32_t pcr_mask) {
@@ -34,26 +35,6 @@ static inline bool TPM2_PCR_MASK_VALID(uint32_t pcr_mask) {
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);
-
typedef struct {
unsigned n_ref;
@@ -88,6 +69,26 @@ int tpm2_handle_new(Tpm2Context *context, Tpm2Handle **ret_handle);
Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle);
DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Handle*, tpm2_handle_free);
+typedef struct {
+ unsigned index;
+ TPMI_ALG_HASH hash;
+ TPM2B_DIGEST value;
+} Tpm2PCRValue;
+
+#define TPM2_PCR_VALUE_MAKE(i, h, v) (Tpm2PCRValue) { .index = (i), .hash = (h), .value = ((TPM2B_DIGEST) v), }
+bool TPM2_PCR_VALUE_VALID(const Tpm2PCRValue *pcr_value);
+int tpm2_pcr_value_from_string(const char *arg, Tpm2PCRValue *ret_pcr_value);
+char *tpm2_pcr_value_to_string(const Tpm2PCRValue *pcr_value);
+
+bool TPM2_PCR_VALUES_VALID(const Tpm2PCRValue *pcr_values, size_t n_pcr_values);
+void tpm2_sort_pcr_values(Tpm2PCRValue *pcr_values, size_t n_pcr_values);
+int tpm2_pcr_values_from_mask(uint32_t mask, TPMI_ALG_HASH hash, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values);
+int tpm2_pcr_values_to_mask(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPMI_ALG_HASH hash, uint32_t *ret_mask);
+int tpm2_pcr_values_from_string(const char *arg, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values);
+char *tpm2_pcr_values_to_string(const Tpm2PCRValue *pcr_values, size_t n_pcr_values);
+int tpm2_pcr_values_hash_count(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, size_t *ret_count);
+int tpm2_tpml_pcr_selection_from_pcr_values(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPML_PCR_SELECTION *ret_selection, TPM2B_DIGEST **ret_values, size_t *ret_n_values);
+
int tpm2_create_primary(Tpm2Context *c, const Tpm2Handle *session, const TPM2B_PUBLIC *template, const TPM2B_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, Tpm2Handle **ret_handle);
int tpm2_create(Tpm2Context *c, const Tpm2Handle *parent, const Tpm2Handle *session, const TPMT_PUBLIC *template, const TPMS_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, TPM2B_PRIVATE **ret_private);
int tpm2_create_loaded(Tpm2Context *c, const Tpm2Handle *parent, const Tpm2Handle *session, const TPMT_PUBLIC *template, const TPMS_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, TPM2B_PRIVATE **ret_private, Tpm2Handle **ret_handle);
@@ -99,11 +100,15 @@ bool tpm2_test_parms(Tpm2Context *c, TPMI_ALG_PUBLIC alg, const TPMU_PUBLIC_PARM
int tpm2_get_good_pcr_banks(Tpm2Context *c, uint32_t pcr_mask, TPMI_ALG_HASH **ret_banks);
int tpm2_get_good_pcr_banks_strv(Tpm2Context *c, uint32_t pcr_mask, char ***ret);
+int tpm2_get_best_pcr_bank(Tpm2Context *c, uint32_t pcr_mask, TPMI_ALG_HASH *ret);
int tpm2_extend_bytes(Tpm2Context *c, char **banks, unsigned pcr_index, const void *data, size_t data_size, const void *secret, size_t secret_size);
-void tpm2_tpms_pcr_selection_to_mask(const TPMS_PCR_SELECTION *s, uint32_t *ret);
+uint32_t tpm2_tpms_pcr_selection_to_mask(const TPMS_PCR_SELECTION *s);
void tpm2_tpms_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash, TPMS_PCR_SELECTION *ret);
+bool tpm2_tpms_pcr_selection_has_mask(const TPMS_PCR_SELECTION *s, uint32_t mask);
+void tpm2_tpms_pcr_selection_add_mask(TPMS_PCR_SELECTION *s, uint32_t mask);
+void tpm2_tpms_pcr_selection_sub_mask(TPMS_PCR_SELECTION *s, uint32_t mask);
void tpm2_tpms_pcr_selection_add(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b);
void tpm2_tpms_pcr_selection_sub(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b);
void tpm2_tpms_pcr_selection_move(TPMS_PCR_SELECTION *a, TPMS_PCR_SELECTION *b);
@@ -111,8 +116,11 @@ char *tpm2_tpms_pcr_selection_to_string(const TPMS_PCR_SELECTION *s);
size_t tpm2_tpms_pcr_selection_weight(const TPMS_PCR_SELECTION *s);
#define tpm2_tpms_pcr_selection_is_empty(s) (tpm2_tpms_pcr_selection_weight(s) == 0)
-int tpm2_tpml_pcr_selection_to_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t *ret);
+uint32_t tpm2_tpml_pcr_selection_to_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash);
void tpm2_tpml_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash, TPML_PCR_SELECTION *ret);
+bool tpm2_tpml_pcr_selection_has_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t mask);
+void tpm2_tpml_pcr_selection_add_mask(TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t mask);
+void tpm2_tpml_pcr_selection_sub_mask(TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t mask);
void tpm2_tpml_pcr_selection_add_tpms_pcr_selection(TPML_PCR_SELECTION *l, const TPMS_PCR_SELECTION *s);
void tpm2_tpml_pcr_selection_sub_tpms_pcr_selection(TPML_PCR_SELECTION *l, const TPMS_PCR_SELECTION *s);
void tpm2_tpml_pcr_selection_add(TPML_PCR_SELECTION *a, const TPML_PCR_SELECTION *b);
@@ -121,9 +129,106 @@ char *tpm2_tpml_pcr_selection_to_string(const TPML_PCR_SELECTION *l);
size_t tpm2_tpml_pcr_selection_weight(const TPML_PCR_SELECTION *l);
#define tpm2_tpml_pcr_selection_is_empty(l) (tpm2_tpml_pcr_selection_weight(l) == 0)
+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);
+}
+
+void tpm2_log_debug_tpml_pcr_selection(const TPML_PCR_SELECTION *l, const char *msg);
+void tpm2_log_debug_pcr_value(const Tpm2PCRValue *pcr_value, const char *msg);
+void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *msg);
+void tpm2_log_debug_digest(const TPM2B_DIGEST *digest, const char *msg);
+void tpm2_log_debug_name(const TPM2B_NAME *name, const char *msg);
+
+int tpm2_pcr_read(Tpm2Context *c, const TPML_PCR_SELECTION *pcr_selection, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values);
+int tpm2_pcr_read_missing_values(Tpm2Context *c, Tpm2PCRValue *pcr_values, size_t n_pcr_values);
+
+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 Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPM2B_DIGEST *digest);
+int tpm2_calculate_sealing_policy(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, const TPM2B_PUBLIC *public, bool use_pin, TPM2B_DIGEST *digest);
+
+int tpm2_seal(Tpm2Context *c, const TPM2B_DIGEST *policy, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, 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);
+
+#if HAVE_OPENSSL
+int tpm2_tpm2b_public_to_openssl_pkey(const TPM2B_PUBLIC *public, EVP_PKEY **ret);
+int tpm2_tpm2b_public_from_openssl_pkey(const EVP_PKEY *pkey, TPM2B_PUBLIC *ret);
+#endif
+
+int tpm2_tpm2b_public_from_pem(const void *pem, size_t pem_size, TPM2B_PUBLIC *ret);
+int tpm2_tpm2b_public_to_fingerprint(const TPM2B_PUBLIC *public, void **ret_fingerprint, size_t *ret_fingerprint_size);
+
+/* The tpm2-tss library has many structs that are simply a combination of an array (or object) and
+ * size. These macros allow easily initializing or assigning instances of such structs from an existing
+ * buffer/object and size, while also checking the size for safety with the struct buffer/object size. If the
+ * provided buffer/object is NULL, the resulting struct's buffer/object will be 0s. If the provided size is
+ * larger than the struct's buffer/object size, this results in assertion failure; to check the size, use one
+ * of the TPM2B_*_CHECK_SIZE() macros. */
+#define TPM2B_AUTH_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_AUTH, buffer, size)
+#define TPM2B_DATA_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_DATA, buffer, size)
+#define TPM2B_DIGEST_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_DIGEST, buffer, size)
+#define TPM2B_ECC_PARAMETER_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_ECC_PARAMETER, buffer, size)
+#define TPM2B_ENCRYPTED_SECRET_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_ENCRYPTED_SECRET, secret, size)
+#define TPM2B_MAX_BUFFER_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_MAX_BUFFER, buffer, size)
+#define TPM2B_NAME_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_NAME, name, size)
+#define TPM2B_PRIVATE_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_PRIVATE, buffer, size)
+#define TPM2B_PRIVATE_KEY_RSA_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_PRIVATE_KEY_RSA, buffer, size)
+#define TPM2B_PUBLIC_KEY_RSA_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_PUBLIC_KEY_RSA, buffer, size)
+#define TPM2B_SENSITIVE_DATA_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_SENSITIVE_DATA, buffer, size)
+#define TPM2B_BUF_SIZE_STRUCT_MAKE(buf, size, struct_type, buffer_field, size_field) \
+ _TPM2B_BUF_SIZE_STRUCT_MAKE(buf, size, UNIQ, struct_type, buffer_field, size_field)
+#define _TPM2B_BUF_SIZE_STRUCT_MAKE(buf, size, uniq, struct_type, buffer_field, size_field) \
+ ({ \
+ typeof(buf) UNIQ_T(BUF, uniq) = (buf); \
+ typeof(size) UNIQ_T(SIZE, uniq) = (size); \
+ struct_type UNIQ_T(STRUCT, uniq) = { .size_field = UNIQ_T(SIZE, uniq), }; \
+ assert(sizeof(UNIQ_T(STRUCT, uniq).buffer_field) >= (size_t) UNIQ_T(SIZE, uniq)); \
+ if (UNIQ_T(BUF, uniq)) \
+ memcpy(UNIQ_T(STRUCT, uniq).buffer_field, UNIQ_T(BUF, uniq), UNIQ_T(SIZE, uniq)); \
+ UNIQ_T(STRUCT, uniq); \
+ })
+
+/* Check if the size will fit in the TPM2B struct buffer. Returns 0 if the size will fit, otherwise this logs
+ * a debug message and returns < 0. */
+#define TPM2B_AUTH_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_AUTH, buffer)
+#define TPM2B_DATA_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_DATA, buffer)
+#define TPM2B_DIGEST_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_DIGEST, buffer)
+#define TPM2B_ECC_PARAMETER_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_ECC_PARAMETER, buffer)
+#define TPM2B_ENCRYPTED_SECRET_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_ENCRYPTED_SECRET, buffer)
+#define TPM2B_MAX_BUFFER_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_MAX_BUFFER, buffer)
+#define TPM2B_NAME_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_NAME, name)
+#define TPM2B_PRIVATE_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_PRIVATE, buffer)
+#define TPM2B_PRIVATE_KEY_RSA_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_PRIVATE_KEY_RSA, buffer)
+#define TPM2B_PUBLIC_KEY_RSA_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_PUBLIC_KEY_RSA, buffer)
+#define TPM2B_SENSITIVE_DATA_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_SENSITIVE_DATA, buffer)
+#define TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(size, struct_type, buffer_field) \
+ _TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(size, UNIQ, struct_type, buffer_field)
+#define _TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(size, uniq, struct_type, buffer_field) \
+ ({ \
+ size_t UNIQ_T(SIZE, uniq) = (size_t) (size); \
+ size_t UNIQ_T(BUFSIZE, uniq) = sizeof_field(struct_type, buffer_field); \
+ UNIQ_T(BUFSIZE, uniq) < UNIQ_T(SIZE, uniq) ? \
+ log_debug_errno(SYNTHETIC_ERRNO(EINVAL), \
+ "Size %zu larger than " #struct_type " buffer size %zu.", \
+ UNIQ_T(SIZE, uniq), UNIQ_T(BUFSIZE, uniq)) : \
+ 0; \
+ })
+
#else /* HAVE_TPM2 */
typedef struct {} Tpm2Context;
typedef struct {} Tpm2Handle;
+typedef struct {} Tpm2PCRValue;
+
+#define TPM2_PCR_VALUE_MAKE(i, h, v) (Tpm2PCRValue) {}
#endif /* HAVE_TPM2 */
int tpm2_list_devices(void);
@@ -136,7 +241,8 @@ int tpm2_make_luks2_json(int keyslot, uint32_t hash_pcr_mask, uint16_t pcr_bank,
int tpm2_parse_luks2_json(JsonVariant *v, int *ret_keyslot, uint32_t *ret_hash_pcr_mask, uint16_t *ret_pcr_bank, void **ret_pubkey, size_t *ret_pubkey_size, uint32_t *ret_pubkey_pcr_mask, uint16_t *ret_primary_alg, void **ret_blob, size_t *ret_blob_size, void **ret_policy_hash, size_t *ret_policy_hash_size, void **ret_salt, size_t *ret_salt_size, void **ret_srk_buf, size_t *ret_srk_buf_size, TPM2Flags *ret_flags);
/* Default to PCR 7 only */
-#define TPM2_PCR_MASK_DEFAULT (UINT32_C(1) << 7)
+#define TPM2_PCR_INDEX_DEFAULT (7)
+#define TPM2_PCR_MASK_DEFAULT INDEX_TO_MASK(uint32_t, TPM2_PCR_INDEX_DEFAULT)
/* We want the helpers below to work also if TPM2 libs are not available, hence define these four defines if
* they are missing. */
@@ -164,6 +270,8 @@ int tpm2_parse_luks2_json(JsonVariant *v, int *ret_keyslot, uint32_t *ret_hash_p
#define TPM2_ALG_RSA 0x1
#endif
+int tpm2_hash_alg_to_size(uint16_t alg);
+
const char *tpm2_hash_alg_to_string(uint16_t alg);
int tpm2_hash_alg_from_string(const char *alg);
@@ -171,7 +279,6 @@ const char *tpm2_asym_alg_to_string(uint16_t alg);
int tpm2_asym_alg_from_string(const char *alg);
char *tpm2_pcr_mask_to_string(uint32_t mask);
-int tpm2_pcr_mask_from_string(const char *arg, uint32_t *mask);
typedef struct {
uint32_t search_pcr_mask;
@@ -220,7 +327,9 @@ enum {
Tpm2Support tpm2_support(void);
-int tpm2_parse_pcr_argument(const char *arg, uint32_t *mask);
+int tpm2_parse_pcr_argument(const char *arg, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values);
+int tpm2_parse_pcr_argument_append(const char *arg, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values);
+int tpm2_parse_pcr_argument_to_mask(const char *arg, uint32_t *mask);
int tpm2_load_pcr_signature(const char *path, JsonVariant **ret);
int tpm2_load_pcr_public_key(const char *path, void **ret_pubkey, size_t *ret_pubkey_size);
diff --git a/src/test/meson.build b/src/test/meson.build
index 0f7dc8f9d2..1857c9c53b 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -167,7 +167,6 @@ simple_tests += files(
'test-sysctl-util.c',
'test-terminal-util.c',
'test-tmpfile-util.c',
- 'test-tpm2.c',
'test-udev-util.c',
'test-uid-alloc-range.c',
'test-uid-range.c',
@@ -353,6 +352,11 @@ executables += [
'dependencies' : libdl,
'conditions' : ['ENABLE_NSS'],
},
+ test_template + {
+ 'sources' : files('test-openssl.c'),
+ 'dependencies' : libopenssl,
+ 'conditions' : ['HAVE_OPENSSL'],
+ },
test_template + {
'sources' : files('test-parse-util.c'),
'dependencies' : libm,
@@ -415,6 +419,10 @@ executables += [
'sources' : files('test-time-util.c'),
'timeout' : 120,
},
+ test_template + {
+ 'sources' : files('test-tpm2.c'),
+ 'dependencies' : libopenssl,
+ },
test_template + {
'sources' : files('test-utmp.c'),
'conditions' : ['ENABLE_UTMP'],
diff --git a/src/test/test-openssl.c b/src/test/test-openssl.c
new file mode 100644
index 0000000000..7672c8959d
--- /dev/null
+++ b/src/test/test-openssl.c
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "hexdecoct.h"
+#include "openssl-util.h"
+#include "tests.h"
+
+TEST(openssl_pkey_from_pem) {
+ DEFINE_HEX_PTR(key_ecc, "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a30444151634451674145726a6e4575424c73496c3972687068777976584e50686a346a426e500a44586e794a304b395579724e6764365335413532542b6f5376746b436a365a726c34685847337741515558706f426c532b7448717452714c35513d3d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d0a");
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey_ecc = NULL;
+ assert_se(openssl_pkey_from_pem(key_ecc, key_ecc_len, &pkey_ecc) >= 0);
+
+ _cleanup_free_ void *x = NULL, *y = NULL;
+ size_t x_len, y_len;
+ int curve_id;
+ assert_se(ecc_pkey_to_curve_x_y(pkey_ecc, &curve_id, &x, &x_len, &y, &y_len) >= 0);
+ assert_se(curve_id == NID_X9_62_prime256v1);
+
+ DEFINE_HEX_PTR(expected_x, "ae39c4b812ec225f6b869870caf5cd3e18f88c19cf0d79f22742bd532acd81de");
+ assert_se(x_len == expected_x_len);
+ assert_se(memcmp(x, expected_x, x_len) == 0);
+
+ DEFINE_HEX_PTR(expected_y, "92e40e764fea12bed9028fa66b9788571b7c004145e9a01952fad1eab51a8be5");
+ assert_se(y_len == expected_y_len);
+ assert_se(memcmp(y, expected_y, y_len) == 0);
+
+ DEFINE_HEX_PTR(key_rsa, "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d494942496a414e42676b71686b6947397730424151454641414f43415138414d49494243674b4341514541795639434950652f505852337a436f63787045300a6a575262546c3568585844436b472f584b79374b6d2f4439584942334b734f5a31436a5937375571372f674359363170697838697552756a73413464503165380a593445336c68556d374a332b6473766b626f4b64553243626d52494c2f6675627771694c4d587a41673342575278747234547545443533527a373634554650640a307a70304b68775231496230444c67772f344e67566f314146763378784b4d6478774d45683567676b73733038326332706c354a504e32587677426f744e6b4d0a5471526c745a4a35355244436170696e7153334577376675646c4e735851357746766c7432377a7637344b585165616d704c59433037584f6761304c676c536b0a79754774586b6a50542f735542544a705374615769674d5a6f714b7479563463515a58436b4a52684459614c47587673504233687a766d5671636e6b47654e540a65774944415141420a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d0a");
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey_rsa = NULL;
+ assert_se(openssl_pkey_from_pem(key_rsa, key_rsa_len, &pkey_rsa) >= 0);
+
+ _cleanup_free_ void *n = NULL, *e = NULL;
+ size_t n_len, e_len;
+ assert_se(rsa_pkey_to_n_e(pkey_rsa, &n, &n_len, &e, &e_len) >= 0);
+
+ DEFINE_HEX_PTR(expected_n, "c95f4220f7bf3d7477cc2a1cc691348d645b4e5e615d70c2906fd72b2eca9bf0fd5c80772ac399d428d8efb52aeff80263ad698b1f22b91ba3b00e1d3f57bc638137961526ec9dfe76cbe46e829d53609b99120bfdfb9bc2a88b317cc0837056471b6be13b840f9dd1cfbeb85053ddd33a742a1c11d486f40cb830ff8360568d4016fdf1c4a31dc7030487982092cb34f36736a65e493cdd97bf0068b4d90c4ea465b59279e510c26a98a7a92dc4c3b7ee76536c5d0e7016f96ddbbcefef829741e6a6a4b602d3b5ce81ad0b8254a4cae1ad5e48cf4ffb140532694ad6968a0319a2a2adc95e1c4195c29094610d868b197bec3c1de1cef995a9c9e419e3537b");
+ assert_se(n_len == expected_n_len);
+ assert_se(memcmp(n, expected_n, n_len) == 0);
+
+ DEFINE_HEX_PTR(expected_e, "010001");
+ assert_se(e_len == expected_e_len);
+ assert_se(memcmp(e, expected_e, e_len) == 0);
+}
+
+TEST(rsa_pkey_n_e) {
+ DEFINE_HEX_PTR(n, "e3975a2124a7c9fe57752d106314ff62f6da731632eac221f1c0255bdcf2a34eeb21e3ab89ba8759ddad3b68be99463c7f03f3d004028a35e6f7c6596aeab2558d490f1e1c38aed2ff796bda8d6d55704eefb6ac55842dd6e606bb707f66acc02f0db2aed0dabab885bd0c850f1bdc8ac4b6bc1f74858db8ca2ab57a3d4217c091e9cd78727a2e36b8126ea629e81fecc69b0bea601000a6c0b749c5be16f53f4fa9f208a581d804234eb6526ba3fee9822d58d1ab9cac2761d7f630eb7ad6054dff0856d41aea219e1adfd87256aa1532202a070f4b1044e718d1f38bbc5a4b1fcb024f04afaafda5edeacfdf0d0bdf35c359acd059e3edb5024e588458f9b5");
+ uint32_t e = htobe32(0x10001);
+
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL;
+ assert_se(rsa_pkey_from_n_e(n, n_len, &e, sizeof(e), &pkey) >= 0);
+
+ _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new((EVP_PKEY*) pkey, NULL);
+ assert_se(ctx);
+ assert_se(EVP_PKEY_verify_init(ctx) == 1);
+
+ const char *msg = "this is a secret";
+ DEFINE_HEX_PTR(sig, "14b53e0c6ad99a350c3d7811e8160f4ae03ad159815bb91bddb9735b833588df2eac221fbd3fc4ece0dd63bfaeddfdaf4ae67021e759f3638bc194836413414f54e8c4d01c9c37fa4488ea2ef772276b8a33822a53c97b1c35acfb4bc621cfb8fad88f0cf7d5491f05236886afbf9ed47f9469536482f50f74a20defa59d99676bed62a17b5eb98641df5a2f8080fa4b24f2749cc152fa65ba34c14022fcb27f1b36f52021950d7b9b6c3042c50b84cfb7d55a5f9235bfd58e1bf1f604eb93416c5fb5fd90cb68f1270dfa9daf67f52c604f62c2f2beee5e7e672b0e6e9833dd43dba99b77668540c850c9a81a5ea7aaf6297383e6135bd64572362333121fc7");
+ assert_se(EVP_PKEY_verify(ctx, sig, sig_len, (unsigned char*) msg, strlen(msg)) == 1);
+
+ DEFINE_HEX_PTR(invalid_sig, "1234");
+ assert_se(EVP_PKEY_verify(ctx, invalid_sig, invalid_sig_len, (unsigned char*) msg, strlen(msg)) != 1);
+
+ _cleanup_free_ void *n2 = NULL, *e2 = NULL;
+ size_t n2_size, e2_size;
+ assert_se(rsa_pkey_to_n_e(pkey, &n2, &n2_size, &e2, &e2_size) >= 0);
+ assert_se(memcmp_nn(n, n_len, n2, n2_size) == 0);
+ assert_se(e2_size <= sizeof(uint32_t));
+ assert_se(memcmp(&((uint8_t*) &e)[sizeof(uint32_t) - e2_size], e2, e2_size) == 0);
+}
+
+TEST(ecc_pkey_curve_x_y) {
+ int curveid = NID_X9_62_prime256v1;
+ DEFINE_HEX_PTR(x, "2830d2c8f65d3efbef12303b968b91692f8bd04045dcb8a9656374e4ae61d818");
+ DEFINE_HEX_PTR(y, "8a80750f76729defdcc2a4bc1a91c22e60109dd6e1ffde634a650a20bab172e9");
+
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL;
+ assert_se(ecc_pkey_from_curve_x_y(curveid, x, x_len, y, y_len, &pkey) >= 0);
+
+ _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new((EVP_PKEY*) pkey, NULL);
+ assert_se(ctx);
+ assert_se(EVP_PKEY_verify_init(ctx) == 1);
+
+ const char *msg = "this is a secret";
+ DEFINE_HEX_PTR(sig, "3045022100f6ca10f7ed57a020679899b26dd5ac5a1079265885e2a6477f527b6a3f02b5ca02207b550eb3e7b69360aff977f7f6afac99c3f28266b6c5338ce373f6b59263000a");
+ assert_se(EVP_PKEY_verify(ctx, sig, sig_len, (unsigned char*) msg, strlen(msg)) == 1);
+
+ DEFINE_HEX_PTR(invalid_sig, "1234");
+ assert_se(EVP_PKEY_verify(ctx, invalid_sig, invalid_sig_len, (unsigned char*) msg, strlen(msg)) != 1);
+
+ _cleanup_free_ void *x2 = NULL, *y2 = NULL;
+ size_t x2_size, y2_size;
+ int curveid2;
+ assert_se(ecc_pkey_to_curve_x_y(pkey, &curveid2, &x2, &x2_size, &y2, &y2_size) >= 0);
+ assert_se(curveid == curveid2);
+ assert_se(memcmp_nn(x, x_len, x2, x2_size) == 0);
+ assert_se(memcmp_nn(y, y_len, y2, y2_size) == 0);
+}
+
+DEFINE_TEST_MAIN(LOG_DEBUG);
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
index 9eb9085a59..7e65396b14 100644
--- a/src/test/test-tpm2.c
+++ b/src/test/test-tpm2.c
@@ -4,39 +4,6 @@
#include "tpm2-util.h"
#include "tests.h"
-static void test_tpm2_pcr_mask_from_string_one(const char *s, uint32_t mask, int ret) {
- uint32_t m;
-
- assert_se(tpm2_pcr_mask_from_string(s, &m) == ret);
-
- if (ret >= 0)
- assert_se(m == mask);
-}
-
-TEST(tpm2_mask_from_string) {
- test_tpm2_pcr_mask_from_string_one("", 0, 0);
- test_tpm2_pcr_mask_from_string_one("0", 1, 0);
- test_tpm2_pcr_mask_from_string_one("1", 2, 0);
- test_tpm2_pcr_mask_from_string_one("0,1", 3, 0);
- test_tpm2_pcr_mask_from_string_one("0+1", 3, 0);
- test_tpm2_pcr_mask_from_string_one("0-1", 0, -EINVAL);
- test_tpm2_pcr_mask_from_string_one("0,1,2", 7, 0);
- test_tpm2_pcr_mask_from_string_one("0+1+2", 7, 0);
- test_tpm2_pcr_mask_from_string_one("0+1,2", 7, 0);
- test_tpm2_pcr_mask_from_string_one("0,1+2", 7, 0);
- test_tpm2_pcr_mask_from_string_one("0,2", 5, 0);
- test_tpm2_pcr_mask_from_string_one("0+2", 5, 0);
- test_tpm2_pcr_mask_from_string_one("foo", 0, -EINVAL);
- test_tpm2_pcr_mask_from_string_one("7+application-support", 8388736, 0);
- test_tpm2_pcr_mask_from_string_one("8+boot-loader-code", 272, 0);
- test_tpm2_pcr_mask_from_string_one("6+boot-loader-code,44", 0, -EINVAL);
- test_tpm2_pcr_mask_from_string_one("7,shim-policy,4", 16528, 0);
- test_tpm2_pcr_mask_from_string_one("sysexts,shim-policy+kernel-boot", 26624, 0);
- test_tpm2_pcr_mask_from_string_one("sysexts,shim+kernel-boot", 0, -EINVAL);
- test_tpm2_pcr_mask_from_string_one("sysexts+17+23", 8527872, 0);
- test_tpm2_pcr_mask_from_string_one("debug+24", 0, -EINVAL);
-}
-
TEST(pcr_index_from_string) {
assert_se(pcr_index_from_string("platform-code") == 0);
assert_se(pcr_index_from_string("0") == 0);
@@ -158,9 +125,7 @@ static void verify_tpms_pcr_selection(TPMS_PCR_SELECTION *s, uint32_t mask, TPMI
assert_se(s->pcrSelect[2] == ((mask >> 16) & 0xff));
assert_se(s->pcrSelect[3] == 0);
- uint32_t m = POISON_U32;
- tpm2_tpms_pcr_selection_to_mask(s, &m);
- assert_se(m == mask);
+ assert_se(tpm2_tpms_pcr_selection_to_mask(s) == mask);
}
static void verify_tpml_pcr_selection(TPML_PCR_SELECTION *l, TPMS_PCR_SELECTION s[], size_t count) {
@@ -168,10 +133,8 @@ static void verify_tpml_pcr_selection(TPML_PCR_SELECTION *l, TPMS_PCR_SELECTION
for (size_t i = 0; i < count; i++) {
assert_tpms_pcr_selection_eq(&s[i], &l->pcrSelections[i]);
- uint32_t mask = POISON_U32;
TPMI_ALG_HASH hash = l->pcrSelections[i].hash;
- assert_se(tpm2_tpml_pcr_selection_to_mask(l, hash, &mask) == 0);
- verify_tpms_pcr_selection(&l->pcrSelections[i], mask, hash);
+ verify_tpms_pcr_selection(&l->pcrSelections[i], tpm2_tpml_pcr_selection_to_mask(l, hash), hash);
}
}
@@ -467,21 +430,18 @@ static bool digest_check(const TPM2B_DIGEST *digest, const char *expect) {
h = hexmem(digest->buffer, digest->size);
assert_se(h);
- return streq(expect, h);
+ return strcaseeq(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);
+static void digest_init(TPM2B_DIGEST *digest, const char *hash) {
assert_se(strlen(hash) <= sizeof(digest->buffer) * 2);
- assert_se(unhexmem(hash, strlen(hash), &h, &s) == 0);
- assert_se(s == SHA256_DIGEST_SIZE);
+ DEFINE_HEX_PTR(h, hash);
- memcpy_safe(digest->buffer, h, s);
- digest->size = s;
+ /* Make sure the length matches a known hash algorithm */
+ assert_se(IN_SET(h_len, TPM2_SHA1_DIGEST_SIZE, TPM2_SHA256_DIGEST_SIZE, TPM2_SHA384_DIGEST_SIZE, TPM2_SHA512_DIGEST_SIZE));
+
+ *digest = TPM2B_DIGEST_MAKE(h, h_len);
assert_se(digest_check(digest, hash));
}
@@ -489,11 +449,11 @@ static void digest_init_sha256(TPM2B_DIGEST *digest, const char *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");
+ digest_init(&d0, "0000000000000000000000000000000000000000000000000000000000000000");
+ digest_init(&d1, "17b7703d9d00776310ba032e88c1a8c2a9c630ebdd799db622f6631530789175");
+ digest_init(&d2, "12998c017066eb0d2a70b94e6ed3192985855ce390f321bbdb832022888bd251");
+ digest_init(&d3, "c3a65887fedd3fb4f5d0047e906dff830bcbd1293160909eb4b05f485e7387ad");
+ digest_init(&d4, "6491fb4bc08fc0b2ef47fc63db57e249917885e69d8c0d99667df83a59107a33");
/* tpm2_digest_init, tpm2_digest_rehash */
d = (TPM2B_DIGEST){ .size = 1, .buffer = { 2, }, };
@@ -557,7 +517,192 @@ TEST(digest_many) {
assert_se(digest_check(&d, "02ecb0628264235111e0053e271092981c8b15d59cd46617836bee3149a4ecb0"));
}
-static void tpm2b_public_init(TPM2B_PUBLIC *public) {
+static void check_parse_pcr_argument(
+ const char *arg,
+ const Tpm2PCRValue *prev_values,
+ size_t n_prev_values,
+ const Tpm2PCRValue *expected_values,
+ size_t n_expected_values) {
+
+ _cleanup_free_ Tpm2PCRValue *values = NULL;
+ size_t n_values = 0;
+
+ if (n_prev_values > 0) {
+ assert_se(GREEDY_REALLOC_APPEND(values, n_values, prev_values, n_prev_values));
+ assert_se(tpm2_parse_pcr_argument_append(arg, &values, &n_values) == 0);
+ } else
+ assert_se(tpm2_parse_pcr_argument(arg, &values, &n_values) == 0);
+
+ assert_se(n_values == n_expected_values);
+ for (size_t i = 0; i < n_values; i++) {
+ const Tpm2PCRValue *v = &values[i], *e = &expected_values[i];
+ //tpm2_log_debug_pcr_value(e, "Expected value");
+ //tpm2_log_debug_pcr_value(v, "Actual value");
+
+ assert_se(v->index == e->index);
+ assert_se(v->hash == e->hash);
+ assert_se(v->value.size == e->value.size);
+ assert_se(memcmp(v->value.buffer, e->value.buffer, e->value.size) == 0);
+ }
+
+ size_t hash_count;
+ assert_se(tpm2_pcr_values_hash_count(expected_values, n_expected_values, &hash_count) == 0);
+ if (hash_count == 1) {
+ uint32_t mask = UINT32_MAX, expected_mask = 0;
+
+ if (n_prev_values > 0)
+ assert_se(tpm2_pcr_values_to_mask(prev_values, n_prev_values, prev_values[0].hash, &mask) == 0);
+
+ assert_se(tpm2_pcr_values_to_mask(expected_values, n_expected_values, expected_values[0].hash, &expected_mask) == 0);
+
+ assert_se(tpm2_parse_pcr_argument_to_mask(arg, &mask) == 0);
+ assert_se(mask == expected_mask);
+ }
+
+ size_t old_n_values = n_values;
+ assert_se(tpm2_parse_pcr_argument_append("", &values, &n_values) == 0);
+ assert_se(values);
+ assert_se(n_values == old_n_values);
+}
+
+static void check_parse_pcr_argument_to_mask(const char *arg, int mask) {
+ uint32_t m = 0;
+ int r = tpm2_parse_pcr_argument_to_mask(arg, &m);
+
+ if (mask < 0)
+ assert_se(mask == r);
+ else
+ assert_se((uint32_t) mask == m);
+}
+
+TEST(parse_pcr_argument) {
+ _cleanup_free_ Tpm2PCRValue *t0p = NULL;
+ size_t n_t0p;
+ assert_se(tpm2_parse_pcr_argument("", &t0p, &n_t0p) == 0);
+ assert_se(n_t0p == 0);
+ assert_se(tpm2_parse_pcr_argument_append("", &t0p, &n_t0p) == 0);
+ assert_se(n_t0p == 0);
+ uint32_t m0 = 0xf;
+ assert_se(tpm2_parse_pcr_argument_to_mask("", &m0) == 0);
+ assert_se(m0 == 0);
+ assert_se(tpm2_parse_pcr_argument_to_mask("", &m0) == 0);
+ assert_se(m0 == 0);
+
+ Tpm2PCRValue t1[] = {
+ TPM2_PCR_VALUE_MAKE(0, 0, {}),
+ TPM2_PCR_VALUE_MAKE(4, 0, {}),
+ TPM2_PCR_VALUE_MAKE(7, 0, {}),
+ TPM2_PCR_VALUE_MAKE(11, 0, {}),
+ };
+ check_parse_pcr_argument("0,4,7,11", NULL, 0, t1, ELEMENTSOF(t1));
+ check_parse_pcr_argument("11,4,7,0", NULL, 0, t1, ELEMENTSOF(t1));
+ check_parse_pcr_argument("7,4,0,11", NULL, 0, t1, ELEMENTSOF(t1));
+ check_parse_pcr_argument("11,7,4,0", NULL, 0, t1, ELEMENTSOF(t1));
+ check_parse_pcr_argument("0+4+7+11", NULL, 0, t1, ELEMENTSOF(t1));
+ check_parse_pcr_argument("0,4+7,11", NULL, 0, t1, ELEMENTSOF(t1));
+
+ Tpm2PCRValue t2[] = {
+ TPM2_PCR_VALUE_MAKE(0, TPM2_ALG_SHA1, {}),
+ TPM2_PCR_VALUE_MAKE(4, TPM2_ALG_SHA1, {}),
+ TPM2_PCR_VALUE_MAKE(7, TPM2_ALG_SHA1, {}),
+ TPM2_PCR_VALUE_MAKE(11, TPM2_ALG_SHA1, {}),
+ };
+ check_parse_pcr_argument("0:sha1,4,7,11", NULL, 0, t2, ELEMENTSOF(t2));
+ check_parse_pcr_argument("11,4,7,0:sha1", NULL, 0, t2, ELEMENTSOF(t2));
+ check_parse_pcr_argument("7,4:sha1,0,11", NULL, 0, t2, ELEMENTSOF(t2));
+ check_parse_pcr_argument("0:sha1,4:sha1,7:sha1,11:sha1", NULL, 0, t2, ELEMENTSOF(t2));
+ check_parse_pcr_argument("0:sha1+4:sha1,11:sha1+7:sha1", NULL, 0, t2, ELEMENTSOF(t2));
+
+ Tpm2PCRValue t3[] = {
+ TPM2_PCR_VALUE_MAKE(0, TPM2_ALG_SHA1, {}),
+ TPM2_PCR_VALUE_MAKE(1, TPM2_ALG_SHA1, {}),
+ TPM2_PCR_VALUE_MAKE(2, TPM2_ALG_SHA1, {}),
+ TPM2_PCR_VALUE_MAKE(3, TPM2_ALG_SHA1, {}),
+ TPM2_PCR_VALUE_MAKE(4, TPM2_ALG_SHA1, {}),
+ TPM2_PCR_VALUE_MAKE(7, TPM2_ALG_SHA1, {}),
+ TPM2_PCR_VALUE_MAKE(11, TPM2_ALG_SHA1, {}),
+ TPM2_PCR_VALUE_MAKE(12, TPM2_ALG_SHA1, {}),
+ };
+ check_parse_pcr_argument("1,2,3,12", t2, ELEMENTSOF(t2), t3, ELEMENTSOF(t3));
+ check_parse_pcr_argument("12,2,3,1", t2, ELEMENTSOF(t2), t3, ELEMENTSOF(t3));
+ check_parse_pcr_argument("1,2,3,12:sha1", t1, ELEMENTSOF(t1), t3, ELEMENTSOF(t3));
+ check_parse_pcr_argument("1,2,3,12:sha1", t2, ELEMENTSOF(t2), t3, ELEMENTSOF(t3));
+ check_parse_pcr_argument("1:sha1,2,3,12", t1, ELEMENTSOF(t1), t3, ELEMENTSOF(t3));
+ check_parse_pcr_argument("1:sha1,2,3,12", t2, ELEMENTSOF(t2), t3, ELEMENTSOF(t3));
+ check_parse_pcr_argument("1:sha1,2:sha1,3:sha1,12:sha1", t1, ELEMENTSOF(t1), t3, ELEMENTSOF(t3));
+ check_parse_pcr_argument("1:sha1,2:sha1,3:sha1,12:sha1", t2, ELEMENTSOF(t2), t3, ELEMENTSOF(t3));
+
+ TPM2B_DIGEST d4;
+ digest_init(&d4, "FCE7F1083082B16CFE2B085DD7858BB11A37C09B78E36C79E5A2FD529353C4E2");
+ Tpm2PCRValue t4[] = {
+ TPM2_PCR_VALUE_MAKE(0, TPM2_ALG_SHA256, {}),
+ TPM2_PCR_VALUE_MAKE(1, TPM2_ALG_SHA256, d4),
+ TPM2_PCR_VALUE_MAKE(2, TPM2_ALG_SHA256, {}),
+ TPM2_PCR_VALUE_MAKE(3, TPM2_ALG_SHA256, {}),
+ TPM2_PCR_VALUE_MAKE(4, TPM2_ALG_SHA256, {}),
+ TPM2_PCR_VALUE_MAKE(7, TPM2_ALG_SHA256, {}),
+ TPM2_PCR_VALUE_MAKE(11, TPM2_ALG_SHA256, {}),
+ TPM2_PCR_VALUE_MAKE(12, TPM2_ALG_SHA256, {}),
+ };
+ check_parse_pcr_argument("1:sha256=0xFCE7F1083082B16CFE2B085DD7858BB11A37C09B78E36C79E5A2FD529353C4E2,2,3,12", t1, ELEMENTSOF(t1), t4, ELEMENTSOF(t4));
+ check_parse_pcr_argument("12,2,3,1:sha256=FCE7F1083082B16CFE2B085DD7858BB11A37C09B78E36C79E5A2FD529353C4E2", t1, ELEMENTSOF(t1), t4, ELEMENTSOF(t4));
+ check_parse_pcr_argument("12,2,3,1:sha256=0xFCE7F1083082B16CFE2B085DD7858BB11A37C09B78E36C79E5A2FD529353C4E2", t1, ELEMENTSOF(t1), t4, ELEMENTSOF(t4));
+ check_parse_pcr_argument("1:sha256=0xFCE7F1083082B16CFE2B085DD7858BB11A37C09B78E36C79E5A2FD529353C4E2,2,3,12:SHA256", t1, ELEMENTSOF(t1), t4, ELEMENTSOF(t4));
+ check_parse_pcr_argument("1:sha256=0xFCE7F1083082B16CFE2B085DD7858BB11A37C09B78E36C79E5A2FD529353C4E2,2,3,12", t1, ELEMENTSOF(t1), t4, ELEMENTSOF(t4));
+ check_parse_pcr_argument("1:sha256=FCE7F1083082B16CFE2B085DD7858BB11A37C09B78E36C79E5A2FD529353C4E2,2:sha256,3:sha256,12:sha256", t1, ELEMENTSOF(t1), t4, ELEMENTSOF(t4));
+ check_parse_pcr_argument("1:sha256=0xFCE7F1083082B16CFE2B085DD7858BB11A37C09B78E36C79E5A2FD529353C4E2,2:sha256,3:sha256,12:sha256", t1, ELEMENTSOF(t1), t4, ELEMENTSOF(t4));
+
+ TPM2B_DIGEST d5;
+ digest_init(&d5, "0F21EADB7F27377668E3C8069BE88D116491FBEE");
+ Tpm2PCRValue t5[] = {
+ TPM2_PCR_VALUE_MAKE(1, TPM2_ALG_SHA1, d5),
+ TPM2_PCR_VALUE_MAKE(0, TPM2_ALG_SHA256, {}),
+ TPM2_PCR_VALUE_MAKE(1, TPM2_ALG_SHA256, d4),
+ TPM2_PCR_VALUE_MAKE(2, TPM2_ALG_SHA256, {}),
+ TPM2_PCR_VALUE_MAKE(3, TPM2_ALG_SHA256, {}),
+ TPM2_PCR_VALUE_MAKE(4, TPM2_ALG_SHA256, {}),
+ TPM2_PCR_VALUE_MAKE(7, TPM2_ALG_SHA256, {}),
+ TPM2_PCR_VALUE_MAKE(11, TPM2_ALG_SHA256, {}),
+ TPM2_PCR_VALUE_MAKE(12, TPM2_ALG_SHA256, {}),
+ TPM2_PCR_VALUE_MAKE(5, TPM2_ALG_SHA384, {}),
+ TPM2_PCR_VALUE_MAKE(6, TPM2_ALG_SHA512, {}),
+ };
+ check_parse_pcr_argument("0,1:sha256=0xFCE7F1083082B16CFE2B085DD7858BB11A37C09B78E36C79E5A2FD529353C4E2,1:sha1=0F21EADB7F27377668E3C8069BE88D116491FBEE,2,3,4,7,11,12,5:sha384,6:sha512", NULL, 0, t5, ELEMENTSOF(t5));
+ check_parse_pcr_argument("1:sha1=0F21EADB7F27377668E3C8069BE88D116491FBEE,6:sha512,5:sha384", t4, ELEMENTSOF(t4), t5, ELEMENTSOF(t5));
+
+ Tpm2PCRValue *v = NULL;
+ size_t n_v = 0;
+ assert_se(tpm2_parse_pcr_argument("1,100", &v, &n_v) < 0);
+ assert_se(tpm2_parse_pcr_argument("1,2=123456abc", &v, &n_v) < 0);
+ assert_se(tpm2_parse_pcr_argument("1,2:invalid", &v, &n_v) < 0);
+ assert_se(tpm2_parse_pcr_argument("1:sha1=invalid", &v, &n_v) < 0);
+ assert_se(v == NULL);
+ assert_se(n_v == 0);
+
+ check_parse_pcr_argument_to_mask("", 0x0);
+ check_parse_pcr_argument_to_mask("0", 0x1);
+ check_parse_pcr_argument_to_mask("1", 0x2);
+ check_parse_pcr_argument_to_mask("0,1", 0x3);
+ check_parse_pcr_argument_to_mask("0+1", 0x3);
+ check_parse_pcr_argument_to_mask("0-1", -EINVAL);
+ check_parse_pcr_argument_to_mask("foo", -EINVAL);
+ check_parse_pcr_argument_to_mask("0,1,2", 0x7);
+ check_parse_pcr_argument_to_mask("0+1+2", 0x7);
+ check_parse_pcr_argument_to_mask("0+1,2", 0x7);
+ check_parse_pcr_argument_to_mask("0,1+2", 0x7);
+ check_parse_pcr_argument_to_mask("0,2", 0x5);
+ check_parse_pcr_argument_to_mask("0+2", 0x5);
+ check_parse_pcr_argument_to_mask("7+application-support", 0x800080);
+ check_parse_pcr_argument_to_mask("8+boot-loader-code", 0x110);
+ check_parse_pcr_argument_to_mask("7,shim-policy,4", 0x4090);
+ check_parse_pcr_argument_to_mask("sysexts,shim-policy+kernel-boot", 0x6800);
+ check_parse_pcr_argument_to_mask("sysexts,shim+kernel-boot", -EINVAL);
+ check_parse_pcr_argument_to_mask("sysexts+17+23", 0x822000);
+ check_parse_pcr_argument_to_mask("6+boot-loader-code,44", -EINVAL);
+ check_parse_pcr_argument_to_mask("debug+24", -EINVAL);
+}
+
+static void tpm2b_public_rsa_init(TPM2B_PUBLIC *public, const char *rsa_n) {
TPMT_PUBLIC tpmt = {
.type = TPM2_ALG_RSA,
.nameAlg = TPM2_ALG_SHA256,
@@ -573,37 +718,155 @@ static void tpm2b_public_init(TPM2B_PUBLIC *public) {
},
};
- 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;
+ DEFINE_HEX_PTR(key, rsa_n);
+ tpmt.unique.rsa = TPM2B_PUBLIC_KEY_RSA_MAKE(key, key_len);
+ public->size = sizeof(tpmt);
public->publicArea = tpmt;
}
+static void tpm2b_public_ecc_init(TPM2B_PUBLIC *public, TPMI_ECC_CURVE curve, const char *x, const char *y) {
+ TPMT_PUBLIC tpmt = {
+ .type = TPM2_ALG_ECC,
+ .nameAlg = TPM2_ALG_SHA256,
+ .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
+ .parameters.eccDetail = {
+ .symmetric = {
+ .algorithm = TPM2_ALG_AES,
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB,
+ },
+ .scheme.scheme = TPM2_ALG_NULL,
+ .curveID = curve,
+ .kdf.scheme = TPM2_ALG_NULL,
+ },
+ };
+
+ DEFINE_HEX_PTR(buf_x, x);
+ tpmt.unique.ecc.x = TPM2B_ECC_PARAMETER_MAKE(buf_x, buf_x_len);
+
+ DEFINE_HEX_PTR(buf_y, y);
+ tpmt.unique.ecc.y = TPM2B_ECC_PARAMETER_MAKE(buf_y, buf_y_len);
+
+ public->size = sizeof(tpmt);
+ public->publicArea = tpmt;
+}
+
+#if HAVE_OPENSSL
+TEST(tpm2b_public_to_openssl_pkey) {
+ DEFINE_HEX_PTR(msg, "edc64c6523778961fe9ba03ab7d624b27ca1dd5b01e7734cc6c891d50db04269");
+ TPM2B_PUBLIC public;
+
+ /* RSA */
+ tpm2b_public_rsa_init(&public, "d71cff5bba2173f0434a389171048e7da8cf8409b892c62946481cc383089bc754324620967fea3d00a02a717cdda4bfe1525ad957d294b88434e0a3933e86fb40f234e4935fd2ba27eb1d21da87efa466b74eb4ad18d26059904643441cf402ee933d138a2151f40459c49d87fef59e2cb822768b2d8689a9b58f82bf9a37e70693f2b2d40dfa388d365c1b1f029a14c4fc8dadb68978ef377d20ff2ca24e7078464c705eab42f531557c9c6dc0df66b506d0c26ef604f8110c64867099267453c71871e7ed22505a09daf102afc34355209ca7680eccc0ed368d148f402fa58cbb6c9d52351f535f09e4e24ad805e149f130edaa2f5e7efed3a4d2d03adb85");
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey_rsa = NULL;
+ assert_se(tpm2_tpm2b_public_to_openssl_pkey(&public, &pkey_rsa) >= 0);
+
+ _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx_rsa = EVP_PKEY_CTX_new((EVP_PKEY*) pkey_rsa, NULL);
+ assert_se(ctx_rsa);
+ assert_se(EVP_PKEY_verify_init(ctx_rsa) == 1);
+ assert_se(EVP_PKEY_CTX_set_signature_md(ctx_rsa, EVP_sha256()) > 0);
+
+ DEFINE_HEX_PTR(sig_rsa, "9f70a9e68911be3ec464cae91126328307bf355872127e042d6c61e0a80982872c151033bcf727abfae5fc9500c923120011e7ef4aa5fc690a59a034697b6022c141b4b209e2df6f4b282288cd9181073fbe7158ce113c79d87623423c1f3996ff931e59cc91db74f8e8656215b1436fc93ddec0f1f8fa8510826e674b250f047e6cba94c95ff98072a286baca94646b577974a1e00d56c21944e38960d8ee90511a2f938e5cf1ac7b7cc7ff8e3ac001d321254d3e4f988b90e9f6f873c26ecd0a12a626b3474833cdbb9e9f793238f6c97ee5b75a1a89bb7a7858d34ecfa6d34ac58d95085e6c4fbbebd47a4364be2725c2c6b3fa15d916f3c0b62a66fe76ae");
+ assert_se(EVP_PKEY_verify(ctx_rsa, sig_rsa, sig_rsa_len, (unsigned char*) msg, msg_len) == 1);
+
+ /* ECC */
+ tpm2b_public_ecc_init(&public, TPM2_ECC_NIST_P256, "6fc0ecf3645c673ab7e86d1ec5b315afb950257c5f68ab23296160006711fac2", "8dd2ef7a2c9ecede91493ba98c8fb3f893aff325c6a1e0f752c657b2d6ca1413");
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey_ecc = NULL;
+ assert_se(tpm2_tpm2b_public_to_openssl_pkey(&public, &pkey_ecc) >= 0);
+
+ _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx_ecc = EVP_PKEY_CTX_new((EVP_PKEY*) pkey_ecc, NULL);
+ assert_se(ctx_ecc);
+ assert_se(EVP_PKEY_verify_init(ctx_ecc) == 1);
+
+ DEFINE_HEX_PTR(sig_ecc, "304602210092447ac0b5b32e90923f79bb4aba864b9c546a9900cf193a83243d35d189a2110221009a8b4df1dfa85e225eff9c606694d4d205a7a3968c9552f50bc2790209a90001");
+ assert_se(EVP_PKEY_verify(ctx_ecc, sig_ecc, sig_ecc_len, (unsigned char*) msg, msg_len) == 1);
+}
+
+static void get_tpm2b_public_from_pem(const void *pem, size_t pem_size, TPM2B_PUBLIC *ret) {
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL;
+ TPM2B_PUBLIC p1 = {}, p2 = {};
+
+ assert(pem);
+ assert(ret);
+
+ assert_se(openssl_pkey_from_pem(pem, pem_size, &pkey) >= 0);
+ assert_se(tpm2_tpm2b_public_from_openssl_pkey(pkey, &p1) >= 0);
+ assert_se(tpm2_tpm2b_public_from_pem(pem, pem_size, &p2) >= 0);
+ assert_se(memcmp_nn(&p1, sizeof(p1), &p2, sizeof(p2)) == 0);
+
+ *ret = p1;
+}
+
+static void check_tpm2b_public_fingerprint(const TPM2B_PUBLIC *public, const char *hexfp) {
+ DEFINE_HEX_PTR(expected, hexfp);
+ _cleanup_free_ void *fp = NULL;
+ size_t fp_size;
+
+ assert_se(tpm2_tpm2b_public_to_fingerprint(public, &fp, &fp_size) >= 0);
+ assert_se(memcmp_nn(fp, fp_size, expected, expected_len) == 0);
+}
+
+TEST(tpm2b_public_from_openssl_pkey) {
+ TPM2B_PUBLIC public;
+ TPMT_PUBLIC *p = &public.publicArea;
+
+ DEFINE_HEX_PTR(key_ecc, "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a30444151634451674145726a6e4575424c73496c3972687068777976584e50686a346a426e500a44586e794a304b395579724e6764365335413532542b6f5376746b436a365a726c34685847337741515558706f426c532b7448717452714c35513d3d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d0a");
+ get_tpm2b_public_from_pem(key_ecc, key_ecc_len, &public);
+
+ assert_se(p->type == TPM2_ALG_ECC);
+ assert_se(p->parameters.eccDetail.curveID == TPM2_ECC_NIST_P256);
+
+ DEFINE_HEX_PTR(expected_x, "ae39c4b812ec225f6b869870caf5cd3e18f88c19cf0d79f22742bd532acd81de");
+ assert_se(memcmp_nn(p->unique.ecc.x.buffer, p->unique.ecc.x.size, expected_x, expected_x_len) == 0);
+
+ DEFINE_HEX_PTR(expected_y, "92e40e764fea12bed9028fa66b9788571b7c004145e9a01952fad1eab51a8be5");
+ assert_se(memcmp_nn(p->unique.ecc.y.buffer, p->unique.ecc.y.size, expected_y, expected_y_len) == 0);
+
+ check_tpm2b_public_fingerprint(&public, "cd3373293b62a52b48c12100e80ea9bfd806266ce76893a5ec31cb128052d97c");
+
+ DEFINE_HEX_PTR(key_rsa, "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d494942496a414e42676b71686b6947397730424151454641414f43415138414d49494243674b4341514541795639434950652f505852337a436f63787045300a6a575262546c3568585844436b472f584b79374b6d2f4439584942334b734f5a31436a5937375571372f674359363170697838697552756a73413464503165380a593445336c68556d374a332b6473766b626f4b64553243626d52494c2f6675627771694c4d587a41673342575278747234547545443533527a373634554650640a307a70304b68775231496230444c67772f344e67566f314146763378784b4d6478774d45683567676b73733038326332706c354a504e32587677426f744e6b4d0a5471526c745a4a35355244436170696e7153334577376675646c4e735851357746766c7432377a7637344b585165616d704c59433037584f6761304c676c536b0a79754774586b6a50542f735542544a705374615769674d5a6f714b7479563463515a58436b4a52684459614c47587673504233687a766d5671636e6b47654e540a65774944415141420a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d0a");
+ get_tpm2b_public_from_pem(key_rsa, key_rsa_len, &public);
+
+ DEFINE_HEX_PTR(expected_n, "c95f4220f7bf3d7477cc2a1cc691348d645b4e5e615d70c2906fd72b2eca9bf0fd5c80772ac399d428d8efb52aeff80263ad698b1f22b91ba3b00e1d3f57bc638137961526ec9dfe76cbe46e829d53609b99120bfdfb9bc2a88b317cc0837056471b6be13b840f9dd1cfbeb85053ddd33a742a1c11d486f40cb830ff8360568d4016fdf1c4a31dc7030487982092cb34f36736a65e493cdd97bf0068b4d90c4ea465b59279e510c26a98a7a92dc4c3b7ee76536c5d0e7016f96ddbbcefef829741e6a6a4b602d3b5ce81ad0b8254a4cae1ad5e48cf4ffb140532694ad6968a0319a2a2adc95e1c4195c29094610d868b197bec3c1de1cef995a9c9e419e3537b");
+ assert_se(p->unique.rsa.size == expected_n_len);
+ assert_se(memcmp(p->unique.rsa.buffer, expected_n, expected_n_len) == 0);
+
+ assert_se(p->parameters.rsaDetail.keyBits == expected_n_len * 8);
+
+ assert_se(p->parameters.rsaDetail.exponent == 0);
+
+ check_tpm2b_public_fingerprint(&public, "d9186d13a7fd5b3644cee05448f49ad3574e82a2942ff93cf89598d36cca78a9");
+}
+#endif
+
+static void check_name(const TPM2B_NAME *name, const char *expect) {
+ assert_se(name->size == SHA256_DIGEST_SIZE + 2);
+
+ DEFINE_HEX_PTR(e, expect);
+ assert_se(name->size == e_len);
+ assert_se(memcmp(name->name, e, e_len) == 0);
+}
+
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);
+ /* RSA */
+ tpm2b_public_rsa_init(&public, "9ec7341c52093ac40a1965a5df10432513c539adcf905e30577ab6ebc88ffe53cd08cef12ed9bec6125432f4fada3629b8b96d31b8f507aa35029188fe396da823fcb236027f7fbb01b0da3d87be7f999390449ced604bdf7e26c48657cc0671000f1147da195c3861c96642e54427cb7a11572e07567ec3fd6316978abc4bd92b27bb0a0e4958e599804eeb41d682b3b7fc1f960209f80a4fb8a1b64abfd96bf5d554e73cdd6ad1c8becb4fcf5e8f0c3e621d210e5e2f308f6520ad9a966779231b99f06c5989e5a23a9415c8808ab89ce81117632e2f8461cd4428bded40979236aeadafe8de3f51660a45e1dbc87694e6a36360201cca3ff9e7263e712727");
+ assert_se(tpm2_calculate_name(&public.publicArea, &name) >= 0);
+ check_name(&name, "000be78f74a470dd92e979ca067cdb2293a35f075e8560b436bd2ccea5da21486a07");
- 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));
+ /* ECC */
+ tpm2b_public_ecc_init(&public, TPM2_ECC_NIST_P256, "238e02ee4fd5598add6b502429f1815418515e4b0d6551c8e816b38cb15451d1", "70c2d491769775ec43ccd5a571c429233e9d30cf0f486c2e01acd6cb32ba93b6");
+ assert_se(tpm2_calculate_name(&public.publicArea, &name) >= 0);
+ check_name(&name, "000b302787187ba19c82011c987bd2dcdbb652b3a543ccc5cb0b49c33d4caae604a6");
}
TEST(calculate_policy_auth_value) {
TPM2B_DIGEST d;
- digest_init_sha256(&d, "0000000000000000000000000000000000000000000000000000000000000000");
+ digest_init(&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);
@@ -614,59 +877,76 @@ TEST(calculate_policy_authorize) {
TPM2B_PUBLIC public;
TPM2B_DIGEST d;
- tpm2b_public_init(&public);
- digest_init_sha256(&d, "0000000000000000000000000000000000000000000000000000000000000000");
+ /* RSA */
+ tpm2b_public_rsa_init(&public, "9ec7341c52093ac40a1965a5df10432513c539adcf905e30577ab6ebc88ffe53cd08cef12ed9bec6125432f4fada3629b8b96d31b8f507aa35029188fe396da823fcb236027f7fbb01b0da3d87be7f999390449ced604bdf7e26c48657cc0671000f1147da195c3861c96642e54427cb7a11572e07567ec3fd6316978abc4bd92b27bb0a0e4958e599804eeb41d682b3b7fc1f960209f80a4fb8a1b64abfd96bf5d554e73cdd6ad1c8becb4fcf5e8f0c3e621d210e5e2f308f6520ad9a966779231b99f06c5989e5a23a9415c8808ab89ce81117632e2f8461cd4428bded40979236aeadafe8de3f51660a45e1dbc87694e6a36360201cca3ff9e7263e712727");
+ digest_init(&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"));
+
+ /* ECC */
+ tpm2b_public_ecc_init(&public, TPM2_ECC_NIST_P256, "423a89da6f0998f510489ab9682706e762031ef8f9faef2a185eff67065a187e", "996f73291670cef9e303d6cd9fa19ddf2c9c1fb1e283324ca9acca07c405c8d0");
+ digest_init(&d, "0000000000000000000000000000000000000000000000000000000000000000");
+ assert_se(tpm2_calculate_policy_authorize(&public, NULL, &d) == 0);
+ assert_se(digest_check(&d, "2a5b705e83f949c27ac4d2e79e54fb5fb0a60f0b37bbd54a0ee1022ba00d3628"));
+ assert_se(tpm2_calculate_policy_authorize(&public, NULL, &d) == 0);
+ assert_se(digest_check(&d, "2a5b705e83f949c27ac4d2e79e54fb5fb0a60f0b37bbd54a0ee1022ba00d3628"));
}
TEST(calculate_policy_pcr) {
- TPML_PCR_SELECTION pcr_selection;
- TPM2B_DIGEST pcr_values[16];
- TPM2B_DIGEST d;
- uint32_t pcr_mask;
+ TPM2B_DIGEST d, dN[16];
- 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);
+ digest_init(&dN[ 0], "2124793cbbe60c3a8637d3b84a5d054e87c351e1469a285acc04755e8b204dec");
+ digest_init(&dN[ 1], "bf7592f18adcfdc549fc0b94939f5069a24697f9cff4a0dca29014767b97559d");
+ digest_init(&dN[ 2], "4b00cff9dee3a364979b2dc241b34568a8ad49fcf2713df259e47dff8875feed");
+ digest_init(&dN[ 3], "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969");
+ digest_init(&dN[ 4], "368f85b3013041dfe203faaa364f00b07c5da7b1e5f1dbf2efb06fa6b9bd92de");
+ digest_init(&dN[ 5], "c97c40369691c8e4aa78fb3a52655cd193b780a838b8e23f5f476576919db5e5");
+ digest_init(&dN[ 6], "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969");
+ digest_init(&dN[ 7], "aa1154c9e0a774854ccbed4c8ce7e9b906b3d700a1a8db1772d0341a62dbe51b");
+ digest_init(&dN[ 8], "cfde439a2c06af3479ca6bdc60429b90553d65300c5cfcc40004a08c6b5ad81a");
+ digest_init(&dN[ 9], "9c2bac22ef5ec84fcdb71c3ebf776cba1247e5da980e5ee08e45666a2edf0b8b");
+ digest_init(&dN[10], "9885873f4d7348199ad286f8f2476d4f866940950f6f9fb9f945ed352dbdcbd2");
+ digest_init(&dN[11], "42400ab950d21aa79d12cc4fdef67d1087a39ad64900619831c0974dbae54e44");
+ digest_init(&dN[12], "767d064382e56ca1ad3bdcc6bc596112e6c2008b593d3570d24c2bfa64c4628c");
+ digest_init(&dN[13], "30c16133175959408c9745d8dafadef5daf4b39cb2be04df0d60089bd46d3cc4");
+ digest_init(&dN[14], "e3991b7ddd47be7e92726a832d6874c5349b52b789fa0db8b558c69fea29574e");
+ digest_init(&dN[15], "852dae3ecb992bdeb13d6002fefeeffdd90feca8b378d56681ef2c885d0e5137");
+
+ digest_init(&d, "0000000000000000000000000000000000000000000000000000000000000000");
+ Tpm2PCRValue v1[] = {
+ TPM2_PCR_VALUE_MAKE(4, TPM2_ALG_SHA256, dN[4]),
+ TPM2_PCR_VALUE_MAKE(7, TPM2_ALG_SHA256, dN[7]),
+ TPM2_PCR_VALUE_MAKE(8, TPM2_ALG_SHA256, dN[8]),
+ };
+ assert_se(tpm2_calculate_policy_pcr(v1, ELEMENTSOF(v1), &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(tpm2_calculate_policy_pcr(v1, ELEMENTSOF(v1), &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);
+ digest_init(&d, "0000000000000000000000000000000000000000000000000000000000000000");
+ Tpm2PCRValue v2[] = {
+ TPM2_PCR_VALUE_MAKE( 0, TPM2_ALG_SHA256, dN[ 0]),
+ TPM2_PCR_VALUE_MAKE( 1, TPM2_ALG_SHA256, dN[ 1]),
+ TPM2_PCR_VALUE_MAKE( 2, TPM2_ALG_SHA256, dN[ 2]),
+ TPM2_PCR_VALUE_MAKE( 3, TPM2_ALG_SHA256, dN[ 3]),
+ TPM2_PCR_VALUE_MAKE( 4, TPM2_ALG_SHA256, dN[ 4]),
+ TPM2_PCR_VALUE_MAKE( 5, TPM2_ALG_SHA256, dN[ 5]),
+ TPM2_PCR_VALUE_MAKE( 6, TPM2_ALG_SHA256, dN[ 6]),
+ TPM2_PCR_VALUE_MAKE( 7, TPM2_ALG_SHA256, dN[ 7]),
+ TPM2_PCR_VALUE_MAKE( 8, TPM2_ALG_SHA256, dN[ 8]),
+ TPM2_PCR_VALUE_MAKE( 9, TPM2_ALG_SHA256, dN[ 9]),
+ TPM2_PCR_VALUE_MAKE(10, TPM2_ALG_SHA256, dN[10]),
+ TPM2_PCR_VALUE_MAKE(11, TPM2_ALG_SHA256, dN[11]),
+ TPM2_PCR_VALUE_MAKE(12, TPM2_ALG_SHA256, dN[12]),
+ TPM2_PCR_VALUE_MAKE(13, TPM2_ALG_SHA256, dN[13]),
+ TPM2_PCR_VALUE_MAKE(14, TPM2_ALG_SHA256, dN[14]),
+ TPM2_PCR_VALUE_MAKE(15, TPM2_ALG_SHA256, dN[15]),
+ };
+ assert_se(tpm2_calculate_policy_pcr(v2, ELEMENTSOF(v2), &d) == 0);
assert_se(digest_check(&d, "22be4f1674f792d6345cea9427701068f0e8d9f42755dcc0e927e545a68f9c13"));
- assert_se(tpm2_calculate_policy_pcr(&pcr_selection, pcr_values, 16, &d) == 0);
+ assert_se(tpm2_calculate_policy_pcr(v2, ELEMENTSOF(v2), &d) == 0);
assert_se(digest_check(&d, "7481fd1b116078eb3ac2456e4ad542c9b46b9b8eb891335771ca8e7c8f8e4415"));
}
diff --git a/test/units/testsuite-70.sh b/test/units/testsuite-70.sh
index f01642f71a..dd8cdb7efa 100755
--- a/test/units/testsuite-70.sh
+++ b/test/units/testsuite-70.sh
@@ -93,7 +93,43 @@ PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 "$img
# Check with wrong PCR 0
tpm2_pcrextend 0:sha256=0000000000000000000000000000000000000000000000000000000000000000
-"$SD_CRYPTSETUP" attach test-volume "$img" - tpm2-device=auto,headless=1 && exit 1
+(! "$SD_CRYPTSETUP" attach test-volume "$img" - tpm2-device=auto,headless=1)
+
+if tpm_has_pcr sha256 12; then
+ # Enroll using an explict PCR value (that does match current PCR value)
+ systemd-cryptenroll --wipe-slot=tpm2 "$img"
+ EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
+ PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$img"
+ "$SD_CRYPTSETUP" attach test-volume "$img" - tpm2-device=auto,headless=1
+ "$SD_CRYPTSETUP" detach test-volume
+
+ # Same as above plus more PCRs without the value or alg specified
+ systemd-cryptenroll --wipe-slot=tpm2 "$img"
+ EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
+ PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1,12:sha256=$EXPECTED_PCR_VALUE,3" "$img"
+ "$SD_CRYPTSETUP" attach test-volume "$img" - tpm2-device=auto,headless=1
+ "$SD_CRYPTSETUP" detach test-volume
+
+ # Same as above plus more PCRs with hash alg specified but hash value not specified
+ systemd-cryptenroll --wipe-slot=tpm2 "$img"
+ EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
+ PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1:sha256,12:sha256=$EXPECTED_PCR_VALUE,3" "$img"
+ "$SD_CRYPTSETUP" attach test-volume "$img" - tpm2-device=auto,headless=1
+ "$SD_CRYPTSETUP" detach test-volume
+
+ # Now the interesting part, enrolling using a hash value that doesn't match the current PCR value
+ systemd-cryptenroll --wipe-slot=tpm2 "$img"
+ tpm2_pcrread -Q -o /tmp/pcr.dat sha256:12
+ CURRENT_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
+ EXPECTED_PCR_VALUE=$(cat /tmp/pcr.dat /tmp/pcr.dat | openssl dgst -sha256 -r | cut -d ' ' -f 1)
+ PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$img"
+ (! "$SD_CRYPTSETUP" attach test-volume "$img" - tpm2-device=auto,headless=1)
+ tpm2_pcrextend "12:sha256=$CURRENT_PCR_VALUE"
+ "$SD_CRYPTSETUP" attach test-volume "$img" - tpm2-device=auto,headless=1
+ "$SD_CRYPTSETUP" detach test-volume
+
+ rm -f /tmp/pcr.dat
+fi
rm -f "${img:?}"