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 int (*sym_fido_assert_allow_cred
)(fido_assert_t
*, const unsigned char *, size_t) = NULL
;
20 void (*sym_fido_assert_free
)(fido_assert_t
**) = NULL
;
21 size_t (*sym_fido_assert_hmac_secret_len
)(const fido_assert_t
*, size_t) = NULL
;
22 const unsigned char* (*sym_fido_assert_hmac_secret_ptr
)(const fido_assert_t
*, size_t) = NULL
;
23 fido_assert_t
* (*sym_fido_assert_new
)(void) = NULL
;
24 int (*sym_fido_assert_set_clientdata_hash
)(fido_assert_t
*, const unsigned char *, size_t) = NULL
;
25 int (*sym_fido_assert_set_extensions
)(fido_assert_t
*, int) = NULL
;
26 int (*sym_fido_assert_set_hmac_salt
)(fido_assert_t
*, const unsigned char *, size_t) = NULL
;
27 int (*sym_fido_assert_set_rp
)(fido_assert_t
*, const char *) = NULL
;
28 int (*sym_fido_assert_set_up
)(fido_assert_t
*, fido_opt_t
) = NULL
;
29 int (*sym_fido_assert_set_uv
)(fido_assert_t
*, fido_opt_t
) = NULL
;
30 size_t (*sym_fido_cbor_info_extensions_len
)(const fido_cbor_info_t
*) = NULL
;
31 char **(*sym_fido_cbor_info_extensions_ptr
)(const fido_cbor_info_t
*) = NULL
;
32 void (*sym_fido_cbor_info_free
)(fido_cbor_info_t
**) = NULL
;
33 fido_cbor_info_t
* (*sym_fido_cbor_info_new
)(void) = NULL
;
34 size_t (*sym_fido_cbor_info_options_len
)(const fido_cbor_info_t
*) = NULL
;
35 char** (*sym_fido_cbor_info_options_name_ptr
)(const fido_cbor_info_t
*) = NULL
;
36 const bool* (*sym_fido_cbor_info_options_value_ptr
)(const fido_cbor_info_t
*) = NULL
;
37 void (*sym_fido_cred_free
)(fido_cred_t
**) = NULL
;
38 size_t (*sym_fido_cred_id_len
)(const fido_cred_t
*) = NULL
;
39 const unsigned char* (*sym_fido_cred_id_ptr
)(const fido_cred_t
*) = NULL
;
40 fido_cred_t
* (*sym_fido_cred_new
)(void) = NULL
;
41 int (*sym_fido_cred_set_clientdata_hash
)(fido_cred_t
*, const unsigned char *, size_t) = NULL
;
42 int (*sym_fido_cred_set_extensions
)(fido_cred_t
*, int) = NULL
;
43 int (*sym_fido_cred_set_rk
)(fido_cred_t
*, fido_opt_t
) = NULL
;
44 int (*sym_fido_cred_set_rp
)(fido_cred_t
*, const char *, const char *) = NULL
;
45 int (*sym_fido_cred_set_type
)(fido_cred_t
*, int) = NULL
;
46 int (*sym_fido_cred_set_user
)(fido_cred_t
*, const unsigned char *, size_t, const char *, const char *, const char *) = NULL
;
47 int (*sym_fido_cred_set_uv
)(fido_cred_t
*, fido_opt_t
) = NULL
;
48 void (*sym_fido_dev_free
)(fido_dev_t
**) = NULL
;
49 int (*sym_fido_dev_get_assert
)(fido_dev_t
*, fido_assert_t
*, const char *) = NULL
;
50 int (*sym_fido_dev_get_cbor_info
)(fido_dev_t
*, fido_cbor_info_t
*) = NULL
;
51 void (*sym_fido_dev_info_free
)(fido_dev_info_t
**, size_t) = NULL
;
52 int (*sym_fido_dev_info_manifest
)(fido_dev_info_t
*, size_t, size_t *) = NULL
;
53 const char* (*sym_fido_dev_info_manufacturer_string
)(const fido_dev_info_t
*) = NULL
;
54 const char* (*sym_fido_dev_info_product_string
)(const fido_dev_info_t
*) = NULL
;
55 fido_dev_info_t
* (*sym_fido_dev_info_new
)(size_t) = NULL
;
56 const char* (*sym_fido_dev_info_path
)(const fido_dev_info_t
*) = NULL
;
57 const fido_dev_info_t
* (*sym_fido_dev_info_ptr
)(const fido_dev_info_t
*, size_t) = NULL
;
58 bool (*sym_fido_dev_is_fido2
)(const fido_dev_t
*) = NULL
;
59 int (*sym_fido_dev_make_cred
)(fido_dev_t
*, fido_cred_t
*, const char *) = NULL
;
60 fido_dev_t
* (*sym_fido_dev_new
)(void) = NULL
;
61 int (*sym_fido_dev_open
)(fido_dev_t
*, const char *) = NULL
;
62 int (*sym_fido_dev_close
)(fido_dev_t
*) = NULL
;
63 const char* (*sym_fido_strerr
)(int) = NULL
;
65 int dlopen_libfido2(void) {
66 return dlopen_many_sym_or_warn(
67 &libfido2_dl
, "libfido2.so.1", LOG_DEBUG
,
68 DLSYM_ARG(fido_assert_allow_cred
),
69 DLSYM_ARG(fido_assert_free
),
70 DLSYM_ARG(fido_assert_hmac_secret_len
),
71 DLSYM_ARG(fido_assert_hmac_secret_ptr
),
72 DLSYM_ARG(fido_assert_new
),
73 DLSYM_ARG(fido_assert_set_clientdata_hash
),
74 DLSYM_ARG(fido_assert_set_extensions
),
75 DLSYM_ARG(fido_assert_set_hmac_salt
),
76 DLSYM_ARG(fido_assert_set_rp
),
77 DLSYM_ARG(fido_assert_set_up
),
78 DLSYM_ARG(fido_assert_set_uv
),
79 DLSYM_ARG(fido_cbor_info_extensions_len
),
80 DLSYM_ARG(fido_cbor_info_extensions_ptr
),
81 DLSYM_ARG(fido_cbor_info_free
),
82 DLSYM_ARG(fido_cbor_info_new
),
83 DLSYM_ARG(fido_cbor_info_options_len
),
84 DLSYM_ARG(fido_cbor_info_options_name_ptr
),
85 DLSYM_ARG(fido_cbor_info_options_value_ptr
),
86 DLSYM_ARG(fido_cred_free
),
87 DLSYM_ARG(fido_cred_id_len
),
88 DLSYM_ARG(fido_cred_id_ptr
),
89 DLSYM_ARG(fido_cred_new
),
90 DLSYM_ARG(fido_cred_set_clientdata_hash
),
91 DLSYM_ARG(fido_cred_set_extensions
),
92 DLSYM_ARG(fido_cred_set_rk
),
93 DLSYM_ARG(fido_cred_set_rp
),
94 DLSYM_ARG(fido_cred_set_type
),
95 DLSYM_ARG(fido_cred_set_user
),
96 DLSYM_ARG(fido_cred_set_uv
),
97 DLSYM_ARG(fido_dev_free
),
98 DLSYM_ARG(fido_dev_get_assert
),
99 DLSYM_ARG(fido_dev_get_cbor_info
),
100 DLSYM_ARG(fido_dev_info_free
),
101 DLSYM_ARG(fido_dev_info_manifest
),
102 DLSYM_ARG(fido_dev_info_manufacturer_string
),
103 DLSYM_ARG(fido_dev_info_new
),
104 DLSYM_ARG(fido_dev_info_path
),
105 DLSYM_ARG(fido_dev_info_product_string
),
106 DLSYM_ARG(fido_dev_info_ptr
),
107 DLSYM_ARG(fido_dev_is_fido2
),
108 DLSYM_ARG(fido_dev_make_cred
),
109 DLSYM_ARG(fido_dev_new
),
110 DLSYM_ARG(fido_dev_open
),
111 DLSYM_ARG(fido_dev_close
),
112 DLSYM_ARG(fido_strerr
));
115 static int verify_features(
118 int log_level
, /* the log level to use when device is not FIDO2 with hmac-secret */
120 bool *ret_has_client_pin
,
124 _cleanup_(fido_cbor_info_free_wrapper
) fido_cbor_info_t
*di
= NULL
;
125 bool found_extension
= false;
128 bool has_rk
= false, has_client_pin
= false, has_up
= true, has_uv
= false; /* Defaults are per table in 5.4 in FIDO2 spec */
135 if (!sym_fido_dev_is_fido2(d
))
136 return log_full_errno(log_level
,
137 SYNTHETIC_ERRNO(ENODEV
),
138 "Specified device %s is not a FIDO2 device.", path
);
140 di
= sym_fido_cbor_info_new();
144 r
= sym_fido_dev_get_cbor_info(d
, di
);
146 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
147 "Failed to get CBOR device info for %s: %s", path
, sym_fido_strerr(r
));
149 e
= sym_fido_cbor_info_extensions_ptr(di
);
150 n
= sym_fido_cbor_info_extensions_len(di
);
151 for (size_t i
= 0; i
< n
; i
++) {
152 log_debug("FIDO2 device implements extension: %s", e
[i
]);
153 if (streq(e
[i
], "hmac-secret"))
154 found_extension
= true;
157 o
= sym_fido_cbor_info_options_name_ptr(di
);
158 b
= sym_fido_cbor_info_options_value_ptr(di
);
159 n
= sym_fido_cbor_info_options_len(di
);
160 for (size_t i
= 0; i
< n
; i
++) {
161 log_debug("FIDO2 device implements option %s: %s", o
[i
], yes_no(b
[i
]));
162 if (streq(o
[i
], "rk"))
164 if (streq(o
[i
], "clientPin"))
165 has_client_pin
= b
[i
];
166 if (streq(o
[i
], "up"))
168 if (streq(o
[i
], "uv"))
172 if (!found_extension
)
173 return log_full_errno(log_level
,
174 SYNTHETIC_ERRNO(ENODEV
),
175 "Specified device %s is a FIDO2 device, but does not support the required HMAC-SECRET extension.", path
);
177 log_debug("Has rk ('Resident Key') support: %s\n"
178 "Has clientPin support: %s\n"
179 "Has up ('User Presence') support: %s\n"
180 "Has uv ('User Verification') support: %s\n",
182 yes_no(has_client_pin
),
187 *ret_has_rk
= has_rk
;
188 if (ret_has_client_pin
)
189 *ret_has_client_pin
= has_client_pin
;
191 *ret_has_up
= has_up
;
193 *ret_has_uv
= has_uv
;
198 static int fido2_use_hmac_hash_specific_token(
206 Fido2EnrollFlags required
, /* client pin/user presence required */
208 size_t *ret_hmac_size
) {
210 _cleanup_(fido_assert_free_wrapper
) fido_assert_t
*a
= NULL
;
211 _cleanup_(fido_dev_free_wrapper
) fido_dev_t
*d
= NULL
;
212 _cleanup_(erase_and_freep
) void *hmac_copy
= NULL
;
213 bool has_up
, has_client_pin
, has_uv
;
223 assert(ret_hmac_size
);
225 d
= sym_fido_dev_new();
229 r
= sym_fido_dev_open(d
, path
);
231 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
232 "Failed to open FIDO2 device %s: %s", path
, sym_fido_strerr(r
));
234 r
= verify_features(d
, path
, LOG_ERR
, NULL
, &has_client_pin
, &has_up
, &has_uv
);
238 if (!has_client_pin
&& FLAGS_SET(required
, FIDO2ENROLL_PIN
))
239 return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON
),
240 "PIN required to unlock, but FIDO2 device %s does not support it.",
243 if (!has_up
&& FLAGS_SET(required
, FIDO2ENROLL_UP
))
244 return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON
),
245 "User presence test required to unlock, but FIDO2 device %s does not support it.",
248 if (!has_uv
&& FLAGS_SET(required
, FIDO2ENROLL_UV
))
249 return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON
),
250 "User verification required to unlock, but FIDO2 device %s does not support it.",
253 a
= sym_fido_assert_new();
257 r
= sym_fido_assert_set_extensions(a
, FIDO_EXT_HMAC_SECRET
);
259 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
260 "Failed to enable HMAC-SECRET extension on FIDO2 assertion: %s", sym_fido_strerr(r
));
262 r
= sym_fido_assert_set_hmac_salt(a
, salt
, salt_size
);
264 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
265 "Failed to set salt on FIDO2 assertion: %s", sym_fido_strerr(r
));
267 r
= sym_fido_assert_set_rp(a
, rp_id
);
269 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
270 "Failed to set FIDO2 assertion ID: %s", sym_fido_strerr(r
));
272 r
= sym_fido_assert_set_clientdata_hash(a
, (const unsigned char[32]) {}, 32);
274 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
275 "Failed to set FIDO2 assertion client data hash: %s", sym_fido_strerr(r
));
277 r
= sym_fido_assert_allow_cred(a
, cid
, cid_size
);
279 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
280 "Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r
));
282 log_info("Asking FIDO2 token for authentication.");
285 r
= sym_fido_assert_set_up(a
, FLAGS_SET(required
, FIDO2ENROLL_UP
) ? FIDO_OPT_TRUE
: FIDO_OPT_FALSE
);
287 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
288 "Failed to %s FIDO2 user presence test: %s",
289 enable_disable(FLAGS_SET(required
, FIDO2ENROLL_UP
)),
292 if (FLAGS_SET(required
, FIDO2ENROLL_UP
))
293 log_notice("%s%sPlease confirm presence on security token to unlock.",
294 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
295 emoji_enabled() ? " " : "");
298 if (has_uv
&& !FLAGS_SET(required
, FIDO2ENROLL_UV_OMIT
)) {
299 r
= sym_fido_assert_set_uv(a
, FLAGS_SET(required
, FIDO2ENROLL_UV
) ? FIDO_OPT_TRUE
: FIDO_OPT_FALSE
);
301 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
302 "Failed to %s FIDO2 user verification: %s",
303 enable_disable(FLAGS_SET(required
, FIDO2ENROLL_UV
)),
306 if (FLAGS_SET(required
, FIDO2ENROLL_UV
))
307 log_notice("%s%sPlease verify user on security token to unlock.",
308 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
309 emoji_enabled() ? " " : "");
313 bool retry_with_up
= false, retry_with_pin
= false;
315 if (FLAGS_SET(required
, FIDO2ENROLL_PIN
)) {
316 /* OK, we need a pin, try with all pins in turn */
317 if (strv_isempty(pins
))
318 r
= FIDO_ERR_PIN_REQUIRED
;
320 STRV_FOREACH(i
, pins
) {
321 r
= sym_fido_dev_get_assert(d
, a
, *i
);
322 if (r
!= FIDO_ERR_PIN_INVALID
)
327 r
= sym_fido_dev_get_assert(d
, a
, NULL
);
329 /* In some conditions, where a PIN or UP is required we might accept that. Let's check the
330 * conditions and if so try immediately again. */
334 case FIDO_ERR_UP_REQUIRED
:
335 /* So the token asked for "up". Try to turn it on, for compat with systemd 248 and try again. */
338 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
339 "Token asks for user presence test but doesn't advertise 'up' feature.");
341 if (FLAGS_SET(required
, FIDO2ENROLL_UP
))
342 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
343 "Token asks for user presence test but was already enabled.");
345 if (FLAGS_SET(required
, FIDO2ENROLL_UP_IF_NEEDED
)) {
346 log_notice("%s%sPlease confirm presence on security to unlock.",
347 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
348 emoji_enabled() ? " " : "");
349 retry_with_up
= true;
354 case FIDO_ERR_UNSUPPORTED_OPTION
:
355 /* AuthenTrend ATKey.Pro returns this instead of FIDO_ERR_UP_REQUIRED, let's handle
356 * it gracefully (also see below.) */
358 if (has_up
&& (required
& (FIDO2ENROLL_UP
|FIDO2ENROLL_UP_IF_NEEDED
)) == FIDO2ENROLL_UP_IF_NEEDED
) {
359 log_notice("%s%sGot unsupported option error when when user presence test is turned off. Trying with user presence test turned on.",
360 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
361 emoji_enabled() ? " " : "");
362 retry_with_up
= true;
367 case FIDO_ERR_PIN_REQUIRED
:
368 /* A pin was requested. Maybe supply one, if we are configured to do so on request */
371 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
372 "Token asks for PIN but doesn't advertise 'clientPin' feature.");
374 if (FLAGS_SET(required
, FIDO2ENROLL_PIN
) && !strv_isempty(pins
))
375 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
376 "Token asks for PIN but one was already supplied.");
378 if ((required
& (FIDO2ENROLL_PIN
|FIDO2ENROLL_PIN_IF_NEEDED
)) == FIDO2ENROLL_PIN_IF_NEEDED
) {
379 /* If a PIN so far wasn't specified but is requested by the device, and
380 * FIDO2ENROLL_PIN_IF_NEEDED is set, then provide it */
381 log_debug("Retrying to create credential with PIN.");
382 retry_with_pin
= true;
391 if (!retry_with_up
&& !retry_with_pin
)
395 r
= sym_fido_assert_set_up(a
, FIDO_OPT_TRUE
);
397 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
398 "Failed to enable FIDO2 user presence test: %s", sym_fido_strerr(r
));
400 required
|= FIDO2ENROLL_UP
;
404 required
|= FIDO2ENROLL_PIN
;
410 case FIDO_ERR_NO_CREDENTIALS
:
411 return log_error_errno(SYNTHETIC_ERRNO(EBADSLT
),
412 "Wrong security token; needed credentials not present on token.");
413 case FIDO_ERR_PIN_REQUIRED
:
414 return log_error_errno(SYNTHETIC_ERRNO(ENOANO
),
415 "Security token requires PIN.");
416 case FIDO_ERR_PIN_AUTH_BLOCKED
:
417 return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD
),
418 "PIN of security token is blocked, please remove/reinsert token.");
419 #ifdef FIDO_ERR_UV_BLOCKED
420 case FIDO_ERR_UV_BLOCKED
:
421 return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD
),
422 "Verification of security token is blocked, please remove/reinsert token.");
424 case FIDO_ERR_PIN_INVALID
:
425 return log_error_errno(SYNTHETIC_ERRNO(ENOLCK
),
426 "PIN of security token incorrect.");
427 case FIDO_ERR_UP_REQUIRED
:
428 return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE
),
429 "User presence required.");
430 case FIDO_ERR_ACTION_TIMEOUT
:
431 return log_error_errno(SYNTHETIC_ERRNO(ENOSTR
),
432 "Token action timeout. (User didn't interact with token quickly enough.)");
434 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
435 "Failed to ask token for assertion: %s", sym_fido_strerr(r
));
438 hmac
= sym_fido_assert_hmac_secret_ptr(a
, 0);
440 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to retrieve HMAC secret.");
442 hmac_size
= sym_fido_assert_hmac_secret_len(a
, 0);
444 hmac_copy
= memdup(hmac
, hmac_size
);
448 *ret_hmac
= TAKE_PTR(hmac_copy
);
449 *ret_hmac_size
= hmac_size
;
453 int fido2_use_hmac_hash(
461 Fido2EnrollFlags required
, /* client pin/user presence required */
463 size_t *ret_hmac_size
) {
465 size_t allocated
= 64, found
= 0;
466 fido_dev_info_t
*di
= NULL
;
469 r
= dlopen_libfido2();
471 return log_error_errno(r
, "FIDO2 support is not installed.");
474 return fido2_use_hmac_hash_specific_token(device
, rp_id
, salt
, salt_size
, cid
, cid_size
, pins
, required
, ret_hmac
, ret_hmac_size
);
476 di
= sym_fido_dev_info_new(allocated
);
480 r
= sym_fido_dev_info_manifest(di
, allocated
, &found
);
481 if (r
== FIDO_ERR_INTERNAL
) {
482 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
483 r
= log_debug_errno(SYNTHETIC_ERRNO(EAGAIN
), "Got FIDO_ERR_INTERNAL, assuming no devices.");
487 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to enumerate FIDO2 devices: %s", sym_fido_strerr(r
));
491 for (size_t i
= 0; i
< found
; i
++) {
492 const fido_dev_info_t
*entry
;
495 entry
= sym_fido_dev_info_ptr(di
, i
);
497 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
),
498 "Failed to get device information for FIDO device %zu.", i
);
502 path
= sym_fido_dev_info_path(entry
);
504 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
),
505 "Failed to query FIDO device path.");
509 r
= fido2_use_hmac_hash_specific_token(path
, rp_id
, salt
, salt_size
, cid
, cid_size
, pins
, required
, ret_hmac
, ret_hmac_size
);
511 -EBADSLT
, /* device doesn't understand our credential hash */
512 -ENODEV
/* device is not a FIDO2 device with HMAC-SECRET */))
519 sym_fido_dev_info_free(&di
, allocated
);
523 #define FIDO2_SALT_SIZE 32
525 int fido2_generate_hmac_hash(
529 const void *user_id
, size_t user_id_len
,
530 const char *user_name
,
531 const char *user_display_name
,
532 const char *user_icon
,
533 const char *askpw_icon_name
,
534 Fido2EnrollFlags lock_with
,
535 void **ret_cid
, size_t *ret_cid_size
,
536 void **ret_salt
, size_t *ret_salt_size
,
537 void **ret_secret
, size_t *ret_secret_size
,
539 Fido2EnrollFlags
*ret_locked_with
) {
541 _cleanup_(erase_and_freep
) void *salt
= NULL
, *secret_copy
= NULL
;
542 _cleanup_(fido_assert_free_wrapper
) fido_assert_t
*a
= NULL
;
543 _cleanup_(fido_cred_free_wrapper
) fido_cred_t
*c
= NULL
;
544 _cleanup_(fido_dev_free_wrapper
) fido_dev_t
*d
= NULL
;
545 _cleanup_(erase_and_freep
) char *used_pin
= NULL
;
546 bool has_rk
, has_client_pin
, has_up
, has_uv
;
547 _cleanup_free_
char *cid_copy
= NULL
;
548 size_t cid_size
, secret_size
;
549 const void *cid
, *secret
;
554 assert(ret_cid_size
);
556 assert(ret_salt_size
);
558 assert(ret_secret_size
);
560 /* Construction is like this: we generate a salt of 32 bytes. We then ask the FIDO2 device to
561 * HMAC-SHA256 it for us with its internal key. The result is the key used by LUKS and account
562 * authentication. LUKS and UNIX password auth all do their own salting before hashing, so that FIDO2
563 * device never sees the volume key.
565 * S = HMAC-SHA256(I, D)
567 * with: S → LUKS/account authentication key (never stored)
568 * I → internal key on FIDO2 device (stored in the FIDO2 device)
569 * D → salt we generate here (stored in the privileged part of the JSON record)
574 assert((lock_with
& ~(FIDO2ENROLL_PIN
|FIDO2ENROLL_UP
|FIDO2ENROLL_UV
)) == 0);
576 r
= dlopen_libfido2();
578 return log_error_errno(r
, "FIDO2 token support is not installed.");
580 salt
= malloc(FIDO2_SALT_SIZE
);
584 r
= genuine_random_bytes(salt
, FIDO2_SALT_SIZE
, RANDOM_BLOCK
);
586 return log_error_errno(r
, "Failed to generate salt: %m");
588 d
= sym_fido_dev_new();
592 r
= sym_fido_dev_open(d
, device
);
594 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
595 "Failed to open FIDO2 device %s: %s", device
, sym_fido_strerr(r
));
597 r
= verify_features(d
, device
, LOG_ERR
, &has_rk
, &has_client_pin
, &has_up
, &has_uv
);
601 /* While enrolling degrade gracefully if the requested feature set isn't available, but let the user know */
602 if (!has_client_pin
&& FLAGS_SET(lock_with
, FIDO2ENROLL_PIN
)) {
603 log_notice("Requested to lock with PIN, but FIDO2 device %s does not support it, disabling.", device
);
604 lock_with
&= ~FIDO2ENROLL_PIN
;
607 if (!has_up
&& FLAGS_SET(lock_with
, FIDO2ENROLL_UP
)) {
608 log_notice("Locking with user presence test requested, but FIDO2 device %s does not support it, disabling.", device
);
609 lock_with
&= ~FIDO2ENROLL_UP
;
612 if (!has_uv
&& FLAGS_SET(lock_with
, FIDO2ENROLL_UV
)) {
613 log_notice("Locking with user verification test requested, but FIDO2 device %s does not support it, disabling.", device
);
614 lock_with
&= ~FIDO2ENROLL_UV
;
617 c
= sym_fido_cred_new();
621 r
= sym_fido_cred_set_extensions(c
, FIDO_EXT_HMAC_SECRET
);
623 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
624 "Failed to enable HMAC-SECRET extension on FIDO2 credential: %s", sym_fido_strerr(r
));
626 r
= sym_fido_cred_set_rp(c
, rp_id
, rp_name
);
628 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
629 "Failed to set FIDO2 credential relying party ID/name: %s", sym_fido_strerr(r
));
631 r
= sym_fido_cred_set_type(c
, COSE_ES256
);
633 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
634 "Failed to set FIDO2 credential type to ES256: %s", sym_fido_strerr(r
));
636 r
= sym_fido_cred_set_user(
638 user_id
, user_id_len
,
643 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
644 "Failed to set FIDO2 credential user data: %s", sym_fido_strerr(r
));
646 r
= sym_fido_cred_set_clientdata_hash(c
, (const unsigned char[32]) {}, 32);
648 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
649 "Failed to set FIDO2 client data hash: %s", sym_fido_strerr(r
));
652 r
= sym_fido_cred_set_rk(c
, FIDO_OPT_FALSE
);
654 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
655 "Failed to turn off FIDO2 resident key option of credential: %s", sym_fido_strerr(r
));
659 r
= sym_fido_cred_set_uv(c
, FIDO_OPT_FALSE
);
661 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
662 "Failed to turn off FIDO2 user verification option of credential: %s", sym_fido_strerr(r
));
665 /* As per specification "up" is assumed to be implicit when making credentials, hence we don't
666 * explicitly enable/disable it here */
668 log_info("Initializing FIDO2 credential on security token.");
670 if (has_uv
|| has_up
)
671 log_notice("%s%s(Hint: This might require confirmation of user presence on security token.)",
672 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
673 emoji_enabled() ? " " : "");
675 r
= sym_fido_dev_make_cred(d
, c
, NULL
);
676 if (r
== FIDO_ERR_PIN_REQUIRED
) {
679 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
680 "Token asks for PIN but doesn't advertise 'clientPin' feature.");
683 _cleanup_(strv_free_erasep
) char **pin
= NULL
;
685 r
= ask_password_auto("Please enter security token PIN:", askpw_icon_name
, NULL
, "fido2-pin", "fido2-pin", USEC_INFINITY
, 0, &pin
);
687 return log_error_errno(r
, "Failed to acquire user PIN: %m");
689 r
= FIDO_ERR_PIN_INVALID
;
690 STRV_FOREACH(i
, pin
) {
692 log_notice("PIN may not be empty.");
696 r
= sym_fido_dev_make_cred(d
, c
, *i
);
698 used_pin
= strdup(*i
);
703 if (r
!= FIDO_ERR_PIN_INVALID
)
707 if (r
!= FIDO_ERR_PIN_INVALID
)
710 log_notice("PIN incorrect, please try again.");
713 if (r
== FIDO_ERR_PIN_AUTH_BLOCKED
)
714 return log_notice_errno(SYNTHETIC_ERRNO(EPERM
),
715 "Token PIN is currently blocked, please remove and reinsert token.");
716 #ifdef FIDO_ERR_UV_BLOCKED
717 if (r
== FIDO_ERR_UV_BLOCKED
)
718 return log_notice_errno(SYNTHETIC_ERRNO(EPERM
),
719 "Token verification is currently blocked, please remove and reinsert token.");
721 if (r
== FIDO_ERR_ACTION_TIMEOUT
)
722 return log_error_errno(SYNTHETIC_ERRNO(ENOSTR
),
723 "Token action timeout. (User didn't interact with token quickly enough.)");
725 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
726 "Failed to generate FIDO2 credential: %s", sym_fido_strerr(r
));
728 cid
= sym_fido_cred_id_ptr(c
);
730 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to get FIDO2 credential ID.");
732 cid_size
= sym_fido_cred_id_len(c
);
734 a
= sym_fido_assert_new();
738 r
= sym_fido_assert_set_extensions(a
, FIDO_EXT_HMAC_SECRET
);
740 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
741 "Failed to enable HMAC-SECRET extension on FIDO2 assertion: %s", sym_fido_strerr(r
));
743 r
= sym_fido_assert_set_hmac_salt(a
, salt
, FIDO2_SALT_SIZE
);
745 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
746 "Failed to set salt on FIDO2 assertion: %s", sym_fido_strerr(r
));
748 r
= sym_fido_assert_set_rp(a
, rp_id
);
750 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
751 "Failed to set FIDO2 assertion ID: %s", sym_fido_strerr(r
));
753 r
= sym_fido_assert_set_clientdata_hash(a
, (const unsigned char[32]) {}, 32);
755 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
756 "Failed to set FIDO2 assertion client data hash: %s", sym_fido_strerr(r
));
758 r
= sym_fido_assert_allow_cred(a
, cid
, cid_size
);
760 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
761 "Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r
));
763 log_info("Generating secret key on FIDO2 security token.");
766 r
= sym_fido_assert_set_up(a
, FLAGS_SET(lock_with
, FIDO2ENROLL_UP
) ? FIDO_OPT_TRUE
: FIDO_OPT_FALSE
);
768 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
769 "Failed to %s FIDO2 user presence test: %s",
770 enable_disable(FLAGS_SET(lock_with
, FIDO2ENROLL_UP
)),
773 if (FLAGS_SET(lock_with
, FIDO2ENROLL_UP
))
774 log_notice("%s%sIn order to allow secret key generation, please confirm presence on security token.",
775 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
776 emoji_enabled() ? " " : "");
780 r
= sym_fido_assert_set_uv(a
, FLAGS_SET(lock_with
, FIDO2ENROLL_UV
) ? FIDO_OPT_TRUE
: FIDO_OPT_FALSE
);
782 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
783 "Failed to %s FIDO user verification: %s",
784 enable_disable(FLAGS_SET(lock_with
, FIDO2ENROLL_UV
)),
787 if (FLAGS_SET(lock_with
, FIDO2ENROLL_UV
))
788 log_notice("%s%sIn order to allow secret key generation, please verify user on security token.",
789 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
790 emoji_enabled() ? " " : "");
794 bool retry_with_up
= false, retry_with_pin
= false;
796 r
= sym_fido_dev_get_assert(d
, a
, FLAGS_SET(lock_with
, FIDO2ENROLL_PIN
) ? used_pin
: NULL
);
800 case FIDO_ERR_UP_REQUIRED
:
801 /* If the token asks for "up" when we turn off, then this might be a feature that
802 * isn't optional. Let's enable it */
805 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
806 "Token asks for user presence test but doesn't advertise 'up' feature.");
808 if (FLAGS_SET(lock_with
, FIDO2ENROLL_UP
))
809 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
810 "Token asks for user presence test but was already enabled.");
812 log_notice("%s%sLocking without user presence test requested, but FIDO2 device %s requires it, enabling.",
813 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
814 emoji_enabled() ? " " : "",
817 retry_with_up
= true;
820 case FIDO_ERR_UNSUPPORTED_OPTION
:
821 /* AuthenTrend ATKey.Pro says it supports "up", but if we disable it it will fail
822 * with FIDO_ERR_UNSUPPORTED_OPTION, probably because it isn't actually
823 * optional. Let's see if turning it on works. This is very similar to the
824 * FIDO_ERR_UP_REQUIRED case, but since the error is so vague we implement it
825 * slightly more defensively. */
827 if (has_up
&& !FLAGS_SET(lock_with
, FIDO2ENROLL_UP
)) {
828 log_notice("%s%sGot unsupported option error when when user presence test is turned off. Trying with user presence test turned on.",
829 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
830 emoji_enabled() ? " " : "");
831 retry_with_up
= true;
836 case FIDO_ERR_PIN_REQUIRED
:
838 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
839 "Token asks for client PIN check but doesn't advertise 'clientPin' feature.");
841 if (FLAGS_SET(lock_with
, FIDO2ENROLL_PIN
))
842 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
843 "Token asks for user client PIN check but was already enabled.");
845 log_debug("Token requires PIN for assertion, enabling.");
846 retry_with_pin
= true;
853 if (!retry_with_up
&& !retry_with_pin
)
857 r
= sym_fido_assert_set_up(a
, FIDO_OPT_TRUE
);
859 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to enable FIDO2 user presence test: %s", sym_fido_strerr(r
));
861 lock_with
|= FIDO2ENROLL_UP
;
865 lock_with
|= FIDO2ENROLL_PIN
;
868 if (r
== FIDO_ERR_ACTION_TIMEOUT
)
869 return log_error_errno(SYNTHETIC_ERRNO(ENOSTR
),
870 "Token action timeout. (User didn't interact with token quickly enough.)");
872 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
873 "Failed to ask token for assertion: %s", sym_fido_strerr(r
));
875 secret
= sym_fido_assert_hmac_secret_ptr(a
, 0);
877 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to retrieve HMAC secret.");
879 secret_size
= sym_fido_assert_hmac_secret_len(a
, 0);
881 secret_copy
= memdup(secret
, secret_size
);
885 cid_copy
= memdup(cid
, cid_size
);
889 *ret_cid
= TAKE_PTR(cid_copy
);
890 *ret_cid_size
= cid_size
;
891 *ret_salt
= TAKE_PTR(salt
);
892 *ret_salt_size
= FIDO2_SALT_SIZE
;
893 *ret_secret
= TAKE_PTR(secret_copy
);
894 *ret_secret_size
= secret_size
;
897 *ret_usedpin
= TAKE_PTR(used_pin
);
900 *ret_locked_with
= lock_with
;
907 static int check_device_is_fido2_with_hmac_secret(const char *path
) {
908 _cleanup_(fido_dev_free_wrapper
) fido_dev_t
*d
= NULL
;
911 d
= sym_fido_dev_new();
915 r
= sym_fido_dev_open(d
, path
);
917 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
918 "Failed to open FIDO2 device %s: %s", path
, sym_fido_strerr(r
));
920 r
= verify_features(d
, path
, LOG_DEBUG
, NULL
, NULL
, NULL
, NULL
);
921 if (r
== -ENODEV
) /* Not a FIDO2 device, or not implementing 'hmac-secret' */
930 int fido2_list_devices(void) {
932 _cleanup_(table_unrefp
) Table
*t
= NULL
;
933 size_t allocated
= 64, found
= 0;
934 fido_dev_info_t
*di
= NULL
;
937 r
= dlopen_libfido2();
939 return log_error_errno(r
, "FIDO2 token support is not installed.");
941 di
= sym_fido_dev_info_new(allocated
);
945 r
= sym_fido_dev_info_manifest(di
, allocated
, &found
);
946 if (r
== FIDO_ERR_INTERNAL
|| (r
== FIDO_OK
&& found
== 0)) {
947 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
948 log_info("No FIDO2 devices found.");
953 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to enumerate FIDO2 devices: %s", sym_fido_strerr(r
));
957 t
= table_new("path", "manufacturer", "product");
963 for (size_t i
= 0; i
< found
; i
++) {
964 const fido_dev_info_t
*entry
;
966 entry
= sym_fido_dev_info_ptr(di
, i
);
968 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
),
969 "Failed to get device information for FIDO device %zu.", i
);
973 r
= check_device_is_fido2_with_hmac_secret(sym_fido_dev_info_path(entry
));
981 TABLE_PATH
, sym_fido_dev_info_path(entry
),
982 TABLE_STRING
, sym_fido_dev_info_manufacturer_string(entry
),
983 TABLE_STRING
, sym_fido_dev_info_product_string(entry
));
985 table_log_add_error(r
);
990 r
= table_print(t
, stdout
);
992 log_error_errno(r
, "Failed to show device table: %m");
999 sym_fido_dev_info_free(&di
, allocated
);
1002 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
1003 "FIDO2 tokens not supported on this build.");
1007 int fido2_find_device_auto(char **ret
) {
1009 _cleanup_free_
char *copy
= NULL
;
1010 size_t di_size
= 64, found
= 0;
1011 const fido_dev_info_t
*entry
;
1012 fido_dev_info_t
*di
= NULL
;
1016 r
= dlopen_libfido2();
1018 return log_error_errno(r
, "FIDO2 token support is not installed.");
1020 di
= sym_fido_dev_info_new(di_size
);
1024 r
= sym_fido_dev_info_manifest(di
, di_size
, &found
);
1025 if (r
== FIDO_ERR_INTERNAL
|| (r
== FIDO_OK
&& found
== 0)) {
1026 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
1027 r
= log_error_errno(SYNTHETIC_ERRNO(ENODEV
), "No FIDO devices found.");
1031 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to enumerate FIDO devices: %s", sym_fido_strerr(r
));
1035 r
= log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ
), "More than one FIDO device found.");
1039 entry
= sym_fido_dev_info_ptr(di
, 0);
1041 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
),
1042 "Failed to get device information for FIDO device 0.");
1046 r
= check_device_is_fido2_with_hmac_secret(sym_fido_dev_info_path(entry
));
1050 r
= log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
), "FIDO device discovered does not implement FIDO2 with 'hmac-secret' extension.");
1054 path
= sym_fido_dev_info_path(entry
);
1056 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
),
1057 "Failed to query FIDO device path.");
1061 copy
= strdup(path
);
1067 *ret
= TAKE_PTR(copy
);
1071 sym_fido_dev_info_free(&di
, di_size
);
1074 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
1075 "FIDO2 tokens not supported on this build.");
1079 int fido2_have_device(const char *device
) {
1081 size_t allocated
= 64, found
= 0;
1082 fido_dev_info_t
*di
= NULL
;
1085 /* Return == 0 if not devices are found, > 0 if at least one is found */
1087 r
= dlopen_libfido2();
1089 return log_error_errno(r
, "FIDO2 support is not installed.");
1092 if (access(device
, F_OK
) < 0) {
1093 if (errno
== ENOENT
)
1096 return log_error_errno(errno
, "Failed to determine whether device '%s' exists: %m", device
);
1102 di
= sym_fido_dev_info_new(allocated
);
1106 r
= sym_fido_dev_info_manifest(di
, allocated
, &found
);
1107 if (r
== FIDO_ERR_INTERNAL
) {
1108 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
1113 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to enumerate FIDO2 devices: %s", sym_fido_strerr(r
));
1120 sym_fido_dev_info_free(&di
, allocated
);
1123 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
1124 "FIDO2 tokens not supported on this build.");