mirror of
https://github.com/systemd/systemd.git
synced 2025-03-14 04:58:28 +03:00
Merge pull request #29427 from ddstreet/cryptenroll_specify_handle_index
Cryptenroll specify handle index
This commit is contained in:
commit
98d8c37595
@ -411,6 +411,28 @@
|
||||
<xi:include href="version-info.xml" xpointer="v248"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--tpm2-seal-key-handle=</option><replaceable>HANDLE</replaceable></term>
|
||||
|
||||
<listitem><para>Configures which parent key to use for sealing, using the TPM handle (index) of the
|
||||
key. This is used to "seal" (encrypt) a secret and must be used later to "unseal" (decrypt) the
|
||||
secret. Expects a hexadecimal 32bit integer, optionally prefixed with
|
||||
<literal>0x</literal>. Allowable values are any handle index in the persistent
|
||||
(<literal>0x81000000</literal>-<literal>0x81ffffff</literal>) or transient
|
||||
(<literal>0x80000000</literal>-<literal>0x80ffffff</literal>) ranges. Since transient handles are
|
||||
lost after a TPM reset, and may be flushed during TPM context switching, they should not be used
|
||||
except for very specific use cases, e.g. testing.</para>
|
||||
|
||||
<para>The default is the Storage Root Key (SRK) handle index <literal>0x81000001</literal>. A value
|
||||
of 0 will use the default. For the SRK handle, a new key will be created and stored in the TPM if one
|
||||
does not already exist; for any other handle, the key must already exist in the TPM at the specified
|
||||
handle index.</para>
|
||||
|
||||
<para>This should not be changed unless you know what you are doing.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--tpm2-pcrs=</option><arg rep="repeat">PCR</arg></term>
|
||||
|
||||
|
@ -133,6 +133,7 @@ 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_pcr_values,
|
||||
size_t n_hash_pcr_values,
|
||||
const char *pubkey_path,
|
||||
@ -252,6 +253,7 @@ int enroll_tpm2(struct crypt_device *cd,
|
||||
return r;
|
||||
|
||||
r = tpm2_seal(tpm2_context,
|
||||
seal_key_handle,
|
||||
&policy,
|
||||
pin_str,
|
||||
&secret, &secret_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, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, 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, 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);
|
||||
#else
|
||||
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) {
|
||||
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) {
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"TPM2 key enrollment not supported.");
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ 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_seal_key_handle = 0;
|
||||
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;
|
||||
@ -127,6 +128,8 @@ static int help(void) {
|
||||
" Whether to require user verification to unlock the volume\n"
|
||||
" --tpm2-device=PATH\n"
|
||||
" Enroll a TPM2 device\n"
|
||||
" --tpm2-seal-key-handle=HANDLE\n"
|
||||
" Specify handle of key to use for sealing\n"
|
||||
" --tpm2-pcrs=PCR1+PCR2+PCR3+…\n"
|
||||
" Specify TPM2 PCRs to seal against\n"
|
||||
" --tpm2-public-key=PATH\n"
|
||||
@ -159,6 +162,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_PKCS11_TOKEN_URI,
|
||||
ARG_FIDO2_DEVICE,
|
||||
ARG_TPM2_DEVICE,
|
||||
ARG_TPM2_SEAL_KEY_HANDLE,
|
||||
ARG_TPM2_PCRS,
|
||||
ARG_TPM2_PUBLIC_KEY,
|
||||
ARG_TPM2_PUBLIC_KEY_PCRS,
|
||||
@ -185,6 +189,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "fido2-with-user-presence", required_argument, NULL, ARG_FIDO2_WITH_UP },
|
||||
{ "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-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 },
|
||||
@ -357,6 +362,13 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
case ARG_TPM2_SEAL_KEY_HANDLE:
|
||||
r = safe_atou32_full(optarg, 16, &arg_tpm2_seal_key_handle);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not parse TPM2 seal key handle index '%s': %m", optarg);
|
||||
|
||||
break;
|
||||
|
||||
case ARG_TPM2_PCRS:
|
||||
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);
|
||||
@ -664,7 +676,7 @@ static int run(int argc, char *argv[]) {
|
||||
break;
|
||||
|
||||
case ENROLL_TPM2:
|
||||
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);
|
||||
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);
|
||||
break;
|
||||
|
||||
case _ENROLL_TYPE_INVALID:
|
||||
|
@ -3830,6 +3830,7 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
|
||||
return log_error_errno(r, "Could not calculate sealing policy digest: %m");
|
||||
|
||||
r = tpm2_seal(tpm2_context,
|
||||
/* seal_key_handle= */ 0,
|
||||
&policy,
|
||||
/* pin= */ NULL,
|
||||
&secret, &secret_size,
|
||||
|
@ -861,6 +861,7 @@ int encrypt_credential_and_warn(
|
||||
return log_error_errno(r, "Could not calculate sealing policy digest: %m");
|
||||
|
||||
r = tpm2_seal(tpm2_context,
|
||||
/* seal_key_handle= */ 0,
|
||||
&tpm2_policy,
|
||||
/* pin= */ NULL,
|
||||
&tpm2_key, &tpm2_key_size,
|
||||
|
@ -699,12 +699,22 @@ static void tpm2_handle_cleanup(ESYS_CONTEXT *esys_context, ESYS_TR esys_handle,
|
||||
if (flush)
|
||||
rc = sym_Esys_FlushContext(esys_context, esys_handle);
|
||||
else
|
||||
rc = sym_Esys_TR_Close(esys_context, &esys_handle);
|
||||
if (rc != TSS2_RC_SUCCESS) /* We ignore failures here (besides debug logging), since this is called
|
||||
* in error paths, where we cannot do anything about failures anymore. And
|
||||
* when it is called in successful codepaths by this time we already did
|
||||
* what we wanted to do, and got the results we wanted so there's no
|
||||
* reason to make this fail more loudly than necessary. */
|
||||
/* We can't use Esys_TR_Close() because the tpm2-tss library does not use reference counting
|
||||
* for handles, and a single Esys_TR_Close() will remove the handle (internal to the tpm2-tss
|
||||
* library) that might be in use by other code that is using the same ESYS_CONTEXT. This
|
||||
* directly affects us; for example the src/test/test-tpm2.c test function
|
||||
* check_seal_unseal() will encounter this issue and will result in a failure when trying to
|
||||
* cleanup (i.e. Esys_FlushContext) the transient primary key that the test function
|
||||
* generates. However, not calling Esys_TR_Close() here should be ok, since any leaked handle
|
||||
* references will be cleaned up when we free our ESYS_CONTEXT.
|
||||
*
|
||||
* An upstream bug is open here: https://github.com/tpm2-software/tpm2-tss/issues/2693 */
|
||||
rc = TSS2_RC_SUCCESS; // FIXME: restore sym_Esys_TR_Close() use once tpm2-tss is fixed and adopted widely enough
|
||||
if (rc != TSS2_RC_SUCCESS)
|
||||
/* We ignore failures here (besides debug logging), since this is called in error paths,
|
||||
* where we cannot do anything about failures anymore. And when it is called in successful
|
||||
* codepaths by this time we already did what we wanted to do, and got the results we wanted
|
||||
* so there's no reason to make this fail more loudly than necessary. */
|
||||
log_debug("Failed to %s TPM handle, ignoring: %s", flush ? "flush" : "close", sym_Tss2_RC_Decode(rc));
|
||||
}
|
||||
|
||||
@ -758,20 +768,23 @@ int tpm2_index_to_handle(
|
||||
|
||||
assert(c);
|
||||
|
||||
/* Let's restrict this, at least for now, to allow only some handle types. */
|
||||
/* Only allow persistent, transient, or NV index handle types. */
|
||||
switch (TPM2_HANDLE_TYPE(index)) {
|
||||
case TPM2_HT_PERSISTENT:
|
||||
case TPM2_HT_NV_INDEX:
|
||||
case TPM2_HT_TRANSIENT:
|
||||
break;
|
||||
case TPM2_HT_PCR:
|
||||
/* PCR handles are referenced by their actual index number and do not need a Tpm2Handle */
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Invalid handle 0x%08" PRIx32 " (in PCR range).", index);
|
||||
case TPM2_HT_HMAC_SESSION:
|
||||
case TPM2_HT_POLICY_SESSION:
|
||||
/* Session indexes are only used internally by tpm2-tss (or lower code) */
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Invalid handle 0x%08" PRIx32 " (in session range).", index);
|
||||
case TPM2_HT_PERMANENT: /* Permanent handles are defined, e.g. ESYS_TR_RH_OWNER. */
|
||||
case TPM2_HT_PERMANENT:
|
||||
/* Permanent handles are defined, e.g. ESYS_TR_RH_OWNER. */
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Invalid handle 0x%08" PRIx32 " (in permanent range).", index);
|
||||
default:
|
||||
@ -779,20 +792,26 @@ int tpm2_index_to_handle(
|
||||
"Invalid handle 0x%08" PRIx32 " (in unknown range).", index);
|
||||
}
|
||||
|
||||
r = tpm2_get_capability_handle(c, index);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
log_debug("TPM handle 0x%08" PRIx32 " not populated.", index);
|
||||
if (ret_public)
|
||||
*ret_public = NULL;
|
||||
if (ret_name)
|
||||
*ret_name = NULL;
|
||||
if (ret_qname)
|
||||
*ret_qname = NULL;
|
||||
if (ret_handle)
|
||||
*ret_handle = NULL;
|
||||
return 0;
|
||||
/* For transient handles, the kernel tpm "resource manager" (i.e. /dev/tpmrm0) never acknowleges that
|
||||
* any transient handles exist, even if they actually do. So a failure to find the requested handle
|
||||
* index, if it's a transient handle, may not actually mean it's not present in the tpm; thus, only
|
||||
* check GetCapability() if the handle isn't transient. */
|
||||
if (TPM2_HANDLE_TYPE(index) != TPM2_HT_TRANSIENT) { // FIXME: once kernel tpmrm is fixed to acknowledge transient handles, check transient handles too
|
||||
r = tpm2_get_capability_handle(c, index);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
log_debug("TPM handle 0x%08" PRIx32 " not populated.", index);
|
||||
if (ret_public)
|
||||
*ret_public = NULL;
|
||||
if (ret_name)
|
||||
*ret_name = NULL;
|
||||
if (ret_qname)
|
||||
*ret_qname = NULL;
|
||||
if (ret_handle)
|
||||
*ret_handle = NULL;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
_cleanup_(tpm2_handle_freep) Tpm2Handle *handle = NULL;
|
||||
@ -1080,7 +1099,7 @@ static int tpm2_get_legacy_template(TPMI_ALG_PUBLIC alg, TPMT_PUBLIC *ret_templa
|
||||
*
|
||||
* These templates are only needed to create a new persistent SRK (or a new transient key that is
|
||||
* SRK-compatible). Preferably, the TPM should contain a shared SRK located at the reserved shared SRK handle
|
||||
* (see TPM2_SRK_HANDLE and tpm2_get_srk() below).
|
||||
* (see TPM2_SRK_HANDLE in tpm2-util.h, and tpm2_get_srk() below).
|
||||
*
|
||||
* The alg must be TPM2_ALG_RSA or TPM2_ALG_ECC. Returns error if the requested template is not supported on
|
||||
* this TPM. Also see tpm2_get_best_srk_template() below. */
|
||||
@ -1179,14 +1198,6 @@ static int tpm2_get_best_srk_template(Tpm2Context *c, TPMT_PUBLIC *ret_template)
|
||||
"TPM does not support either SRK template L-1 (RSA) or L-2 (ECC).");
|
||||
}
|
||||
|
||||
/* The SRK handle is defined in the Provisioning Guidance document (see above) in the table "Reserved Handles
|
||||
* for TPM Provisioning Fundamental Elements". The SRK is useful because it is "shared", meaning it has no
|
||||
* authValue nor authPolicy set, and thus may be used by anyone on the system to generate derived keys or
|
||||
* seal secrets. This is useful if the TPM has an auth (password) set for the 'owner hierarchy', which would
|
||||
* prevent users from generating primary transient keys, unless they knew the owner hierarchy auth. See
|
||||
* the Provisioning Guidance document for more details. */
|
||||
#define TPM2_SRK_HANDLE UINT32_C(0x81000001)
|
||||
|
||||
/* Get the SRK. Returns 1 if SRK is found, 0 if there is no SRK, or < 0 on error. Also see
|
||||
* tpm2_get_or_create_srk() below. */
|
||||
static int tpm2_get_srk(
|
||||
@ -3931,6 +3942,7 @@ static int tpm2_deserialize(
|
||||
}
|
||||
|
||||
int tpm2_seal(Tpm2Context *c,
|
||||
uint32_t seal_key_handle,
|
||||
const TPM2B_DIGEST *policy,
|
||||
const char *pin,
|
||||
void **ret_secret,
|
||||
@ -4004,18 +4016,42 @@ int tpm2_seal(Tpm2Context *c,
|
||||
_cleanup_(tpm2_handle_freep) Tpm2Handle *primary_handle = NULL;
|
||||
if (ret_srk_buf) {
|
||||
_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;
|
||||
|
||||
if (IN_SET(seal_key_handle, 0, TPM2_SRK_HANDLE)) {
|
||||
r = tpm2_get_or_create_srk(
|
||||
c,
|
||||
/* session= */ NULL,
|
||||
&primary_public,
|
||||
/* ret_name= */ NULL,
|
||||
/* ret_qname= */ NULL,
|
||||
&primary_handle);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else if (IN_SET(TPM2_HANDLE_TYPE(seal_key_handle), TPM2_HT_TRANSIENT, TPM2_HT_PERSISTENT)) {
|
||||
r = tpm2_index_to_handle(
|
||||
c,
|
||||
seal_key_handle,
|
||||
/* session= */ NULL,
|
||||
&primary_public,
|
||||
/* ret_name= */ NULL,
|
||||
/* ret_qname= */ NULL,
|
||||
&primary_handle);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
/* We do NOT automatically create anything other than the SRK */
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
|
||||
"No handle found at index 0x%" PRIx32, seal_key_handle);
|
||||
} else
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Seal key handle 0x%" PRIx32 " is neither transient nor persistent.",
|
||||
seal_key_handle);
|
||||
|
||||
primary_alg = primary_public->publicArea.type;
|
||||
} else {
|
||||
if (seal_key_handle != 0)
|
||||
log_debug("Using primary alg sealing, but seal key handle also provided; ignoring seal key handle.");
|
||||
|
||||
/* TODO: force all callers to provide ret_srk_buf, so we can stop sealing with the legacy templates. */
|
||||
primary_alg = TPM2_ALG_ECC;
|
||||
|
||||
|
@ -20,6 +20,14 @@ typedef enum TPM2Flags {
|
||||
#define TPM2_PCRS_MAX 24U
|
||||
#define TPM2_PCRS_MASK ((UINT32_C(1) << TPM2_PCRS_MAX) - 1)
|
||||
|
||||
/* The SRK handle is defined in the Provisioning Guidance document (see above) in the table "Reserved Handles
|
||||
* for TPM Provisioning Fundamental Elements". The SRK is useful because it is "shared", meaning it has no
|
||||
* authValue nor authPolicy set, and thus may be used by anyone on the system to generate derived keys or
|
||||
* seal secrets. This is useful if the TPM has an auth (password) set for the 'owner hierarchy', which would
|
||||
* prevent users from generating primary transient keys, unless they knew the owner hierarchy auth. See
|
||||
* the Provisioning Guidance document for more details. */
|
||||
#define TPM2_SRK_HANDLE UINT32_C(0x81000001)
|
||||
|
||||
static inline bool TPM2_PCR_INDEX_VALID(unsigned pcr) {
|
||||
return pcr < TPM2_PCRS_MAX;
|
||||
}
|
||||
@ -195,7 +203,7 @@ int tpm2_unmarshal_blob(const void *blob, size_t blob_size, TPM2B_PUBLIC *ret_pu
|
||||
|
||||
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, 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_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);
|
||||
int tpm2_unseal(Tpm2Context *c, 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
|
||||
|
@ -1,6 +1,7 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "hexdecoct.h"
|
||||
#include "macro.h"
|
||||
#include "tpm2-util.h"
|
||||
#include "tests.h"
|
||||
|
||||
@ -702,21 +703,44 @@ TEST(parse_pcr_argument) {
|
||||
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,
|
||||
.objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
|
||||
.parameters.rsaDetail = {
|
||||
.symmetric = {
|
||||
.algorithm = TPM2_ALG_AES,
|
||||
.keyBits.aes = 128,
|
||||
.mode.aes = TPM2_ALG_CFB,
|
||||
},
|
||||
.scheme.scheme = TPM2_ALG_NULL,
|
||||
.keyBits = 2048,
|
||||
static const TPMT_PUBLIC test_rsa_template = {
|
||||
.type = TPM2_ALG_RSA,
|
||||
.nameAlg = TPM2_ALG_SHA256,
|
||||
.objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
|
||||
.parameters.rsaDetail = {
|
||||
.symmetric = {
|
||||
.algorithm = TPM2_ALG_AES,
|
||||
.keyBits.aes = 128,
|
||||
.mode.aes = TPM2_ALG_CFB,
|
||||
},
|
||||
};
|
||||
.scheme.scheme = TPM2_ALG_NULL,
|
||||
.keyBits = 2048,
|
||||
},
|
||||
};
|
||||
|
||||
static const TPMT_PUBLIC test_ecc_template = {
|
||||
.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 = TPM2_ECC_NIST_P256,
|
||||
.kdf.scheme = TPM2_ALG_NULL,
|
||||
},
|
||||
};
|
||||
|
||||
static const TPMT_PUBLIC *test_templates[] = {
|
||||
&test_rsa_template,
|
||||
&test_ecc_template,
|
||||
};
|
||||
|
||||
static void tpm2b_public_rsa_init(TPM2B_PUBLIC *public, const char *rsa_n) {
|
||||
TPMT_PUBLIC tpmt = test_rsa_template;
|
||||
|
||||
DEFINE_HEX_PTR(key, rsa_n);
|
||||
tpmt.unique.rsa = TPM2B_PUBLIC_KEY_RSA_MAKE(key, key_len);
|
||||
@ -726,21 +750,8 @@ static void tpm2b_public_rsa_init(TPM2B_PUBLIC *public, const char *rsa_n) {
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
};
|
||||
TPMT_PUBLIC tpmt = test_ecc_template;
|
||||
tpmt.parameters.eccDetail.curveID = curve;
|
||||
|
||||
DEFINE_HEX_PTR(buf_x, x);
|
||||
tpmt.unique.ecc.x = TPM2B_ECC_PARAMETER_MAKE(buf_x, buf_x_len);
|
||||
@ -950,15 +961,8 @@ TEST(calculate_policy_pcr) {
|
||||
assert_se(digest_check(&d, "7481fd1b116078eb3ac2456e4ad542c9b46b9b8eb891335771ca8e7c8f8e4415"));
|
||||
}
|
||||
|
||||
TEST(tpm_required_tests) {
|
||||
int r;
|
||||
|
||||
_cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
|
||||
r = tpm2_context_new(NULL, &c);
|
||||
if (r < 0) {
|
||||
log_tests_skipped("Could not find TPM");
|
||||
return;
|
||||
}
|
||||
static void check_test_parms(Tpm2Context *c) {
|
||||
assert(c);
|
||||
|
||||
TPMU_PUBLIC_PARMS parms = {
|
||||
.symDetail.sym = {
|
||||
@ -977,6 +981,10 @@ TEST(tpm_required_tests) {
|
||||
|
||||
/* Test with valid parms */
|
||||
assert_se(tpm2_test_parms(c, TPM2_ALG_SYMCIPHER, &parms));
|
||||
}
|
||||
|
||||
static void check_supports_alg(Tpm2Context *c) {
|
||||
assert(c);
|
||||
|
||||
/* Test invalid algs */
|
||||
assert_se(!tpm2_supports_alg(c, TPM2_ALG_ERROR));
|
||||
@ -986,6 +994,10 @@ TEST(tpm_required_tests) {
|
||||
assert_se(tpm2_supports_alg(c, TPM2_ALG_RSA));
|
||||
assert_se(tpm2_supports_alg(c, TPM2_ALG_AES));
|
||||
assert_se(tpm2_supports_alg(c, TPM2_ALG_CFB));
|
||||
}
|
||||
|
||||
static void check_supports_command(Tpm2Context *c) {
|
||||
assert(c);
|
||||
|
||||
/* Test invalid commands. TPM specification Part 2 ("Structures") section "TPM_CC (Command Codes)"
|
||||
* states bits 31:30 and 28:16 are reserved and must be 0. */
|
||||
@ -1004,6 +1016,91 @@ TEST(tpm_required_tests) {
|
||||
assert_se(tpm2_supports_command(c, TPM2_CC_Unseal));
|
||||
}
|
||||
|
||||
static void check_seal_unseal_for_handle(Tpm2Context *c, TPM2_HANDLE handle) {
|
||||
TPM2B_DIGEST policy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
|
||||
|
||||
assert(c);
|
||||
|
||||
log_debug("Check seal/unseal for handle 0x%" PRIx32, handle);
|
||||
|
||||
_cleanup_free_ void *secret = NULL, *blob = NULL, *srk = NULL, *unsealed_secret = NULL;
|
||||
size_t secret_size, blob_size, srk_size, unsealed_secret_size;
|
||||
assert_se(tpm2_seal(
|
||||
c,
|
||||
handle,
|
||||
&policy,
|
||||
/* pin= */ NULL,
|
||||
&secret, &secret_size,
|
||||
&blob, &blob_size,
|
||||
/* ret_primary_alg= */ NULL,
|
||||
&srk, &srk_size) >= 0);
|
||||
|
||||
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,
|
||||
/* primary_alg= */ 0,
|
||||
blob, blob_size,
|
||||
/* policy_hash= */ NULL, /* policy_hash_size= */ 0,
|
||||
srk, srk_size,
|
||||
&unsealed_secret, &unsealed_secret_size) >= 0);
|
||||
|
||||
assert_se(memcmp_nn(secret, secret_size, unsealed_secret, unsealed_secret_size) == 0);
|
||||
}
|
||||
|
||||
static void check_seal_unseal(Tpm2Context *c) {
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
|
||||
check_seal_unseal_for_handle(c, 0);
|
||||
check_seal_unseal_for_handle(c, TPM2_SRK_HANDLE);
|
||||
|
||||
FOREACH_ARRAY(template, test_templates, ELEMENTSOF(test_templates)) {
|
||||
TPM2B_PUBLIC public = {
|
||||
.publicArea = **template,
|
||||
.size = sizeof(**template),
|
||||
};
|
||||
_cleanup_(tpm2_handle_freep) Tpm2Handle *transient_handle = NULL;
|
||||
assert_se(tpm2_create_primary(
|
||||
c,
|
||||
/* session= */ NULL,
|
||||
&public,
|
||||
/* sensitive= */ NULL,
|
||||
/* ret_public= */ NULL,
|
||||
&transient_handle) >= 0);
|
||||
|
||||
TPMI_DH_PERSISTENT transient_handle_index;
|
||||
r = tpm2_index_from_handle(c, transient_handle, &transient_handle_index);
|
||||
if (r == -EOPNOTSUPP) {
|
||||
/* libesys too old */
|
||||
log_tests_skipped("libesys too old for tpm2_index_from_handle");
|
||||
return;
|
||||
}
|
||||
assert_se(r >= 0);
|
||||
|
||||
check_seal_unseal_for_handle(c, transient_handle_index);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_RET(tests_which_require_tpm) {
|
||||
_cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
|
||||
|
||||
if (tpm2_context_new(NULL, &c) < 0)
|
||||
return log_tests_skipped("Could not find TPM");
|
||||
|
||||
check_test_parms(c);
|
||||
check_supports_alg(c);
|
||||
check_supports_command(c);
|
||||
check_seal_unseal(c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* HAVE_TPM2 */
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
|
@ -11,7 +11,7 @@ TEST_REQUIRE_INSTALL_TESTS=0
|
||||
# shellcheck source=test/test-functions
|
||||
. "${TEST_BASE_DIR:?}/test-functions"
|
||||
|
||||
test_require_bin swtpm tpm2_pcrextend tpm2_dictionarylockout
|
||||
test_require_bin openssl swtpm tpm2_createprimary tpm2_dictionarylockout tpm2_evictcontrol tpm2_flushcontext tpm2_pcrextend tpm2_pcrread
|
||||
|
||||
test_append_files() {
|
||||
local workspace="${1:?}"
|
||||
@ -24,10 +24,13 @@ test_append_files() {
|
||||
fi
|
||||
install_dmevent
|
||||
generate_module_dependencies
|
||||
inst_binary openssl
|
||||
inst_binary tpm2_createprimary
|
||||
inst_binary tpm2_dictionarylockout
|
||||
inst_binary tpm2_evictcontrol
|
||||
inst_binary tpm2_flushcontext
|
||||
inst_binary tpm2_pcrextend
|
||||
inst_binary tpm2_pcrread
|
||||
inst_binary openssl
|
||||
}
|
||||
|
||||
do_test "$@"
|
||||
|
@ -8,6 +8,15 @@ SD_PCREXTEND="/usr/lib/systemd/systemd-pcrextend"
|
||||
SD_TPM2SETUP="/usr/lib/systemd/systemd-tpm2-setup"
|
||||
export SYSTEMD_LOG_LEVEL=debug
|
||||
|
||||
trap cleanup ERR
|
||||
cleanup() {
|
||||
# Evict the TPM primary key that we persisted
|
||||
if [[ -n $persistent ]]; then
|
||||
tpm2_evictcontrol -c "$persistent"
|
||||
fi
|
||||
}
|
||||
persistent=""
|
||||
|
||||
cryptsetup_has_token_plugin_support() {
|
||||
local plugin_path
|
||||
|
||||
@ -131,6 +140,57 @@ if tpm_has_pcr sha256 12; then
|
||||
rm -f /tmp/pcr.dat
|
||||
fi
|
||||
|
||||
# Use default (0) seal key handle
|
||||
systemd-cryptenroll --wipe-slot=tpm2 "$img"
|
||||
PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0 "$img"
|
||||
systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1
|
||||
systemd-cryptsetup detach test-volume
|
||||
|
||||
systemd-cryptenroll --wipe-slot=tpm2 "$img"
|
||||
PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x0 "$img"
|
||||
systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1
|
||||
systemd-cryptsetup detach test-volume
|
||||
|
||||
# Use SRK seal key handle
|
||||
systemd-cryptenroll --wipe-slot=tpm2 "$img"
|
||||
PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=81000001 "$img"
|
||||
systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1
|
||||
systemd-cryptsetup detach test-volume
|
||||
|
||||
systemd-cryptenroll --wipe-slot=tpm2 "$img"
|
||||
PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x81000001 "$img"
|
||||
systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1
|
||||
systemd-cryptsetup detach test-volume
|
||||
|
||||
# Test invalid ranges: pcr, nv, session, permanent
|
||||
systemd-cryptenroll --wipe-slot=tpm2 "$img"
|
||||
(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=7 "$img") # PCR
|
||||
(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x01000001 "$img") # NV index
|
||||
(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x02000001 "$img") # HMAC/loaded session
|
||||
(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x03000001 "$img") # Policy/saved session
|
||||
(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x40000001 "$img") # Permanent
|
||||
|
||||
# Use non-SRK persistent seal key handle (by creating/persisting new key)
|
||||
primary=/tmp/primary.ctx
|
||||
tpm2_createprimary -c "$primary"
|
||||
persistent_line=$(tpm2_evictcontrol -c "$primary" | grep persistent-handle)
|
||||
persistent="0x${persistent_line##*0x}"
|
||||
tpm2_flushcontext -t
|
||||
|
||||
systemd-cryptenroll --wipe-slot=tpm2 "$img"
|
||||
PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="${persistent#0x}" "$img"
|
||||
systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1
|
||||
systemd-cryptsetup detach test-volume
|
||||
|
||||
systemd-cryptenroll --wipe-slot=tpm2 "$img"
|
||||
PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="$persistent" "$img"
|
||||
systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1
|
||||
systemd-cryptsetup detach test-volume
|
||||
|
||||
tpm2_evictcontrol -c "$persistent"
|
||||
persistent=""
|
||||
rm -f "$primary"
|
||||
|
||||
rm -f "${img:?}"
|
||||
|
||||
if [[ -x "$SD_MEASURE" ]]; then
|
||||
@ -390,4 +450,6 @@ if [[ -x "$SD_TPM2SETUP" ]]; then
|
||||
"$SD_TPM2SETUP" --early=no
|
||||
fi
|
||||
|
||||
cleanup
|
||||
|
||||
touch /testok
|
||||
|
Loading…
x
Reference in New Issue
Block a user