from the key file. See
<citerefentry project='die-net'><refentrytitle>cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>
for possible values and the default value of this option. This
- option is ignored in plain encryption mode, as the key file
- size is then given by the key size.</para>
+ option is ignored in plain encryption mode, where the key file
+ size is determined by the key size. It is also ignored when
+ the key file is used as a salt file for a FIDO2 token, as the
+ salt size in that case is defined by the FIDO2 specification
+ to be exactly 32 bytes.</para>
<xi:include href="version-info.xml" xpointer="v188"/></listitem>
</varlistentry>
(configured in the line's third column) to operate. If not configured and the volume is of type
LUKS2, the CID and the key are read from LUKS2 JSON token metadata instead. Use
<citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- as simple tool for enrolling FIDO2 security tokens, compatible with this automatic mode, which is
- only available for LUKS2 volumes.</para>
+ as simple tool for enrolling FIDO2 security tokens for LUKS2 volumes.</para>
<para>Use <command>systemd-cryptenroll --fido2-device=list</command> to list all suitable FIDO2
security tokens currently plugged in, along with their device nodes.</para>
<filename>/dev/hidraw1</filename>). Alternatively the special value <literal>auto</literal> may be
specified, in order to automatically determine the device node of a currently plugged in security
token (of which there must be exactly one). This automatic discovery is unsupported if
- <option>--fido2-device=</option> option is also specified.</para>
+ <option>--fido2-device=</option> option is also specified. Note that currently FIDO2 devices
+ enrolled without an accompanying LUKS2 token (i.e. <option>--fido2-parameters-in-header=no</option>)
+ cannot be used for unlocking.</para>
<xi:include href="version-info.xml" xpointer="v253"/></listitem>
</varlistentry>
<xi:include href="version-info.xml" xpointer="v248"/></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--fido2-salt-file=<replaceable>PATH</replaceable></option></term>
+
+ <listitem><para>When enrolling a FIDO2 security token, specifies the path to a file or an
+ <constant>AF_UNIX</constant> socket from which we should read the salt value to be used in the
+ HMAC operation performed by the FIDO2 security token. If this option is not specified, the salt
+ will be randomly generated.</para>
+
+ <xi:include href="version-info.xml" xpointer="v257"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--fido2-parameters-in-header=<replaceable>BOOL</replaceable></option></term>
+
+ <listitem><para>When enrolling a FIDO2 security token, controls whether to store FIDO2
+ parameters in a token in the LUKS2 superblock. Defaults to <literal>yes</literal>.
+ If set to <literal>no</literal>, the <option>fido2-cid=</option> option has to be specified manually
+ in the respective <filename>/etc/crypttab</filename> line along with a key file. See
+ <citerefentry><refentrytitle>crypttab</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for details.</para>
+
+ <xi:include href="version-info.xml" xpointer="v257"/></listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>--fido2-with-client-pin=<replaceable>BOOL</replaceable></option></term>
--pkcs11-token-uri
--fido2-credential-algorithm
--fido2-device
+ --fido2-salt-file
+ --fido2-parameters-in-header
--fido2-with-client-pin
--fido2-with-user-presence
--fido2-with-user-verification
if __contains_word "$prev" ${OPTS[ARG]}; then
case $prev in
- --unlock-key-file|--tpm2-device-key|--tpm2-public-key|--tpm2-signature|--tpm2-pcrlock)
+ --unlock-key-file|--fido2-salt-file|--tpm2-device-key|--tpm2-public-key|--tpm2-signature|--tpm2-pcrlock)
comps=$(compgen -A file -- "$cur")
compopt -o filenames
;;
--fido2-device)
comps="auto list $(__get_fido2_devices)"
;;
- --fido2-with-client-pin|--fido2-with-user-presence|--fido2-with-user-verification|--tpm2-with-pin)
+ --fido2-parameters-in-header|--fido2-with-client-pin|--fido2-with-user-presence|--fido2-with-user-verification|--tpm2-with-pin)
comps='yes no'
;;
--tpm2-device)
#include "ask-password-api.h"
#include "cryptenroll-fido2.h"
#include "cryptsetup-fido2.h"
+#include "fido2-util.h"
+#include "glyph-util.h"
#include "hexdecoct.h"
+#include "iovec-util.h"
#include "json-util.h"
#include "libfido2-util.h"
#include "memory-util.h"
+#include "pretty-print.h"
#include "random-util.h"
int load_volume_key_fido2(
size_t volume_key_size,
const char *device,
Fido2EnrollFlags lock_with,
- int cred_alg) {
+ int cred_alg,
+ const char *salt_file,
+ bool parameters_in_header) {
- _cleanup_(erase_and_freep) void *salt = NULL, *secret = NULL;
+ _cleanup_(iovec_done_erase) struct iovec salt = {};
+ _cleanup_(erase_and_freep) void *secret = NULL;
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
_cleanup_free_ char *keyslot_as_string = NULL;
- size_t cid_size, salt_size, secret_size;
+ size_t cid_size, secret_size;
_cleanup_free_ void *cid = NULL;
ssize_t base64_encoded_size;
const char *node, *un;
un = strempty(crypt_get_uuid(cd));
+ if (salt_file)
+ r = fido2_read_salt_file(
+ salt_file,
+ /* offset= */ UINT64_MAX,
+ /* client= */ "cryptenroll",
+ /* node= */ un,
+ &salt);
+ else
+ r = fido2_generate_salt(&salt);
+ if (r < 0)
+ return r;
+
r = fido2_generate_hmac_hash(
device,
/* rp_id= */ "io.systemd.cryptsetup",
/* askpw_credential= */ "cryptenroll.fido2-pin",
lock_with,
cred_alg,
+ &salt,
&cid, &cid_size,
- &salt, &salt_size,
&secret, &secret_size,
NULL,
&lock_with);
if (keyslot < 0)
return log_error_errno(keyslot, "Failed to add new FIDO2 key to %s: %m", node);
- if (asprintf(&keyslot_as_string, "%i", keyslot) < 0)
- return log_oom();
-
- r = sd_json_buildo(&v,
- SD_JSON_BUILD_PAIR("type", JSON_BUILD_CONST_STRING("systemd-fido2")),
- SD_JSON_BUILD_PAIR("keyslots", SD_JSON_BUILD_ARRAY(SD_JSON_BUILD_STRING(keyslot_as_string))),
- SD_JSON_BUILD_PAIR("fido2-credential", SD_JSON_BUILD_BASE64(cid, cid_size)),
- SD_JSON_BUILD_PAIR("fido2-salt", SD_JSON_BUILD_BASE64(salt, salt_size)),
- SD_JSON_BUILD_PAIR("fido2-rp", JSON_BUILD_CONST_STRING("io.systemd.cryptsetup")),
- SD_JSON_BUILD_PAIR("fido2-clientPin-required", SD_JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_PIN))),
- SD_JSON_BUILD_PAIR("fido2-up-required", SD_JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_UP))),
- SD_JSON_BUILD_PAIR("fido2-uv-required", SD_JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_UV))));
- if (r < 0)
- return log_error_errno(r, "Failed to prepare FIDO2 JSON token object: %m");
-
- r = cryptsetup_add_token_json(cd, v);
- if (r < 0)
- return log_error_errno(r, "Failed to add FIDO2 JSON token to LUKS2 header: %m");
+ if (parameters_in_header) {
+ if (asprintf(&keyslot_as_string, "%i", keyslot) < 0)
+ return log_oom();
+
+ r = sd_json_buildo(&v,
+ SD_JSON_BUILD_PAIR("type", JSON_BUILD_CONST_STRING("systemd-fido2")),
+ SD_JSON_BUILD_PAIR("keyslots", SD_JSON_BUILD_ARRAY(SD_JSON_BUILD_STRING(keyslot_as_string))),
+ SD_JSON_BUILD_PAIR("fido2-credential", SD_JSON_BUILD_BASE64(cid, cid_size)),
+ SD_JSON_BUILD_PAIR("fido2-salt", JSON_BUILD_IOVEC_BASE64(&salt)),
+ SD_JSON_BUILD_PAIR("fido2-rp", JSON_BUILD_CONST_STRING("io.systemd.cryptsetup")),
+ SD_JSON_BUILD_PAIR("fido2-clientPin-required", SD_JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_PIN))),
+ SD_JSON_BUILD_PAIR("fido2-up-required", SD_JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_UP))),
+ SD_JSON_BUILD_PAIR("fido2-uv-required", SD_JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_UV))));
+ if (r < 0)
+ return log_error_errno(r, "Failed to prepare FIDO2 JSON token object: %m");
+
+ r = cryptsetup_add_token_json(cd, v);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add FIDO2 JSON token to LUKS2 header: %m");
+ } else {
+ _cleanup_free_ char *base64_encoded_cid = NULL, *link = NULL;
+
+ r = base64mem(cid, cid_size, &base64_encoded_cid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to base64 encode FIDO2 credential ID: %m");
+
+ r = terminal_urlify_man("crypttab", "5", &link);
+ if (r < 0)
+ return log_oom();
+
+ fflush(stdout);
+ fprintf(stderr,
+ "A FIDO2 credential has been registered for this volume:\n\n"
+ " %s%sfido2-cid=%s",
+ emoji_enabled() ? special_glyph(SPECIAL_GLYPH_LOCK_AND_KEY) : "",
+ emoji_enabled() ? " " : "",
+ ansi_highlight());
+ fflush(stderr);
+
+ fputs(base64_encoded_cid, stdout);
+ fflush(stdout);
+
+ fputs(ansi_normal(), stderr);
+ fflush(stderr);
+
+ fputc('\n', stdout);
+ fflush(stdout);
+
+ fprintf(stderr,
+ "\nPlease save this FIDO2 credential ID. It is required when unloocking the volume\n"
+ "using the associated FIDO2 keyslot which we just created. To configure automatic\n"
+ "unlocking using this FIDO2 token, add an appropriate entry to your /etc/crypttab\n"
+ "file, see %s for details.\n", link);
+ fflush(stderr);
+ }
log_info("New FIDO2 token enrolled as key slot %i.", keyslot);
return keyslot;
#if HAVE_LIBFIDO2
int load_volume_key_fido2(struct crypt_device *cd, const char *cd_node, const char *device, void *ret_vk, size_t *ret_vks);
-int enroll_fido2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, Fido2EnrollFlags lock_with, int cred_alg);
+int enroll_fido2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, Fido2EnrollFlags lock_with, int cred_alg, const char *salt_file, bool parameters_in_header);
#else
static inline int load_volume_key_fido2(struct crypt_device *cd, const char *cd_node, const char *device, void *ret_vk, size_t *ret_vks) {
"FIDO2 unlocking not supported.");
}
-static inline int enroll_fido2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, Fido2EnrollFlags lock_with, int cred_alg) {
+static inline int enroll_fido2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, Fido2EnrollFlags lock_with, int cred_alg, const char *salt_file, bool parameters_in_header) {
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"FIDO2 key enrollment not supported.");
}
static char *arg_unlock_tpm2_device = NULL;
static char *arg_pkcs11_token_uri = NULL;
static char *arg_fido2_device = NULL;
+static char *arg_fido2_salt_file = NULL;
+static bool arg_fido2_parameters_in_header = true;
static char *arg_tpm2_device = NULL;
static uint32_t arg_tpm2_seal_key_handle = 0;
static char *arg_tpm2_device_key = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_unlock_tpm2_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri, freep);
STATIC_DESTRUCTOR_REGISTER(arg_fido2_device, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_fido2_salt_file, 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);
"\n%3$sFIDO2 Enrollment:%4$s\n"
" --fido2-device=PATH\n"
" Enroll a FIDO2-HMAC security token\n"
+ " --fido2-salt-file=PATH\n"
+ " Use salt from a file instead of generating one\n"
+ " --fido2-parameters-in-header=BOOL\n"
+ " Whether to store FIDO2 parameters in the LUKS2 header\n"
" --fido2-credential-algorithm=STRING\n"
" Specify COSE algorithm for FIDO2 credential\n"
" --fido2-with-client-pin=BOOL\n"
ARG_UNLOCK_TPM2_DEVICE,
ARG_PKCS11_TOKEN_URI,
ARG_FIDO2_DEVICE,
+ ARG_FIDO2_SALT_FILE,
+ ARG_FIDO2_PARAMETERS_IN_HEADER,
ARG_TPM2_DEVICE,
ARG_TPM2_DEVICE_KEY,
ARG_TPM2_SEAL_KEY_HANDLE,
};
static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "password", no_argument, NULL, ARG_PASSWORD },
- { "recovery-key", no_argument, NULL, ARG_RECOVERY_KEY },
- { "unlock-key-file", required_argument, NULL, ARG_UNLOCK_KEYFILE },
- { "unlock-fido2-device", required_argument, NULL, ARG_UNLOCK_FIDO2_DEVICE },
- { "unlock-tpm2-device", required_argument, NULL, ARG_UNLOCK_TPM2_DEVICE },
- { "pkcs11-token-uri", required_argument, NULL, ARG_PKCS11_TOKEN_URI },
- { "fido2-credential-algorithm", required_argument, NULL, ARG_FIDO2_CRED_ALG },
- { "fido2-device", required_argument, NULL, ARG_FIDO2_DEVICE },
- { "fido2-with-client-pin", required_argument, NULL, ARG_FIDO2_WITH_PIN },
- { "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-device-key", required_argument, NULL, ARG_TPM2_DEVICE_KEY },
- { "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 },
- { "tpm2-signature", required_argument, NULL, ARG_TPM2_SIGNATURE },
- { "tpm2-pcrlock", required_argument, NULL, ARG_TPM2_PCRLOCK },
- { "tpm2-with-pin", required_argument, NULL, ARG_TPM2_WITH_PIN },
- { "wipe-slot", required_argument, NULL, ARG_WIPE_SLOT },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "password", no_argument, NULL, ARG_PASSWORD },
+ { "recovery-key", no_argument, NULL, ARG_RECOVERY_KEY },
+ { "unlock-key-file", required_argument, NULL, ARG_UNLOCK_KEYFILE },
+ { "unlock-fido2-device", required_argument, NULL, ARG_UNLOCK_FIDO2_DEVICE },
+ { "unlock-tpm2-device", required_argument, NULL, ARG_UNLOCK_TPM2_DEVICE },
+ { "pkcs11-token-uri", required_argument, NULL, ARG_PKCS11_TOKEN_URI },
+ { "fido2-credential-algorithm", required_argument, NULL, ARG_FIDO2_CRED_ALG },
+ { "fido2-device", required_argument, NULL, ARG_FIDO2_DEVICE },
+ { "fido2-salt-file", required_argument, NULL, ARG_FIDO2_SALT_FILE },
+ { "fido2-parameters-in-header", required_argument, NULL, ARG_FIDO2_PARAMETERS_IN_HEADER },
+ { "fido2-with-client-pin", required_argument, NULL, ARG_FIDO2_WITH_PIN },
+ { "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-device-key", required_argument, NULL, ARG_TPM2_DEVICE_KEY },
+ { "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 },
+ { "tpm2-signature", required_argument, NULL, ARG_TPM2_SIGNATURE },
+ { "tpm2-pcrlock", required_argument, NULL, ARG_TPM2_PCRLOCK },
+ { "tpm2-with-pin", required_argument, NULL, ARG_TPM2_WITH_PIN },
+ { "wipe-slot", required_argument, NULL, ARG_WIPE_SLOT },
{}
};
break;
}
+ case ARG_FIDO2_SALT_FILE:
+ r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_fido2_salt_file);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case ARG_FIDO2_PARAMETERS_IN_HEADER:
+ r = parse_boolean_argument("--fido2-parameters-in-header=", optarg, &arg_fido2_parameters_in_header);
+ if (r < 0)
+ return r;
+
+ break;
+
case ARG_TPM2_DEVICE: {
_cleanup_free_ char *device = NULL;
"When both enrolling and unlocking with FIDO2 tokens, automatic discovery is unsupported. "
"Please specify device paths for enrolling and unlocking respectively.");
+ if (!arg_fido2_parameters_in_header && !arg_fido2_salt_file)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "FIDO2 parameters' storage in the LUKS2 header was disabled, but no salt file provided, refusing.");
+
if (!arg_fido2_device) {
r = fido2_find_device_auto(&arg_fido2_device);
if (r < 0)
break;
case ENROLL_FIDO2:
- slot = enroll_fido2(cd, vk, vks, arg_fido2_device, arg_fido2_lock_with, arg_fido2_cred_alg);
+ slot = enroll_fido2(cd, vk, vks, arg_fido2_device, arg_fido2_lock_with, arg_fido2_cred_alg, arg_fido2_salt_file, arg_fido2_parameters_in_header);
break;
case ENROLL_TPM2:
return false;
#endif
+ /* Disable tokens if we're in FIDO2 mode with manual parameters. */
+ if (arg_fido2_cid)
+ return false;
+
#if HAVE_LIBCRYPTSETUP_PLUGINS
int r;
#include "ask-password-api.h"
#include "errno-util.h"
+#include "fido2-util.h"
#include "format-table.h"
#include "hexdecoct.h"
#include "homectl-fido2.h"
#include "homectl-pkcs11.h"
+#include "iovec-util.h"
+#include "json-util.h"
#include "libcrypt-util.h"
#include "libfido2-util.h"
#include "locale-util.h"
sd_json_variant **v,
const void *cid,
size_t cid_size,
- const void *fido2_salt,
- size_t fido2_salt_size,
+ const struct iovec *salt,
const void *secret,
size_t secret_size,
Fido2EnrollFlags lock_with) {
ssize_t base64_encoded_size;
int r;
+ assert(v);
+ assert(cid);
+ assert(iovec_is_set(salt));
+ assert(secret);
+
/* Before using UNIX hashing on the supplied key we base64 encode it, since crypt_r() and friends
* expect a NUL terminated string, and we use a binary key */
base64_encoded_size = base64mem(secret, secret_size, &base64_encoded);
r = sd_json_buildo(&e,
SD_JSON_BUILD_PAIR("credential", SD_JSON_BUILD_BASE64(cid, cid_size)),
- SD_JSON_BUILD_PAIR("salt", SD_JSON_BUILD_BASE64(fido2_salt, fido2_salt_size)),
+ SD_JSON_BUILD_PAIR("salt", JSON_BUILD_IOVEC_BASE64(salt)),
SD_JSON_BUILD_PAIR("hashedPassword", SD_JSON_BUILD_STRING(hashed)),
SD_JSON_BUILD_PAIR("up", SD_JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_UP))),
SD_JSON_BUILD_PAIR("uv", SD_JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_UV))),
r = sd_json_variant_append_array(&l, e);
if (r < 0)
- return log_error_errno(r, "Failed append FIDO2 salt: %m");
+ return log_error_errno(r, "Failed to append FIDO2 salt: %m");
r = sd_json_variant_set_field(&w, "fido2HmacSalt", l);
if (r < 0)
- return log_error_errno(r, "Failed to set FDO2 salt: %m");
+ return log_error_errno(r, "Failed to set FIDO2 salt: %m");
r = sd_json_variant_set_field(v, "privileged", w);
if (r < 0)
#if HAVE_LIBFIDO2
sd_json_variant *un, *realm, *rn;
- _cleanup_(erase_and_freep) void *secret = NULL, *salt = NULL;
+ _cleanup_(iovec_done) struct iovec salt = {};
+ _cleanup_(erase_and_freep) void *secret = NULL;
_cleanup_(erase_and_freep) char *used_pin = NULL;
- size_t cid_size, salt_size, secret_size;
+ size_t cid_size, secret_size;
_cleanup_free_ void *cid = NULL;
const char *fido_un;
int r;
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"realName field of user record is not a string");
+ r = fido2_generate_salt(&salt);
+ if (r < 0)
+ return r;
+
r = fido2_generate_hmac_hash(
device,
/* rp_id= */ "io.systemd.home",
/* askpw_credential= */ "home.token-pin",
lock_with,
cred_alg,
+ &salt,
&cid, &cid_size,
- &salt, &salt_size,
&secret, &secret_size,
&used_pin,
&lock_with);
v,
cid,
cid_size,
- salt,
- salt_size,
+ &salt,
secret,
secret_size,
lock_with);
#include "ask-password-api.h"
#include "cryptsetup-fido2.h"
#include "env-util.h"
+#include "fido2-util.h"
#include "fileio.h"
#include "hexdecoct.h"
+#include "iovec-util.h"
#include "libfido2-util.h"
#include "parse-util.h"
#include "random-util.h"
_cleanup_(erase_and_freep) char *envpw = NULL;
_cleanup_strv_free_erase_ char **pins = NULL;
- _cleanup_free_ void *loaded_salt = NULL;
+ _cleanup_(iovec_done_erase) struct iovec loaded_salt = {};
bool device_exists = false;
- const char *salt;
- size_t salt_size;
+ struct iovec salt;
int r;
if ((required & (FIDO2ENROLL_PIN | FIDO2ENROLL_UP | FIDO2ENROLL_UV)) && FLAGS_SET(askpw_flags, ASK_PASSWORD_HEADLESS))
assert(cid);
assert(key_file || key_data);
- if (key_data) {
- salt = key_data;
- salt_size = key_data_size;
- } else {
- _cleanup_free_ char *bindname = NULL;
-
- /* If we read the salt via AF_UNIX, make this client recognizable */
- if (asprintf(&bindname, "@%" PRIx64"/cryptsetup-fido2/%s", random_u64(), volume_name) < 0)
- return log_oom();
-
- r = read_full_file_full(
- AT_FDCWD, key_file,
- key_file_offset == 0 ? UINT64_MAX : key_file_offset,
- key_file_size == 0 ? SIZE_MAX : key_file_size,
- READ_FULL_FILE_CONNECT_SOCKET,
- bindname,
- (char**) &loaded_salt, &salt_size);
+ if (key_data)
+ salt = IOVEC_MAKE(key_data, key_data_size);
+ else {
+ if (key_file_size > 0)
+ log_debug("Ignoring 'keyfile-size=' option for a FIDO2 salt file.");
+
+ r = fido2_read_salt_file(
+ key_file, key_file_offset,
+ /* client= */ "cryptsetup",
+ /* node= */ volume_name,
+ &loaded_salt);
if (r < 0)
return r;
r = fido2_use_hmac_hash(
device,
rp_id ?: "io.systemd.cryptsetup",
- salt, salt_size,
+ salt.iov_base, salt.iov_len,
cid, cid_size,
pins,
required,
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "fido2-util.h"
+#include "fileio.h"
+#include "libfido2-util.h"
+#include "random-util.h"
+
+int fido2_generate_salt(struct iovec *ret_salt) {
+ _cleanup_(iovec_done) struct iovec salt = {};
+ int r;
+
+ r = crypto_random_bytes_allocate_iovec(FIDO2_SALT_SIZE, &salt);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate FIDO2 salt: %m");
+
+ *ret_salt = TAKE_STRUCT(salt);
+ return 0;
+}
+
+int fido2_read_salt_file(const char *filename, uint64_t offset, const char *client, const char *node, struct iovec *ret_salt) {
+ _cleanup_(iovec_done_erase) struct iovec salt = {};
+ _cleanup_free_ char *bind_name = NULL;
+ int r;
+
+ /* If we read the salt via AF_UNIX, make the client recognizable */
+ if (asprintf(&bind_name, "@%" PRIx64"/%s-fido2-salt/%s", random_u64(), client, node) < 0)
+ return log_oom();
+
+ r = read_full_file_full(
+ AT_FDCWD, filename,
+ offset == 0 ? UINT64_MAX : offset,
+ /* size= */ FIDO2_SALT_SIZE,
+ READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|
+ READ_FULL_FILE_CONNECT_SOCKET|READ_FULL_FILE_FAIL_WHEN_LARGER,
+ bind_name, (char**) &salt.iov_base, &salt.iov_len);
+ if (r == -E2BIG || (r >= 0 && salt.iov_len != FIDO2_SALT_SIZE))
+ return log_error_errno(r < 0 ? r : SYNTHETIC_ERRNO(EINVAL),
+ "FIDO2 salt file must contain exactly %u bytes.", FIDO2_SALT_SIZE);
+ if (r < 0)
+ return log_error_errno(r, "Reading FIDO2 salt file '%s' failed: %m", filename);
+
+ *ret_salt = TAKE_STRUCT(salt);
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdint.h>
+
+#include "iovec-util.h"
+
+int fido2_generate_salt(struct iovec *ret_salt);
+int fido2_read_salt_file(const char *filename, uint64_t offset, const char *client, const char *node, struct iovec *ret_salt);
#include "glyph-util.h"
#include "log.h"
#include "memory-util.h"
-#include "random-util.h"
#include "strv.h"
#include "unistd.h"
return r;
}
-#define FIDO2_SALT_SIZE 32
-
int fido2_generate_hmac_hash(
const char *device,
const char *rp_id,
const char *askpw_credential,
Fido2EnrollFlags lock_with,
int cred_alg,
+ const struct iovec *salt,
void **ret_cid, size_t *ret_cid_size,
- void **ret_salt, size_t *ret_salt_size,
void **ret_secret, size_t *ret_secret_size,
char **ret_usedpin,
Fido2EnrollFlags *ret_locked_with) {
- _cleanup_(erase_and_freep) void *salt = NULL, *secret_copy = NULL;
+ _cleanup_(erase_and_freep) void *secret_copy = NULL;
_cleanup_(fido_assert_free_wrapper) fido_assert_t *a = NULL;
_cleanup_(fido_cred_free_wrapper) fido_cred_t *c = NULL;
_cleanup_(fido_dev_free_wrapper) fido_dev_t *d = NULL;
assert(device);
assert(ret_cid);
assert(ret_cid_size);
- assert(ret_salt);
- assert(ret_salt_size);
assert(ret_secret);
assert(ret_secret_size);
- /* Construction is like this: we generate a salt of 32 bytes. We then ask the FIDO2 device to
+ /* Construction is like this: we read or generate a salt of 32 bytes. We then ask the FIDO2 device to
* HMAC-SHA256 it for us with its internal key. The result is the key used by LUKS and account
* authentication. LUKS and UNIX password auth all do their own salting before hashing, so that FIDO2
* device never sees the volume key.
*
* with: S → LUKS/account authentication key (never stored)
* I → internal key on FIDO2 device (stored in the FIDO2 device)
- * D → salt we generate here (stored in the privileged part of the JSON record)
+ * D → salt (stored in the privileged part of the JSON record or read from a file/socket)
*
*/
assert(device);
assert((lock_with & ~(FIDO2ENROLL_PIN|FIDO2ENROLL_UP|FIDO2ENROLL_UV)) == 0);
+ assert(iovec_is_set(salt));
r = dlopen_libfido2();
if (r < 0)
return log_error_errno(r, "FIDO2 token support is not installed.");
- salt = malloc(FIDO2_SALT_SIZE);
- if (!salt)
- return log_oom();
-
- r = crypto_random_bytes(salt, FIDO2_SALT_SIZE);
- if (r < 0)
- return log_error_errno(r, "Failed to generate salt: %m");
-
d = sym_fido_dev_new();
if (!d)
return log_oom();
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to enable HMAC-SECRET extension on FIDO2 assertion: %s", sym_fido_strerr(r));
- r = sym_fido_assert_set_hmac_salt(a, salt, FIDO2_SALT_SIZE);
+ r = sym_fido_assert_set_hmac_salt(a, salt->iov_base, salt->iov_len);
if (r != FIDO_OK)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to set salt on FIDO2 assertion: %s", sym_fido_strerr(r));
*ret_cid = TAKE_PTR(cid_copy);
*ret_cid_size = cid_size;
- *ret_salt = TAKE_PTR(salt);
- *ret_salt_size = FIDO2_SALT_SIZE;
*ret_secret = TAKE_PTR(secret_copy);
*ret_secret_size = secret_size;
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include "iovec-util.h"
#include "macro.h"
+#define FIDO2_SALT_SIZE 32U
+
typedef enum Fido2EnrollFlags {
FIDO2ENROLL_PIN = 1 << 0,
FIDO2ENROLL_UP = 1 << 1, /* User presence (ie: touching token) */
const char *askpw_credential,
Fido2EnrollFlags lock_with,
int cred_alg,
+ const struct iovec *salt,
void **ret_cid, size_t *ret_cid_size,
- void **ret_salt, size_t *ret_salt_size,
void **ret_secret, size_t *ret_secret_size,
char **ret_usedpin,
Fido2EnrollFlags *ret_locked_with);
'exit-status.c',
'extension-util.c',
'fdset.c',
+ 'fido2-util.c',
'fileio-label.c',
'find-esp.c',
'firewall-util-nft.c',