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
)) {
318 /* OK, we need a pin, try with all pins in turn */
319 if (strv_isempty(pins
))
320 r
= FIDO_ERR_PIN_REQUIRED
;
322 STRV_FOREACH(i
, pins
) {
323 r
= sym_fido_dev_get_assert(d
, a
, *i
);
324 if (r
!= FIDO_ERR_PIN_INVALID
)
329 r
= sym_fido_dev_get_assert(d
, a
, NULL
);
331 /* In some conditions, where a PIN or UP is required we might accept that. Let's check the
332 * conditions and if so try immediately again. */
336 case FIDO_ERR_UP_REQUIRED
:
337 /* So the token asked for "up". Try to turn it on, for compat with systemd 248 and try again. */
340 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
341 "Token asks for user presence test but doesn't advertise 'up' feature.");
343 if (FLAGS_SET(required
, FIDO2ENROLL_UP
))
344 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
345 "Token asks for user presence test but was already enabled.");
347 if (FLAGS_SET(required
, FIDO2ENROLL_UP_IF_NEEDED
)) {
348 log_notice("%s%sPlease confirm presence on security to unlock.",
349 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
350 emoji_enabled() ? " " : "");
351 retry_with_up
= true;
356 case FIDO_ERR_UNSUPPORTED_OPTION
:
357 /* AuthenTrend ATKey.Pro returns this instead of FIDO_ERR_UP_REQUIRED, let's handle
358 * it gracefully (also see below.) */
360 if (has_up
&& (required
& (FIDO2ENROLL_UP
|FIDO2ENROLL_UP_IF_NEEDED
)) == FIDO2ENROLL_UP_IF_NEEDED
) {
361 log_notice("%s%sGot unsupported option error when when user presence test is turned off. Trying with user presence test turned on.",
362 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
363 emoji_enabled() ? " " : "");
364 retry_with_up
= true;
369 case FIDO_ERR_PIN_REQUIRED
:
370 /* A pin was requested. Maybe supply one, if we are configured to do so on request */
373 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
374 "Token asks for PIN but doesn't advertise 'clientPin' feature.");
376 if (FLAGS_SET(required
, FIDO2ENROLL_PIN
) && !strv_isempty(pins
))
377 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
378 "Token asks for PIN but one was already supplied.");
380 if ((required
& (FIDO2ENROLL_PIN
|FIDO2ENROLL_PIN_IF_NEEDED
)) == FIDO2ENROLL_PIN_IF_NEEDED
) {
381 /* If a PIN so far wasn't specified but is requested by the device, and
382 * FIDO2ENROLL_PIN_IF_NEEDED is set, then provide it */
383 log_debug("Retrying to create credential with PIN.");
384 retry_with_pin
= true;
393 if (!retry_with_up
&& !retry_with_pin
)
397 r
= sym_fido_assert_set_up(a
, FIDO_OPT_TRUE
);
399 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
400 "Failed to enable FIDO2 user presence test: %s", sym_fido_strerr(r
));
402 required
|= FIDO2ENROLL_UP
;
406 required
|= FIDO2ENROLL_PIN
;
412 case FIDO_ERR_NO_CREDENTIALS
:
413 return log_error_errno(SYNTHETIC_ERRNO(EBADSLT
),
414 "Wrong security token; needed credentials not present on token.");
415 case FIDO_ERR_PIN_REQUIRED
:
416 return log_error_errno(SYNTHETIC_ERRNO(ENOANO
),
417 "Security token requires PIN.");
418 case FIDO_ERR_PIN_AUTH_BLOCKED
:
419 return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD
),
420 "PIN of security token is blocked, please remove/reinsert token.");
421 #ifdef FIDO_ERR_UV_BLOCKED
422 case FIDO_ERR_UV_BLOCKED
:
423 return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD
),
424 "Verification of security token is blocked, please remove/reinsert token.");
426 case FIDO_ERR_PIN_INVALID
:
427 return log_error_errno(SYNTHETIC_ERRNO(ENOLCK
),
428 "PIN of security token incorrect.");
429 case FIDO_ERR_UP_REQUIRED
:
430 return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE
),
431 "User presence required.");
432 case FIDO_ERR_ACTION_TIMEOUT
:
433 return log_error_errno(SYNTHETIC_ERRNO(ENOSTR
),
434 "Token action timeout. (User didn't interact with token quickly enough.)");
436 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
437 "Failed to ask token for assertion: %s", sym_fido_strerr(r
));
440 hmac
= sym_fido_assert_hmac_secret_ptr(a
, 0);
442 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to retrieve HMAC secret.");
444 hmac_size
= sym_fido_assert_hmac_secret_len(a
, 0);
446 hmac_copy
= memdup(hmac
, hmac_size
);
450 *ret_hmac
= TAKE_PTR(hmac_copy
);
451 *ret_hmac_size
= hmac_size
;
455 int fido2_use_hmac_hash(
463 Fido2EnrollFlags required
, /* client pin/user presence required */
465 size_t *ret_hmac_size
) {
467 size_t allocated
= 64, found
= 0;
468 fido_dev_info_t
*di
= NULL
;
471 r
= dlopen_libfido2();
473 return log_error_errno(r
, "FIDO2 support is not installed.");
476 return fido2_use_hmac_hash_specific_token(device
, rp_id
, salt
, salt_size
, cid
, cid_size
, pins
, required
, ret_hmac
, ret_hmac_size
);
478 di
= sym_fido_dev_info_new(allocated
);
482 r
= sym_fido_dev_info_manifest(di
, allocated
, &found
);
483 if (r
== FIDO_ERR_INTERNAL
) {
484 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
485 r
= log_debug_errno(SYNTHETIC_ERRNO(EAGAIN
), "Got FIDO_ERR_INTERNAL, assuming no devices.");
489 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to enumerate FIDO2 devices: %s", sym_fido_strerr(r
));
493 for (size_t i
= 0; i
< found
; i
++) {
494 const fido_dev_info_t
*entry
;
497 entry
= sym_fido_dev_info_ptr(di
, i
);
499 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
),
500 "Failed to get device information for FIDO device %zu.", i
);
504 path
= sym_fido_dev_info_path(entry
);
506 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
),
507 "Failed to query FIDO device path.");
511 r
= fido2_use_hmac_hash_specific_token(path
, rp_id
, salt
, salt_size
, cid
, cid_size
, pins
, required
, ret_hmac
, ret_hmac_size
);
513 -EBADSLT
, /* device doesn't understand our credential hash */
514 -ENODEV
/* device is not a FIDO2 device with HMAC-SECRET */))
521 sym_fido_dev_info_free(&di
, allocated
);
525 #define FIDO2_SALT_SIZE 32
527 int fido2_generate_hmac_hash(
531 const void *user_id
, size_t user_id_len
,
532 const char *user_name
,
533 const char *user_display_name
,
534 const char *user_icon
,
535 const char *askpw_icon_name
,
536 Fido2EnrollFlags lock_with
,
537 void **ret_cid
, size_t *ret_cid_size
,
538 void **ret_salt
, size_t *ret_salt_size
,
539 void **ret_secret
, size_t *ret_secret_size
,
541 Fido2EnrollFlags
*ret_locked_with
) {
543 _cleanup_(erase_and_freep
) void *salt
= NULL
, *secret_copy
= NULL
;
544 _cleanup_(fido_assert_free_wrapper
) fido_assert_t
*a
= NULL
;
545 _cleanup_(fido_cred_free_wrapper
) fido_cred_t
*c
= NULL
;
546 _cleanup_(fido_dev_free_wrapper
) fido_dev_t
*d
= NULL
;
547 _cleanup_(erase_and_freep
) char *used_pin
= NULL
;
548 bool has_rk
, has_client_pin
, has_up
, has_uv
;
549 _cleanup_free_
char *cid_copy
= NULL
;
550 size_t cid_size
, secret_size
;
551 const void *cid
, *secret
;
556 assert(ret_cid_size
);
558 assert(ret_salt_size
);
560 assert(ret_secret_size
);
562 /* Construction is like this: we generate a salt of 32 bytes. We then ask the FIDO2 device to
563 * HMAC-SHA256 it for us with its internal key. The result is the key used by LUKS and account
564 * authentication. LUKS and UNIX password auth all do their own salting before hashing, so that FIDO2
565 * device never sees the volume key.
567 * S = HMAC-SHA256(I, D)
569 * with: S → LUKS/account authentication key (never stored)
570 * I → internal key on FIDO2 device (stored in the FIDO2 device)
571 * D → salt we generate here (stored in the privileged part of the JSON record)
576 assert((lock_with
& ~(FIDO2ENROLL_PIN
|FIDO2ENROLL_UP
|FIDO2ENROLL_UV
)) == 0);
578 r
= dlopen_libfido2();
580 return log_error_errno(r
, "FIDO2 token support is not installed.");
582 salt
= malloc(FIDO2_SALT_SIZE
);
586 r
= genuine_random_bytes(salt
, FIDO2_SALT_SIZE
, RANDOM_BLOCK
);
588 return log_error_errno(r
, "Failed to generate salt: %m");
590 d
= sym_fido_dev_new();
594 r
= sym_fido_dev_open(d
, device
);
596 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
597 "Failed to open FIDO2 device %s: %s", device
, sym_fido_strerr(r
));
599 r
= verify_features(d
, device
, LOG_ERR
, &has_rk
, &has_client_pin
, &has_up
, &has_uv
);
603 /* While enrolling degrade gracefully if the requested feature set isn't available, but let the user know */
604 if (!has_client_pin
&& FLAGS_SET(lock_with
, FIDO2ENROLL_PIN
)) {
605 log_notice("Requested to lock with PIN, but FIDO2 device %s does not support it, disabling.", device
);
606 lock_with
&= ~FIDO2ENROLL_PIN
;
609 if (!has_up
&& FLAGS_SET(lock_with
, FIDO2ENROLL_UP
)) {
610 log_notice("Locking with user presence test requested, but FIDO2 device %s does not support it, disabling.", device
);
611 lock_with
&= ~FIDO2ENROLL_UP
;
614 if (!has_uv
&& FLAGS_SET(lock_with
, FIDO2ENROLL_UV
)) {
615 log_notice("Locking with user verification test requested, but FIDO2 device %s does not support it, disabling.", device
);
616 lock_with
&= ~FIDO2ENROLL_UV
;
619 c
= sym_fido_cred_new();
623 r
= sym_fido_cred_set_extensions(c
, FIDO_EXT_HMAC_SECRET
);
625 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
626 "Failed to enable HMAC-SECRET extension on FIDO2 credential: %s", sym_fido_strerr(r
));
628 r
= sym_fido_cred_set_rp(c
, rp_id
, rp_name
);
630 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
631 "Failed to set FIDO2 credential relying party ID/name: %s", sym_fido_strerr(r
));
633 r
= sym_fido_cred_set_type(c
, COSE_ES256
);
635 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
636 "Failed to set FIDO2 credential type to ES256: %s", sym_fido_strerr(r
));
638 r
= sym_fido_cred_set_user(
640 user_id
, user_id_len
,
645 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
646 "Failed to set FIDO2 credential user data: %s", sym_fido_strerr(r
));
648 r
= sym_fido_cred_set_clientdata_hash(c
, (const unsigned char[32]) {}, 32);
650 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
651 "Failed to set FIDO2 client data hash: %s", sym_fido_strerr(r
));
654 r
= sym_fido_cred_set_rk(c
, FIDO_OPT_FALSE
);
656 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
657 "Failed to turn off FIDO2 resident key option of credential: %s", sym_fido_strerr(r
));
661 r
= sym_fido_cred_set_uv(c
, FIDO_OPT_FALSE
);
663 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
664 "Failed to turn off FIDO2 user verification option of credential: %s", sym_fido_strerr(r
));
667 /* As per specification "up" is assumed to be implicit when making credentials, hence we don't
668 * explicitly enable/disable it here */
670 log_info("Initializing FIDO2 credential on security token.");
672 if (has_uv
|| has_up
)
673 log_notice("%s%s(Hint: This might require confirmation of user presence on security token.)",
674 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
675 emoji_enabled() ? " " : "");
677 r
= sym_fido_dev_make_cred(d
, c
, NULL
);
678 if (r
== FIDO_ERR_PIN_REQUIRED
) {
681 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
682 "Token asks for PIN but doesn't advertise 'clientPin' feature.");
685 _cleanup_(strv_free_erasep
) char **pin
= NULL
;
688 r
= ask_password_auto("Please enter security token PIN:", askpw_icon_name
, NULL
, "fido2-pin", "fido2-pin", USEC_INFINITY
, 0, &pin
);
690 return log_error_errno(r
, "Failed to acquire user PIN: %m");
692 r
= FIDO_ERR_PIN_INVALID
;
693 STRV_FOREACH(i
, pin
) {
695 log_notice("PIN may not be empty.");
699 r
= sym_fido_dev_make_cred(d
, c
, *i
);
701 used_pin
= strdup(*i
);
706 if (r
!= FIDO_ERR_PIN_INVALID
)
710 if (r
!= FIDO_ERR_PIN_INVALID
)
713 log_notice("PIN incorrect, please try again.");
716 if (r
== FIDO_ERR_PIN_AUTH_BLOCKED
)
717 return log_notice_errno(SYNTHETIC_ERRNO(EPERM
),
718 "Token PIN is currently blocked, please remove and reinsert token.");
719 #ifdef FIDO_ERR_UV_BLOCKED
720 if (r
== FIDO_ERR_UV_BLOCKED
)
721 return log_notice_errno(SYNTHETIC_ERRNO(EPERM
),
722 "Token verification is currently blocked, please remove and reinsert token.");
724 if (r
== FIDO_ERR_ACTION_TIMEOUT
)
725 return log_error_errno(SYNTHETIC_ERRNO(ENOSTR
),
726 "Token action timeout. (User didn't interact with token quickly enough.)");
728 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
729 "Failed to generate FIDO2 credential: %s", sym_fido_strerr(r
));
731 cid
= sym_fido_cred_id_ptr(c
);
733 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to get FIDO2 credential ID.");
735 cid_size
= sym_fido_cred_id_len(c
);
737 a
= sym_fido_assert_new();
741 r
= sym_fido_assert_set_extensions(a
, FIDO_EXT_HMAC_SECRET
);
743 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
744 "Failed to enable HMAC-SECRET extension on FIDO2 assertion: %s", sym_fido_strerr(r
));
746 r
= sym_fido_assert_set_hmac_salt(a
, salt
, FIDO2_SALT_SIZE
);
748 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
749 "Failed to set salt on FIDO2 assertion: %s", sym_fido_strerr(r
));
751 r
= sym_fido_assert_set_rp(a
, rp_id
);
753 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
754 "Failed to set FIDO2 assertion ID: %s", sym_fido_strerr(r
));
756 r
= sym_fido_assert_set_clientdata_hash(a
, (const unsigned char[32]) {}, 32);
758 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
759 "Failed to set FIDO2 assertion client data hash: %s", sym_fido_strerr(r
));
761 r
= sym_fido_assert_allow_cred(a
, cid
, cid_size
);
763 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
764 "Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r
));
766 log_info("Generating secret key on FIDO2 security token.");
769 r
= sym_fido_assert_set_up(a
, FLAGS_SET(lock_with
, FIDO2ENROLL_UP
) ? FIDO_OPT_TRUE
: FIDO_OPT_FALSE
);
771 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
772 "Failed to %s FIDO2 user presence test: %s",
773 enable_disable(FLAGS_SET(lock_with
, FIDO2ENROLL_UP
)),
776 if (FLAGS_SET(lock_with
, FIDO2ENROLL_UP
))
777 log_notice("%s%sIn order to allow secret key generation, please confirm presence on security token.",
778 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
779 emoji_enabled() ? " " : "");
783 r
= sym_fido_assert_set_uv(a
, FLAGS_SET(lock_with
, FIDO2ENROLL_UV
) ? FIDO_OPT_TRUE
: FIDO_OPT_FALSE
);
785 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
786 "Failed to %s FIDO user verification: %s",
787 enable_disable(FLAGS_SET(lock_with
, FIDO2ENROLL_UV
)),
790 if (FLAGS_SET(lock_with
, FIDO2ENROLL_UV
))
791 log_notice("%s%sIn order to allow secret key generation, please verify user on security token.",
792 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
793 emoji_enabled() ? " " : "");
797 bool retry_with_up
= false, retry_with_pin
= false;
799 r
= sym_fido_dev_get_assert(d
, a
, FLAGS_SET(lock_with
, FIDO2ENROLL_PIN
) ? used_pin
: NULL
);
803 case FIDO_ERR_UP_REQUIRED
:
804 /* If the token asks for "up" when we turn off, then this might be a feature that
805 * isn't optional. Let's enable it */
808 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
809 "Token asks for user presence test but doesn't advertise 'up' feature.");
811 if (FLAGS_SET(lock_with
, FIDO2ENROLL_UP
))
812 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
813 "Token asks for user presence test but was already enabled.");
815 log_notice("%s%sLocking without user presence test requested, but FIDO2 device %s requires it, enabling.",
816 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
817 emoji_enabled() ? " " : "",
820 retry_with_up
= true;
823 case FIDO_ERR_UNSUPPORTED_OPTION
:
824 /* AuthenTrend ATKey.Pro says it supports "up", but if we disable it it will fail
825 * with FIDO_ERR_UNSUPPORTED_OPTION, probably because it isn't actually
826 * optional. Let's see if turning it on works. This is very similar to the
827 * FIDO_ERR_UP_REQUIRED case, but since the error is so vague we implement it
828 * slightly more defensively. */
830 if (has_up
&& !FLAGS_SET(lock_with
, FIDO2ENROLL_UP
)) {
831 log_notice("%s%sGot unsupported option error when when user presence test is turned off. Trying with user presence test turned on.",
832 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
833 emoji_enabled() ? " " : "");
834 retry_with_up
= true;
839 case FIDO_ERR_PIN_REQUIRED
:
841 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
842 "Token asks for client PIN check but doesn't advertise 'clientPin' feature.");
844 if (FLAGS_SET(lock_with
, FIDO2ENROLL_PIN
))
845 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
846 "Token asks for user client PIN check but was already enabled.");
848 log_debug("Token requires PIN for assertion, enabling.");
849 retry_with_pin
= true;
856 if (!retry_with_up
&& !retry_with_pin
)
860 r
= sym_fido_assert_set_up(a
, FIDO_OPT_TRUE
);
862 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to enable FIDO2 user presence test: %s", sym_fido_strerr(r
));
864 lock_with
|= FIDO2ENROLL_UP
;
868 lock_with
|= FIDO2ENROLL_PIN
;
871 if (r
== FIDO_ERR_ACTION_TIMEOUT
)
872 return log_error_errno(SYNTHETIC_ERRNO(ENOSTR
),
873 "Token action timeout. (User didn't interact with token quickly enough.)");
875 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
876 "Failed to ask token for assertion: %s", sym_fido_strerr(r
));
878 secret
= sym_fido_assert_hmac_secret_ptr(a
, 0);
880 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to retrieve HMAC secret.");
882 secret_size
= sym_fido_assert_hmac_secret_len(a
, 0);
884 secret_copy
= memdup(secret
, secret_size
);
888 cid_copy
= memdup(cid
, cid_size
);
892 *ret_cid
= TAKE_PTR(cid_copy
);
893 *ret_cid_size
= cid_size
;
894 *ret_salt
= TAKE_PTR(salt
);
895 *ret_salt_size
= FIDO2_SALT_SIZE
;
896 *ret_secret
= TAKE_PTR(secret_copy
);
897 *ret_secret_size
= secret_size
;
900 *ret_usedpin
= TAKE_PTR(used_pin
);
903 *ret_locked_with
= lock_with
;
910 static int check_device_is_fido2_with_hmac_secret(const char *path
) {
911 _cleanup_(fido_dev_free_wrapper
) fido_dev_t
*d
= NULL
;
914 d
= sym_fido_dev_new();
918 r
= sym_fido_dev_open(d
, path
);
920 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
921 "Failed to open FIDO2 device %s: %s", path
, sym_fido_strerr(r
));
923 r
= verify_features(d
, path
, LOG_DEBUG
, NULL
, NULL
, NULL
, NULL
);
924 if (r
== -ENODEV
) /* Not a FIDO2 device, or not implementing 'hmac-secret' */
933 int fido2_list_devices(void) {
935 _cleanup_(table_unrefp
) Table
*t
= NULL
;
936 size_t allocated
= 64, found
= 0;
937 fido_dev_info_t
*di
= NULL
;
940 r
= dlopen_libfido2();
942 return log_error_errno(r
, "FIDO2 token support is not installed.");
944 di
= sym_fido_dev_info_new(allocated
);
948 r
= sym_fido_dev_info_manifest(di
, allocated
, &found
);
949 if (r
== FIDO_ERR_INTERNAL
|| (r
== FIDO_OK
&& found
== 0)) {
950 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
951 log_info("No FIDO2 devices found.");
956 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to enumerate FIDO2 devices: %s", sym_fido_strerr(r
));
960 t
= table_new("path", "manufacturer", "product");
966 for (size_t i
= 0; i
< found
; i
++) {
967 const fido_dev_info_t
*entry
;
969 entry
= sym_fido_dev_info_ptr(di
, i
);
971 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
),
972 "Failed to get device information for FIDO device %zu.", i
);
976 r
= check_device_is_fido2_with_hmac_secret(sym_fido_dev_info_path(entry
));
984 TABLE_PATH
, sym_fido_dev_info_path(entry
),
985 TABLE_STRING
, sym_fido_dev_info_manufacturer_string(entry
),
986 TABLE_STRING
, sym_fido_dev_info_product_string(entry
));
988 table_log_add_error(r
);
993 r
= table_print(t
, stdout
);
995 log_error_errno(r
, "Failed to show device table: %m");
1002 sym_fido_dev_info_free(&di
, allocated
);
1005 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
1006 "FIDO2 tokens not supported on this build.");
1010 int fido2_find_device_auto(char **ret
) {
1012 _cleanup_free_
char *copy
= NULL
;
1013 size_t di_size
= 64, found
= 0;
1014 const fido_dev_info_t
*entry
;
1015 fido_dev_info_t
*di
= NULL
;
1019 r
= dlopen_libfido2();
1021 return log_error_errno(r
, "FIDO2 token support is not installed.");
1023 di
= sym_fido_dev_info_new(di_size
);
1027 r
= sym_fido_dev_info_manifest(di
, di_size
, &found
);
1028 if (r
== FIDO_ERR_INTERNAL
|| (r
== FIDO_OK
&& found
== 0)) {
1029 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
1030 r
= log_error_errno(SYNTHETIC_ERRNO(ENODEV
), "No FIDO devices found.");
1034 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to enumerate FIDO devices: %s", sym_fido_strerr(r
));
1038 r
= log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ
), "More than one FIDO device found.");
1042 entry
= sym_fido_dev_info_ptr(di
, 0);
1044 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
),
1045 "Failed to get device information for FIDO device 0.");
1049 r
= check_device_is_fido2_with_hmac_secret(sym_fido_dev_info_path(entry
));
1053 r
= log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
), "FIDO device discovered does not implement FIDO2 with 'hmac-secret' extension.");
1057 path
= sym_fido_dev_info_path(entry
);
1059 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
),
1060 "Failed to query FIDO device path.");
1064 copy
= strdup(path
);
1070 *ret
= TAKE_PTR(copy
);
1074 sym_fido_dev_info_free(&di
, di_size
);
1077 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
1078 "FIDO2 tokens not supported on this build.");
1082 int fido2_have_device(const char *device
) {
1084 size_t allocated
= 64, found
= 0;
1085 fido_dev_info_t
*di
= NULL
;
1088 /* Return == 0 if not devices are found, > 0 if at least one is found */
1090 r
= dlopen_libfido2();
1092 return log_error_errno(r
, "FIDO2 support is not installed.");
1095 if (access(device
, F_OK
) < 0) {
1096 if (errno
== ENOENT
)
1099 return log_error_errno(errno
, "Failed to determine whether device '%s' exists: %m", device
);
1105 di
= sym_fido_dev_info_new(allocated
);
1109 r
= sym_fido_dev_info_manifest(di
, allocated
, &found
);
1110 if (r
== FIDO_ERR_INTERNAL
) {
1111 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
1116 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to enumerate FIDO2 devices: %s", sym_fido_strerr(r
));
1123 sym_fido_dev_info_free(&di
, allocated
);
1126 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
1127 "FIDO2 tokens not supported on this build.");