fido2: don't use up/uv/rk when device doesn't support it
Apparently devices are supposed to generate failures if we try to turn off features they don't have. Thus don't. Prompted-by: https://github.com/systemd/systemd/issues/17784#issuecomment-737730395
This commit is contained in:
parent
ebcb3f38d2
commit
e3fb662b67
|
@ -29,6 +29,9 @@ size_t (*sym_fido_cbor_info_extensions_len)(const fido_cbor_info_t *) = NULL;
|
||||||
char **(*sym_fido_cbor_info_extensions_ptr)(const fido_cbor_info_t *) = NULL;
|
char **(*sym_fido_cbor_info_extensions_ptr)(const fido_cbor_info_t *) = NULL;
|
||||||
void (*sym_fido_cbor_info_free)(fido_cbor_info_t **) = NULL;
|
void (*sym_fido_cbor_info_free)(fido_cbor_info_t **) = NULL;
|
||||||
fido_cbor_info_t* (*sym_fido_cbor_info_new)(void) = NULL;
|
fido_cbor_info_t* (*sym_fido_cbor_info_new)(void) = NULL;
|
||||||
|
size_t (*sym_fido_cbor_info_options_len)(const fido_cbor_info_t *) = NULL;
|
||||||
|
char** (*sym_fido_cbor_info_options_name_ptr)(const fido_cbor_info_t *) = NULL;
|
||||||
|
const bool* (*sym_fido_cbor_info_options_value_ptr)(const fido_cbor_info_t *) = NULL;
|
||||||
void (*sym_fido_cred_free)(fido_cred_t **) = NULL;
|
void (*sym_fido_cred_free)(fido_cred_t **) = NULL;
|
||||||
size_t (*sym_fido_cred_id_len)(const fido_cred_t *) = NULL;
|
size_t (*sym_fido_cred_id_len)(const fido_cred_t *) = NULL;
|
||||||
const unsigned char* (*sym_fido_cred_id_ptr)(const fido_cred_t *) = NULL;
|
const unsigned char* (*sym_fido_cred_id_ptr)(const fido_cred_t *) = NULL;
|
||||||
|
@ -85,6 +88,9 @@ int dlopen_libfido2(void) {
|
||||||
DLSYM_ARG(fido_cbor_info_extensions_ptr),
|
DLSYM_ARG(fido_cbor_info_extensions_ptr),
|
||||||
DLSYM_ARG(fido_cbor_info_free),
|
DLSYM_ARG(fido_cbor_info_free),
|
||||||
DLSYM_ARG(fido_cbor_info_new),
|
DLSYM_ARG(fido_cbor_info_new),
|
||||||
|
DLSYM_ARG(fido_cbor_info_options_len),
|
||||||
|
DLSYM_ARG(fido_cbor_info_options_name_ptr),
|
||||||
|
DLSYM_ARG(fido_cbor_info_options_value_ptr),
|
||||||
DLSYM_ARG(fido_cred_free),
|
DLSYM_ARG(fido_cred_free),
|
||||||
DLSYM_ARG(fido_cred_id_len),
|
DLSYM_ARG(fido_cred_id_len),
|
||||||
DLSYM_ARG(fido_cred_id_ptr),
|
DLSYM_ARG(fido_cred_id_ptr),
|
||||||
|
@ -121,6 +127,86 @@ int dlopen_libfido2(void) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int verify_features(
|
||||||
|
fido_dev_t *d,
|
||||||
|
const char *path,
|
||||||
|
bool *ret_has_rk,
|
||||||
|
bool *ret_has_client_pin,
|
||||||
|
bool *ret_has_up,
|
||||||
|
bool *ret_has_uv) {
|
||||||
|
|
||||||
|
_cleanup_(fido_cbor_info_free_wrapper) fido_cbor_info_t *di = NULL;
|
||||||
|
bool found_extension = false;
|
||||||
|
char **e, **o;
|
||||||
|
const bool *b;
|
||||||
|
bool has_rk = false, has_client_pin = false, has_up = true, has_uv = false; /* Defaults are per table in 5.4 in FIDO2 spec */
|
||||||
|
size_t n;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(d);
|
||||||
|
assert(path);
|
||||||
|
|
||||||
|
if (!sym_fido_dev_is_fido2(d))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
|
||||||
|
"Specified device %s is not a FIDO2 device.", path);
|
||||||
|
|
||||||
|
di = sym_fido_cbor_info_new();
|
||||||
|
if (!di)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
r = sym_fido_dev_get_cbor_info(d, di);
|
||||||
|
if (r != FIDO_OK)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||||
|
"Failed to get CBOR device info for %s: %s", path, sym_fido_strerr(r));
|
||||||
|
|
||||||
|
e = sym_fido_cbor_info_extensions_ptr(di);
|
||||||
|
n = sym_fido_cbor_info_extensions_len(di);
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
log_debug("FIDO2 device implements extension: %s", e[i]);
|
||||||
|
if (streq(e[i], "hmac-secret"))
|
||||||
|
found_extension = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
o = sym_fido_cbor_info_options_name_ptr(di);
|
||||||
|
b = sym_fido_cbor_info_options_value_ptr(di);
|
||||||
|
n = sym_fido_cbor_info_options_len(di);
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
log_debug("FIDO2 device implements option %s: %s", o[i], yes_no(b[i]));
|
||||||
|
if (streq(o[i], "rk"))
|
||||||
|
has_rk = b[i];
|
||||||
|
if (streq(o[i], "clientPin"))
|
||||||
|
has_client_pin = b[i];
|
||||||
|
if (streq(o[i], "up"))
|
||||||
|
has_up = b[i];
|
||||||
|
if (streq(o[i], "uv"))
|
||||||
|
has_uv = b[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found_extension)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
|
||||||
|
"Specified device %s is a FIDO2 device, but does not support the required HMAC-SECRET extension.", path);
|
||||||
|
|
||||||
|
log_debug("Has rk ('Resident Key') support: %s\n"
|
||||||
|
"Has clientPin support: %s\n"
|
||||||
|
"Has up ('User Presence') support: %s\n"
|
||||||
|
"Has uv ('User Verification') support: %s\n",
|
||||||
|
yes_no(has_rk),
|
||||||
|
yes_no(has_client_pin),
|
||||||
|
yes_no(has_up),
|
||||||
|
yes_no(has_uv));
|
||||||
|
|
||||||
|
if (ret_has_rk)
|
||||||
|
*ret_has_rk = has_rk;
|
||||||
|
if (ret_has_client_pin)
|
||||||
|
*ret_has_client_pin = has_client_pin;
|
||||||
|
if (ret_has_up)
|
||||||
|
*ret_has_up = has_up;
|
||||||
|
if (ret_has_uv)
|
||||||
|
*ret_has_uv = has_uv;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int fido2_use_hmac_hash_specific_token(
|
static int fido2_use_hmac_hash_specific_token(
|
||||||
const char *path,
|
const char *path,
|
||||||
const char *rp_id,
|
const char *rp_id,
|
||||||
|
@ -133,14 +219,12 @@ static int fido2_use_hmac_hash_specific_token(
|
||||||
void **ret_hmac,
|
void **ret_hmac,
|
||||||
size_t *ret_hmac_size) {
|
size_t *ret_hmac_size) {
|
||||||
|
|
||||||
_cleanup_(fido_cbor_info_free_wrapper) fido_cbor_info_t *di = NULL;
|
|
||||||
_cleanup_(fido_assert_free_wrapper) fido_assert_t *a = NULL;
|
_cleanup_(fido_assert_free_wrapper) fido_assert_t *a = NULL;
|
||||||
_cleanup_(fido_dev_free_wrapper) fido_dev_t *d = NULL;
|
_cleanup_(fido_dev_free_wrapper) fido_dev_t *d = NULL;
|
||||||
_cleanup_(erase_and_freep) void *hmac_copy = NULL;
|
_cleanup_(erase_and_freep) void *hmac_copy = NULL;
|
||||||
bool found_extension = false;
|
bool has_up, has_client_pin;
|
||||||
size_t n, hmac_size;
|
size_t hmac_size;
|
||||||
const void *hmac;
|
const void *hmac;
|
||||||
char **e;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(path);
|
assert(path);
|
||||||
|
@ -159,31 +243,9 @@ static int fido2_use_hmac_hash_specific_token(
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||||
"Failed to open FIDO2 device %s: %s", path, sym_fido_strerr(r));
|
"Failed to open FIDO2 device %s: %s", path, sym_fido_strerr(r));
|
||||||
|
|
||||||
if (!sym_fido_dev_is_fido2(d))
|
r = verify_features(d, path, NULL, &has_client_pin, &has_up, NULL);
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
|
if (r < 0)
|
||||||
"Specified device %s is not a FIDO2 device.", path);
|
return r;
|
||||||
|
|
||||||
di = sym_fido_cbor_info_new();
|
|
||||||
if (!di)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
r = sym_fido_dev_get_cbor_info(d, di);
|
|
||||||
if (r != FIDO_OK)
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
|
||||||
"Failed to get CBOR device info for %s: %s", path, sym_fido_strerr(r));
|
|
||||||
|
|
||||||
e = sym_fido_cbor_info_extensions_ptr(di);
|
|
||||||
n = sym_fido_cbor_info_extensions_len(di);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < n; i++)
|
|
||||||
if (streq(e[i], "hmac-secret")) {
|
|
||||||
found_extension = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found_extension)
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
|
|
||||||
"Specified device %s is a FIDO2 device, but does not support the required HMAC-SECRET extension.", path);
|
|
||||||
|
|
||||||
a = sym_fido_assert_new();
|
a = sym_fido_assert_new();
|
||||||
if (!a)
|
if (!a)
|
||||||
|
@ -214,15 +276,21 @@ static int fido2_use_hmac_hash_specific_token(
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||||
"Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r));
|
"Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r));
|
||||||
|
|
||||||
r = sym_fido_assert_set_up(a, FIDO_OPT_FALSE);
|
if (has_up) {
|
||||||
if (r != FIDO_OK)
|
r = sym_fido_assert_set_up(a, FIDO_OPT_FALSE);
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
if (r != FIDO_OK)
|
||||||
"Failed to set FIDO2 assertion user presence: %s", sym_fido_strerr(r));
|
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||||
|
"Failed to set FIDO2 assertion user presence: %s", sym_fido_strerr(r));
|
||||||
|
}
|
||||||
|
|
||||||
log_info("Asking FIDO2 token for authentication.");
|
log_info("Asking FIDO2 token for authentication.");
|
||||||
|
|
||||||
r = sym_fido_dev_get_assert(d, a, NULL); /* try without pin and without up first */
|
r = sym_fido_dev_get_assert(d, a, NULL); /* try without pin and without up first */
|
||||||
if (r == FIDO_ERR_UP_REQUIRED && up) {
|
if (r == FIDO_ERR_UP_REQUIRED && up) {
|
||||||
|
|
||||||
|
if (!has_up)
|
||||||
|
log_warning("Weird, device asked for User Presence check, but does not advertise it as feature. Ignoring.");
|
||||||
|
|
||||||
r = sym_fido_assert_set_up(a, FIDO_OPT_TRUE);
|
r = sym_fido_assert_set_up(a, FIDO_OPT_TRUE);
|
||||||
if (r != FIDO_OK)
|
if (r != FIDO_OK)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||||
|
@ -235,6 +303,9 @@ static int fido2_use_hmac_hash_specific_token(
|
||||||
if (r == FIDO_ERR_PIN_REQUIRED) {
|
if (r == FIDO_ERR_PIN_REQUIRED) {
|
||||||
char **i;
|
char **i;
|
||||||
|
|
||||||
|
if (!has_client_pin)
|
||||||
|
log_warning("Weird, device asked for client PIN, but does not advertise it as feature. Ignoring.");
|
||||||
|
|
||||||
/* OK, we needed a pin, try with all pins in turn */
|
/* OK, we needed a pin, try with all pins in turn */
|
||||||
STRV_FOREACH(i, pins) {
|
STRV_FOREACH(i, pins) {
|
||||||
r = sym_fido_dev_get_assert(d, a, *i);
|
r = sym_fido_dev_get_assert(d, a, *i);
|
||||||
|
@ -370,17 +441,15 @@ int fido2_generate_hmac_hash(
|
||||||
void **ret_secret, size_t *ret_secret_size,
|
void **ret_secret, size_t *ret_secret_size,
|
||||||
char **ret_usedpin) {
|
char **ret_usedpin) {
|
||||||
|
|
||||||
_cleanup_(fido_cbor_info_free_wrapper) fido_cbor_info_t *di = NULL;
|
|
||||||
_cleanup_(erase_and_freep) void *salt = NULL, *secret_copy = NULL;
|
_cleanup_(erase_and_freep) void *salt = NULL, *secret_copy = NULL;
|
||||||
_cleanup_(fido_assert_free_wrapper) fido_assert_t *a = NULL;
|
_cleanup_(fido_assert_free_wrapper) fido_assert_t *a = NULL;
|
||||||
_cleanup_(fido_cred_free_wrapper) fido_cred_t *c = NULL;
|
_cleanup_(fido_cred_free_wrapper) fido_cred_t *c = NULL;
|
||||||
_cleanup_(fido_dev_free_wrapper) fido_dev_t *d = NULL;
|
_cleanup_(fido_dev_free_wrapper) fido_dev_t *d = NULL;
|
||||||
_cleanup_(erase_and_freep) char *used_pin = NULL;
|
_cleanup_(erase_and_freep) char *used_pin = NULL;
|
||||||
|
bool has_rk, has_client_pin, has_up, has_uv;
|
||||||
_cleanup_free_ char *cid_copy = NULL;
|
_cleanup_free_ char *cid_copy = NULL;
|
||||||
size_t n, cid_size, secret_size;
|
size_t cid_size, secret_size;
|
||||||
bool found_extension = false;
|
|
||||||
const void *cid, *secret;
|
const void *cid, *secret;
|
||||||
char **e;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(device);
|
assert(device);
|
||||||
|
@ -427,31 +496,9 @@ int fido2_generate_hmac_hash(
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||||
"Failed to open FIDO2 device %s: %s", device, sym_fido_strerr(r));
|
"Failed to open FIDO2 device %s: %s", device, sym_fido_strerr(r));
|
||||||
|
|
||||||
if (!sym_fido_dev_is_fido2(d))
|
r = verify_features(d, device, &has_rk, &has_client_pin, &has_up, &has_uv);
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
|
if (r < 0)
|
||||||
"Specified device %s is not a FIDO2 device.", device);
|
return r;
|
||||||
|
|
||||||
di = sym_fido_cbor_info_new();
|
|
||||||
if (!di)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
r = sym_fido_dev_get_cbor_info(d, di);
|
|
||||||
if (r != FIDO_OK)
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
|
||||||
"Failed to get CBOR device info for %s: %s", device, sym_fido_strerr(r));
|
|
||||||
|
|
||||||
e = sym_fido_cbor_info_extensions_ptr(di);
|
|
||||||
n = sym_fido_cbor_info_extensions_len(di);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < n; i++)
|
|
||||||
if (streq(e[i], "hmac-secret")) {
|
|
||||||
found_extension = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found_extension)
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
|
|
||||||
"Specified device %s is a FIDO2 device, but does not support the required HMAC-SECRET extension.", device);
|
|
||||||
|
|
||||||
c = sym_fido_cred_new();
|
c = sym_fido_cred_new();
|
||||||
if (!c)
|
if (!c)
|
||||||
|
@ -487,15 +534,19 @@ int fido2_generate_hmac_hash(
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||||
"Failed to set FIDO2 client data hash: %s", sym_fido_strerr(r));
|
"Failed to set FIDO2 client data hash: %s", sym_fido_strerr(r));
|
||||||
|
|
||||||
r = sym_fido_cred_set_rk(c, FIDO_OPT_FALSE);
|
if (has_rk) {
|
||||||
if (r != FIDO_OK)
|
r = sym_fido_cred_set_rk(c, FIDO_OPT_FALSE);
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
if (r != FIDO_OK)
|
||||||
"Failed to turn off FIDO2 resident key option of credential: %s", sym_fido_strerr(r));
|
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||||
|
"Failed to turn off FIDO2 resident key option of credential: %s", sym_fido_strerr(r));
|
||||||
|
}
|
||||||
|
|
||||||
r = sym_fido_cred_set_uv(c, FIDO_OPT_FALSE);
|
if (has_uv) {
|
||||||
if (r != FIDO_OK)
|
r = sym_fido_cred_set_uv(c, FIDO_OPT_FALSE);
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
if (r != FIDO_OK)
|
||||||
"Failed to turn off FIDO2 user verification option of credential: %s", sym_fido_strerr(r));
|
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||||
|
"Failed to turn off FIDO2 user verification option of credential: %s", sym_fido_strerr(r));
|
||||||
|
}
|
||||||
|
|
||||||
log_info("Initializing FIDO2 credential on security token.");
|
log_info("Initializing FIDO2 credential on security token.");
|
||||||
|
|
||||||
|
@ -509,6 +560,9 @@ int fido2_generate_hmac_hash(
|
||||||
_cleanup_(strv_free_erasep) char **pin = NULL;
|
_cleanup_(strv_free_erasep) char **pin = NULL;
|
||||||
char **i;
|
char **i;
|
||||||
|
|
||||||
|
if (!has_client_pin)
|
||||||
|
log_warning("Weird, device asked for client PIN, but does not advertise it as feature. Ignoring.");
|
||||||
|
|
||||||
r = ask_password_auto("Please enter security token PIN:", askpw_icon_name, NULL, "fido2-pin", USEC_INFINITY, 0, &pin);
|
r = ask_password_auto("Please enter security token PIN:", askpw_icon_name, NULL, "fido2-pin", USEC_INFINITY, 0, &pin);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to acquire user PIN: %m");
|
return log_error_errno(r, "Failed to acquire user PIN: %m");
|
||||||
|
@ -582,15 +636,21 @@ int fido2_generate_hmac_hash(
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||||
"Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r));
|
"Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r));
|
||||||
|
|
||||||
r = sym_fido_assert_set_up(a, FIDO_OPT_FALSE);
|
if (has_up) {
|
||||||
if (r != FIDO_OK)
|
r = sym_fido_assert_set_up(a, FIDO_OPT_FALSE);
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
if (r != FIDO_OK)
|
||||||
"Failed to turn off FIDO2 assertion user presence: %s", sym_fido_strerr(r));
|
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||||
|
"Failed to turn off FIDO2 assertion user presence: %s", sym_fido_strerr(r));
|
||||||
|
}
|
||||||
|
|
||||||
log_info("Generating secret key on FIDO2 security token.");
|
log_info("Generating secret key on FIDO2 security token.");
|
||||||
|
|
||||||
r = sym_fido_dev_get_assert(d, a, used_pin);
|
r = sym_fido_dev_get_assert(d, a, used_pin);
|
||||||
if (r == FIDO_ERR_UP_REQUIRED) {
|
if (r == FIDO_ERR_UP_REQUIRED) {
|
||||||
|
|
||||||
|
if (!has_up)
|
||||||
|
log_warning("Weird, device asked for User Presence check, but does not advertise it as feature. Ignoring.");
|
||||||
|
|
||||||
r = sym_fido_assert_set_up(a, FIDO_OPT_TRUE);
|
r = sym_fido_assert_set_up(a, FIDO_OPT_TRUE);
|
||||||
if (r != FIDO_OK)
|
if (r != FIDO_OK)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||||
|
|
|
@ -20,6 +20,9 @@ extern size_t (*sym_fido_cbor_info_extensions_len)(const fido_cbor_info_t *);
|
||||||
extern char **(*sym_fido_cbor_info_extensions_ptr)(const fido_cbor_info_t *);
|
extern char **(*sym_fido_cbor_info_extensions_ptr)(const fido_cbor_info_t *);
|
||||||
extern void (*sym_fido_cbor_info_free)(fido_cbor_info_t **);
|
extern void (*sym_fido_cbor_info_free)(fido_cbor_info_t **);
|
||||||
extern fido_cbor_info_t* (*sym_fido_cbor_info_new)(void);
|
extern fido_cbor_info_t* (*sym_fido_cbor_info_new)(void);
|
||||||
|
extern size_t (*sym_fido_cbor_info_options_len)(const fido_cbor_info_t *);
|
||||||
|
extern char** (*sym_fido_cbor_info_options_name_ptr)(const fido_cbor_info_t *);
|
||||||
|
extern const bool* (*sym_fido_cbor_info_options_value_ptr)(const fido_cbor_info_t *);
|
||||||
extern void (*sym_fido_cred_free)(fido_cred_t **);
|
extern void (*sym_fido_cred_free)(fido_cred_t **);
|
||||||
extern size_t (*sym_fido_cred_id_len)(const fido_cred_t *);
|
extern size_t (*sym_fido_cred_id_len)(const fido_cred_t *);
|
||||||
extern const unsigned char* (*sym_fido_cred_id_ptr)(const fido_cred_t *);
|
extern const unsigned char* (*sym_fido_cred_id_ptr)(const fido_cred_t *);
|
||||||
|
|
Loading…
Reference in New Issue