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 "locale-util.h"
12 #include "memory-util.h"
13 #include "random-util.h"
16 static void *libfido2_dl
= NULL
;
18 int (*sym_fido_assert_allow_cred
)(fido_assert_t
*, const unsigned char *, size_t) = NULL
;
19 void (*sym_fido_assert_free
)(fido_assert_t
**) = NULL
;
20 size_t (*sym_fido_assert_hmac_secret_len
)(const fido_assert_t
*, size_t) = NULL
;
21 const unsigned char* (*sym_fido_assert_hmac_secret_ptr
)(const fido_assert_t
*, size_t) = NULL
;
22 fido_assert_t
* (*sym_fido_assert_new
)(void) = NULL
;
23 int (*sym_fido_assert_set_clientdata_hash
)(fido_assert_t
*, const unsigned char *, size_t) = NULL
;
24 int (*sym_fido_assert_set_extensions
)(fido_assert_t
*, int) = NULL
;
25 int (*sym_fido_assert_set_hmac_salt
)(fido_assert_t
*, const unsigned char *, size_t) = NULL
;
26 int (*sym_fido_assert_set_rp
)(fido_assert_t
*, const char *) = NULL
;
27 int (*sym_fido_assert_set_up
)(fido_assert_t
*, fido_opt_t
) = NULL
;
28 size_t (*sym_fido_cbor_info_extensions_len
)(const fido_cbor_info_t
*) = NULL
;
29 char **(*sym_fido_cbor_info_extensions_ptr
)(const fido_cbor_info_t
*) = NULL
;
30 void (*sym_fido_cbor_info_free
)(fido_cbor_info_t
**) = NULL
;
31 fido_cbor_info_t
* (*sym_fido_cbor_info_new
)(void) = NULL
;
32 size_t (*sym_fido_cbor_info_options_len
)(const fido_cbor_info_t
*) = NULL
;
33 char** (*sym_fido_cbor_info_options_name_ptr
)(const fido_cbor_info_t
*) = NULL
;
34 const bool* (*sym_fido_cbor_info_options_value_ptr
)(const fido_cbor_info_t
*) = NULL
;
35 void (*sym_fido_cred_free
)(fido_cred_t
**) = NULL
;
36 size_t (*sym_fido_cred_id_len
)(const fido_cred_t
*) = NULL
;
37 const unsigned char* (*sym_fido_cred_id_ptr
)(const fido_cred_t
*) = NULL
;
38 fido_cred_t
* (*sym_fido_cred_new
)(void) = NULL
;
39 int (*sym_fido_cred_set_clientdata_hash
)(fido_cred_t
*, const unsigned char *, size_t) = NULL
;
40 int (*sym_fido_cred_set_extensions
)(fido_cred_t
*, int) = NULL
;
41 int (*sym_fido_cred_set_rk
)(fido_cred_t
*, fido_opt_t
) = NULL
;
42 int (*sym_fido_cred_set_rp
)(fido_cred_t
*, const char *, const char *) = NULL
;
43 int (*sym_fido_cred_set_type
)(fido_cred_t
*, int) = NULL
;
44 int (*sym_fido_cred_set_user
)(fido_cred_t
*, const unsigned char *, size_t, const char *, const char *, const char *) = NULL
;
45 int (*sym_fido_cred_set_uv
)(fido_cred_t
*, fido_opt_t
) = NULL
;
46 void (*sym_fido_dev_free
)(fido_dev_t
**) = NULL
;
47 int (*sym_fido_dev_get_assert
)(fido_dev_t
*, fido_assert_t
*, const char *) = NULL
;
48 int (*sym_fido_dev_get_cbor_info
)(fido_dev_t
*, fido_cbor_info_t
*) = NULL
;
49 void (*sym_fido_dev_info_free
)(fido_dev_info_t
**, size_t) = NULL
;
50 int (*sym_fido_dev_info_manifest
)(fido_dev_info_t
*, size_t, size_t *) = NULL
;
51 const char* (*sym_fido_dev_info_manufacturer_string
)(const fido_dev_info_t
*) = NULL
;
52 const char* (*sym_fido_dev_info_product_string
)(const fido_dev_info_t
*) = NULL
;
53 fido_dev_info_t
* (*sym_fido_dev_info_new
)(size_t) = NULL
;
54 const char* (*sym_fido_dev_info_path
)(const fido_dev_info_t
*) = NULL
;
55 const fido_dev_info_t
* (*sym_fido_dev_info_ptr
)(const fido_dev_info_t
*, size_t) = NULL
;
56 bool (*sym_fido_dev_is_fido2
)(const fido_dev_t
*) = NULL
;
57 int (*sym_fido_dev_make_cred
)(fido_dev_t
*, fido_cred_t
*, const char *) = NULL
;
58 fido_dev_t
* (*sym_fido_dev_new
)(void) = NULL
;
59 int (*sym_fido_dev_open
)(fido_dev_t
*, const char *) = NULL
;
60 const char* (*sym_fido_strerr
)(int) = NULL
;
62 int dlopen_libfido2(void) {
63 _cleanup_(dlclosep
) void *dl
= NULL
;
67 return 0; /* Already loaded */
69 dl
= dlopen("libfido2.so.1", RTLD_LAZY
);
71 return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
72 "libfido2 support is not installed: %s", dlerror());
74 r
= dlsym_many_and_warn(
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_cbor_info_extensions_len
),
88 DLSYM_ARG(fido_cbor_info_extensions_ptr
),
89 DLSYM_ARG(fido_cbor_info_free
),
90 DLSYM_ARG(fido_cbor_info_new
),
91 DLSYM_ARG(fido_cbor_info_options_len
),
92 DLSYM_ARG(fido_cbor_info_options_name_ptr
),
93 DLSYM_ARG(fido_cbor_info_options_value_ptr
),
94 DLSYM_ARG(fido_cred_free
),
95 DLSYM_ARG(fido_cred_id_len
),
96 DLSYM_ARG(fido_cred_id_ptr
),
97 DLSYM_ARG(fido_cred_new
),
98 DLSYM_ARG(fido_cred_set_clientdata_hash
),
99 DLSYM_ARG(fido_cred_set_extensions
),
100 DLSYM_ARG(fido_cred_set_rk
),
101 DLSYM_ARG(fido_cred_set_rp
),
102 DLSYM_ARG(fido_cred_set_type
),
103 DLSYM_ARG(fido_cred_set_user
),
104 DLSYM_ARG(fido_cred_set_uv
),
105 DLSYM_ARG(fido_dev_free
),
106 DLSYM_ARG(fido_dev_get_assert
),
107 DLSYM_ARG(fido_dev_get_cbor_info
),
108 DLSYM_ARG(fido_dev_info_free
),
109 DLSYM_ARG(fido_dev_info_manifest
),
110 DLSYM_ARG(fido_dev_info_manufacturer_string
),
111 DLSYM_ARG(fido_dev_info_new
),
112 DLSYM_ARG(fido_dev_info_path
),
113 DLSYM_ARG(fido_dev_info_product_string
),
114 DLSYM_ARG(fido_dev_info_ptr
),
115 DLSYM_ARG(fido_dev_is_fido2
),
116 DLSYM_ARG(fido_dev_make_cred
),
117 DLSYM_ARG(fido_dev_new
),
118 DLSYM_ARG(fido_dev_open
),
119 DLSYM_ARG(fido_strerr
),
124 /* Note that we never release the reference here, because there's no real reason to, after all this
125 * was traditionally a regular shared library dependency which lives forever too. */
126 libfido2_dl
= TAKE_PTR(dl
);
130 static int verify_features(
133 int log_level
, /* the log level to use when device is not FIDO2 with hmac-secret */
135 bool *ret_has_client_pin
,
139 _cleanup_(fido_cbor_info_free_wrapper
) fido_cbor_info_t
*di
= NULL
;
140 bool found_extension
= false;
143 bool has_rk
= false, has_client_pin
= false, has_up
= true, has_uv
= false; /* Defaults are per table in 5.4 in FIDO2 spec */
150 if (!sym_fido_dev_is_fido2(d
))
151 return log_full_errno(log_level
,
152 SYNTHETIC_ERRNO(ENODEV
),
153 "Specified device %s is not a FIDO2 device.", path
);
155 di
= sym_fido_cbor_info_new();
159 r
= sym_fido_dev_get_cbor_info(d
, di
);
161 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
162 "Failed to get CBOR device info for %s: %s", path
, sym_fido_strerr(r
));
164 e
= sym_fido_cbor_info_extensions_ptr(di
);
165 n
= sym_fido_cbor_info_extensions_len(di
);
166 for (size_t i
= 0; i
< n
; i
++) {
167 log_debug("FIDO2 device implements extension: %s", e
[i
]);
168 if (streq(e
[i
], "hmac-secret"))
169 found_extension
= true;
172 o
= sym_fido_cbor_info_options_name_ptr(di
);
173 b
= sym_fido_cbor_info_options_value_ptr(di
);
174 n
= sym_fido_cbor_info_options_len(di
);
175 for (size_t i
= 0; i
< n
; i
++) {
176 log_debug("FIDO2 device implements option %s: %s", o
[i
], yes_no(b
[i
]));
177 if (streq(o
[i
], "rk"))
179 if (streq(o
[i
], "clientPin"))
180 has_client_pin
= b
[i
];
181 if (streq(o
[i
], "up"))
183 if (streq(o
[i
], "uv"))
187 if (!found_extension
)
188 return log_full_errno(log_level
,
189 SYNTHETIC_ERRNO(ENODEV
),
190 "Specified device %s is a FIDO2 device, but does not support the required HMAC-SECRET extension.", path
);
192 log_debug("Has rk ('Resident Key') support: %s\n"
193 "Has clientPin support: %s\n"
194 "Has up ('User Presence') support: %s\n"
195 "Has uv ('User Verification') support: %s\n",
197 yes_no(has_client_pin
),
202 *ret_has_rk
= has_rk
;
203 if (ret_has_client_pin
)
204 *ret_has_client_pin
= has_client_pin
;
206 *ret_has_up
= has_up
;
208 *ret_has_uv
= has_uv
;
213 static int fido2_use_hmac_hash_specific_token(
221 bool up
, /* user presence permitted */
223 size_t *ret_hmac_size
) {
225 _cleanup_(fido_assert_free_wrapper
) fido_assert_t
*a
= NULL
;
226 _cleanup_(fido_dev_free_wrapper
) fido_dev_t
*d
= NULL
;
227 _cleanup_(erase_and_freep
) void *hmac_copy
= NULL
;
228 bool has_up
, has_client_pin
;
238 assert(ret_hmac_size
);
240 d
= sym_fido_dev_new();
244 r
= sym_fido_dev_open(d
, path
);
246 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
247 "Failed to open FIDO2 device %s: %s", path
, sym_fido_strerr(r
));
249 r
= verify_features(d
, path
, LOG_ERR
, NULL
, &has_client_pin
, &has_up
, NULL
);
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
));
283 r
= sym_fido_assert_set_up(a
, FIDO_OPT_FALSE
);
285 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
286 "Failed to set FIDO2 assertion user presence: %s", sym_fido_strerr(r
));
289 log_info("Asking FIDO2 token for authentication.");
291 r
= sym_fido_dev_get_assert(d
, a
, NULL
); /* try without pin and without up first */
292 if (r
== FIDO_ERR_UP_REQUIRED
&& up
) {
295 log_warning("Weird, device asked for User Presence check, but does not advertise it as feature. Ignoring.");
297 r
= sym_fido_assert_set_up(a
, FIDO_OPT_TRUE
);
299 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
300 "Failed to set FIDO2 assertion user presence: %s", sym_fido_strerr(r
));
302 log_info("Security token requires user presence.");
304 r
= sym_fido_dev_get_assert(d
, a
, NULL
); /* try without pin but with up now */
306 if (r
== FIDO_ERR_PIN_REQUIRED
) {
310 log_warning("Weird, device asked for client PIN, but does not advertise it as feature. Ignoring.");
312 /* OK, we needed a pin, try with all pins in turn */
313 STRV_FOREACH(i
, pins
) {
314 r
= sym_fido_dev_get_assert(d
, a
, *i
);
315 if (r
!= FIDO_ERR_PIN_INVALID
)
323 case FIDO_ERR_NO_CREDENTIALS
:
324 return log_error_errno(SYNTHETIC_ERRNO(EBADSLT
),
325 "Wrong security token; needed credentials not present on token.");
326 case FIDO_ERR_PIN_REQUIRED
:
327 return log_error_errno(SYNTHETIC_ERRNO(ENOANO
),
328 "Security token requires PIN.");
329 case FIDO_ERR_PIN_AUTH_BLOCKED
:
330 return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD
),
331 "PIN of security token is blocked, please remove/reinsert token.");
332 case FIDO_ERR_PIN_INVALID
:
333 return log_error_errno(SYNTHETIC_ERRNO(ENOLCK
),
334 "PIN of security token incorrect.");
335 case FIDO_ERR_UP_REQUIRED
:
336 return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE
),
337 "User presence required.");
338 case FIDO_ERR_ACTION_TIMEOUT
:
339 return log_error_errno(SYNTHETIC_ERRNO(ENOSTR
),
340 "Token action timeout. (User didn't interact with token quickly enough.)");
342 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
343 "Failed to ask token for assertion: %s", sym_fido_strerr(r
));
346 hmac
= sym_fido_assert_hmac_secret_ptr(a
, 0);
348 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to retrieve HMAC secret.");
350 hmac_size
= sym_fido_assert_hmac_secret_len(a
, 0);
352 hmac_copy
= memdup(hmac
, hmac_size
);
356 *ret_hmac
= TAKE_PTR(hmac_copy
);
357 *ret_hmac_size
= hmac_size
;
361 int fido2_use_hmac_hash(
369 bool up
, /* user presence permitted */
371 size_t *ret_hmac_size
) {
373 size_t allocated
= 64, found
= 0;
374 fido_dev_info_t
*di
= NULL
;
377 r
= dlopen_libfido2();
379 return log_error_errno(r
, "FIDO2 support is not installed.");
382 return fido2_use_hmac_hash_specific_token(device
, rp_id
, salt
, salt_size
, cid
, cid_size
, pins
, up
, ret_hmac
, ret_hmac_size
);
384 di
= sym_fido_dev_info_new(allocated
);
388 r
= sym_fido_dev_info_manifest(di
, allocated
, &found
);
389 if (r
== FIDO_ERR_INTERNAL
) {
390 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
391 r
= log_debug_errno(SYNTHETIC_ERRNO(EAGAIN
), "Got FIDO_ERR_INTERNAL, assuming no devices.");
395 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to enumerate FIDO2 devices: %s", sym_fido_strerr(r
));
399 for (size_t i
= 0; i
< found
; i
++) {
400 const fido_dev_info_t
*entry
;
403 entry
= sym_fido_dev_info_ptr(di
, i
);
405 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
),
406 "Failed to get device information for FIDO device %zu.", i
);
410 path
= sym_fido_dev_info_path(entry
);
412 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
),
413 "Failed to query FIDO device path.");
417 r
= fido2_use_hmac_hash_specific_token(path
, rp_id
, salt
, salt_size
, cid
, cid_size
, pins
, up
, ret_hmac
, ret_hmac_size
);
419 -EBADSLT
, /* device doesn't understand our credential hash */
420 -ENODEV
/* device is not a FIDO2 device with HMAC-SECRET */))
427 sym_fido_dev_info_free(&di
, allocated
);
431 #define FIDO2_SALT_SIZE 32
433 int fido2_generate_hmac_hash(
437 const void *user_id
, size_t user_id_len
,
438 const char *user_name
,
439 const char *user_display_name
,
440 const char *user_icon
,
441 const char *askpw_icon_name
,
442 void **ret_cid
, size_t *ret_cid_size
,
443 void **ret_salt
, size_t *ret_salt_size
,
444 void **ret_secret
, size_t *ret_secret_size
,
445 char **ret_usedpin
) {
447 _cleanup_(erase_and_freep
) void *salt
= NULL
, *secret_copy
= NULL
;
448 _cleanup_(fido_assert_free_wrapper
) fido_assert_t
*a
= NULL
;
449 _cleanup_(fido_cred_free_wrapper
) fido_cred_t
*c
= NULL
;
450 _cleanup_(fido_dev_free_wrapper
) fido_dev_t
*d
= NULL
;
451 _cleanup_(erase_and_freep
) char *used_pin
= NULL
;
452 bool has_rk
, has_client_pin
, has_up
, has_uv
;
453 _cleanup_free_
char *cid_copy
= NULL
;
454 size_t cid_size
, secret_size
;
455 const void *cid
, *secret
;
460 assert(ret_cid_size
);
462 assert(ret_salt_size
);
464 assert(ret_secret_size
);
466 /* Construction is like this: we generate a salt of 32 bytes. We then ask the FIDO2 device to
467 * HMAC-SHA256 it for us with its internal key. The result is the key used by LUKS and account
468 * authentication. LUKS and UNIX password auth all do their own salting before hashing, so that FIDO2
469 * device never sees the volume key.
471 * S = HMAC-SHA256(I, D)
473 * with: S → LUKS/account authentication key (never stored)
474 * I → internal key on FIDO2 device (stored in the FIDO2 device)
475 * D → salt we generate here (stored in the privileged part of the JSON record)
481 r
= dlopen_libfido2();
483 return log_error_errno(r
, "FIDO2 token support is not installed.");
485 salt
= malloc(FIDO2_SALT_SIZE
);
489 r
= genuine_random_bytes(salt
, FIDO2_SALT_SIZE
, RANDOM_BLOCK
);
491 return log_error_errno(r
, "Failed to generate salt: %m");
493 d
= sym_fido_dev_new();
497 r
= sym_fido_dev_open(d
, device
);
499 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
500 "Failed to open FIDO2 device %s: %s", device
, sym_fido_strerr(r
));
502 r
= verify_features(d
, device
, LOG_ERR
, &has_rk
, &has_client_pin
, &has_up
, &has_uv
);
506 c
= sym_fido_cred_new();
510 r
= sym_fido_cred_set_extensions(c
, FIDO_EXT_HMAC_SECRET
);
512 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
513 "Failed to enable HMAC-SECRET extension on FIDO2 credential: %s", sym_fido_strerr(r
));
515 r
= sym_fido_cred_set_rp(c
, rp_id
, rp_name
);
517 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
518 "Failed to set FIDO2 credential relying party ID/name: %s", sym_fido_strerr(r
));
520 r
= sym_fido_cred_set_type(c
, COSE_ES256
);
522 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
523 "Failed to set FIDO2 credential type to ES256: %s", sym_fido_strerr(r
));
525 r
= sym_fido_cred_set_user(
527 user_id
, user_id_len
,
532 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
533 "Failed to set FIDO2 credential user data: %s", sym_fido_strerr(r
));
535 r
= sym_fido_cred_set_clientdata_hash(c
, (const unsigned char[32]) {}, 32);
537 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
538 "Failed to set FIDO2 client data hash: %s", sym_fido_strerr(r
));
541 r
= sym_fido_cred_set_rk(c
, FIDO_OPT_FALSE
);
543 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
544 "Failed to turn off FIDO2 resident key option of credential: %s", sym_fido_strerr(r
));
548 r
= sym_fido_cred_set_uv(c
, FIDO_OPT_FALSE
);
550 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
551 "Failed to turn off FIDO2 user verification option of credential: %s", sym_fido_strerr(r
));
554 log_info("Initializing FIDO2 credential on security token.");
556 log_notice("%s%s(Hint: This might require verification of user presence on security token.)",
557 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
558 emoji_enabled() ? " " : "");
560 r
= sym_fido_dev_make_cred(d
, c
, NULL
);
561 if (r
== FIDO_ERR_PIN_REQUIRED
) {
563 _cleanup_(strv_free_erasep
) char **pin
= NULL
;
567 log_warning("Weird, device asked for client PIN, but does not advertise it as feature. Ignoring.");
569 r
= ask_password_auto("Please enter security token PIN:", askpw_icon_name
, NULL
, "fido2-pin", USEC_INFINITY
, 0, &pin
);
571 return log_error_errno(r
, "Failed to acquire user PIN: %m");
573 r
= FIDO_ERR_PIN_INVALID
;
574 STRV_FOREACH(i
, pin
) {
576 log_info("PIN may not be empty.");
580 r
= sym_fido_dev_make_cred(d
, c
, *i
);
582 used_pin
= strdup(*i
);
587 if (r
!= FIDO_ERR_PIN_INVALID
)
591 if (r
!= FIDO_ERR_PIN_INVALID
)
594 log_notice("PIN incorrect, please try again.");
597 if (r
== FIDO_ERR_PIN_AUTH_BLOCKED
)
598 return log_notice_errno(SYNTHETIC_ERRNO(EPERM
),
599 "Token PIN is currently blocked, please remove and reinsert token.");
600 if (r
== FIDO_ERR_ACTION_TIMEOUT
)
601 return log_error_errno(SYNTHETIC_ERRNO(ENOSTR
),
602 "Token action timeout. (User didn't interact with token quickly enough.)");
604 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
605 "Failed to generate FIDO2 credential: %s", sym_fido_strerr(r
));
607 cid
= sym_fido_cred_id_ptr(c
);
609 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to get FIDO2 credential ID.");
611 cid_size
= sym_fido_cred_id_len(c
);
613 a
= sym_fido_assert_new();
617 r
= sym_fido_assert_set_extensions(a
, FIDO_EXT_HMAC_SECRET
);
619 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
620 "Failed to enable HMAC-SECRET extension on FIDO2 assertion: %s", sym_fido_strerr(r
));
622 r
= sym_fido_assert_set_hmac_salt(a
, salt
, FIDO2_SALT_SIZE
);
624 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
625 "Failed to set salt on FIDO2 assertion: %s", sym_fido_strerr(r
));
627 r
= sym_fido_assert_set_rp(a
, rp_id
);
629 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
630 "Failed to set FIDO2 assertion ID: %s", sym_fido_strerr(r
));
632 r
= sym_fido_assert_set_clientdata_hash(a
, (const unsigned char[32]) {}, 32);
634 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
635 "Failed to set FIDO2 assertion client data hash: %s", sym_fido_strerr(r
));
637 r
= sym_fido_assert_allow_cred(a
, cid
, cid_size
);
639 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
640 "Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r
));
643 r
= sym_fido_assert_set_up(a
, FIDO_OPT_FALSE
);
645 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
646 "Failed to turn off FIDO2 assertion user presence: %s", sym_fido_strerr(r
));
649 log_info("Generating secret key on FIDO2 security token.");
651 r
= sym_fido_dev_get_assert(d
, a
, used_pin
);
652 if (r
== FIDO_ERR_UP_REQUIRED
) {
655 log_warning("Weird, device asked for User Presence check, but does not advertise it as feature. Ignoring.");
657 r
= sym_fido_assert_set_up(a
, FIDO_OPT_TRUE
);
659 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
660 "Failed to turn on FIDO2 assertion user presence: %s", sym_fido_strerr(r
));
662 log_notice("%s%sIn order to allow secret key generation, please verify presence on security token.",
663 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
664 emoji_enabled() ? " " : "");
666 r
= sym_fido_dev_get_assert(d
, a
, used_pin
);
668 if (r
== FIDO_ERR_ACTION_TIMEOUT
)
669 return log_error_errno(SYNTHETIC_ERRNO(ENOSTR
),
670 "Token action timeout. (User didn't interact with token quickly enough.)");
672 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
673 "Failed to ask token for assertion: %s", sym_fido_strerr(r
));
675 secret
= sym_fido_assert_hmac_secret_ptr(a
, 0);
677 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to retrieve HMAC secret.");
679 secret_size
= sym_fido_assert_hmac_secret_len(a
, 0);
681 secret_copy
= memdup(secret
, secret_size
);
685 cid_copy
= memdup(cid
, cid_size
);
689 *ret_cid
= TAKE_PTR(cid_copy
);
690 *ret_cid_size
= cid_size
;
691 *ret_salt
= TAKE_PTR(salt
);
692 *ret_salt_size
= FIDO2_SALT_SIZE
;
693 *ret_secret
= TAKE_PTR(secret_copy
);
694 *ret_secret_size
= secret_size
;
697 *ret_usedpin
= TAKE_PTR(used_pin
);
704 static int check_device_is_fido2_with_hmac_secret(const char *path
) {
705 _cleanup_(fido_dev_free_wrapper
) fido_dev_t
*d
= NULL
;
708 d
= sym_fido_dev_new();
712 r
= sym_fido_dev_open(d
, path
);
714 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
715 "Failed to open FIDO2 device %s: %s", path
, sym_fido_strerr(r
));
717 r
= verify_features(d
, path
, LOG_DEBUG
, NULL
, NULL
, NULL
, NULL
);
718 if (r
== -ENODEV
) /* Not a FIDO2 device, or not implementing 'hmac-secret' */
727 int fido2_list_devices(void) {
729 _cleanup_(table_unrefp
) Table
*t
= NULL
;
730 size_t allocated
= 64, found
= 0;
731 fido_dev_info_t
*di
= NULL
;
734 r
= dlopen_libfido2();
736 return log_error_errno(r
, "FIDO2 token support is not installed.");
738 di
= sym_fido_dev_info_new(allocated
);
742 r
= sym_fido_dev_info_manifest(di
, allocated
, &found
);
743 if (r
== FIDO_ERR_INTERNAL
|| (r
== FIDO_OK
&& found
== 0)) {
744 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
745 log_info("No FIDO2 devices found.");
750 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to enumerate FIDO2 devices: %s", sym_fido_strerr(r
));
754 t
= table_new("path", "manufacturer", "product");
760 for (size_t i
= 0; i
< found
; i
++) {
761 const fido_dev_info_t
*entry
;
763 entry
= sym_fido_dev_info_ptr(di
, i
);
765 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
),
766 "Failed to get device information for FIDO device %zu.", i
);
770 r
= check_device_is_fido2_with_hmac_secret(sym_fido_dev_info_path(entry
));
778 TABLE_PATH
, sym_fido_dev_info_path(entry
),
779 TABLE_STRING
, sym_fido_dev_info_manufacturer_string(entry
),
780 TABLE_STRING
, sym_fido_dev_info_product_string(entry
));
782 table_log_add_error(r
);
787 r
= table_print(t
, stdout
);
789 log_error_errno(r
, "Failed to show device table: %m");
796 sym_fido_dev_info_free(&di
, allocated
);
799 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
800 "FIDO2 tokens not supported on this build.");
804 int fido2_find_device_auto(char **ret
) {
806 _cleanup_free_
char *copy
= NULL
;
807 size_t di_size
= 64, found
= 0;
808 const fido_dev_info_t
*entry
;
809 fido_dev_info_t
*di
= NULL
;
813 r
= dlopen_libfido2();
815 return log_error_errno(r
, "FIDO2 token support is not installed.");
817 di
= sym_fido_dev_info_new(di_size
);
821 r
= sym_fido_dev_info_manifest(di
, di_size
, &found
);
822 if (r
== FIDO_ERR_INTERNAL
|| (r
== FIDO_OK
&& found
== 0)) {
823 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
824 r
= log_error_errno(SYNTHETIC_ERRNO(ENODEV
), "No FIDO devices found.");
828 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to enumerate FIDO devices: %s", sym_fido_strerr(r
));
832 r
= log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ
), "More than one FIDO device found.");
836 entry
= sym_fido_dev_info_ptr(di
, 0);
838 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
),
839 "Failed to get device information for FIDO device 0.");
843 r
= check_device_is_fido2_with_hmac_secret(sym_fido_dev_info_path(entry
));
847 r
= log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
), "FIDO device discovered does not implement FIDO2 with 'hmac-secret' extension.");
851 path
= sym_fido_dev_info_path(entry
);
853 r
= log_error_errno(SYNTHETIC_ERRNO(EIO
),
854 "Failed to query FIDO device path.");
864 *ret
= TAKE_PTR(copy
);
868 sym_fido_dev_info_free(&di
, di_size
);
871 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
872 "FIDO2 tokens not supported on this build.");