diff --git a/man/systemd-cryptenroll.xml b/man/systemd-cryptenroll.xml
index 5b1b60db64..c7f4e63f60 100644
--- a/man/systemd-cryptenroll.xml
+++ b/man/systemd-cryptenroll.xml
@@ -141,6 +141,14 @@
+
+ BOOL
+
+ When enrolling a FIDO2 security token, controls whether to require user verification
+ when unlocking the volume (the FIDO2 uv feature)). Defaults to no.
+
+
+
PATH
diff --git a/src/cryptenroll/cryptenroll-fido2.c b/src/cryptenroll/cryptenroll-fido2.c
index eab8f220e4..3ba7866738 100644
--- a/src/cryptenroll/cryptenroll-fido2.c
+++ b/src/cryptenroll/cryptenroll-fido2.c
@@ -79,7 +79,8 @@ int enroll_fido2(
JSON_BUILD_PAIR("fido2-salt", JSON_BUILD_BASE64(salt, salt_size)),
JSON_BUILD_PAIR("fido2-rp", JSON_BUILD_STRING("io.systemd.cryptsetup")),
JSON_BUILD_PAIR("fido2-clientPin-required", JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_PIN))),
- JSON_BUILD_PAIR("fido2-up-required", JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_UP)))));
+ JSON_BUILD_PAIR("fido2-up-required", JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_UP))),
+ JSON_BUILD_PAIR("fido2-uv-required", JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_UV)))));
if (r < 0)
return log_error_errno(r, "Failed to prepare PKCS#11 JSON token object: %m");
diff --git a/src/cryptenroll/cryptenroll.c b/src/cryptenroll/cryptenroll.c
index 5eca69f851..559a346804 100644
--- a/src/cryptenroll/cryptenroll.c
+++ b/src/cryptenroll/cryptenroll.c
@@ -93,6 +93,8 @@ static int help(void) {
" Whether to require entering a PIN to unlock the volume\n"
" --fido2-with-user-presence=BOOL\n"
" Whether to require user presence to unlock the volume\n"
+ " --fido2-with-user-verification=BOOL\n"
+ " Whether to require user verification to unlock the volume\n"
" --tpm2-device=PATH\n"
" Enroll a TPM2 device\n"
" --tpm2-pcrs=PCR1,PCR2,PCR3,…\n"
@@ -121,6 +123,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_WIPE_SLOT,
ARG_FIDO2_WITH_PIN,
ARG_FIDO2_WITH_UP,
+ ARG_FIDO2_WITH_UV,
};
static const struct option options[] = {
@@ -132,6 +135,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "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-pcrs", required_argument, NULL, ARG_TPM2_PCRS },
{ "wipe-slot", required_argument, NULL, ARG_WIPE_SLOT },
@@ -177,6 +181,18 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
+ case ARG_FIDO2_WITH_UV: {
+ bool lock_with_uv;
+
+ r = parse_boolean_argument("--fido2-with-user-verification=", optarg, &lock_with_uv);
+ if (r < 0)
+ return r;
+
+ SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_UV, lock_with_uv);
+
+ break;
+ }
+
case ARG_PASSWORD:
if (arg_enroll_type >= 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
diff --git a/src/cryptsetup/cryptsetup-fido2.c b/src/cryptsetup/cryptsetup-fido2.c
index 9a3af2d8ff..b21f970db7 100644
--- a/src/cryptsetup/cryptsetup-fido2.c
+++ b/src/cryptsetup/cryptsetup-fido2.c
@@ -205,6 +205,17 @@ int find_fido2_auto_data(
SET_FLAG(required, FIDO2ENROLL_UP, json_variant_boolean(w));
}
+
+ w = json_variant_by_key(v, "fido2-uv-required");
+ if (w) {
+ /* The "fido2-uv-required" field is optional. */
+
+ if (!json_variant_is_boolean(w))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "FIDO2 token data's 'fido2-uv-required' field is not a boolean.");
+
+ SET_FLAG(required, FIDO2ENROLL_UV, json_variant_boolean(w));
+ }
}
if (!cid)
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index d47e758cd7..e8e5b6dbfc 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -769,7 +769,7 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
if (r < 0)
return r;
- if (FLAGS_SET(required, FIDO2ENROLL_PIN | FIDO2ENROLL_UP) && arg_headless)
+ if (FLAGS_SET(required, FIDO2ENROLL_PIN | FIDO2ENROLL_UP | FIDO2ENROLL_UV) && arg_headless)
return log_error_errno(SYNTHETIC_ERRNO(ENOPKG),
"Local verification is required to unlock this volume, but the 'headless' parameter was set.");
diff --git a/src/shared/libfido2-util.c b/src/shared/libfido2-util.c
index 50e1efb2dc..ec69793f7c 100644
--- a/src/shared/libfido2-util.c
+++ b/src/shared/libfido2-util.c
@@ -25,6 +25,7 @@ int (*sym_fido_assert_set_extensions)(fido_assert_t *, int) = NULL;
int (*sym_fido_assert_set_hmac_salt)(fido_assert_t *, const unsigned char *, size_t) = NULL;
int (*sym_fido_assert_set_rp)(fido_assert_t *, const char *) = NULL;
int (*sym_fido_assert_set_up)(fido_assert_t *, fido_opt_t) = NULL;
+int (*sym_fido_assert_set_uv)(fido_assert_t *, fido_opt_t) = NULL;
size_t (*sym_fido_cbor_info_extensions_len)(const fido_cbor_info_t *) = NULL;
char **(*sym_fido_cbor_info_extensions_ptr)(const fido_cbor_info_t *) = NULL;
void (*sym_fido_cbor_info_free)(fido_cbor_info_t **) = NULL;
@@ -84,6 +85,7 @@ int dlopen_libfido2(void) {
DLSYM_ARG(fido_assert_set_hmac_salt),
DLSYM_ARG(fido_assert_set_rp),
DLSYM_ARG(fido_assert_set_up),
+ DLSYM_ARG(fido_assert_set_uv),
DLSYM_ARG(fido_cbor_info_extensions_len),
DLSYM_ARG(fido_cbor_info_extensions_ptr),
DLSYM_ARG(fido_cbor_info_free),
@@ -225,7 +227,7 @@ static int fido2_use_hmac_hash_specific_token(
_cleanup_(fido_assert_free_wrapper) fido_assert_t *a = NULL;
_cleanup_(fido_dev_free_wrapper) fido_dev_t *d = NULL;
_cleanup_(erase_and_freep) void *hmac_copy = NULL;
- bool has_up, has_client_pin;
+ bool has_up, has_client_pin, has_uv;
size_t hmac_size;
const void *hmac;
int r;
@@ -246,7 +248,7 @@ static int fido2_use_hmac_hash_specific_token(
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to open FIDO2 device %s: %s", path, sym_fido_strerr(r));
- r = verify_features(d, path, LOG_ERR, NULL, &has_client_pin, &has_up, NULL);
+ r = verify_features(d, path, LOG_ERR, NULL, &has_client_pin, &has_up, &has_uv);
if (r < 0)
return r;
@@ -260,6 +262,11 @@ static int fido2_use_hmac_hash_specific_token(
"User presence test required to unlock, but FIDO2 device %s does not support it.",
path);
+ if (!has_uv && FLAGS_SET(required, FIDO2ENROLL_UV))
+ return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON),
+ "User verification required to unlock, but FIDO2 device %s does not support it.",
+ path);
+
a = sym_fido_assert_new();
if (!a)
return log_oom();
@@ -303,6 +310,18 @@ static int fido2_use_hmac_hash_specific_token(
log_info("User presence required to unlock.");
}
+ if (has_uv) {
+ r = sym_fido_assert_set_uv(a, FLAGS_SET(required, FIDO2ENROLL_UV) ? FIDO_OPT_TRUE : FIDO_OPT_FALSE);
+ if (r != FIDO_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to %s FIDO2 user verification: %s",
+ enable_disable(FLAGS_SET(required, FIDO2ENROLL_UV)),
+ sym_fido_strerr(r));
+
+ if (FLAGS_SET(required, FIDO2ENROLL_UV))
+ log_info("User verification required to unlock.");
+ }
+
if (FLAGS_SET(required, FIDO2ENROLL_PIN)) {
char **i;
@@ -515,6 +534,11 @@ int fido2_generate_hmac_hash(
"Locking with user presence test requested, but FIDO2 device %s does not support it.",
device);
+ if (!has_uv && FLAGS_SET(lock_with, FIDO2ENROLL_UV))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Locking with user verification requested, but FIDO2 device %s does not support it.",
+ device);
+
c = sym_fido_cred_new();
if (!c)
return log_oom();
@@ -667,6 +691,20 @@ int fido2_generate_hmac_hash(
emoji_enabled() ? " " : "");
}
+ if (has_uv) {
+ r = sym_fido_assert_set_uv(a, FLAGS_SET(lock_with, FIDO2ENROLL_UV) ? FIDO_OPT_TRUE : FIDO_OPT_FALSE);
+ if (r != FIDO_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to %s FIDO user verification: %s",
+ enable_disable(FLAGS_SET(lock_with, FIDO2ENROLL_UV)),
+ sym_fido_strerr(r));
+
+ if (FLAGS_SET(lock_with, FIDO2ENROLL_UV))
+ log_notice("%s%sIn order to allow secret key generation, please verify user on security token.",
+ emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
+ emoji_enabled() ? " " : "");
+ }
+
r = sym_fido_dev_get_assert(d, a, FLAGS_SET(lock_with, FIDO2ENROLL_PIN) ? used_pin : NULL);
if (r == FIDO_ERR_UP_REQUIRED && !FLAGS_SET(lock_with, FIDO2ENROLL_UP))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
diff --git a/src/shared/libfido2-util.h b/src/shared/libfido2-util.h
index 9eddf5ca78..1b31577e06 100644
--- a/src/shared/libfido2-util.h
+++ b/src/shared/libfido2-util.h
@@ -6,6 +6,7 @@
typedef enum Fido2EnrollFlags {
FIDO2ENROLL_PIN = 1 << 0,
FIDO2ENROLL_UP = 1 << 1, /* User presence (ie: touching token) */
+ FIDO2ENROLL_UV = 1 << 2, /* User verification (ie: fingerprint) */
_FIDO2ENROLL_TYPE_MAX,
_FIDO2ENROLL_TYPE_INVALID = -EINVAL,
} Fido2EnrollFlags;
@@ -23,6 +24,7 @@ extern int (*sym_fido_assert_set_extensions)(fido_assert_t *, int);
extern int (*sym_fido_assert_set_hmac_salt)(fido_assert_t *, const unsigned char *, size_t);
extern int (*sym_fido_assert_set_rp)(fido_assert_t *, const char *);
extern int (*sym_fido_assert_set_up)(fido_assert_t *, fido_opt_t);
+extern int (*sym_fido_assert_set_uv)(fido_assert_t *, fido_opt_t);
extern size_t (*sym_fido_cbor_info_extensions_len)(const fido_cbor_info_t *);
extern char **(*sym_fido_cbor_info_extensions_ptr)(const fido_cbor_info_t *);
extern void (*sym_fido_cbor_info_free)(fido_cbor_info_t **);