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:
@@ -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) {
|
||||
for (size_t e = 0; e < allowList_len; e++) {
|
||||
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;
|
||||
}
|
||||
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]);
|
||||
}
|
||||
else {
|
||||
@@ -342,15 +343,32 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
}
|
||||
}
|
||||
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++) {
|
||||
for (int j = i + 1; j < numberOfCredentials; j++) {
|
||||
if (creds[j].creation > creds[i].creation) {
|
||||
Credential tmp = creds[j];
|
||||
creds[j] = creds[i];
|
||||
creds[i] = tmp;
|
||||
if (!silent) {
|
||||
for (int i = 0; i < numberOfCredentials; i++) {
|
||||
for (int j = i + 1; j < numberOfCredentials; j++) {
|
||||
if (creds[j].creation > creds[i].creation) {
|
||||
Credential tmp = creds[j];
|
||||
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);
|
||||
}
|
||||
|
||||
if (up == false && uv == false) {
|
||||
selcred = &creds[0];
|
||||
if (silent && !resident) {
|
||||
// Silent authentication, do nothing
|
||||
}
|
||||
else {
|
||||
selcred = &creds[0];
|
||||
@@ -410,16 +428,18 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
|
||||
int ret = 0;
|
||||
uint8_t largeBlobKey[32] = {0};
|
||||
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
ret = credential_derive_large_blob_key(selcred->id.data, selcred->id.len, largeBlobKey);
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
if (selcred) {
|
||||
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
ret = credential_derive_large_blob_key(selcred->id.data, selcred->id.len, largeBlobKey);
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t ext_len = 0;
|
||||
uint8_t ext[512] = {0};
|
||||
if (extensions.present == true) {
|
||||
if (selcred && extensions.present == true) {
|
||||
cbor_encoder_init(&encoder, ext, sizeof(ext), 0);
|
||||
int l = 0;
|
||||
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);
|
||||
mbedtls_ecdsa_context 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;
|
||||
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);
|
||||
|
||||
uint8_t lfields = 3;
|
||||
if (selcred->opts.present == true && selcred->opts.rk == ptrue) {
|
||||
if (selcred && selcred->opts.present == true && selcred->opts.rk == ptrue) {
|
||||
lfields++;
|
||||
}
|
||||
if (numberOfCredentials > 1 && next == false) {
|
||||
lfields++;
|
||||
}
|
||||
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
if (selcred && extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
lfields++;
|
||||
}
|
||||
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_encoder_create_map(&mapEncoder, &mapEncoder2, 2));
|
||||
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, "public-key"));
|
||||
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_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));
|
||||
uint8_t lu = 1;
|
||||
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, 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_byte_string(&mapEncoder, largeBlobKey, sizeof(largeBlobKey)));
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ int cmd_authenticate() {
|
||||
int ret = 0;
|
||||
uint8_t *tmp_kh = (uint8_t *) calloc(1, 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);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -27,22 +27,52 @@
|
||||
#include "random.h"
|
||||
#include "files.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) {
|
||||
return -1;
|
||||
}
|
||||
uint8_t key[32], *iv = cred_id + 4, *cipher = cred_id + 4 + 12,
|
||||
*tag = cred_id + cred_id_len - 16;
|
||||
memset(key, 0, sizeof(key));
|
||||
credential_derive_chacha_key(key);
|
||||
mbedtls_chachapoly_context chatx;
|
||||
mbedtls_chachapoly_init(&chatx);
|
||||
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);
|
||||
mbedtls_chachapoly_free(&chatx);
|
||||
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 - CRED_TAG_LEN;
|
||||
cred_proto_t proto = CRED_PROTO_21;
|
||||
if (memcmp(cred_id, CRED_PROTO_22_S, CRED_PROTO_LEN) == 0) { // New format
|
||||
tag = cred_id + cred_id_len - CRED_SILENT_TAG_LEN - CRED_TAG_LEN;
|
||||
proto = CRED_PROTO_22;
|
||||
}
|
||||
int ret = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -113,25 +143,25 @@ int credential_create(CborCharString *rpId,
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
size_t rs = cbor_encoder_get_buffer_size(&encoder, cred_id);
|
||||
*cred_id_len = 4 + 12 + rs + 16;
|
||||
uint8_t key[32];
|
||||
memset(key, 0, sizeof(key));
|
||||
credential_derive_chacha_key(key);
|
||||
uint8_t iv[12];
|
||||
*cred_id_len = CRED_PROTO_LEN + CRED_IV_LEN + rs + CRED_TAG_LEN + CRED_SILENT_TAG_LEN;
|
||||
uint8_t key[32] = {0};
|
||||
credential_derive_chacha_key(key, (const uint8_t *)CRED_PROTO);
|
||||
uint8_t iv[CRED_IV_LEN] = {0};
|
||||
random_gen(NULL, iv, sizeof(iv));
|
||||
mbedtls_chachapoly_context chatx;
|
||||
mbedtls_chachapoly_init(&chatx);
|
||||
mbedtls_chachapoly_setkey(&chatx, key);
|
||||
int ret = mbedtls_chachapoly_encrypt_and_tag(&chatx, rs, iv, rp_id_hash, 32,
|
||||
cred_id + 4 + 12,
|
||||
cred_id + 4 + 12,
|
||||
cred_id + 4 + 12 + rs);
|
||||
cred_id + CRED_PROTO_LEN + CRED_IV_LEN,
|
||||
cred_id + CRED_PROTO_LEN + CRED_IV_LEN,
|
||||
cred_id + CRED_PROTO_LEN + CRED_IV_LEN + rs);
|
||||
mbedtls_chachapoly_free(&chatx);
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
memcpy(cred_id, CRED_PROTO, 4);
|
||||
memcpy(cred_id + 4, iv, 12);
|
||||
memcpy(cred_id, CRED_PROTO, CRED_PROTO_LEN);
|
||||
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:
|
||||
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));
|
||||
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 (cred_id_len != KEY_HANDLE_LEN || verify_key(rp_id_hash, cred_id, NULL) != 0) {
|
||||
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);
|
||||
|
||||
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, cred_id, cred_id_len, outk);
|
||||
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);
|
||||
int r = 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);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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, cred_id, cred_id_len, outk);
|
||||
return 0;
|
||||
|
||||
@@ -56,9 +56,23 @@ typedef struct Credential {
|
||||
#define CRED_PROT_UV_OPTIONAL_WITH_LIST 0x02
|
||||
#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,
|
||||
CborByteString *userId,
|
||||
CborCharString *userName,
|
||||
|
||||
Reference in New Issue
Block a user