Added support for silent authentication.

Fixes #91.

It requires FIDO22 credential protocol, meaning that old credentials have to be reissued.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
Pol Henarejos
2025-02-08 15:00:12 +01:00
parent 353d782970
commit f43bc9701f
4 changed files with 140 additions and 64 deletions

View File

@@ -279,6 +279,8 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
} }
} }
bool silent = (up == false && uv == false);
if (allowList_len > 0) { if (allowList_len > 0) {
for (size_t e = 0; e < allowList_len; e++) { for (size_t e = 0; e < allowList_len; e++) {
if (allowList[e].type.present == false || allowList[e].id.present == false) { if (allowList[e].type.present == false || allowList[e].id.present == false) {
@@ -288,7 +290,6 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
continue; continue;
} }
if (credential_load(allowList[e].id.data, allowList[e].id.len, rp_id_hash, &creds[creds_len]) != 0) { if (credential_load(allowList[e].id.data, allowList[e].id.len, rp_id_hash, &creds[creds_len]) != 0) {
CBOR_FREE_BYTE_STRING(allowList[e].id);
credential_free(&creds[creds_len]); credential_free(&creds[creds_len]);
} }
else { else {
@@ -342,15 +343,32 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
} }
} }
if (numberOfCredentials == 0) { if (numberOfCredentials == 0) {
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS); if (silent && allowList_len > 0) {
for (size_t e = 0; e < allowList_len; e++) {
if (allowList[e].type.present == false || allowList[e].id.present == false) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
if (strcmp(allowList[e].type.data, "public-key") != 0) {
continue;
}
if (credential_verify(allowList[e].id.data, allowList[e].id.len, rp_id_hash, true) == 0) {
numberOfCredentials++;
}
}
}
if (numberOfCredentials == 0) {
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS);
}
} }
for (int i = 0; i < numberOfCredentials; i++) { if (!silent) {
for (int j = i + 1; j < numberOfCredentials; j++) { for (int i = 0; i < numberOfCredentials; i++) {
if (creds[j].creation > creds[i].creation) { for (int j = i + 1; j < numberOfCredentials; j++) {
Credential tmp = creds[j]; if (creds[j].creation > creds[i].creation) {
creds[j] = creds[i]; Credential tmp = creds[j];
creds[i] = tmp; creds[j] = creds[i];
creds[i] = tmp;
}
} }
} }
} }
@@ -380,8 +398,8 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION); CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
} }
if (up == false && uv == false) { if (silent && !resident) {
selcred = &creds[0]; // Silent authentication, do nothing
} }
else { else {
selcred = &creds[0]; selcred = &creds[0];
@@ -410,16 +428,18 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
int ret = 0; int ret = 0;
uint8_t largeBlobKey[32] = {0}; uint8_t largeBlobKey[32] = {0};
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) { if (selcred) {
ret = credential_derive_large_blob_key(selcred->id.data, selcred->id.len, largeBlobKey); if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
if (ret != 0) { ret = credential_derive_large_blob_key(selcred->id.data, selcred->id.len, largeBlobKey);
CBOR_ERROR(CTAP2_ERR_PROCESSING); if (ret != 0) {
CBOR_ERROR(CTAP2_ERR_PROCESSING);
}
} }
} }
size_t ext_len = 0; size_t ext_len = 0;
uint8_t ext[512] = {0}; uint8_t ext[512] = {0};
if (extensions.present == true) { if (selcred && extensions.present == true) {
cbor_encoder_init(&encoder, ext, sizeof(ext), 0); cbor_encoder_init(&encoder, ext, sizeof(ext), 0);
int l = 0; int l = 0;
if (options.up == pfalse) { if (options.up == pfalse) {
@@ -530,32 +550,39 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
const mbedtls_md_info_t *md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); const mbedtls_md_info_t *md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
mbedtls_ecdsa_context ekey; mbedtls_ecdsa_context ekey;
mbedtls_ecdsa_init(&ekey); mbedtls_ecdsa_init(&ekey);
ret = fido_load_key((int)selcred->curve, selcred->id.data, &ekey);
if (ret != 0) {
if (derive_key(rp_id_hash, false, selcred->id.data, MBEDTLS_ECP_DP_SECP256R1, &ekey) != 0) {
mbedtls_ecdsa_free(&ekey);
CBOR_ERROR(CTAP1_ERR_OTHER);
}
}
if (ekey.grp.id == MBEDTLS_ECP_DP_SECP384R1) {
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
}
else if (ekey.grp.id == MBEDTLS_ECP_DP_SECP521R1) {
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
}
ret = mbedtls_md(md, aut_data, aut_data_len + clientDataHash.len, hash);
size_t olen = 0; size_t olen = 0;
ret = mbedtls_ecdsa_write_signature(&ekey, mbedtls_md_get_type(md), hash, mbedtls_md_get_size(md), sig, sizeof(sig), &olen, random_gen, NULL); if (selcred) {
ret = fido_load_key((int)selcred->curve, selcred->id.data, &ekey);
if (ret != 0) {
if (derive_key(rp_id_hash, false, selcred->id.data, MBEDTLS_ECP_DP_SECP256R1, &ekey) != 0) {
mbedtls_ecdsa_free(&ekey);
CBOR_ERROR(CTAP1_ERR_OTHER);
}
}
if (ekey.grp.id == MBEDTLS_ECP_DP_SECP384R1) {
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
}
else if (ekey.grp.id == MBEDTLS_ECP_DP_SECP521R1) {
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
}
ret = mbedtls_md(md, aut_data, aut_data_len + clientDataHash.len, hash);
ret = mbedtls_ecdsa_write_signature(&ekey, mbedtls_md_get_type(md), hash, mbedtls_md_get_size(md), sig, sizeof(sig), &olen, random_gen, NULL);
}
else {
// Bogus signature
olen = 64;
memset(sig, 0x0B, olen);
}
mbedtls_ecdsa_free(&ekey); mbedtls_ecdsa_free(&ekey);
uint8_t lfields = 3; uint8_t lfields = 3;
if (selcred->opts.present == true && selcred->opts.rk == ptrue) { if (selcred && selcred->opts.present == true && selcred->opts.rk == ptrue) {
lfields++; lfields++;
} }
if (numberOfCredentials > 1 && next == false) { if (numberOfCredentials > 1 && next == false) {
lfields++; lfields++;
} }
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) { if (selcred && extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
lfields++; lfields++;
} }
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0); cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0);
@@ -564,7 +591,12 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01)); CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 2)); CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 2));
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "id")); CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "id"));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, selcred->id.data, selcred->id.len)); if (selcred) {
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, selcred->id.data, selcred->id.len));
}
else {
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, (uint8_t *)"\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01", 16));
}
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "type")); CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "type"));
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "public-key")); CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "public-key"));
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2)); CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
@@ -574,7 +606,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03)); CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, sig, olen)); CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, sig, olen));
if (selcred->opts.present == true && selcred->opts.rk == ptrue) { if (selcred && selcred->opts.present == true && selcred->opts.rk == ptrue) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04)); CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
uint8_t lu = 1; uint8_t lu = 1;
if (numberOfCredentials > 1 && allowList_len == 0) { if (numberOfCredentials > 1 && allowList_len == 0) {
@@ -605,7 +637,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x05)); CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x05));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, numberOfCredentials)); CBOR_CHECK(cbor_encode_uint(&mapEncoder, numberOfCredentials));
} }
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) { if (selcred && extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x07)); CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x07));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, largeBlobKey, sizeof(largeBlobKey))); CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, largeBlobKey, sizeof(largeBlobKey)));
} }

