mirror of
https://github.com/systemd/systemd.git
synced 2025-03-19 22:50:17 +03:00
Merge pull request #22563 from grigorig/cryptenroll-tpm2-pin
sd-cryptenroll TPM2 PIN protected unlock
This commit is contained in:
commit
c2c7eea1e9
@ -677,6 +677,14 @@
|
||||
of the current PCR state.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>tpm2-pin=</option></term>
|
||||
|
||||
<listitem><para>Takes a boolean argument, defaults to <literal>false</literal>. Controls whether
|
||||
TPM2 volume unlocking is bound to a PIN in addition to PCRs. Similarly, this option is only useful
|
||||
when TPM2 enrollment metadata is not available.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>token-timeout=</option></term>
|
||||
|
||||
|
@ -299,6 +299,24 @@
|
||||
signatures likely will validate against pre-existing certificates.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--tpm2-with-pin=</option><replaceable>BOOL</replaceable></term>
|
||||
|
||||
<listitem><para>When enrolling a TPM2 device, controls whether to require the user to enter a PIN
|
||||
when unlocking the volume in addition to PCR binding, based on TPM2 policy authentication. Defaults
|
||||
to <literal>no</literal>. Despite being called PIN, any character can be used, not just numbers.
|
||||
</para>
|
||||
|
||||
<para>Note that incorrect PIN entry when unlocking increments the
|
||||
TPM dictionary attack lockout mechanism, and may lock out users for a prolonged time, depending on
|
||||
its configuration. The lockout mechanism is a global property of the TPM,
|
||||
<command>systemd-cryptenroll</command> does not control or configure the lockout mechanism. You may
|
||||
use tpm2-tss tools to inspect or configure the dictionary attack lockout, with
|
||||
<citerefentry><refentrytitle>tpm2_getcap</refentrytitle><manvolnum>1</manvolnum></citerefentry> and
|
||||
<citerefentry><refentrytitle>tpm2_dictionarylockout</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
commands, respectively.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--wipe-slot=</option><arg rep="repeat">SLOT</arg></term>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define SHA256_DIGEST_SIZE 32
|
||||
#include "sha256.h"
|
||||
|
||||
/* Unoptimized implementation based on FIPS 198. 'res' has to be allocated by
|
||||
* the caller. Prefer external OpenSSL functions, and use this only when
|
||||
|
@ -1,7 +1,9 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "ask-password-api.h"
|
||||
#include "cryptenroll-tpm2.h"
|
||||
#include "env-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "json.h"
|
||||
#include "memory-util.h"
|
||||
@ -58,11 +60,78 @@ static int search_policy_hash(
|
||||
return -ENOENT; /* Not found */
|
||||
}
|
||||
|
||||
static int get_pin(char **ret_pin_str, TPM2Flags *ret_flags) {
|
||||
_cleanup_free_ char *pin_str = NULL;
|
||||
int r;
|
||||
TPM2Flags flags = 0;
|
||||
|
||||
assert(ret_pin_str);
|
||||
assert(ret_flags);
|
||||
|
||||
r = getenv_steal_erase("NEWPIN", &pin_str);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to acquire PIN from environment: %m");
|
||||
if (r > 0)
|
||||
flags |= TPM2_FLAGS_USE_PIN;
|
||||
else {
|
||||
for (size_t i = 5;; i--) {
|
||||
_cleanup_strv_free_erase_ char **pin = NULL, **pin2 = NULL;
|
||||
|
||||
if (i <= 0)
|
||||
return log_error_errno(
|
||||
SYNTHETIC_ERRNO(ENOKEY), "Too many attempts, giving up.");
|
||||
|
||||
pin = strv_free_erase(pin);
|
||||
r = ask_password_auto(
|
||||
"Please enter TPM2 PIN:",
|
||||
"drive-harddisk",
|
||||
NULL,
|
||||
"tpm2-pin",
|
||||
"cryptenroll.tpm2-pin",
|
||||
USEC_INFINITY,
|
||||
0,
|
||||
&pin);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to ask for user pin: %m");
|
||||
assert(strv_length(pin) == 1);
|
||||
|
||||
r = ask_password_auto(
|
||||
"Please enter TPM2 PIN (repeat):",
|
||||
"drive-harddisk",
|
||||
NULL,
|
||||
"tpm2-pin",
|
||||
"cryptenroll.tpm2-pin",
|
||||
USEC_INFINITY,
|
||||
0,
|
||||
&pin2);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to ask for user pin: %m");
|
||||
assert(strv_length(pin) == 1);
|
||||
|
||||
if (strv_equal(pin, pin2)) {
|
||||
pin_str = strdup(*pin);
|
||||
if (!pin_str)
|
||||
return log_oom();
|
||||
flags |= TPM2_FLAGS_USE_PIN;
|
||||
break;
|
||||
}
|
||||
|
||||
log_error("PINs didn't match, please try again!");
|
||||
}
|
||||
}
|
||||
|
||||
*ret_flags = flags;
|
||||
*ret_pin_str = TAKE_PTR(pin_str);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int enroll_tpm2(struct crypt_device *cd,
|
||||
const void *volume_key,
|
||||
size_t volume_key_size,
|
||||
const char *device,
|
||||
uint32_t pcr_mask) {
|
||||
uint32_t pcr_mask,
|
||||
bool use_pin) {
|
||||
|
||||
_cleanup_(erase_and_freep) void *secret = NULL, *secret2 = NULL;
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
||||
@ -71,7 +140,9 @@ int enroll_tpm2(struct crypt_device *cd,
|
||||
_cleanup_free_ void *blob = NULL, *hash = NULL;
|
||||
uint16_t pcr_bank, primary_alg;
|
||||
const char *node;
|
||||
_cleanup_(erase_and_freep) char *pin_str = NULL;
|
||||
int r, keyslot;
|
||||
TPM2Flags flags = 0;
|
||||
|
||||
assert(cd);
|
||||
assert(volume_key);
|
||||
@ -80,7 +151,13 @@ int enroll_tpm2(struct crypt_device *cd,
|
||||
|
||||
assert_se(node = crypt_get_device_name(cd));
|
||||
|
||||
r = tpm2_seal(device, pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank, &primary_alg);
|
||||
if (use_pin) {
|
||||
r = get_pin(&pin_str, &flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = tpm2_seal(device, pcr_mask, pin_str, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank, &primary_alg);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -97,7 +174,7 @@ int enroll_tpm2(struct crypt_device *cd,
|
||||
|
||||
/* Quick verification that everything is in order, we are not in a hurry after all. */
|
||||
log_debug("Unsealing for verification...");
|
||||
r = tpm2_unseal(device, pcr_mask, pcr_bank, primary_alg, blob, blob_size, hash, hash_size, &secret2, &secret2_size);
|
||||
r = tpm2_unseal(device, pcr_mask, pcr_bank, primary_alg, blob, blob_size, hash, hash_size, pin_str, &secret2, &secret2_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -123,7 +200,7 @@ int enroll_tpm2(struct crypt_device *cd,
|
||||
if (keyslot < 0)
|
||||
return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node);
|
||||
|
||||
r = tpm2_make_luks2_json(keyslot, pcr_mask, pcr_bank, primary_alg, blob, blob_size, hash, hash_size, &v);
|
||||
r = tpm2_make_luks2_json(keyslot, pcr_mask, pcr_bank, primary_alg, blob, blob_size, hash, hash_size, flags, &v);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m");
|
||||
|
||||
|
@ -7,9 +7,9 @@
|
||||
#include "log.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 pcr_mask);
|
||||
int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t pcr_mask, 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 pcr_mask) {
|
||||
static inline int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t pcr_mask, bool use_pin) {
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"TPM2 key enrollment not supported.");
|
||||
}
|
||||
|
@ -32,6 +32,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_pcr_mask = UINT32_MAX;
|
||||
static bool arg_tpm2_pin = false;
|
||||
static char *arg_node = NULL;
|
||||
static int *arg_wipe_slots = NULL;
|
||||
static size_t arg_n_wipe_slots = 0;
|
||||
@ -100,6 +101,8 @@ static int help(void) {
|
||||
" Enroll a TPM2 device\n"
|
||||
" --tpm2-pcrs=PCR1+PCR2+PCR3+…\n"
|
||||
" Specify TPM2 PCRs to seal against\n"
|
||||
" --tpm2-with-pin=BOOL\n"
|
||||
" Whether to require entering a PIN to unlock the volume\n"
|
||||
" --wipe-slot=SLOT1,SLOT2,…\n"
|
||||
" Wipe specified slots\n"
|
||||
"\nSee the %s for details.\n",
|
||||
@ -121,6 +124,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_FIDO2_DEVICE,
|
||||
ARG_TPM2_DEVICE,
|
||||
ARG_TPM2_PCRS,
|
||||
ARG_TPM2_PIN,
|
||||
ARG_WIPE_SLOT,
|
||||
ARG_FIDO2_WITH_PIN,
|
||||
ARG_FIDO2_WITH_UP,
|
||||
@ -139,6 +143,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-pcrs", required_argument, NULL, ARG_TPM2_PCRS },
|
||||
{ "tpm2-with-pin", required_argument, NULL, ARG_TPM2_PIN },
|
||||
{ "wipe-slot", required_argument, NULL, ARG_WIPE_SLOT },
|
||||
{}
|
||||
};
|
||||
@ -301,6 +306,14 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
case ARG_TPM2_PIN: {
|
||||
r = parse_boolean_argument("--tpm2-with-pin=", optarg, &arg_tpm2_pin);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ARG_WIPE_SLOT: {
|
||||
const char *p = optarg;
|
||||
|
||||
@ -558,7 +571,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);
|
||||
slot = enroll_tpm2(cd, vk, vks, arg_tpm2_device, arg_tpm2_pcr_mask, arg_tpm2_pin);
|
||||
break;
|
||||
|
||||
case _ENROLL_TYPE_INVALID:
|
||||
|
@ -6,8 +6,10 @@
|
||||
#include "cryptsetup-token.h"
|
||||
#include "cryptsetup-token-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "json.h"
|
||||
#include "luks2-tpm2.h"
|
||||
#include "memory-util.h"
|
||||
#include "strv.h"
|
||||
#include "tpm2-util.h"
|
||||
#include "version.h"
|
||||
|
||||
@ -78,7 +80,8 @@ _public_ int cryptsetup_token_open(
|
||||
if (usrptr)
|
||||
params = *(systemd_tpm2_plugin_params *)usrptr;
|
||||
|
||||
r = parse_luks2_tpm2_data(json, params.search_pcr_mask, &pcr_mask, &pcr_bank, &primary_alg, &base64_blob, &hex_policy_hash);
|
||||
TPM2Flags flags = 0;
|
||||
r = parse_luks2_tpm2_data(json, params.search_pcr_mask, &pcr_mask, &pcr_bank, &primary_alg, &base64_blob, &hex_policy_hash, &flags);
|
||||
if (r < 0)
|
||||
return log_debug_open_error(cd, r);
|
||||
|
||||
@ -101,6 +104,7 @@ _public_ int cryptsetup_token_open(
|
||||
blob_size,
|
||||
policy_hash,
|
||||
policy_hash_size,
|
||||
flags,
|
||||
&decrypted_key,
|
||||
&decrypted_key_size);
|
||||
if (r < 0)
|
||||
@ -135,6 +139,7 @@ _public_ void cryptsetup_token_dump(
|
||||
const char *json /* validated 'systemd-tpm2' token if cryptsetup_token_validate is defined */) {
|
||||
|
||||
int r;
|
||||
TPM2Flags flags = 0;
|
||||
uint32_t pcr_mask;
|
||||
uint16_t pcr_bank, primary_alg;
|
||||
size_t decoded_blob_size;
|
||||
@ -144,7 +149,7 @@ _public_ void cryptsetup_token_dump(
|
||||
|
||||
assert(json);
|
||||
|
||||
r = parse_luks2_tpm2_data(json, UINT32_MAX, &pcr_mask, &pcr_bank, &primary_alg, &base64_blob, &hex_policy_hash);
|
||||
r = parse_luks2_tpm2_data(json, UINT32_MAX, &pcr_mask, &pcr_bank, &primary_alg, &base64_blob, &hex_policy_hash, &flags);
|
||||
if (r < 0)
|
||||
return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " metadata: %m.");
|
||||
|
||||
@ -171,6 +176,7 @@ _public_ void cryptsetup_token_dump(
|
||||
crypt_log(cd, "\ttpm2-primary-alg: %s\n", strna(tpm2_primary_alg_to_string(primary_alg)));
|
||||
crypt_log(cd, "\ttpm2-blob: %s\n", blob_str);
|
||||
crypt_log(cd, "\ttpm2-policy-hash:" CRYPT_DUMP_LINE_SEP "%s\n", policy_hash_str);
|
||||
crypt_log(cd, "\ttpm2-pin: %s\n", true_false(flags & TPM2_FLAGS_USE_PIN));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -268,5 +274,13 @@ _public_ int cryptsetup_token_validate(
|
||||
if (r < 0)
|
||||
return crypt_log_debug_errno(cd, r, "Invalid base64 data in 'tpm2-policy-hash' field: %m");
|
||||
|
||||
w = json_variant_by_key(v, "tpm2-pin");
|
||||
if (w) {
|
||||
if (!json_variant_is_boolean(w)) {
|
||||
crypt_log_debug(cd, "TPM2 PIN policy is not a boolean.");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,11 +1,15 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "ask-password-api.h"
|
||||
#include "env-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "json.h"
|
||||
#include "log.h"
|
||||
#include "luks2-tpm2.h"
|
||||
#include "parse-util.h"
|
||||
#include "random-util.h"
|
||||
#include "strv.h"
|
||||
#include "tpm2-util.h"
|
||||
|
||||
int acquire_luks2_key(
|
||||
@ -17,10 +21,12 @@ int acquire_luks2_key(
|
||||
size_t key_data_size,
|
||||
const void *policy_hash,
|
||||
size_t policy_hash_size,
|
||||
TPM2Flags flags,
|
||||
void **ret_decrypted_key,
|
||||
size_t *ret_decrypted_key_size) {
|
||||
|
||||
_cleanup_free_ char *auto_device = NULL;
|
||||
_cleanup_(erase_and_freep) char *pin_str = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret_decrypted_key);
|
||||
@ -36,12 +42,22 @@ int acquire_luks2_key(
|
||||
device = auto_device;
|
||||
}
|
||||
|
||||
r = getenv_steal_erase("PIN", &pin_str);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to acquire PIN from environment: %m");
|
||||
if (!r) {
|
||||
/* PIN entry is not supported by plugin, let it fallback, possibly to sd-cryptsetup's
|
||||
* internal handling. */
|
||||
if (flags & TPM2_FLAGS_USE_PIN)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return tpm2_unseal(
|
||||
device,
|
||||
pcr_mask, pcr_bank,
|
||||
primary_alg,
|
||||
key_data, key_data_size,
|
||||
policy_hash, policy_hash_size,
|
||||
policy_hash, policy_hash_size, pin_str,
|
||||
ret_decrypted_key, ret_decrypted_key_size);
|
||||
}
|
||||
|
||||
@ -53,7 +69,8 @@ int parse_luks2_tpm2_data(
|
||||
uint16_t *ret_pcr_bank,
|
||||
uint16_t *ret_primary_alg,
|
||||
char **ret_base64_blob,
|
||||
char **ret_hex_policy_hash) {
|
||||
char **ret_hex_policy_hash,
|
||||
TPM2Flags *ret_flags) {
|
||||
|
||||
int r;
|
||||
JsonVariant *w, *e;
|
||||
@ -61,6 +78,7 @@ int parse_luks2_tpm2_data(
|
||||
uint16_t pcr_bank = UINT16_MAX, primary_alg = TPM2_ALG_ECC;
|
||||
_cleanup_free_ char *base64_blob = NULL, *hex_policy_hash = NULL;
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
||||
TPM2Flags flags = 0;
|
||||
|
||||
assert(json);
|
||||
assert(ret_pcr_mask);
|
||||
@ -138,11 +156,21 @@ int parse_luks2_tpm2_data(
|
||||
if (!hex_policy_hash)
|
||||
return -ENOMEM;
|
||||
|
||||
w = json_variant_by_key(v, "tpm2-pin");
|
||||
if (w) {
|
||||
if (!json_variant_is_boolean(w))
|
||||
return -EINVAL;
|
||||
|
||||
if (json_variant_boolean(w))
|
||||
flags |= TPM2_FLAGS_USE_PIN;
|
||||
}
|
||||
|
||||
*ret_pcr_mask = pcr_mask;
|
||||
*ret_pcr_bank = pcr_bank;
|
||||
*ret_primary_alg = primary_alg;
|
||||
*ret_base64_blob = TAKE_PTR(base64_blob);
|
||||
*ret_hex_policy_hash = TAKE_PTR(hex_policy_hash);
|
||||
*ret_flags = flags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tpm2-util.h"
|
||||
|
||||
struct crypt_device;
|
||||
|
||||
int acquire_luks2_key(
|
||||
@ -13,6 +15,7 @@ int acquire_luks2_key(
|
||||
size_t key_data_size,
|
||||
const void *policy_hash,
|
||||
size_t policy_hash_size,
|
||||
TPM2Flags flags,
|
||||
void **ret_decrypted_key,
|
||||
size_t *ret_decrypted_key_size);
|
||||
|
||||
@ -23,4 +26,5 @@ int parse_luks2_tpm2_data(
|
||||
uint16_t *ret_pcr_bank,
|
||||
uint16_t *ret_primary_alg,
|
||||
char **ret_base64_blob,
|
||||
char **ret_hex_policy_hash);
|
||||
char **ret_hex_policy_hash,
|
||||
TPM2Flags *ret_flags);
|
||||
|
@ -1,7 +1,9 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "ask-password-api.h"
|
||||
#include "cryptsetup-tpm2.h"
|
||||
#include "env-util.h"
|
||||
#include "fileio.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "json.h"
|
||||
@ -9,6 +11,47 @@
|
||||
#include "random-util.h"
|
||||
#include "tpm2-util.h"
|
||||
|
||||
static int get_pin(usec_t until, AskPasswordFlags ask_password_flags, bool headless, char **ret_pin_str) {
|
||||
_cleanup_free_ char *pin_str = NULL;
|
||||
_cleanup_strv_free_erase_ char **pin = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret_pin_str);
|
||||
|
||||
r = getenv_steal_erase("PIN", &pin_str);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to acquire PIN from environment: %m");
|
||||
if (!r) {
|
||||
if (headless)
|
||||
return log_error_errno(
|
||||
SYNTHETIC_ERRNO(ENOPKG),
|
||||
"PIN querying disabled via 'headless' option. "
|
||||
"Use the '$PIN' environment variable.");
|
||||
|
||||
pin = strv_free_erase(pin);
|
||||
r = ask_password_auto(
|
||||
"Please enter TPM2 PIN:",
|
||||
"drive-harddisk",
|
||||
NULL,
|
||||
"tpm2-pin",
|
||||
"cryptsetup.tpm2-pin",
|
||||
until,
|
||||
ask_password_flags,
|
||||
&pin);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to ask for user pin: %m");
|
||||
assert(strv_length(pin) == 1);
|
||||
|
||||
pin_str = strdup(pin[0]);
|
||||
if (!pin_str)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
*ret_pin_str = TAKE_PTR(pin_str);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int acquire_tpm2_key(
|
||||
const char *volume_name,
|
||||
const char *device,
|
||||
@ -22,6 +65,10 @@ int acquire_tpm2_key(
|
||||
size_t key_data_size,
|
||||
const void *policy_hash,
|
||||
size_t policy_hash_size,
|
||||
TPM2Flags flags,
|
||||
usec_t until,
|
||||
bool headless,
|
||||
AskPasswordFlags ask_password_flags,
|
||||
void **ret_decrypted_key,
|
||||
size_t *ret_decrypted_key_size) {
|
||||
|
||||
@ -64,7 +111,51 @@ int acquire_tpm2_key(
|
||||
blob = loaded_blob;
|
||||
}
|
||||
|
||||
return tpm2_unseal(device, pcr_mask, pcr_bank, primary_alg, blob, blob_size, policy_hash, policy_hash_size, ret_decrypted_key, ret_decrypted_key_size);
|
||||
if (!(flags & TPM2_FLAGS_USE_PIN))
|
||||
return tpm2_unseal(
|
||||
device,
|
||||
pcr_mask,
|
||||
pcr_bank,
|
||||
primary_alg,
|
||||
blob,
|
||||
blob_size,
|
||||
policy_hash,
|
||||
policy_hash_size,
|
||||
NULL,
|
||||
ret_decrypted_key,
|
||||
ret_decrypted_key_size);
|
||||
|
||||
for (int i = 5;; i--) {
|
||||
_cleanup_(erase_and_freep) char *pin_str = NULL;
|
||||
|
||||
if (i <= 0)
|
||||
return -EACCES;
|
||||
|
||||
r = get_pin(until, ask_password_flags, headless, &pin_str);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = tpm2_unseal(
|
||||
device,
|
||||
pcr_mask,
|
||||
pcr_bank,
|
||||
primary_alg,
|
||||
blob,
|
||||
blob_size,
|
||||
policy_hash,
|
||||
policy_hash_size,
|
||||
pin_str,
|
||||
ret_decrypted_key,
|
||||
ret_decrypted_key_size);
|
||||
/* We get this error in case there is an authentication policy mismatch. This should
|
||||
* not happen, but this avoids confusing behavior, just in case. */
|
||||
if (IN_SET(r, -EPERM, -ENOLCK))
|
||||
return r;
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
int find_tpm2_auto_data(
|
||||
@ -79,11 +170,13 @@ int find_tpm2_auto_data(
|
||||
void **ret_policy_hash,
|
||||
size_t *ret_policy_hash_size,
|
||||
int *ret_keyslot,
|
||||
int *ret_token) {
|
||||
int *ret_token,
|
||||
TPM2Flags *ret_flags) {
|
||||
|
||||
_cleanup_free_ void *blob = NULL, *policy_hash = NULL;
|
||||
size_t blob_size = 0, policy_hash_size = 0;
|
||||
int r, keyslot = -1, token = -1;
|
||||
TPM2Flags flags = 0;
|
||||
uint32_t pcr_mask = 0;
|
||||
uint16_t pcr_bank = UINT16_MAX; /* default: pick automatically */
|
||||
uint16_t primary_alg = TPM2_ALG_ECC; /* ECC was the only supported algorithm in systemd < 250, use that as implied default, for compatibility */
|
||||
@ -196,6 +289,16 @@ int find_tpm2_auto_data(
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Invalid base64 data in 'tpm2-policy-hash' field.");
|
||||
|
||||
w = json_variant_by_key(v, "tpm2-pin");
|
||||
if (w) {
|
||||
if (!json_variant_is_boolean(w))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"TPM2 PIN policy is not a boolean.");
|
||||
|
||||
if (json_variant_boolean(w))
|
||||
flags |= TPM2_FLAGS_USE_PIN;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -215,6 +318,7 @@ int find_tpm2_auto_data(
|
||||
*ret_token = token;
|
||||
*ret_pcr_bank = pcr_bank;
|
||||
*ret_primary_alg = primary_alg;
|
||||
*ret_flags = flags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -3,9 +3,11 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "ask-password-api.h"
|
||||
#include "cryptsetup-util.h"
|
||||
#include "log.h"
|
||||
#include "time-util.h"
|
||||
#include "tpm2-util.h"
|
||||
|
||||
#if HAVE_TPM2
|
||||
|
||||
@ -22,6 +24,10 @@ int acquire_tpm2_key(
|
||||
size_t key_data_size,
|
||||
const void *policy_hash,
|
||||
size_t policy_hash_size,
|
||||
TPM2Flags flags,
|
||||
usec_t until,
|
||||
bool headless,
|
||||
AskPasswordFlags ask_password_flags,
|
||||
void **ret_decrypted_key,
|
||||
size_t *ret_decrypted_key_size);
|
||||
|
||||
@ -37,7 +43,8 @@ int find_tpm2_auto_data(
|
||||
void **ret_policy_hash,
|
||||
size_t *ret_policy_hash_size,
|
||||
int *ret_keyslot,
|
||||
int *ret_token);
|
||||
int *ret_token,
|
||||
TPM2Flags *ret_flags);
|
||||
|
||||
#else
|
||||
|
||||
@ -54,6 +61,10 @@ static inline int acquire_tpm2_key(
|
||||
size_t key_data_size,
|
||||
const void *policy_hash,
|
||||
size_t policy_hash_size,
|
||||
TPM2Flags flags,
|
||||
usec_t until,
|
||||
bool headless,
|
||||
AskPasswordFlags ask_password_flags,
|
||||
void **ret_decrypted_key,
|
||||
size_t *ret_decrypted_key_size) {
|
||||
|
||||
@ -73,7 +84,8 @@ static inline int find_tpm2_auto_data(
|
||||
void **ret_policy_hash,
|
||||
size_t *ret_policy_hash_size,
|
||||
int *ret_keyslot,
|
||||
int *ret_token) {
|
||||
int *ret_token,
|
||||
TPM2Flags *ret_flags) {
|
||||
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"TPM2 support not available.");
|
||||
|
@ -82,6 +82,7 @@ static char *arg_fido2_rp_id = NULL;
|
||||
static char *arg_tpm2_device = NULL;
|
||||
static bool arg_tpm2_device_auto = false;
|
||||
static uint32_t arg_tpm2_pcr_mask = UINT32_MAX;
|
||||
static bool arg_tpm2_pin = false;
|
||||
static bool arg_headless = false;
|
||||
static usec_t arg_token_timeout_usec = 30*USEC_PER_SEC;
|
||||
|
||||
@ -387,6 +388,16 @@ static int parse_one_option(const char *option) {
|
||||
arg_tpm2_pcr_mask |= mask;
|
||||
}
|
||||
|
||||
} else if ((val = startswith(option, "tpm2-pin="))) {
|
||||
|
||||
r = parse_boolean(val);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
|
||||
return 0;
|
||||
}
|
||||
|
||||
arg_tpm2_pin = r;
|
||||
|
||||
} else if ((val = startswith(option, "try-empty-password="))) {
|
||||
|
||||
r = parse_boolean(val);
|
||||
@ -1301,9 +1312,15 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
|
||||
key_file, arg_keyfile_size, arg_keyfile_offset,
|
||||
key_data, key_data_size,
|
||||
NULL, 0, /* we don't know the policy hash */
|
||||
arg_tpm2_pin,
|
||||
until,
|
||||
arg_headless,
|
||||
arg_ask_password_flags,
|
||||
&decrypted_key, &decrypted_key_size);
|
||||
if (r >= 0)
|
||||
break;
|
||||
if (IN_SET(r, -EACCES, -ENOLCK))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EAGAIN), "TPM2 PIN unlock failed, falling back to traditional unlocking.");
|
||||
if (ERRNO_IS_NOT_SUPPORTED(r)) /* TPM2 support not compiled in? */
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), "TPM2 support not available, falling back to traditional unlocking.");
|
||||
if (r != -EAGAIN) /* EAGAIN means: no tpm2 chip found */
|
||||
@ -1335,6 +1352,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
|
||||
for (;;) {
|
||||
uint32_t pcr_mask;
|
||||
uint16_t pcr_bank, primary_alg;
|
||||
TPM2Flags tpm2_flags;
|
||||
|
||||
r = find_tpm2_auto_data(
|
||||
cd,
|
||||
@ -1346,7 +1364,8 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
|
||||
&blob, &blob_size,
|
||||
&policy_hash, &policy_hash_size,
|
||||
&keyslot,
|
||||
&token);
|
||||
&token,
|
||||
&tpm2_flags);
|
||||
if (r == -ENXIO)
|
||||
/* No further TPM2 tokens found in the LUKS2 header. */
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
|
||||
@ -1369,7 +1388,13 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
|
||||
NULL, 0, 0, /* no key file */
|
||||
blob, blob_size,
|
||||
policy_hash, policy_hash_size,
|
||||
tpm2_flags,
|
||||
until,
|
||||
arg_headless,
|
||||
arg_ask_password_flags,
|
||||
&decrypted_key, &decrypted_key_size);
|
||||
if (IN_SET(r, -EACCES, -ENOLCK))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EAGAIN), "TPM2 PIN unlock failed, falling back to traditional unlocking.");
|
||||
if (r != -EPERM)
|
||||
break;
|
||||
|
||||
|
@ -8,6 +8,8 @@
|
||||
|
||||
#include "types-fundamental.h"
|
||||
|
||||
#define SHA256_DIGEST_SIZE 32
|
||||
|
||||
struct sha256_ctx {
|
||||
uint32_t H[8];
|
||||
|
||||
|
@ -2656,7 +2656,7 @@ static int partition_encrypt(
|
||||
uint16_t pcr_bank, primary_alg;
|
||||
int keyslot;
|
||||
|
||||
r = tpm2_seal(arg_tpm2_device, arg_tpm2_pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank, &primary_alg);
|
||||
r = tpm2_seal(arg_tpm2_device, arg_tpm2_pcr_mask, NULL, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank, &primary_alg);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to seal to TPM2: %m");
|
||||
|
||||
@ -2678,7 +2678,7 @@ static int partition_encrypt(
|
||||
if (keyslot < 0)
|
||||
return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node);
|
||||
|
||||
r = tpm2_make_luks2_json(keyslot, arg_tpm2_pcr_mask, pcr_bank, primary_alg, blob, blob_size, hash, hash_size, &v);
|
||||
r = tpm2_make_luks2_json(keyslot, arg_tpm2_pcr_mask, pcr_bank, primary_alg, blob, blob_size, hash, hash_size, 0, &v);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m");
|
||||
|
||||
|
@ -534,6 +534,7 @@ int encrypt_credential_and_warn(
|
||||
|
||||
r = tpm2_seal(tpm2_device,
|
||||
tpm2_pcr_mask,
|
||||
NULL,
|
||||
&tpm2_key,
|
||||
&tpm2_key_size,
|
||||
&tpm2_blob,
|
||||
@ -803,6 +804,7 @@ int decrypt_credential_and_warn(
|
||||
le32toh(t->blob_size),
|
||||
t->policy_hash_and_blob + le32toh(t->blob_size),
|
||||
le32toh(t->policy_hash_size),
|
||||
NULL,
|
||||
&tpm2_key,
|
||||
&tpm2_key_size);
|
||||
if (r < 0)
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "hexdecoct.h"
|
||||
#include "memory-util.h"
|
||||
#include "random-util.h"
|
||||
#include "sha256.h"
|
||||
#include "time-util.h"
|
||||
|
||||
static void *libtss2_esys_dl = NULL;
|
||||
@ -30,10 +31,12 @@ TSS2_RC (*sym_Esys_GetRandom)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_
|
||||
TSS2_RC (*sym_Esys_Initialize)(ESYS_CONTEXT **esys_context, TSS2_TCTI_CONTEXT *tcti, TSS2_ABI_VERSION *abiVersion) = NULL;
|
||||
TSS2_RC (*sym_Esys_Load)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_PRIVATE *inPrivate, const TPM2B_PUBLIC *inPublic, ESYS_TR *objectHandle) = NULL;
|
||||
TSS2_RC (*sym_Esys_PCR_Read)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1,ESYS_TR shandle2, ESYS_TR shandle3, const TPML_PCR_SELECTION *pcrSelectionIn, UINT32 *pcrUpdateCounter, TPML_PCR_SELECTION **pcrSelectionOut, TPML_DIGEST **pcrValues);
|
||||
TSS2_RC (*sym_Esys_PolicyAuthValue)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3) = NULL;
|
||||
TSS2_RC (*sym_Esys_PolicyGetDigest)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_DIGEST **policyDigest) = NULL;
|
||||
TSS2_RC (*sym_Esys_PolicyPCR)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *pcrDigest, const TPML_PCR_SELECTION *pcrs) = NULL;
|
||||
TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle) = NULL;
|
||||
TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType) = NULL;
|
||||
TSS2_RC (*sym_Esys_TR_SetAuth)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_AUTH const *authValue) = NULL;
|
||||
TSS2_RC (*sym_Esys_Unseal)(ESYS_CONTEXT *esysContext, ESYS_TR itemHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_SENSITIVE_DATA **outData) = NULL;
|
||||
|
||||
const char* (*sym_Tss2_RC_Decode)(TSS2_RC rc) = NULL;
|
||||
@ -58,10 +61,12 @@ int dlopen_tpm2(void) {
|
||||
DLSYM_ARG(Esys_Initialize),
|
||||
DLSYM_ARG(Esys_Load),
|
||||
DLSYM_ARG(Esys_PCR_Read),
|
||||
DLSYM_ARG(Esys_PolicyAuthValue),
|
||||
DLSYM_ARG(Esys_PolicyGetDigest),
|
||||
DLSYM_ARG(Esys_PolicyPCR),
|
||||
DLSYM_ARG(Esys_StartAuthSession),
|
||||
DLSYM_ARG(Esys_Startup),
|
||||
DLSYM_ARG(Esys_TR_SetAuth),
|
||||
DLSYM_ARG(Esys_Unseal));
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -594,6 +599,7 @@ static int tpm2_make_pcr_session(
|
||||
ESYS_CONTEXT *c,
|
||||
uint32_t pcr_mask,
|
||||
uint16_t pcr_bank, /* If UINT16_MAX, pick best bank automatically, otherwise specify bank explicitly. */
|
||||
bool use_pin,
|
||||
ESYS_TR *ret_session,
|
||||
TPM2B_DIGEST **ret_policy_digest,
|
||||
TPMI_ALG_HASH *ret_pcr_bank) {
|
||||
@ -669,6 +675,21 @@ static int tpm2_make_pcr_session(
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (use_pin) {
|
||||
rc = sym_Esys_PolicyAuthValue(
|
||||
c,
|
||||
session,
|
||||
ESYS_TR_NONE,
|
||||
ESYS_TR_NONE,
|
||||
ESYS_TR_NONE);
|
||||
if (rc != TSS2_RC_SUCCESS) {
|
||||
r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||||
"Failed to add authValue policy to TPM: %s",
|
||||
sym_Tss2_RC_Decode(rc));
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG_LOGGING || ret_policy_digest) {
|
||||
log_debug("Acquiring policy digest.");
|
||||
|
||||
@ -717,9 +738,22 @@ finish:
|
||||
return r;
|
||||
}
|
||||
|
||||
static void hash_pin(const char *pin, size_t len, uint8_t ret_digest[static SHA256_DIGEST_SIZE]) {
|
||||
struct sha256_ctx hash;
|
||||
|
||||
assert(pin);
|
||||
|
||||
sha256_init_ctx(&hash);
|
||||
sha256_process_bytes(pin, len, &hash);
|
||||
sha256_finish_ctx(&hash, ret_digest);
|
||||
|
||||
explicit_bzero_safe(&hash, sizeof(hash));
|
||||
}
|
||||
|
||||
int tpm2_seal(
|
||||
const char *device,
|
||||
uint32_t pcr_mask,
|
||||
const char *pin,
|
||||
void **ret_secret,
|
||||
size_t *ret_secret_size,
|
||||
void **ret_blob,
|
||||
@ -782,7 +816,8 @@ int tpm2_seal(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = tpm2_make_pcr_session(c.esys_context, pcr_mask, UINT16_MAX, NULL, &policy_digest, &pcr_bank);
|
||||
r = tpm2_make_pcr_session(c.esys_context, pcr_mask, UINT16_MAX, !!pin, NULL, &policy_digest,
|
||||
&pcr_bank);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
@ -813,6 +848,10 @@ int tpm2_seal(
|
||||
.size = sizeof(hmac_sensitive.sensitive),
|
||||
.sensitive.data.size = 32,
|
||||
};
|
||||
if (pin) {
|
||||
hash_pin(pin, strlen(pin), hmac_sensitive.sensitive.userAuth.buffer);
|
||||
hmac_sensitive.sensitive.userAuth.size = SHA256_DIGEST_SIZE;
|
||||
}
|
||||
assert(sizeof(hmac_sensitive.sensitive.data.buffer) >= hmac_sensitive.sensitive.data.size);
|
||||
|
||||
(void) tpm2_credit_random(c.esys_context);
|
||||
@ -910,6 +949,7 @@ int tpm2_seal(
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
explicit_bzero_safe(&hmac_sensitive, sizeof(hmac_sensitive));
|
||||
primary = flush_context_verbose(c.esys_context, primary);
|
||||
return r;
|
||||
}
|
||||
@ -923,6 +963,7 @@ int tpm2_unseal(
|
||||
size_t blob_size,
|
||||
const void *known_policy_hash,
|
||||
size_t known_policy_hash_size,
|
||||
const char *pin,
|
||||
void **ret_secret,
|
||||
size_t *ret_secret_size) {
|
||||
|
||||
@ -978,7 +1019,7 @@ int tpm2_unseal(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = tpm2_make_pcr_session(c.esys_context, pcr_mask, pcr_bank, &session, &policy_digest, NULL);
|
||||
r = tpm2_make_pcr_session(c.esys_context, pcr_mask, pcr_bank, !!pin, &session, &policy_digest, NULL);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
@ -1005,11 +1046,38 @@ int tpm2_unseal(
|
||||
&public,
|
||||
&hmac_key);
|
||||
if (rc != TSS2_RC_SUCCESS) {
|
||||
r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||||
"Failed to load HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
|
||||
/* If we're in dictionary attack lockout mode, we should see a lockout error here, which we
|
||||
* need to translate for the caller. */
|
||||
if (rc == TPM2_RC_LOCKOUT)
|
||||
r = log_error_errno(
|
||||
SYNTHETIC_ERRNO(ENOLCK),
|
||||
"TPM2 device is in dictionary attack lockout mode.");
|
||||
else
|
||||
r = log_error_errno(
|
||||
SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||||
"Failed to load HMAC key in TPM: %s",
|
||||
sym_Tss2_RC_Decode(rc));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (pin) {
|
||||
TPM2B_AUTH auth = {
|
||||
.size = SHA256_DIGEST_SIZE
|
||||
};
|
||||
|
||||
hash_pin(pin, strlen(pin), auth.buffer);
|
||||
|
||||
rc = sym_Esys_TR_SetAuth(c.esys_context, hmac_key, &auth);
|
||||
explicit_bzero_safe(&auth, sizeof(auth));
|
||||
if (rc != TSS2_RC_SUCCESS) {
|
||||
r = log_error_errno(
|
||||
SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||||
"Failed to load PIN in TPM: %s",
|
||||
sym_Tss2_RC_Decode(rc));
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
log_debug("Unsealing HMAC key.");
|
||||
|
||||
rc = sym_Esys_Unseal(
|
||||
@ -1223,6 +1291,7 @@ int tpm2_make_luks2_json(
|
||||
size_t blob_size,
|
||||
const void *policy_hash,
|
||||
size_t policy_hash_size,
|
||||
TPM2Flags flags,
|
||||
JsonVariant **ret) {
|
||||
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *a = NULL;
|
||||
@ -1263,7 +1332,9 @@ int tpm2_make_luks2_json(
|
||||
JSON_BUILD_PAIR("tpm2-pcrs", JSON_BUILD_VARIANT(a)),
|
||||
JSON_BUILD_PAIR_CONDITION(!!tpm2_pcr_bank_to_string(pcr_bank), "tpm2-pcr-bank", JSON_BUILD_STRING(tpm2_pcr_bank_to_string(pcr_bank))),
|
||||
JSON_BUILD_PAIR_CONDITION(!!tpm2_primary_alg_to_string(primary_alg), "tpm2-primary-alg", JSON_BUILD_STRING(tpm2_primary_alg_to_string(primary_alg))),
|
||||
JSON_BUILD_PAIR("tpm2-policy-hash", JSON_BUILD_HEX(policy_hash, policy_hash_size))));
|
||||
JSON_BUILD_PAIR("tpm2-policy-hash", JSON_BUILD_HEX(policy_hash, policy_hash_size)),
|
||||
JSON_BUILD_PAIR("tpm2-pin", JSON_BUILD_BOOLEAN(flags & TPM2_FLAGS_USE_PIN)))
|
||||
);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -1,9 +1,15 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "json.h"
|
||||
#include "macro.h"
|
||||
|
||||
typedef enum TPM2Flags {
|
||||
TPM2_FLAGS_USE_PIN = 1 << 0,
|
||||
} TPM2Flags;
|
||||
|
||||
#if HAVE_TPM2
|
||||
|
||||
#include <tss2/tss2_esys.h>
|
||||
@ -20,10 +26,12 @@ extern TSS2_RC (*sym_Esys_GetRandom)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1
|
||||
extern TSS2_RC (*sym_Esys_Initialize)(ESYS_CONTEXT **esys_context, TSS2_TCTI_CONTEXT *tcti, TSS2_ABI_VERSION *abiVersion);
|
||||
extern TSS2_RC (*sym_Esys_Load)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_PRIVATE *inPrivate, const TPM2B_PUBLIC *inPublic, ESYS_TR *objectHandle);
|
||||
extern TSS2_RC (*sym_Esys_PCR_Read)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1,ESYS_TR shandle2, ESYS_TR shandle3, const TPML_PCR_SELECTION *pcrSelectionIn, UINT32 *pcrUpdateCounter, TPML_PCR_SELECTION **pcrSelectionOut, TPML_DIGEST **pcrValues);
|
||||
extern TSS2_RC (*sym_Esys_PolicyAuthValue)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3);
|
||||
extern TSS2_RC (*sym_Esys_PolicyGetDigest)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_DIGEST **policyDigest);
|
||||
extern TSS2_RC (*sym_Esys_PolicyPCR)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *pcrDigest, const TPML_PCR_SELECTION *pcrs);
|
||||
extern TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle);
|
||||
extern TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType);
|
||||
extern TSS2_RC (*sym_Esys_TR_SetAuth)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_AUTH const *authValue);
|
||||
extern TSS2_RC (*sym_Esys_Unseal)(ESYS_CONTEXT *esysContext, ESYS_TR itemHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_SENSITIVE_DATA **outData);
|
||||
|
||||
extern const char* (*sym_Tss2_RC_Decode)(TSS2_RC rc);
|
||||
@ -35,8 +43,8 @@ extern TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], siz
|
||||
|
||||
int dlopen_tpm2(void);
|
||||
|
||||
int tpm2_seal(const char *device, uint32_t pcr_mask, 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);
|
||||
int tpm2_unseal(const char *device, uint32_t pcr_mask, uint16_t pcr_bank, uint16_t primary_alg, const void *blob, size_t blob_size, const void *pcr_hash, size_t pcr_hash_size, void **ret_secret, size_t *ret_secret_size);
|
||||
int tpm2_seal(const char *device, uint32_t 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);
|
||||
int tpm2_unseal(const char *device, uint32_t pcr_mask, uint16_t pcr_bank, uint16_t primary_alg, const void *blob, size_t blob_size, const void *pcr_hash, size_t pcr_hash_size, const char *pin, void **ret_secret, size_t *ret_secret_size);
|
||||
|
||||
#endif
|
||||
|
||||
@ -45,7 +53,7 @@ int tpm2_find_device_auto(int log_level, char **ret);
|
||||
|
||||
int tpm2_parse_pcrs(const char *s, uint32_t *ret);
|
||||
|
||||
int tpm2_make_luks2_json(int keyslot, uint32_t pcr_mask, uint16_t pcr_bank, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, JsonVariant **ret);
|
||||
int tpm2_make_luks2_json(int keyslot, uint32_t pcr_mask, uint16_t pcr_bank, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, TPM2Flags flags, JsonVariant **ret);
|
||||
|
||||
#define TPM2_PCRS_MAX 24
|
||||
|
||||
|
6
test/TEST-70-TPM2/Makefile
Normal file
6
test/TEST-70-TPM2/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
all setup run clean clean-again:
|
||||
@TEST_BASE_DIR=../ ./test.sh --$@
|
||||
|
||||
.PHONY: all setup run clean clean-again
|
40
test/TEST-70-TPM2/test.sh
Executable file
40
test/TEST-70-TPM2/test.sh
Executable file
@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -e
|
||||
|
||||
TEST_DESCRIPTION="cryptenroll/cryptsetup with TPM2 devices"
|
||||
IMAGE_NAME="tpm2"
|
||||
TEST_NO_NSPAWN=1
|
||||
TEST_REQUIRE_INSTALL_TESTS=0
|
||||
|
||||
# shellcheck source=test/test-functions
|
||||
. "${TEST_BASE_DIR:?}/test-functions"
|
||||
|
||||
command -v swtpm >/dev/null 2>&1 || exit 0
|
||||
command -v tpm2_pcrextend >/dev/null 2>&1 || exit 0
|
||||
|
||||
test_append_files() {
|
||||
(
|
||||
local workspace="${1:?}"
|
||||
|
||||
instmods tpm tpm_tis tpm_ibmvtpm
|
||||
install_dmevent
|
||||
generate_module_dependencies
|
||||
inst_binary tpm2_pcrextend
|
||||
)
|
||||
}
|
||||
|
||||
machine="$(uname -m)"
|
||||
tpmdevice="tpm-tis"
|
||||
if [ "$machine" = "ppc64le" ]; then
|
||||
# tpm-spapr support was introduced in qemu 5.0.0. Skip test for old qemu versions.
|
||||
qemu_min_version "5.0.0" || exit 0
|
||||
tpmdevice="tpm-spapr"
|
||||
fi
|
||||
|
||||
tpmstate=$(mktemp -d)
|
||||
swtpm socket --tpm2 --tpmstate dir="$tpmstate" --ctrl type=unixio,path="$tpmstate/sock" &
|
||||
trap 'kill %%; rm -rf $tpmstate' SIGINT EXIT
|
||||
QEMU_OPTIONS="-chardev socket,id=chrtpm,path=$tpmstate/sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device $tpmdevice,tpmdev=tpm0"
|
||||
|
||||
do_test "$@"
|
@ -1213,7 +1213,7 @@ install_missing_libraries() {
|
||||
local lib path
|
||||
# A number of dependencies is now optional via dlopen, so the install
|
||||
# script will not pick them up, since it looks at linkage.
|
||||
for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu libfido2 libbpf libelf libdw; do
|
||||
for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu tss2-tcti-device libfido2 libbpf libelf libdw; do
|
||||
ddebug "Searching for $lib via pkg-config"
|
||||
if pkg-config --exists "$lib"; then
|
||||
path="$(pkg-config --variable=libdir "$lib")"
|
||||
|
7
test/units/testsuite-70.service
Normal file
7
test/units/testsuite-70.service
Normal file
@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[Unit]
|
||||
Description=TEST-70-TPM2
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
|
48
test/units/testsuite-70.sh
Executable file
48
test/units/testsuite-70.sh
Executable file
@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -ex
|
||||
|
||||
export SYSTEMD_LOG_LEVEL=debug
|
||||
|
||||
|
||||
# Prepare fresh disk image
|
||||
img="/var/tmp/test.img"
|
||||
dd if=/dev/zero of=$img bs=1024k count=20 status=none
|
||||
echo -n passphrase >/tmp/passphrase
|
||||
cryptsetup luksFormat -q --use-urandom $img /tmp/passphrase
|
||||
|
||||
# Enroll unlock with default PCR policy
|
||||
env PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto $img
|
||||
/usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1
|
||||
/usr/lib/systemd/systemd-cryptsetup detach test-volume
|
||||
|
||||
# Check with wrong PCR
|
||||
tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000
|
||||
/usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1 && { echo 'unexpected success'; exit 1; }
|
||||
|
||||
# Enroll unlock with PCR+PIN policy
|
||||
systemd-cryptenroll --wipe-slot=tpm2 $img
|
||||
env PASSWORD=passphrase NEWPIN=123456 systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true $img
|
||||
env PIN=123456 /usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1
|
||||
/usr/lib/systemd/systemd-cryptsetup detach test-volume
|
||||
|
||||
# Check failure with wrong PIN
|
||||
env PIN=123457 /usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1 && { echo 'unexpected success'; exit 1; }
|
||||
|
||||
# Check failure with wrong PCR (and correct PIN)
|
||||
tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000
|
||||
env PIN=123456 /usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1 && { echo 'unexpected success'; exit 1; }
|
||||
|
||||
# Enroll unlock with PCR 0+7
|
||||
systemd-cryptenroll --wipe-slot=tpm2 $img
|
||||
env PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 $img
|
||||
/usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1
|
||||
/usr/lib/systemd/systemd-cryptsetup detach test-volume
|
||||
|
||||
# Check with wrong PCR 0
|
||||
tpm2_pcrextend 0:sha256=0000000000000000000000000000000000000000000000000000000000000000
|
||||
/usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1 && exit 1
|
||||
|
||||
echo OK >/testok
|
||||
|
||||
exit 0
|
Loading…
x
Reference in New Issue
Block a user