mirror of
https://github.com/systemd/systemd.git
synced 2025-01-11 09:18:07 +03:00
Merge pull request #28519 from ddstreet/tpm2_external_seal
Update systemd-cryptenroll to enroll TPM using only public key
This commit is contained in:
commit
e3c5842732
@ -411,6 +411,28 @@
|
||||
<xi:include href="version-info.xml" xpointer="v248"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--tpm2-device-key=</option><replaceable>PATH</replaceable></term>
|
||||
|
||||
<listitem><para>Enroll a TPM2 security chip using its public key. Expects a path referring to the
|
||||
TPM2 public key in TPM2B_PUBLIC format. This cannot be used with <option>--tpm2-device=</option>, as
|
||||
it performs the same operation, but without connecting to the TPM2 security chip; instead the
|
||||
enrollment is calculated using the provided TPM2 key. This is useful in situations where the TPM2
|
||||
security chip is not available at the time of enrollment.</para>
|
||||
|
||||
<para>The key, in most cases, should be the Storage Root Key (SRK) from the TPM2 security chip. If a
|
||||
key from a different handle (not the SRK) is used, you must specify its handle index using
|
||||
<option>--tpm2-seal-key-handle=</option>.</para>
|
||||
|
||||
<para>You may use tpm2-tss tools to get the SRK from the TPM2 security chip with <citerefentry
|
||||
project='mankier'><refentrytitle>tpm2_readpublic</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
for example:</para>
|
||||
|
||||
<programlisting>tpm2_readpublic -c 0x81000001 -o srk.pub</programlisting>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--tpm2-seal-key-handle=</option><replaceable>HANDLE</replaceable></term>
|
||||
|
||||
|
@ -134,6 +134,7 @@ int enroll_tpm2(struct crypt_device *cd,
|
||||
size_t volume_key_size,
|
||||
const char *device,
|
||||
uint32_t seal_key_handle,
|
||||
const char *device_key,
|
||||
Tpm2PCRValue *hash_pcr_values,
|
||||
size_t n_hash_pcr_values,
|
||||
const char *pubkey_path,
|
||||
@ -208,25 +209,62 @@ int enroll_tpm2(struct crypt_device *cd,
|
||||
return log_debug_errno(r, "Failed to read TPM PCR signature: %m");
|
||||
}
|
||||
|
||||
bool any_pcr_value_specified = tpm2_pcr_values_has_any_values(hash_pcr_values, n_hash_pcr_values);
|
||||
|
||||
_cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy pcrlock_policy = {};
|
||||
if (pcrlock_path) {
|
||||
r = tpm2_pcrlock_policy_load(pcrlock_path, &pcrlock_policy);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
any_pcr_value_specified = true;
|
||||
flags |= TPM2_FLAGS_USE_PCRLOCK;
|
||||
}
|
||||
|
||||
_cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
|
||||
r = tpm2_context_new(device, &tpm2_context);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create TPM2 context: %m");
|
||||
TPM2B_PUBLIC device_key_public = {};
|
||||
if (device_key) {
|
||||
_cleanup_free_ char *device_key_buffer = NULL;
|
||||
size_t device_key_buffer_size;
|
||||
r = read_full_file(device_key, &device_key_buffer, &device_key_buffer_size);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read device key from file: %m");
|
||||
|
||||
bool pcr_value_specified = tpm2_pcr_values_has_any_values(hash_pcr_values, n_hash_pcr_values);
|
||||
r = dlopen_tpm2();
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "TPM2 support not installed: %m");
|
||||
|
||||
r = tpm2_pcr_read_missing_values(tpm2_context, hash_pcr_values, n_hash_pcr_values);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not read pcr values: %m");
|
||||
TSS2_RC rc;
|
||||
size_t offset = 0;
|
||||
rc = sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal(
|
||||
(uint8_t*) device_key_buffer,
|
||||
device_key_buffer_size,
|
||||
&offset,
|
||||
&device_key_public);
|
||||
if (rc != TSS2_RC_SUCCESS)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Could not unmarshal public key from file.");
|
||||
|
||||
assert(offset <= device_key_buffer_size);
|
||||
if (offset != device_key_buffer_size)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Found %zu bytes of trailing garbage in public key file.",
|
||||
device_key_buffer_size - offset);
|
||||
|
||||
if (!tpm2_pcr_values_has_all_values(hash_pcr_values, n_hash_pcr_values))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Must provide all PCR values when using TPM2 device key.");
|
||||
} else {
|
||||
r = tpm2_context_new(device, &tpm2_context);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create TPM2 context: %m");
|
||||
|
||||
if (!tpm2_pcr_values_has_all_values(hash_pcr_values, n_hash_pcr_values)) {
|
||||
r = tpm2_pcr_read_missing_values(tpm2_context, hash_pcr_values, n_hash_pcr_values);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not read pcr values: %m");
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t hash_pcr_bank = 0;
|
||||
uint32_t hash_pcr_mask = 0;
|
||||
@ -263,15 +301,26 @@ int enroll_tpm2(struct crypt_device *cd,
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = tpm2_seal(tpm2_context,
|
||||
seal_key_handle,
|
||||
&policy,
|
||||
pin_str,
|
||||
&secret, &secret_size,
|
||||
&blob, &blob_size,
|
||||
/* ret_primary_alg= */ NULL,
|
||||
&srk_buf,
|
||||
&srk_buf_size);
|
||||
if (device_key)
|
||||
r = tpm2_calculate_seal(
|
||||
seal_key_handle,
|
||||
&device_key_public,
|
||||
/* attributes= */ NULL,
|
||||
/* secret= */ NULL, /* secret_size= */ 0,
|
||||
&policy,
|
||||
pin_str,
|
||||
&secret, &secret_size,
|
||||
&blob, &blob_size,
|
||||
&srk_buf, &srk_buf_size);
|
||||
else
|
||||
r = tpm2_seal(tpm2_context,
|
||||
seal_key_handle,
|
||||
&policy,
|
||||
pin_str,
|
||||
&secret, &secret_size,
|
||||
&blob, &blob_size,
|
||||
/* ret_primary_alg= */ NULL,
|
||||
&srk_buf, &srk_buf_size);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to seal to TPM2: %m");
|
||||
|
||||
@ -286,8 +335,8 @@ int enroll_tpm2(struct crypt_device *cd,
|
||||
return r; /* return existing keyslot, so that wiping won't kill it */
|
||||
}
|
||||
|
||||
/* Quick verification that everything is in order, we are not in a hurry after all. */
|
||||
if ((!pubkey || signature_json) && !pcr_value_specified) {
|
||||
/* If possible, verify the sealed data object. */
|
||||
if ((!pubkey || signature_json) && !any_pcr_value_specified && !device_key) {
|
||||
_cleanup_(erase_and_freep) void *secret2 = NULL;
|
||||
size_t secret2_size;
|
||||
|
||||
|
@ -8,9 +8,9 @@
|
||||
#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 seal_key_handle, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin, const char *pcrlock_path);
|
||||
int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t seal_key_handle, const char *device_key, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin, const char *pcrlock_path);
|
||||
#else
|
||||
static inline int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t seal_key_handle, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin, const char *pcrlock_path) {
|
||||
static inline int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t seal_key_handle, const char *device_key, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin, const char *pcrlock_path) {
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"TPM2 key enrollment not supported.");
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ static char *arg_pkcs11_token_uri = NULL;
|
||||
static char *arg_fido2_device = NULL;
|
||||
static char *arg_tpm2_device = NULL;
|
||||
static uint32_t arg_tpm2_seal_key_handle = 0;
|
||||
static char *arg_tpm2_device_key = NULL;
|
||||
static Tpm2PCRValue *arg_tpm2_hash_pcr_values = NULL;
|
||||
static size_t arg_tpm2_n_hash_pcr_values = 0;
|
||||
static bool arg_tpm2_pin = false;
|
||||
@ -63,6 +64,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_device_key, 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);
|
||||
@ -137,6 +139,8 @@ static int help(void) {
|
||||
" Enroll a TPM2 device\n"
|
||||
" --tpm2-seal-key-handle=HANDLE\n"
|
||||
" Specify handle of key to use for sealing\n"
|
||||
" --tpm2-device-key=PATH\n"
|
||||
" Enroll a TPM2 device using its public key\n"
|
||||
" --tpm2-pcrs=PCR1+PCR2+PCR3+…\n"
|
||||
" Specify TPM2 PCRs to seal against\n"
|
||||
" --tpm2-public-key=PATH\n"
|
||||
@ -172,6 +176,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_FIDO2_DEVICE,
|
||||
ARG_TPM2_DEVICE,
|
||||
ARG_TPM2_SEAL_KEY_HANDLE,
|
||||
ARG_TPM2_DEVICE_KEY,
|
||||
ARG_TPM2_PCRS,
|
||||
ARG_TPM2_PUBLIC_KEY,
|
||||
ARG_TPM2_PUBLIC_KEY_PCRS,
|
||||
@ -200,6 +205,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "fido2-with-user-verification", required_argument, NULL, ARG_FIDO2_WITH_UV },
|
||||
{ "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
|
||||
{ "tpm2-seal-key-handle", required_argument, NULL, ARG_TPM2_SEAL_KEY_HANDLE },
|
||||
{ "tpm2-device-key", required_argument, NULL, ARG_TPM2_DEVICE_KEY },
|
||||
{ "tpm2-pcrs", required_argument, NULL, ARG_TPM2_PCRS },
|
||||
{ "tpm2-public-key", required_argument, NULL, ARG_TPM2_PUBLIC_KEY },
|
||||
{ "tpm2-public-key-pcrs", required_argument, NULL, ARG_TPM2_PUBLIC_KEY_PCRS },
|
||||
@ -381,6 +387,19 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
break;
|
||||
|
||||
case ARG_TPM2_DEVICE_KEY:
|
||||
if (arg_enroll_type >= 0 || arg_tpm2_device_key)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Multiple operations specified at once, refusing.");
|
||||
|
||||
|
||||
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_device_key);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
arg_enroll_type = ENROLL_TPM2;
|
||||
break;
|
||||
|
||||
case ARG_TPM2_PCRS:
|
||||
auto_hash_pcr_values = false;
|
||||
r = tpm2_parse_pcr_argument_append(optarg, &arg_tpm2_hash_pcr_values, &arg_tpm2_n_hash_pcr_values);
|
||||
@ -715,7 +734,7 @@ static int run(int argc, char *argv[]) {
|
||||
break;
|
||||
|
||||
case ENROLL_TPM2:
|
||||
slot = enroll_tpm2(cd, vk, vks, arg_tpm2_device, arg_tpm2_seal_key_handle, 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, arg_tpm2_pcrlock);
|
||||
slot = enroll_tpm2(cd, vk, vks, arg_tpm2_device, arg_tpm2_seal_key_handle, arg_tpm2_device_key, 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, arg_tpm2_pcrlock);
|
||||
break;
|
||||
|
||||
case _ENROLL_TYPE_INVALID:
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -30,6 +30,11 @@ typedef enum TPM2Flags {
|
||||
* the Provisioning Guidance document for more details. */
|
||||
#define TPM2_SRK_HANDLE UINT32_C(0x81000001)
|
||||
|
||||
/* The TPM specification limits sealed data to MAX_SYM_DATA. Unfortunately, tpm2-tss incorrectly
|
||||
* defines this value as 256; the TPM specification Part 2 ("Structures") section
|
||||
* "TPMU_SENSITIVE_CREATE" states "For interoperability, MAX_SYM_DATA should be 128." */
|
||||
#define TPM2_MAX_SEALED_DATA UINT16_C(128)
|
||||
|
||||
static inline bool TPM2_PCR_INDEX_VALID(unsigned pcr) {
|
||||
return pcr < TPM2_PCRS_MAX;
|
||||
}
|
||||
@ -123,6 +128,8 @@ int tpm2_create_loaded(Tpm2Context *c, const Tpm2Handle *parent, const Tpm2Handl
|
||||
int tpm2_load(Tpm2Context *c, const Tpm2Handle *parent, const Tpm2Handle *session, const TPM2B_PUBLIC *public, const TPM2B_PRIVATE *private, Tpm2Handle **ret_handle);
|
||||
int tpm2_marshal_nv_public(const TPM2B_NV_PUBLIC *nv_public, void **ret, size_t *ret_size);
|
||||
int tpm2_unmarshal_nv_public(const void *data, size_t size, TPM2B_NV_PUBLIC *ret_nv_public);
|
||||
int tpm2_marshal_blob(const TPM2B_PUBLIC *public, const TPM2B_PRIVATE *private, const TPM2B_ENCRYPTED_SECRET *seed, void **ret_blob, size_t *ret_blob_size);
|
||||
int tpm2_unmarshal_blob(const void *blob, size_t blob_size, TPM2B_PUBLIC *ret_public, TPM2B_PRIVATE *ret_private, TPM2B_ENCRYPTED_SECRET *ret_seed);
|
||||
|
||||
bool tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg);
|
||||
bool tpm2_supports_command(Tpm2Context *c, TPM2_CC command);
|
||||
@ -267,8 +274,14 @@ int tpm2_calculate_policy_authorize_nv(const TPM2B_NV_PUBLIC *public, TPM2B_DIGE
|
||||
int tpm2_calculate_policy_pcr(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPM2B_DIGEST *digest);
|
||||
int tpm2_calculate_policy_or(const TPM2B_DIGEST *branches, size_t n_branches, TPM2B_DIGEST *digest);
|
||||
int tpm2_calculate_policy_super_pcr(Tpm2PCRPrediction *prediction, uint16_t algorithm, TPM2B_DIGEST *pcr_policy);
|
||||
int tpm2_calculate_serialize(TPM2_HANDLE handle, const TPM2B_NAME *name, const TPM2B_PUBLIC *public, void **ret_serialized, size_t *ret_serialized_size);
|
||||
int tpm2_calculate_sealing_policy(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, const TPM2B_PUBLIC *public, bool use_pin, const Tpm2PCRLockPolicy *policy, TPM2B_DIGEST *digest);
|
||||
int tpm2_calculate_seal(TPM2_HANDLE parent_handle, const TPM2B_PUBLIC *parent_public, const TPMA_OBJECT *attributes, const void *secret, size_t secret_size, 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_serialized_parent, size_t *ret_serialized_parent_size);
|
||||
|
||||
int tpm2_get_srk_template(TPMI_ALG_PUBLIC alg, TPMT_PUBLIC *ret_template);
|
||||
int tpm2_get_best_srk_template(Tpm2Context *c, TPMT_PUBLIC *ret_template);
|
||||
|
||||
int tpm2_get_srk(Tpm2Context *c, const Tpm2Handle *session, TPM2B_PUBLIC **ret_public, TPM2B_NAME **ret_name, TPM2B_NAME **ret_qname, Tpm2Handle **ret_handle);
|
||||
int tpm2_get_or_create_srk(Tpm2Context *c, const Tpm2Handle *session, TPM2B_PUBLIC **ret_public, TPM2B_NAME **ret_name, TPM2B_NAME **ret_qname, Tpm2Handle **ret_handle);
|
||||
|
||||
int tpm2_seal(Tpm2Context *c, uint32_t seal_key_handle, const TPM2B_DIGEST *policy, const char *pin, 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);
|
||||
@ -348,6 +361,8 @@ int tpm2_deserialize(Tpm2Context *c, const void *serialized, size_t serialized_s
|
||||
0; \
|
||||
})
|
||||
|
||||
extern TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PUBLIC *dest);
|
||||
|
||||
#else /* HAVE_TPM2 */
|
||||
typedef struct {} Tpm2Context;
|
||||
typedef struct {} Tpm2Handle;
|
||||
@ -408,6 +423,12 @@ int tpm2_hash_alg_from_string(const char *alg) _pure_;
|
||||
const char *tpm2_asym_alg_to_string(uint16_t alg) _const_;
|
||||
int tpm2_asym_alg_from_string(const char *alg) _pure_;
|
||||
|
||||
const char *tpm2_sym_alg_to_string(uint16_t alg) _const_;
|
||||
int tpm2_sym_alg_from_string(const char *alg) _pure_;
|
||||
|
||||
const char *tpm2_sym_mode_to_string(uint16_t mode) _const_;
|
||||
int tpm2_sym_mode_from_string(const char *mode) _pure_;
|
||||
|
||||
char *tpm2_pcr_mask_to_string(uint32_t mask);
|
||||
|
||||
extern const uint16_t tpm2_hash_algorithms[];
|
||||
|
@ -961,6 +961,51 @@ TEST(calculate_policy_pcr) {
|
||||
assert_se(digest_check(&d, "7481fd1b116078eb3ac2456e4ad542c9b46b9b8eb891335771ca8e7c8f8e4415"));
|
||||
}
|
||||
|
||||
static void check_srk_rsa_template(TPMT_PUBLIC *template) {
|
||||
assert_se(template->type == TPM2_ALG_RSA);
|
||||
assert_se(template->nameAlg == TPM2_ALG_SHA256);
|
||||
assert_se(template->objectAttributes == 0x30472);
|
||||
assert_se(template->parameters.rsaDetail.symmetric.algorithm == TPM2_ALG_AES);
|
||||
assert_se(template->parameters.rsaDetail.symmetric.keyBits.sym == 128);
|
||||
assert_se(template->parameters.rsaDetail.symmetric.mode.sym == TPM2_ALG_CFB);
|
||||
assert_se(template->parameters.rsaDetail.scheme.scheme == TPM2_ALG_NULL);
|
||||
assert_se(template->parameters.rsaDetail.keyBits == 2048);
|
||||
}
|
||||
|
||||
static void check_srk_ecc_template(TPMT_PUBLIC *template) {
|
||||
assert_se(template->type == TPM2_ALG_ECC);
|
||||
assert_se(template->nameAlg == TPM2_ALG_SHA256);
|
||||
assert_se(template->objectAttributes == 0x30472);
|
||||
assert_se(template->parameters.eccDetail.symmetric.algorithm == TPM2_ALG_AES);
|
||||
assert_se(template->parameters.eccDetail.symmetric.keyBits.sym == 128);
|
||||
assert_se(template->parameters.eccDetail.symmetric.mode.sym == TPM2_ALG_CFB);
|
||||
assert_se(template->parameters.eccDetail.scheme.scheme == TPM2_ALG_NULL);
|
||||
assert_se(template->parameters.eccDetail.kdf.scheme == TPM2_ALG_NULL);
|
||||
assert_se(template->parameters.eccDetail.curveID == TPM2_ECC_NIST_P256);
|
||||
}
|
||||
|
||||
TEST(tpm2_get_srk_template) {
|
||||
TPMT_PUBLIC template;
|
||||
|
||||
assert_se(tpm2_get_srk_template(TPM2_ALG_RSA, &template) >= 0);
|
||||
check_srk_rsa_template(&template);
|
||||
|
||||
assert_se(tpm2_get_srk_template(TPM2_ALG_ECC, &template) >= 0);
|
||||
check_srk_ecc_template(&template);
|
||||
}
|
||||
|
||||
static void check_best_srk_template(Tpm2Context *c) {
|
||||
TPMT_PUBLIC template;
|
||||
assert_se(tpm2_get_best_srk_template(c, &template) >= 0);
|
||||
|
||||
assert_se(IN_SET(template.type, TPM2_ALG_ECC, TPM2_ALG_RSA));
|
||||
|
||||
if (template.type == TPM2_ALG_RSA)
|
||||
check_srk_rsa_template(&template);
|
||||
else
|
||||
check_srk_ecc_template(&template);
|
||||
}
|
||||
|
||||
static void check_test_parms(Tpm2Context *c) {
|
||||
assert(c);
|
||||
|
||||
@ -1016,6 +1061,107 @@ static void check_supports_command(Tpm2Context *c) {
|
||||
assert_se(tpm2_supports_command(c, TPM2_CC_Unseal));
|
||||
}
|
||||
|
||||
static void check_get_or_create_srk(Tpm2Context *c) {
|
||||
_cleanup_free_ TPM2B_PUBLIC *public = NULL;
|
||||
_cleanup_free_ TPM2B_NAME *name = NULL, *qname = NULL;
|
||||
_cleanup_(tpm2_handle_freep) Tpm2Handle *handle = NULL;
|
||||
assert_se(tpm2_get_or_create_srk(c, NULL, &public, &name, &qname, &handle) >= 0);
|
||||
assert_se(public && name && qname && handle);
|
||||
|
||||
_cleanup_free_ TPM2B_PUBLIC *public2 = NULL;
|
||||
_cleanup_free_ TPM2B_NAME *name2 = NULL, *qname2 = NULL;
|
||||
_cleanup_(tpm2_handle_freep) Tpm2Handle *handle2 = NULL;
|
||||
assert_se(tpm2_get_srk(c, NULL, &public2, &name2, &qname2, &handle2) >= 0);
|
||||
assert_se(public2 && name2 && qname2 && handle2);
|
||||
|
||||
assert_se(memcmp_nn(public, sizeof(*public), public2, sizeof(*public2)) == 0);
|
||||
assert_se(memcmp_nn(name->name, name->size, name2->name, name2->size) == 0);
|
||||
assert_se(memcmp_nn(qname->name, qname->size, qname2->name, qname2->size) == 0);
|
||||
}
|
||||
|
||||
#if HAVE_OPENSSL && OPENSSL_VERSION_MAJOR >= 3
|
||||
static void calculate_seal_and_unseal(
|
||||
Tpm2Context *c,
|
||||
TPM2_HANDLE parent_index,
|
||||
const TPM2B_PUBLIC *parent_public) {
|
||||
|
||||
_cleanup_free_ char *secret_string = NULL;
|
||||
assert_se(asprintf(&secret_string, "The classified documents are in room %x", parent_index) > 0);
|
||||
size_t secret_size = strlen(secret_string) + 1;
|
||||
|
||||
_cleanup_free_ void *blob = NULL;
|
||||
size_t blob_size = 0;
|
||||
_cleanup_free_ void *serialized_parent = NULL;
|
||||
size_t serialized_parent_size;
|
||||
assert_se(tpm2_calculate_seal(
|
||||
parent_index,
|
||||
parent_public,
|
||||
/* attributes= */ NULL,
|
||||
secret_string, secret_size,
|
||||
/* policy= */ NULL,
|
||||
/* pin= */ NULL,
|
||||
/* ret_secret= */ NULL, /* ret_secret_size= */ 0,
|
||||
&blob, &blob_size,
|
||||
&serialized_parent, &serialized_parent_size) >= 0);
|
||||
|
||||
_cleanup_free_ void *unsealed_secret = NULL;
|
||||
size_t unsealed_secret_size;
|
||||
assert_se(tpm2_unseal(
|
||||
c,
|
||||
/* hash_pcr_mask= */ 0,
|
||||
/* pcr_bank= */ 0,
|
||||
/* pubkey= */ NULL, /* pubkey_size= */ 0,
|
||||
/* pubkey_pcr_mask= */ 0,
|
||||
/* signature= */ NULL,
|
||||
/* pin= */ NULL,
|
||||
/* pcrlock_policy= */ NULL,
|
||||
/* primary_alg= */ 0,
|
||||
blob, blob_size,
|
||||
/* known_policy_hash= */ NULL, /* known_policy_hash_size= */ 0,
|
||||
serialized_parent, serialized_parent_size,
|
||||
&unsealed_secret, &unsealed_secret_size) >= 0);
|
||||
|
||||
assert_se(memcmp_nn(secret_string, secret_size, unsealed_secret, unsealed_secret_size) == 0);
|
||||
|
||||
char unsealed_string[unsealed_secret_size];
|
||||
assert_se(snprintf(unsealed_string, unsealed_secret_size, "%s", (char*) unsealed_secret) == (int) unsealed_secret_size - 1);
|
||||
log_debug("Unsealed secret is: %s", unsealed_string);
|
||||
}
|
||||
|
||||
static int check_calculate_seal(Tpm2Context *c) {
|
||||
assert(c);
|
||||
int r;
|
||||
|
||||
_cleanup_free_ TPM2B_PUBLIC *srk_public = NULL;
|
||||
assert_se(tpm2_get_srk(c, NULL, &srk_public, NULL, NULL, NULL) >= 0);
|
||||
calculate_seal_and_unseal(c, TPM2_SRK_HANDLE, srk_public);
|
||||
|
||||
TPMI_ALG_ASYM test_algs[] = { TPM2_ALG_RSA, TPM2_ALG_ECC, };
|
||||
for (unsigned i = 0; i < ELEMENTSOF(test_algs); i++) {
|
||||
TPMI_ALG_ASYM alg = test_algs[i];
|
||||
|
||||
TPM2B_PUBLIC template = { .size = sizeof(TPMT_PUBLIC), };
|
||||
assert_se(tpm2_get_srk_template(alg, &template.publicArea) >= 0);
|
||||
|
||||
_cleanup_free_ TPM2B_PUBLIC *public = NULL;
|
||||
_cleanup_(tpm2_handle_freep) Tpm2Handle *handle = NULL;
|
||||
assert_se(tpm2_create_primary(c, NULL, &template, NULL, &public, &handle) >= 0);
|
||||
|
||||
/* Once our minimum libtss2-esys version is 2.4.0 or later, this can assume
|
||||
* tpm2_index_from_handle() should always work. */
|
||||
TPM2_HANDLE index;
|
||||
r = tpm2_index_from_handle(c, handle, &index);
|
||||
if (r == -EOPNOTSUPP)
|
||||
return log_tests_skipped("libtss2-esys version too old to support tpm2_index_from_handle()");
|
||||
assert_se(r >= 0);
|
||||
|
||||
calculate_seal_and_unseal(c, index, public);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* HAVE_OPENSSL && OPENSSL_VERSION_MAJOR >= 3 */
|
||||
|
||||
static void check_seal_unseal_for_handle(Tpm2Context *c, TPM2_HANDLE handle) {
|
||||
TPM2B_DIGEST policy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
|
||||
|
||||
@ -1090,6 +1236,7 @@ static void check_seal_unseal(Tpm2Context *c) {
|
||||
|
||||
TEST_RET(tests_which_require_tpm) {
|
||||
_cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
|
||||
int r = 0;
|
||||
|
||||
if (tpm2_context_new(NULL, &c) < 0)
|
||||
return log_tests_skipped("Could not find TPM");
|
||||
@ -1097,9 +1244,15 @@ TEST_RET(tests_which_require_tpm) {
|
||||
check_test_parms(c);
|
||||
check_supports_alg(c);
|
||||
check_supports_command(c);
|
||||
check_best_srk_template(c);
|
||||
check_get_or_create_srk(c);
|
||||
check_seal_unseal(c);
|
||||
|
||||
return 0;
|
||||
#if HAVE_OPENSSL && OPENSSL_VERSION_MAJOR >= 3 /* calculating sealed object requires openssl >= 3 */
|
||||
r = check_calculate_seal(c);
|
||||
#endif
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#endif /* HAVE_TPM2 */
|
||||
|
@ -11,7 +11,7 @@ TEST_REQUIRE_INSTALL_TESTS=0
|
||||
# shellcheck source=test/test-functions
|
||||
. "${TEST_BASE_DIR:?}/test-functions"
|
||||
|
||||
test_require_bin openssl swtpm tpm2_createprimary tpm2_dictionarylockout tpm2_evictcontrol tpm2_flushcontext tpm2_pcrextend tpm2_pcrread
|
||||
test_require_bin openssl swtpm tpm2_createprimary tpm2_dictionarylockout tpm2_evictcontrol tpm2_flushcontext tpm2_pcrextend tpm2_pcrread tpm2_readpublic
|
||||
|
||||
test_append_files() {
|
||||
local workspace="${1:?}"
|
||||
@ -31,6 +31,7 @@ test_append_files() {
|
||||
inst_binary tpm2_flushcontext
|
||||
inst_binary tpm2_pcrextend
|
||||
inst_binary tpm2_pcrread
|
||||
inst_binary tpm2_readpublic
|
||||
}
|
||||
|
||||
do_test "$@"
|
||||
|
@ -131,7 +131,15 @@ if tpm_has_pcr sha256 12; then
|
||||
systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
|
||||
systemd-cryptsetup detach test-volume
|
||||
|
||||
rm -f /tmp/pcr.dat
|
||||
# enroll TPM using device key instead of direct access, then verify unlock using TPM
|
||||
tpm2_pcrread -Q -o /tmp/pcr.dat sha256:12
|
||||
CURRENT_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
|
||||
tpm2_readpublic -c 0x81000001 -o /tmp/srk.pub
|
||||
PASSWORD=passphrase systemd-cryptenroll --tpm2-device-key=/tmp/srk.pub --tpm2-pcrs="12:sha256=$CURRENT_PCR_VALUE" "$IMAGE"
|
||||
systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
|
||||
systemd-cryptsetup detach test-volume
|
||||
|
||||
rm -f /tmp/pcr.dat /tmp/srk.pub
|
||||
fi
|
||||
|
||||
# Use default (0) seal key handle
|
||||
@ -168,16 +176,16 @@ systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
|
||||
PRIMARY=/tmp/primary.ctx
|
||||
tpm2_createprimary -c "$PRIMARY"
|
||||
PERSISTENT_LINE=$(tpm2_evictcontrol -c "$PRIMARY" | grep persistent-handle)
|
||||
PERSISTENT="0x${PERSISTENT_LINE##*0x}"
|
||||
PERSISTENT_HANDLE="0x${PERSISTENT_LINE##*0x}"
|
||||
tpm2_flushcontext -t
|
||||
|
||||
systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
|
||||
PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="${PERSISTENT#0x}" "$IMAGE"
|
||||
PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="${PERSISTENT_HANDLE#0x}" "$IMAGE"
|
||||
systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
|
||||
systemd-cryptsetup detach test-volume
|
||||
|
||||
systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
|
||||
PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="$PERSISTENT" "$IMAGE"
|
||||
PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="$PERSISTENT_HANDLE" "$IMAGE"
|
||||
systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
|
||||
systemd-cryptsetup detach test-volume
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user