View File

@@ -43,7 +43,7 @@ int cmd_authenticate() {
int ret = 0; int ret = 0;
uint8_t *tmp_kh = (uint8_t *) calloc(1, req->keyHandleLen); uint8_t *tmp_kh = (uint8_t *) calloc(1, req->keyHandleLen);
memcpy(tmp_kh, req->keyHandle, req->keyHandleLen); memcpy(tmp_kh, req->keyHandle, req->keyHandleLen);
if (credential_verify(tmp_kh, req->keyHandleLen, req->appId) == 0) { if (credential_verify(tmp_kh, req->keyHandleLen, req->appId, false) == 0) {
ret = fido_load_key(FIDO2_CURVE_P256, req->keyHandle, &key); ret = fido_load_key(FIDO2_CURVE_P256, req->keyHandle, &key);
} }
else { else {

View File

@@ -27,22 +27,52 @@
#include "random.h" #include "random.h"
#include "files.h" #include "files.h"
#include "pico_keys.h" #include "pico_keys.h"
#include "otp.h"
int credential_derive_chacha_key(uint8_t *outk); int credential_derive_chacha_key(uint8_t *outk, const uint8_t *);
int credential_verify(uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash) { static int credential_silent_tag(const uint8_t *cred_id, size_t cred_id_len, uint8_t *outk) {
if (otp_key_1) {
memcpy(outk, otp_key_1, 32);
}
else {
mbedtls_sha256(pico_serial.id, PICO_UNIQUE_BOARD_ID_SIZE_BYTES, outk, 0);
}
return mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), outk, 32, cred_id, cred_id_len - CRED_SILENT_TAG_LEN, outk);
}
int credential_verify(uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash, bool silent) {
if (cred_id_len < 4 + 12 + 16) { if (cred_id_len < 4 + 12 + 16) {
return -1; return -1;
} }
uint8_t key[32], *iv = cred_id + 4, *cipher = cred_id + 4 + 12, uint8_t key[32] = {0}, *iv = cred_id + CRED_PROTO_LEN, *cipher = cred_id + CRED_PROTO_LEN + CRED_IV_LEN,
*tag = cred_id + cred_id_len - 16; *tag = cred_id + cred_id_len - CRED_TAG_LEN;
memset(key, 0, sizeof(key)); cred_proto_t proto = CRED_PROTO_21;
credential_derive_chacha_key(key); if (memcmp(cred_id, CRED_PROTO_22_S, CRED_PROTO_LEN) == 0) { // New format
mbedtls_chachapoly_context chatx; tag = cred_id + cred_id_len - CRED_SILENT_TAG_LEN - CRED_TAG_LEN;
mbedtls_chachapoly_init(&chatx); proto = CRED_PROTO_22;
mbedtls_chachapoly_setkey(&chatx, key); }
int ret = mbedtls_chachapoly_auth_decrypt(&chatx, cred_id_len - (4 + 12 + 16), iv, rp_id_hash, 32, tag, cipher, cipher); int ret = 0;
mbedtls_chachapoly_free(&chatx); if (!silent) {
int hdr_len = CRED_PROTO_LEN + CRED_IV_LEN + CRED_TAG_LEN;
if (proto == CRED_PROTO_22) {
hdr_len += CRED_SILENT_TAG_LEN;
}
credential_derive_chacha_key(key, cred_id);
mbedtls_chachapoly_context chatx;
mbedtls_chachapoly_init(&chatx);
mbedtls_chachapoly_setkey(&chatx, key);
ret = mbedtls_chachapoly_auth_decrypt(&chatx, cred_id_len - hdr_len, iv, rp_id_hash, 32, tag, cipher, cipher);
mbedtls_chachapoly_free(&chatx);
}
else {
if (proto <= CRED_PROTO_21) {
return -1;
}
uint8_t outk[32];
ret = credential_silent_tag(cred_id, cred_id_len, outk);
ret = memcmp(outk, cred_id + cred_id_len - CRED_SILENT_TAG_LEN, CRED_SILENT_TAG_LEN);
}
return ret; return ret;
} }
@@ -113,25 +143,25 @@ int credential_create(CborCharString *rpId,
} }
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder)); CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
size_t rs = cbor_encoder_get_buffer_size(&encoder, cred_id); size_t rs = cbor_encoder_get_buffer_size(&encoder, cred_id);
*cred_id_len = 4 + 12 + rs + 16; *cred_id_len = CRED_PROTO_LEN + CRED_IV_LEN + rs + CRED_TAG_LEN + CRED_SILENT_TAG_LEN;
uint8_t key[32]; uint8_t key[32] = {0};
memset(key, 0, sizeof(key)); credential_derive_chacha_key(key, (const uint8_t *)CRED_PROTO);
credential_derive_chacha_key(key); uint8_t iv[CRED_IV_LEN] = {0};
uint8_t iv[12];
random_gen(NULL, iv, sizeof(iv)); random_gen(NULL, iv, sizeof(iv));
mbedtls_chachapoly_context chatx; mbedtls_chachapoly_context chatx;
mbedtls_chachapoly_init(&chatx); mbedtls_chachapoly_init(&chatx);
mbedtls_chachapoly_setkey(&chatx, key); mbedtls_chachapoly_setkey(&chatx, key);
int ret = mbedtls_chachapoly_encrypt_and_tag(&chatx, rs, iv, rp_id_hash, 32, int ret = mbedtls_chachapoly_encrypt_and_tag(&chatx, rs, iv, rp_id_hash, 32,
cred_id + 4 + 12, cred_id + CRED_PROTO_LEN + CRED_IV_LEN,
cred_id + 4 + 12, cred_id + CRED_PROTO_LEN + CRED_IV_LEN,
cred_id + 4 + 12 + rs); cred_id + CRED_PROTO_LEN + CRED_IV_LEN + rs);
mbedtls_chachapoly_free(&chatx); mbedtls_chachapoly_free(&chatx);
if (ret != 0) { if (ret != 0) {
CBOR_ERROR(CTAP1_ERR_OTHER); CBOR_ERROR(CTAP1_ERR_OTHER);
} }
memcpy(cred_id, CRED_PROTO, 4); memcpy(cred_id, CRED_PROTO, CRED_PROTO_LEN);
memcpy(cred_id + 4, iv, 12); memcpy(cred_id + CRED_PROTO_LEN, iv, CRED_IV_LEN);
credential_silent_tag(cred_id, *cred_id_len, cred_id + CRED_PROTO_LEN + CRED_IV_LEN + rs + CRED_TAG_LEN);
err: err:
if (error != CborNoError) { if (error != CborNoError) {
@@ -152,7 +182,7 @@ int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *r
} }
memset(cred, 0, sizeof(Credential)); memset(cred, 0, sizeof(Credential));
memcpy(copy_cred_id, cred_id, cred_id_len); memcpy(copy_cred_id, cred_id, cred_id_len);
ret = credential_verify(copy_cred_id, cred_id_len, rp_id_hash); ret = credential_verify(copy_cred_id, cred_id_len, rp_id_hash, false);
if (ret != 0) { // U2F? if (ret != 0) { // U2F?
if (cred_id_len != KEY_HANDLE_LEN || verify_key(rp_id_hash, cred_id, NULL) != 0) { if (cred_id_len != KEY_HANDLE_LEN || verify_key(rp_id_hash, cred_id, NULL) != 0) {
CBOR_ERROR(CTAP2_ERR_INVALID_CREDENTIAL); CBOR_ERROR(CTAP2_ERR_INVALID_CREDENTIAL);
@@ -350,13 +380,13 @@ int credential_derive_hmac_key(const uint8_t *cred_id, size_t cred_id_len, uint8
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512); const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "SLIP-0022", 9, outk); mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "SLIP-0022", 9, outk);
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) CRED_PROTO, 4, outk); mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) cred_id, CRED_PROTO_LEN, outk);
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "hmac-secret", 11, outk); mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "hmac-secret", 11, outk);
mbedtls_md_hmac(md_info, outk, 32, cred_id, cred_id_len, outk); mbedtls_md_hmac(md_info, outk, 32, cred_id, cred_id_len, outk);
return 0; return 0;
} }
int credential_derive_chacha_key(uint8_t *outk) { int credential_derive_chacha_key(uint8_t *outk, const uint8_t *proto) {
memset(outk, 0, 32); memset(outk, 0, 32);
int r = 0; int r = 0;
if ((r = load_keydev(outk)) != 0) { if ((r = load_keydev(outk)) != 0) {
@@ -365,7 +395,7 @@ int credential_derive_chacha_key(uint8_t *outk) {
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "SLIP-0022", 9, outk); mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "SLIP-0022", 9, outk);
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) CRED_PROTO, 4, outk); mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) (proto ? proto : (const uint8_t *)CRED_PROTO), CRED_PROTO_LEN, outk);
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "Encryption key", 14, outk); mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "Encryption key", 14, outk);
return 0; return 0;
} }
@@ -379,7 +409,7 @@ int credential_derive_large_blob_key(const uint8_t *cred_id, size_t cred_id_len,
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "SLIP-0022", 9, outk); mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "SLIP-0022", 9, outk);
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) CRED_PROTO, 4, outk); mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) cred_id, CRED_PROTO_LEN, outk);
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "largeBlobKey", 12, outk); mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "largeBlobKey", 12, outk);
mbedtls_md_hmac(md_info, outk, 32, cred_id, cred_id_len, outk); mbedtls_md_hmac(md_info, outk, 32, cred_id, cred_id_len, outk);
return 0; return 0;

View File

@@ -56,9 +56,23 @@ typedef struct Credential {
#define CRED_PROT_UV_OPTIONAL_WITH_LIST 0x02 #define CRED_PROT_UV_OPTIONAL_WITH_LIST 0x02
#define CRED_PROT_UV_REQUIRED 0x03 #define CRED_PROT_UV_REQUIRED 0x03
#define CRED_PROTO "\xf1\xd0\x02\x01" #define CRED_PROTO_21_S "\xf1\xd0\x02\x01"
#define CRED_PROTO_22_S "\xf1\xd0\x02\x02"
extern int credential_verify(uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash); #define CRED_PROTO CRED_PROTO_22_S
#define CRED_PROTO_LEN 4
#define CRED_IV_LEN 12
#define CRED_TAG_LEN 16
#define CRED_SILENT_TAG_LEN 16
typedef enum
{
CRED_PROTO_21 = 0x01,
CRED_PROTO_22 = 0x02,
} cred_proto_t;
extern int credential_verify(uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash, bool silent);
extern int credential_create(CborCharString *rpId, extern int credential_create(CborCharString *rpId,
CborByteString *userId, CborByteString *userId,
CborCharString *userName, CborCharString *userName,