1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "libfido2-util.h"
6 #include "alloc-util.h"
7 #include "ask-password-api.h"
8 #include "dlfcn-util.h"
9 #include "format-table.h"
10 #include "glyph-util.h"
12 #include "memory-util.h"
13 #include "random-util.h"
17 static void *libfido2_dl
= NULL
;
19 DLSYM_FUNCTION(fido_assert_allow_cred
);
20 DLSYM_FUNCTION(fido_assert_free
);
21 DLSYM_FUNCTION(fido_assert_hmac_secret_len
);
22 DLSYM_FUNCTION(fido_assert_hmac_secret_ptr
);
23 DLSYM_FUNCTION(fido_assert_new
);
24 DLSYM_FUNCTION(fido_assert_set_clientdata_hash
);
25 DLSYM_FUNCTION(fido_assert_set_extensions
);
26 DLSYM_FUNCTION(fido_assert_set_hmac_salt
);
27 DLSYM_FUNCTION(fido_assert_set_rp
);
28 DLSYM_FUNCTION(fido_assert_set_up
);
29 DLSYM_FUNCTION(fido_assert_set_uv
);
30 DLSYM_FUNCTION(fido_cbor_info_extensions_len
);
31 DLSYM_FUNCTION(fido_cbor_info_extensions_ptr
);
32 DLSYM_FUNCTION(fido_cbor_info_free
);
33 DLSYM_FUNCTION(fido_cbor_info_new
);
34 DLSYM_FUNCTION(fido_cbor_info_options_len
);
35 DLSYM_FUNCTION(fido_cbor_info_options_name_ptr
);
36 DLSYM_FUNCTION(fido_cbor_info_options_value_ptr
);
37 DLSYM_FUNCTION(fido_cred_free
);
38 DLSYM_FUNCTION(fido_cred_id_len
);
39 DLSYM_FUNCTION(fido_cred_id_ptr
);
40 DLSYM_FUNCTION(fido_cred_new
);
41 DLSYM_FUNCTION(fido_cred_set_clientdata_hash
);
42 DLSYM_FUNCTION(fido_cred_set_extensions
);
43 DLSYM_FUNCTION(fido_cred_set_prot
);
44 DLSYM_FUNCTION(fido_cred_set_rk
);
45 DLSYM_FUNCTION(fido_cred_set_rp
);
46 DLSYM_FUNCTION(fido_cred_set_type
);
47 DLSYM_FUNCTION(fido_cred_set_user
);
48 DLSYM_FUNCTION(fido_cred_set_uv
);
49 DLSYM_FUNCTION(fido_dev_free
);
50 DLSYM_FUNCTION(fido_dev_get_assert
);
51 DLSYM_FUNCTION(fido_dev_get_cbor_info
);
52 DLSYM_FUNCTION(fido_dev_info_free
);
53 DLSYM_FUNCTION(fido_dev_info_manifest
);
54 DLSYM_FUNCTION(fido_dev_info_manufacturer_string
);
55 DLSYM_FUNCTION(fido_dev_info_product_string
);
56 DLSYM_FUNCTION(fido_dev_info_new
);
57 DLSYM_FUNCTION(fido_dev_info_path
);
58 DLSYM_FUNCTION(fido_dev_info_ptr
);
59 DLSYM_FUNCTION(fido_dev_is_fido2
);
60 DLSYM_FUNCTION(fido_dev_make_cred
);
61 DLSYM_FUNCTION(fido_dev_new
);
62 DLSYM_FUNCTION(fido_dev_open
);
63 DLSYM_FUNCTION(fido_dev_close
);
64 DLSYM_FUNCTION(fido_init
);
65 DLSYM_FUNCTION(fido_set_log_handler
);
66 DLSYM_FUNCTION(fido_strerr
);
68 static void fido_log_propagate_handler(const char *s
) {
69 log_debug("libfido2: %s", strempty(s
));
72 int dlopen_libfido2(void) {
75 r
= dlopen_many_sym_or_warn(
76 &libfido2_dl
, "libfido2.so.1", LOG_DEBUG
,
77 DLSYM_ARG(fido_assert_allow_cred
),
78 DLSYM_ARG(fido_assert_free
),
79 DLSYM_ARG(fido_assert_hmac_secret_len
),
80 DLSYM_ARG(fido_assert_hmac_secret_ptr
),
81 DLSYM_ARG(fido_assert_new
),
82 DLSYM_ARG(fido_assert_set_clientdata_hash
),
83 DLSYM_ARG(fido_assert_set_extensions
),
84 DLSYM_ARG(fido_assert_set_hmac_salt
),
85 DLSYM_ARG(fido_assert_set_rp
),
86 DLSYM_ARG(fido_assert_set_up
),
87 DLSYM_ARG(fido_assert_set_uv
),
88 DLSYM_ARG(fido_cbor_info_extensions_len
),
89 DLSYM_ARG(fido_cbor_info_extensions_ptr
),
90 DLSYM_ARG(fido_cbor_info_free
),
91 DLSYM_ARG(fido_cbor_info_new
),
92 DLSYM_ARG(fido_cbor_info_options_len
),
93 DLSYM_ARG(fido_cbor_info_options_name_ptr
),
94 DLSYM_ARG(fido_cbor_info_options_value_ptr
),
95 DLSYM_ARG(fido_cred_free
),
96 DLSYM_ARG(fido_cred_id_len
),
97 DLSYM_ARG(fido_cred_id_ptr
),
98 DLSYM_ARG(fido_cred_new
),
99 DLSYM_ARG(fido_cred_set_clientdata_hash
),
100 DLSYM_ARG(fido_cred_set_extensions
),
101 DLSYM_ARG(fido_cred_set_prot
),
102 DLSYM_ARG(fido_cred_set_rk
),
103 DLSYM_ARG(fido_cred_set_rp
),
104 DLSYM_ARG(fido_cred_set_type
),
105 DLSYM_ARG(fido_cred_set_user
),
106 DLSYM_ARG(fido_cred_set_uv
),
107 DLSYM_ARG(fido_dev_free
),
108 DLSYM_ARG(fido_dev_get_assert
),
109 DLSYM_ARG(fido_dev_get_cbor_info
),
110 DLSYM_ARG(fido_dev_info_free
),
111 DLSYM_ARG(fido_dev_info_manifest
),
112 DLSYM_ARG(fido_dev_info_manufacturer_string
),
113 DLSYM_ARG(fido_dev_info_new
),
114 DLSYM_ARG(fido_dev_info_path
),
115 DLSYM_ARG(fido_dev_info_product_string
),
116 DLSYM_ARG(fido_dev_info_ptr
),
117 DLSYM_ARG(fido_dev_is_fido2
),
118 DLSYM_ARG(fido_dev_make_cred
),
119 DLSYM_ARG(fido_dev_new
),
120 DLSYM_ARG(fido_dev_open
),
121 DLSYM_ARG(fido_dev_close
),
122 DLSYM_ARG(fido_init
),
123 DLSYM_ARG(fido_set_log_handler
),
124 DLSYM_ARG(fido_strerr
));
128 sym_fido_init(FIDO_DEBUG
);
129 sym_fido_set_log_handler(fido_log_propagate_handler
);
134 static int verify_features(
137 int log_level
, /* the log level to use when device is not FIDO2 with hmac-secret */
139 bool *ret_has_client_pin
,
143 _cleanup_(fido_cbor_info_free_wrapper
) fido_cbor_info_t
*di
= NULL
;
144 bool found_extension
= false;
147 bool has_rk
= false, has_client_pin
= false, has_up
= true, has_uv
= false; /* Defaults are per table in 5.4 in FIDO2 spec */
154 if (!sym_fido_dev_is_fido2(d
))
155 return log_full_errno(log_level
, SYNTHETIC_ERRNO(ENODEV
),
156 "Specified device %s is not a FIDO2 device.", path
);
158 di
= sym_fido_cbor_info_new();
162 r
= sym_fido_dev_get_cbor_info(d
, di
);
164 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
165 "Failed to get CBOR device info for %s: %s", path
, sym_fido_strerr(r
));
167 e
= sym_fido_cbor_info_extensions_ptr(di
);
168 n
= sym_fido_cbor_info_extensions_len(di
);
169 for (size_t i
= 0; i
< n
; i
++) {
170 log_debug("FIDO2 device implements extension: %s", e
[i
]);
171 if (streq(e
[i
], "hmac-secret"))
172 found_extension
= true;
175 o
= sym_fido_cbor_info_options_name_ptr(di
);
176 b
= sym_fido_cbor_info_options_value_ptr(di
);
177 n
= sym_fido_cbor_info_options_len(di
);
178 for (size_t i
= 0; i
< n
; i
++) {
179 log_debug("FIDO2 device implements option %s: %s", o
[i
], yes_no(b
[i
]));
180 if (streq(o
[i
], "rk"))
182 if (streq(o
[i
], "clientPin"))
183 has_client_pin
= b
[i
];
184 if (streq(o
[i
], "up"))
186 if (streq(o
[i
], "uv"))
190 if (!found_extension
)
191 return log_full_errno(log_level
,
192 SYNTHETIC_ERRNO(ENODEV
),
193 "Specified device %s is a FIDO2 device, but does not support the required HMAC-SECRET extension.", path
);
195 log_debug("Has rk ('Resident Key') support: %s\n"
196 "Has clientPin support: %s\n"
197 "Has up ('User Presence') support: %s\n"
198 "Has uv ('User Verification') support: %s\n",
200 yes_no(has_client_pin
),
205 *ret_has_rk
= has_rk
;
206 if (ret_has_client_pin
)
207 *ret_has_client_pin
= has_client_pin
;
209 *ret_has_up
= has_up
;
211 *ret_has_uv
= has_uv
;
216 static int fido2_assert_set_basic_properties(
226 assert(cid_size
> 0);
228 r
= sym_fido_assert_set_rp(a
, rp_id
);
230 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
231 "Failed to set FIDO2 assertion ID: %s", sym_fido_strerr(r
));
233 r
= sym_fido_assert_set_clientdata_hash(a
, (const unsigned char[32]) {}, 32);
235 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
236 "Failed to set FIDO2 assertion client data hash: %s", sym_fido_strerr(r
));
238 r
= sym_fido_assert_allow_cred(a
, cid
, cid_size
);
240 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
241 "Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r
));
246 static int fido2_common_assert_error_handle(int r
) {
250 case FIDO_ERR_NO_CREDENTIALS
:
251 return log_error_errno(SYNTHETIC_ERRNO(EBADSLT
),
252 "Wrong security token; needed credentials not present on token.");
253 case FIDO_ERR_PIN_REQUIRED
:
254 return log_error_errno(SYNTHETIC_ERRNO(ENOANO
),
255 "Security token requires PIN.");
256 case FIDO_ERR_PIN_AUTH_BLOCKED
:
257 return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD
),
258 "PIN of security token is blocked, please remove/reinsert token.");
259 #ifdef FIDO_ERR_UV_BLOCKED
260 case FIDO_ERR_UV_BLOCKED
:
261 return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD
),
262 "Verification of security token is blocked, please remove/reinsert token.");
264 case FIDO_ERR_PIN_INVALID
:
265 return log_error_errno(SYNTHETIC_ERRNO(ENOLCK
),
266 "PIN of security token incorrect.");
267 case FIDO_ERR_UP_REQUIRED
:
268 return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE
),
269 "User presence required.");
270 case FIDO_ERR_ACTION_TIMEOUT
:
271 return log_error_errno(SYNTHETIC_ERRNO(ENOSTR
),
272 "Token action timeout. (User didn't interact with token quickly enough.)");
274 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
275 "Failed to ask token for assertion: %s", sym_fido_strerr(r
));
279 static int fido2_is_cred_in_specific_token(
284 Fido2EnrollFlags flags
) {
291 _cleanup_(fido_dev_free_wrapper
) fido_dev_t
*d
= NULL
;
292 _cleanup_(fido_assert_free_wrapper
) fido_assert_t
*a
= NULL
;
293 bool has_up
= false, has_uv
= false;
296 d
= sym_fido_dev_new();
300 r
= sym_fido_dev_open(d
, path
);
302 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
303 "Failed to open FIDO2 device %s: %s", path
, sym_fido_strerr(r
));
305 r
= verify_features(d
, path
, LOG_ERR
, NULL
, NULL
, &has_up
, &has_uv
);
306 if (r
== -ENODEV
) { /* Not a FIDO2 device or lacking HMAC-SECRET extension */
307 log_debug_errno(r
, "%s is not a FIDO2 device, or it lacks the hmac-secret extension", path
);
313 a
= sym_fido_assert_new();
317 r
= fido2_assert_set_basic_properties(a
, rp_id
, cid
, cid_size
);
321 /* FIDO2 devices may not support pre-flight requests with UV, at least not
322 * without user interaction [1]. As a result, let's just return true
323 * here and go ahead with trying the unlock directly.
325 * 1: https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#sctn-getAssert-authnr-alg
327 if (has_uv
&& FLAGS_SET(flags
, FIDO2ENROLL_UV
)) {
328 log_debug("Pre-flight requests with UV are unsupported, device: %s", path
);
332 /* According to CTAP 2.1 specification, to do pre-flight we need to set up option to false
333 * with optionally pinUvAuthParam in assertion[1]. But for authenticator that doesn't support
334 * user presence, once up option is present, the authenticator may return CTAP2_ERR_UNSUPPORTED_OPTION[2].
335 * So we simplely omit the option in that case.
337 * 1: https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#pre-flight
338 * 2: https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#authenticatorGetAssertion (in step 5)
341 r
= sym_fido_assert_set_up(a
, FIDO_OPT_FALSE
);
343 r
= sym_fido_assert_set_up(a
, FIDO_OPT_OMIT
);
345 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
346 "Failed to set assertion user presence: %s", sym_fido_strerr(r
));
348 r
= sym_fido_dev_get_assert(d
, a
, NULL
);
353 case FIDO_ERR_NO_CREDENTIALS
:
356 return fido2_common_assert_error_handle(r
);
360 static int fido2_use_hmac_hash_specific_token(
368 Fido2EnrollFlags required
, /* client pin/user presence required */
370 size_t *ret_hmac_size
) {
372 _cleanup_(fido_assert_free_wrapper
) fido_assert_t
*a
= NULL
;
373 _cleanup_(fido_dev_free_wrapper
) fido_dev_t
*d
= NULL
;
374 _cleanup_(erase_and_freep
) void *hmac_copy
= NULL
;
375 bool has_up
, has_client_pin
, has_uv
;
385 assert(ret_hmac_size
);
387 d
= sym_fido_dev_new();
391 r
= sym_fido_dev_open(d
, path
);
393 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
394 "Failed to open FIDO2 device %s: %s", path
, sym_fido_strerr(r
));
396 r
= verify_features(d
, path
, LOG_ERR
, NULL
, &has_client_pin
, &has_up
, &has_uv
);
400 if (!has_client_pin
&& FLAGS_SET(required
, FIDO2ENROLL_PIN
))
401 return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON
),
402 "PIN required to unlock, but FIDO2 device %s does not support it.",
405 if (!has_up
&& FLAGS_SET(required
, FIDO2ENROLL_UP
))
406 return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON
),
407 "User presence test required to unlock, but FIDO2 device %s does not support it.",
410 if (!has_uv
&& FLAGS_SET(required
, FIDO2ENROLL_UV
))
411 return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON
),
412 "User verification required to unlock, but FIDO2 device %s does not support it.",
415 a
= sym_fido_assert_new();
419 r
= sym_fido_assert_set_extensions(a
, FIDO_EXT_HMAC_SECRET
);
421 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
422 "Failed to enable HMAC-SECRET extension on FIDO2 assertion: %s", sym_fido_strerr(r
));
424 r
= sym_fido_assert_set_hmac_salt(a
, salt
, salt_size
);
426 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
427 "Failed to set salt on FIDO2 assertion: %s", sym_fido_strerr(r
));
429 r
= fido2_assert_set_basic_properties(a
, rp_id
, cid
, cid_size
);
433 log_info("Asking FIDO2 token for authentication.");
436 r
= sym_fido_assert_set_up(a
, FLAGS_SET(required
, FIDO2ENROLL_UP
) ? FIDO_OPT_TRUE
: FIDO_OPT_FALSE
);
438 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
439 "Failed to %s FIDO2 user presence test: %s",
440 enable_disable(FLAGS_SET(required
, FIDO2ENROLL_UP
)),
443 if (FLAGS_SET(required
, FIDO2ENROLL_UP
))
444 log_notice("%s%sPlease confirm presence on security token to unlock.",
445 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
446 emoji_enabled() ? " " : "");
449 if (has_uv
&& !FLAGS_SET(required
, FIDO2ENROLL_UV_OMIT
)) {
450 r
= sym_fido_assert_set_uv(a
, FLAGS_SET(required
, FIDO2ENROLL_UV
) ? FIDO_OPT_TRUE
: FIDO_OPT_FALSE
);
452 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
453 "Failed to %s FIDO2 user verification: %s",
454 enable_disable(FLAGS_SET(required
, FIDO2ENROLL_UV
)),
457 if (FLAGS_SET(required
, FIDO2ENROLL_UV
))
458 log_notice("%s%sPlease verify user on security token to unlock.",
459 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
460 emoji_enabled() ? " " : "");
464 bool retry_with_up
= false, retry_with_pin
= false;
466 if (FLAGS_SET(required
, FIDO2ENROLL_PIN
)) {
467 /* OK, we need a pin, try with all pins in turn */
468 if (strv_isempty(pins
))
469 r
= FIDO_ERR_PIN_REQUIRED
;
471 STRV_FOREACH(i
, pins
) {
472 r
= sym_fido_dev_get_assert(d
, a
, *i
);
473 if (r
!= FIDO_ERR_PIN_INVALID
)
478 r
= sym_fido_dev_get_assert(d
, a
, NULL
);
480 /* In some conditions, where a PIN or UP is required we might accept that. Let's check the
481 * conditions and if so try immediately again. */
485 case FIDO_ERR_UP_REQUIRED
:
486 /* So the token asked for "up". Try to turn it on, for compat with systemd 248 and try again. */
489 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
490 "Token asks for user presence test but doesn't advertise 'up' feature.");
492 if (FLAGS_SET(required
, FIDO2ENROLL_UP
))
493 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
494 "Token asks for user presence test but was already enabled.");
496 if (FLAGS_SET(required
, FIDO2ENROLL_UP_IF_NEEDED
)) {
497 log_notice("%s%sPlease confirm presence on security to unlock.",
498 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
499 emoji_enabled() ? " " : "");
500 retry_with_up
= true;
505 case FIDO_ERR_UNSUPPORTED_OPTION
:
506 /* AuthenTrend ATKey.Pro returns this instead of FIDO_ERR_UP_REQUIRED, let's handle
507 * it gracefully (also see below.) */
509 if (has_up
&& (required
& (FIDO2ENROLL_UP
|FIDO2ENROLL_UP_IF_NEEDED
)) == FIDO2ENROLL_UP_IF_NEEDED
) {
510 log_notice("%s%sGot unsupported option error when user presence test is turned off. Trying with user presence test turned on.",
511 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
512 emoji_enabled() ? " " : "");
513 retry_with_up
= true;
518 case FIDO_ERR_PIN_REQUIRED
:
519 /* A pin was requested. Maybe supply one, if we are configured to do so on request */
522 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
523 "Token asks for PIN but doesn't advertise 'clientPin' feature.");
525 if (FLAGS_SET(required
, FIDO2ENROLL_PIN
) && !strv_isempty(pins
))
526 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
527 "Token asks for PIN but one was already supplied.");
529 if ((required
& (FIDO2ENROLL_PIN
|FIDO2ENROLL_PIN_IF_NEEDED
)) == FIDO2ENROLL_PIN_IF_NEEDED
) {
530 /* If a PIN so far wasn't specified but is requested by the device, and
531 * FIDO2ENROLL_PIN_IF_NEEDED is set, then provide it */
532 log_debug("Retrying to create credential with PIN.");
533 retry_with_pin
= true;
542 if (!retry_with_up
&& !retry_with_pin
)
546 r
= sym_fido_assert_set_up(a
, FIDO_OPT_TRUE
);
548 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
549 "Failed to enable FIDO2 user presence test: %s", sym_fido_strerr(r
));
551 required
|= FIDO2ENROLL_UP
;
555 required
|= FIDO2ENROLL_PIN
;
558 r
= fido2_common_assert_error_handle(r
);
562 hmac
= sym_fido_assert_hmac_secret_ptr(a
, 0);
564 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to retrieve HMAC secret.");
566 hmac_size
= sym_fido_assert_hmac_secret_len(a
, 0);
568 hmac_copy
= memdup(hmac
, hmac_size
);
572 *ret_hmac
= TAKE_PTR(hmac_copy
);
573 *ret_hmac_size
= hmac_size
;
577 /* COSE_ECDH_ES256 is not usable with fido_cred_set_type() thus it's not listed here. */
578 static const char *fido2_algorithm_to_string(int alg
) {
591 int fido2_use_hmac_hash(
599 Fido2EnrollFlags required
, /* client pin/user presence required */
601 size_t *ret_hmac_size
) {
603 size_t allocated
= 64, found
= 0;
604 fido_dev_info_t
*di
= NULL
;
607 r
= dlopen_libfido2();
609 return log_error_errno(r
, "FIDO2 support is not installed.");
612 r
= fido2_is_cred_in_specific_token(device
, rp_id
, cid
, cid_size
, required
);
614 /* The caller is expected to attempt other key slots in this case,
615 * therefore, do not spam the console with error logs here. */
616 return log_debug_errno(SYNTHETIC_ERRNO(EBADSLT
),
617 "The credential is not in the token %s.", device
);
619 return log_error_errno(r
, "Token returned error during pre-flight: %m");
621 return fido2_use_hmac_hash_specific_token(device
, rp_id
, salt
, salt_size
, cid
, cid_size
, pins
, required
, ret_hmac
, ret_hmac_size
);
624 di
= sym_fido_dev_info_new(allocated
);
628 r
= sym_fido_dev_info_manifest(di
, allocated
, &found
);
629 if (r
== FIDO_ERR_INTERNAL
) {
630 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
631 r
= log_debug_errno(SYNTHETIC_ERRNO(EAGAIN
), "Got FIDO_ERR_INTERNAL, assuming no devices.");
635 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to enumerate FIDO2 devices: %s", sym_fido_strerr(r
));
639 for (size_t i
= 0; i
< found
; i
++) {
640 const fido_dev_info_t
*entry
;
643 entry
= sym_fido_dev_info_ptr(di
, i
);
645 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
),
646 "Failed to get device information for FIDO device %zu.", i
);
650 path
= sym_fido_dev_info_path(entry
);
652 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
),
653 "Failed to query FIDO device path.");
657 r
= fido2_is_cred_in_specific_token(path
, rp_id
, cid
, cid_size
, required
);
659 log_error_errno(r
, "Token returned error during pre-flight: %m");
663 log_debug("The credential is not in the token %s, skipping.", path
);
667 r
= fido2_use_hmac_hash_specific_token(path
, rp_id
, salt
, salt_size
, cid
, cid_size
, pins
, required
, ret_hmac
, ret_hmac_size
);
669 -EBADSLT
, /* device doesn't understand our credential hash */
670 -ENODEV
/* device is not a FIDO2 device with HMAC-SECRET */))
677 sym_fido_dev_info_free(&di
, allocated
);
681 #define FIDO2_SALT_SIZE 32
683 int fido2_generate_hmac_hash(
687 const void *user_id
, size_t user_id_len
,
688 const char *user_name
,
689 const char *user_display_name
,
690 const char *user_icon
,
691 const char *askpw_icon
,
692 const char *askpw_credential
,
693 Fido2EnrollFlags lock_with
,
695 void **ret_cid
, size_t *ret_cid_size
,
696 void **ret_salt
, size_t *ret_salt_size
,
697 void **ret_secret
, size_t *ret_secret_size
,
699 Fido2EnrollFlags
*ret_locked_with
) {
701 _cleanup_(erase_and_freep
) void *salt
= NULL
, *secret_copy
= NULL
;
702 _cleanup_(fido_assert_free_wrapper
) fido_assert_t
*a
= NULL
;
703 _cleanup_(fido_cred_free_wrapper
) fido_cred_t
*c
= NULL
;
704 _cleanup_(fido_dev_free_wrapper
) fido_dev_t
*d
= NULL
;
705 _cleanup_(erase_and_freep
) char *used_pin
= NULL
;
706 bool has_rk
, has_client_pin
, has_up
, has_uv
;
707 _cleanup_free_
char *cid_copy
= NULL
;
708 size_t cid_size
, secret_size
;
709 const void *cid
, *secret
;
714 assert(ret_cid_size
);
716 assert(ret_salt_size
);
718 assert(ret_secret_size
);
720 /* Construction is like this: we generate a salt of 32 bytes. We then ask the FIDO2 device to
721 * HMAC-SHA256 it for us with its internal key. The result is the key used by LUKS and account
722 * authentication. LUKS and UNIX password auth all do their own salting before hashing, so that FIDO2
723 * device never sees the volume key.
725 * S = HMAC-SHA256(I, D)
727 * with: S → LUKS/account authentication key (never stored)
728 * I → internal key on FIDO2 device (stored in the FIDO2 device)
729 * D → salt we generate here (stored in the privileged part of the JSON record)
734 assert((lock_with
& ~(FIDO2ENROLL_PIN
|FIDO2ENROLL_UP
|FIDO2ENROLL_UV
)) == 0);
736 r
= dlopen_libfido2();
738 return log_error_errno(r
, "FIDO2 token support is not installed.");
740 salt
= malloc(FIDO2_SALT_SIZE
);
744 r
= crypto_random_bytes(salt
, FIDO2_SALT_SIZE
);
746 return log_error_errno(r
, "Failed to generate salt: %m");
748 d
= sym_fido_dev_new();
752 r
= sym_fido_dev_open(d
, device
);
754 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
755 "Failed to open FIDO2 device %s: %s", device
, sym_fido_strerr(r
));
757 r
= verify_features(d
, device
, LOG_ERR
, &has_rk
, &has_client_pin
, &has_up
, &has_uv
);
761 /* While enrolling degrade gracefully if the requested feature set isn't available, but let the user know */
762 if (!has_client_pin
&& FLAGS_SET(lock_with
, FIDO2ENROLL_PIN
)) {
763 log_notice("Requested to lock with PIN, but FIDO2 device %s does not support it, disabling.", device
);
764 lock_with
&= ~FIDO2ENROLL_PIN
;
767 if (!has_up
&& FLAGS_SET(lock_with
, FIDO2ENROLL_UP
)) {
768 log_notice("Locking with user presence test requested, but FIDO2 device %s does not support it, disabling.", device
);
769 lock_with
&= ~FIDO2ENROLL_UP
;
772 if (!has_uv
&& FLAGS_SET(lock_with
, FIDO2ENROLL_UV
)) {
773 log_notice("Locking with user verification test requested, but FIDO2 device %s does not support it, disabling.", device
);
774 lock_with
&= ~FIDO2ENROLL_UV
;
777 c
= sym_fido_cred_new();
781 int extensions
= FIDO_EXT_HMAC_SECRET
;
782 if (FLAGS_SET(lock_with
, FIDO2ENROLL_PIN
) || FLAGS_SET(lock_with
, FIDO2ENROLL_UV
)) {
783 /* Attempt to use the "cred protect" extension, requiring user verification (UV) for this
784 * credential. If the authenticator doesn't support the extension, it will be ignored. */
785 extensions
|= FIDO_EXT_CRED_PROTECT
;
787 r
= sym_fido_cred_set_prot(c
, FIDO_CRED_PROT_UV_REQUIRED
);
789 log_warning("Failed to set protection level on FIDO2 credential, ignoring: %s", sym_fido_strerr(r
));
792 r
= sym_fido_cred_set_extensions(c
, extensions
);
794 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
795 "Failed to enable extensions on FIDO2 credential: %s", sym_fido_strerr(r
));
797 r
= sym_fido_cred_set_rp(c
, rp_id
, rp_name
);
799 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
800 "Failed to set FIDO2 credential relying party ID/name: %s", sym_fido_strerr(r
));
802 r
= sym_fido_cred_set_type(c
, cred_alg
);
804 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
805 "Failed to set FIDO2 credential type to %s: %s", fido2_algorithm_to_string(cred_alg
), sym_fido_strerr(r
));
807 r
= sym_fido_cred_set_user(
809 user_id
, user_id_len
,
814 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
815 "Failed to set FIDO2 credential user data: %s", sym_fido_strerr(r
));
817 r
= sym_fido_cred_set_clientdata_hash(c
, (const unsigned char[32]) {}, 32);
819 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
820 "Failed to set FIDO2 client data hash: %s", sym_fido_strerr(r
));
823 r
= sym_fido_cred_set_rk(c
, FIDO_OPT_FALSE
);
825 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
826 "Failed to turn off FIDO2 resident key option of credential: %s", sym_fido_strerr(r
));
830 r
= sym_fido_cred_set_uv(c
, FIDO_OPT_FALSE
);
832 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
833 "Failed to turn off FIDO2 user verification option of credential: %s", sym_fido_strerr(r
));
836 /* As per specification "up" is assumed to be implicit when making credentials, hence we don't
837 * explicitly enable/disable it here */
839 log_info("Initializing FIDO2 credential on security token.");
841 if (has_uv
|| has_up
)
842 log_notice("%s%s(Hint: This might require confirmation of user presence on security token.)",
843 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
844 emoji_enabled() ? " " : "");
846 /* If we are using the user PIN, then we must pass that PIN to the get_assertion call below, or
847 * the authenticator will use the non-user-verification HMAC secret (which differs from the one when
848 * the PIN is passed).
850 * Rather than potentially trying and failing to create the credential, just collect the PIN first
851 * and then pass it to both the make_credential and the get_assertion operations. */
852 if (FLAGS_SET(lock_with
, FIDO2ENROLL_PIN
))
853 r
= FIDO_ERR_PIN_REQUIRED
;
855 r
= sym_fido_dev_make_cred(d
, c
, NULL
);
857 if (r
== FIDO_ERR_PIN_REQUIRED
) {
860 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
861 "Token asks for PIN but doesn't advertise 'clientPin' feature.");
864 _cleanup_strv_free_erase_
char **pin
= NULL
;
865 AskPasswordRequest req
= {
866 .message
= "Please enter security token PIN:",
868 .keyring
= "fido2-pin",
869 .credential
= askpw_credential
,
872 r
= ask_password_auto(&req
, USEC_INFINITY
, /* flags= */ 0, &pin
);
874 return log_error_errno(r
, "Failed to acquire user PIN: %m");
876 r
= FIDO_ERR_PIN_INVALID
;
877 STRV_FOREACH(i
, pin
) {
879 log_notice("PIN may not be empty.");
883 r
= sym_fido_dev_make_cred(d
, c
, *i
);
885 used_pin
= strdup(*i
);
890 if (r
!= FIDO_ERR_PIN_INVALID
)
894 if (r
!= FIDO_ERR_PIN_INVALID
)
897 log_notice("PIN incorrect, please try again.");
900 if (r
== FIDO_ERR_PIN_AUTH_BLOCKED
)
901 return log_notice_errno(SYNTHETIC_ERRNO(EPERM
),
902 "Token PIN is currently blocked, please remove and reinsert token.");
903 #ifdef FIDO_ERR_UV_BLOCKED
904 if (r
== FIDO_ERR_UV_BLOCKED
)
905 return log_notice_errno(SYNTHETIC_ERRNO(EPERM
),
906 "Token verification is currently blocked, please remove and reinsert token.");
908 if (r
== FIDO_ERR_ACTION_TIMEOUT
)
909 return log_error_errno(SYNTHETIC_ERRNO(ENOSTR
),
910 "Token action timeout. (User didn't interact with token quickly enough.)");
911 if (r
== FIDO_ERR_UNSUPPORTED_ALGORITHM
)
912 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
913 "Token doesn't support credential algorithm %s.", fido2_algorithm_to_string(cred_alg
));
915 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
916 "Failed to generate FIDO2 credential: %s", sym_fido_strerr(r
));
918 cid
= sym_fido_cred_id_ptr(c
);
920 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to get FIDO2 credential ID.");
922 cid_size
= sym_fido_cred_id_len(c
);
924 a
= sym_fido_assert_new();
928 r
= sym_fido_assert_set_extensions(a
, FIDO_EXT_HMAC_SECRET
);
930 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
931 "Failed to enable HMAC-SECRET extension on FIDO2 assertion: %s", sym_fido_strerr(r
));
933 r
= sym_fido_assert_set_hmac_salt(a
, salt
, FIDO2_SALT_SIZE
);
935 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
936 "Failed to set salt on FIDO2 assertion: %s", sym_fido_strerr(r
));
938 r
= fido2_assert_set_basic_properties(a
, rp_id
, cid
, cid_size
);
942 log_info("Generating secret key on FIDO2 security token.");
945 r
= sym_fido_assert_set_up(a
, FLAGS_SET(lock_with
, FIDO2ENROLL_UP
) ? FIDO_OPT_TRUE
: FIDO_OPT_FALSE
);
947 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
948 "Failed to %s FIDO2 user presence test: %s",
949 enable_disable(FLAGS_SET(lock_with
, FIDO2ENROLL_UP
)),
952 if (FLAGS_SET(lock_with
, FIDO2ENROLL_UP
))
953 log_notice("%s%sIn order to allow secret key generation, please confirm presence on security token.",
954 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
955 emoji_enabled() ? " " : "");
959 r
= sym_fido_assert_set_uv(a
, FLAGS_SET(lock_with
, FIDO2ENROLL_UV
) ? FIDO_OPT_TRUE
: FIDO_OPT_FALSE
);
961 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
962 "Failed to %s FIDO user verification: %s",
963 enable_disable(FLAGS_SET(lock_with
, FIDO2ENROLL_UV
)),
966 if (FLAGS_SET(lock_with
, FIDO2ENROLL_UV
))
967 log_notice("%s%sIn order to allow secret key generation, please verify user on security token.",
968 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
969 emoji_enabled() ? " " : "");
973 bool retry_with_up
= false, retry_with_pin
= false;
975 r
= sym_fido_dev_get_assert(d
, a
, FLAGS_SET(lock_with
, FIDO2ENROLL_PIN
) ? used_pin
: NULL
);
979 case FIDO_ERR_UP_REQUIRED
:
980 /* If the token asks for "up" when we turn off, then this might be a feature that
981 * isn't optional. Let's enable it */
984 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
985 "Token asks for user presence test but doesn't advertise 'up' feature.");
987 if (FLAGS_SET(lock_with
, FIDO2ENROLL_UP
))
988 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
989 "Token asks for user presence test but was already enabled.");
991 log_notice("%s%sLocking without user presence test requested, but FIDO2 device %s requires it, enabling.",
992 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
993 emoji_enabled() ? " " : "",
996 retry_with_up
= true;
999 case FIDO_ERR_UNSUPPORTED_OPTION
:
1000 /* AuthenTrend ATKey.Pro says it supports "up", but if we disable it it will fail
1001 * with FIDO_ERR_UNSUPPORTED_OPTION, probably because it isn't actually
1002 * optional. Let's see if turning it on works. This is very similar to the
1003 * FIDO_ERR_UP_REQUIRED case, but since the error is so vague we implement it
1004 * slightly more defensively. */
1006 if (has_up
&& !FLAGS_SET(lock_with
, FIDO2ENROLL_UP
)) {
1007 log_notice("%s%sGot unsupported option error when user presence test is turned off. Trying with user presence test turned on.",
1008 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
1009 emoji_enabled() ? " " : "");
1010 retry_with_up
= true;
1015 case FIDO_ERR_PIN_REQUIRED
:
1016 if (!has_client_pin
)
1017 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1018 "Token asks for client PIN check but doesn't advertise 'clientPin' feature.");
1020 if (FLAGS_SET(lock_with
, FIDO2ENROLL_PIN
))
1021 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1022 "Token asks for user client PIN check but was already enabled.");
1024 log_debug("Token requires PIN for assertion, enabling.");
1025 retry_with_pin
= true;
1032 if (!retry_with_up
&& !retry_with_pin
)
1035 if (retry_with_up
) {
1036 r
= sym_fido_assert_set_up(a
, FIDO_OPT_TRUE
);
1038 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to enable FIDO2 user presence test: %s", sym_fido_strerr(r
));
1040 lock_with
|= FIDO2ENROLL_UP
;
1044 lock_with
|= FIDO2ENROLL_PIN
;
1047 if (r
== FIDO_ERR_ACTION_TIMEOUT
)
1048 return log_error_errno(SYNTHETIC_ERRNO(ENOSTR
),
1049 "Token action timeout. (User didn't interact with token quickly enough.)");
1051 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
1052 "Failed to ask token for assertion: %s", sym_fido_strerr(r
));
1054 secret
= sym_fido_assert_hmac_secret_ptr(a
, 0);
1056 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to retrieve HMAC secret.");
1058 secret_size
= sym_fido_assert_hmac_secret_len(a
, 0);
1060 secret_copy
= memdup(secret
, secret_size
);
1064 cid_copy
= memdup(cid
, cid_size
);
1068 *ret_cid
= TAKE_PTR(cid_copy
);
1069 *ret_cid_size
= cid_size
;
1070 *ret_salt
= TAKE_PTR(salt
);
1071 *ret_salt_size
= FIDO2_SALT_SIZE
;
1072 *ret_secret
= TAKE_PTR(secret_copy
);
1073 *ret_secret_size
= secret_size
;
1076 *ret_usedpin
= TAKE_PTR(used_pin
);
1078 if (ret_locked_with
)
1079 *ret_locked_with
= lock_with
;
1086 static int check_device_is_fido2_with_hmac_secret(const char *path
) {
1087 _cleanup_(fido_dev_free_wrapper
) fido_dev_t
*d
= NULL
;
1090 d
= sym_fido_dev_new();
1094 r
= sym_fido_dev_open(d
, path
);
1096 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
1097 "Failed to open FIDO2 device %s: %s", path
, sym_fido_strerr(r
));
1099 r
= verify_features(d
, path
, LOG_DEBUG
, NULL
, NULL
, NULL
, NULL
);
1100 if (r
== -ENODEV
) /* Not a FIDO2 device, or not implementing 'hmac-secret' */
1109 int fido2_list_devices(void) {
1111 _cleanup_(table_unrefp
) Table
*t
= NULL
;
1112 size_t allocated
= 64, found
= 0;
1113 fido_dev_info_t
*di
= NULL
;
1116 r
= dlopen_libfido2();
1118 return log_error_errno(r
, "FIDO2 token support is not installed.");
1120 di
= sym_fido_dev_info_new(allocated
);
1124 r
= sym_fido_dev_info_manifest(di
, allocated
, &found
);
1125 if (r
== FIDO_ERR_INTERNAL
|| (r
== FIDO_OK
&& found
== 0)) {
1126 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
1127 log_info("No FIDO2 devices found.");
1132 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to enumerate FIDO2 devices: %s", sym_fido_strerr(r
));
1136 t
= table_new("path", "manufacturer", "product");
1142 for (size_t i
= 0; i
< found
; i
++) {
1143 const fido_dev_info_t
*entry
;
1145 entry
= sym_fido_dev_info_ptr(di
, i
);
1147 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
),
1148 "Failed to get device information for FIDO device %zu.", i
);
1152 r
= check_device_is_fido2_with_hmac_secret(sym_fido_dev_info_path(entry
));
1160 TABLE_PATH
, sym_fido_dev_info_path(entry
),
1161 TABLE_STRING
, sym_fido_dev_info_manufacturer_string(entry
),
1162 TABLE_STRING
, sym_fido_dev_info_product_string(entry
));
1164 table_log_add_error(r
);
1169 r
= table_print(t
, stdout
);
1171 log_error_errno(r
, "Failed to show device table: %m");
1178 sym_fido_dev_info_free(&di
, allocated
);
1181 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
1182 "FIDO2 tokens not supported on this build.");
1186 int fido2_find_device_auto(char **ret
) {
1188 _cleanup_free_
char *copy
= NULL
;
1189 size_t di_size
= 64, found
= 0;
1190 const fido_dev_info_t
*entry
;
1191 fido_dev_info_t
*di
= NULL
;
1195 r
= dlopen_libfido2();
1197 return log_error_errno(r
, "FIDO2 token support is not installed.");
1199 di
= sym_fido_dev_info_new(di_size
);
1203 r
= sym_fido_dev_info_manifest(di
, di_size
, &found
);
1204 if (r
== FIDO_ERR_INTERNAL
|| (r
== FIDO_OK
&& found
== 0)) {
1205 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
1206 r
= log_error_errno(SYNTHETIC_ERRNO(ENODEV
), "No FIDO devices found.");
1210 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to enumerate FIDO devices: %s", sym_fido_strerr(r
));
1214 r
= log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ
), "More than one FIDO device found.");
1218 entry
= sym_fido_dev_info_ptr(di
, 0);
1220 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
),
1221 "Failed to get device information for FIDO device 0.");
1225 r
= check_device_is_fido2_with_hmac_secret(sym_fido_dev_info_path(entry
));
1229 r
= log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
), "FIDO device discovered does not implement FIDO2 with 'hmac-secret' extension.");
1233 path
= sym_fido_dev_info_path(entry
);
1235 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
),
1236 "Failed to query FIDO device path.");
1240 copy
= strdup(path
);
1246 *ret
= TAKE_PTR(copy
);
1250 sym_fido_dev_info_free(&di
, di_size
);
1253 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
1254 "FIDO2 tokens not supported on this build.");
1258 int fido2_have_device(const char *device
) {
1260 size_t allocated
= 64, found
= 0;
1261 fido_dev_info_t
*di
= NULL
;
1264 /* Return == 0 if not devices are found, > 0 if at least one is found */
1266 r
= dlopen_libfido2();
1268 return log_error_errno(r
, "FIDO2 support is not installed.");
1271 if (access(device
, F_OK
) < 0) {
1272 if (errno
== ENOENT
)
1275 return log_error_errno(errno
, "Failed to determine whether device '%s' exists: %m", device
);
1281 di
= sym_fido_dev_info_new(allocated
);
1285 r
= sym_fido_dev_info_manifest(di
, allocated
, &found
);
1286 if (r
== FIDO_ERR_INTERNAL
) {
1287 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
1292 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to enumerate FIDO2 devices: %s", sym_fido_strerr(r
));
1299 sym_fido_dev_info_free(&di
, allocated
);
1302 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
1303 "FIDO2 tokens not supported on this build.");
1308 int parse_fido2_algorithm(const char *s
, int *ret
) {
1313 if (streq(s
, "es256"))
1315 else if (streq(s
, "rs256"))
1317 else if (streq(s
, "eddsa"))