</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--fido2-with-user-verification=</option><replaceable>BOOL</replaceable></term>
+
+ <listitem><para>When enrolling a FIDO2 security token, controls whether to require user verification
+ when unlocking the volume (the FIDO2 <literal>uv</literal> feature)). Defaults to <literal>no</literal>.
+ </para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>--tpm2-device=</option><replaceable>PATH</replaceable></term>
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");
" 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"
ARG_WIPE_SLOT,
ARG_FIDO2_WITH_PIN,
ARG_FIDO2_WITH_UP,
+ ARG_FIDO2_WITH_UV,
};
static const struct option options[] = {
{ "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 },
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),
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)
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.");
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;
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),
_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;
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;
"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();
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;
"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();
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),
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;
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 **);