add initial resident key support
This commit is contained in:
parent
36fdc64fab
commit
fc8bc892c1
286
fido2/ctap.c
286
fido2/ctap.c
@ -56,6 +56,7 @@ static struct {
|
||||
uint32_t count;
|
||||
uint32_t index;
|
||||
uint32_t time;
|
||||
uint8_t user_verified;
|
||||
} getAssertionState;
|
||||
|
||||
uint8_t verify_pin_auth(uint8_t * pinAuth, uint8_t * clientDataHash)
|
||||
@ -154,7 +155,7 @@ uint8_t ctap_get_info(CborEncoder * encoder)
|
||||
ret = cbor_encode_text_string(&options, "rk", 2);
|
||||
check_ret(ret);
|
||||
{
|
||||
ret = cbor_encode_boolean(&options, 0); // State-less device, requires allowList parameter.
|
||||
ret = cbor_encode_boolean(&options, 1); // Capable of storing keys locally
|
||||
check_ret(ret);
|
||||
}
|
||||
|
||||
@ -265,23 +266,32 @@ static int ctap_generate_cose_key(CborEncoder * cose_key, uint8_t * hmac_input,
|
||||
printf2(TAG_ERR,"Error, COSE alg %d not supported\n", algtype);
|
||||
return -1;
|
||||
}
|
||||
ctap_add_cose_key(cose_key, x, y, credtype, algtype);
|
||||
int ret = ctap_add_cose_key(cose_key, x, y, credtype, algtype);
|
||||
check_ret(ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void make_auth_tag(uint8_t * nonce, CTAP_userEntity * user, uint32_t count, uint8_t * tag)
|
||||
void make_auth_tag(uint8_t * rpIdHash, uint8_t * nonce, uint32_t count, uint8_t * tag)
|
||||
{
|
||||
uint8_t hashbuf[32];
|
||||
crypto_sha256_hmac_init(CRYPTO_TRANSPORT_KEY, 0, hashbuf);
|
||||
crypto_sha256_update(rpIdHash, 32);
|
||||
crypto_sha256_update(nonce, CREDENTIAL_NONCE_SIZE);
|
||||
crypto_sha256_update(user->id, user->id_size);
|
||||
crypto_sha256_update(user->name, strnlen((const char*)user->name, USER_NAME_LIMIT));
|
||||
crypto_sha256_update((uint8_t*)&count, 4);
|
||||
crypto_sha256_hmac_final(CRYPTO_TRANSPORT_KEY,0,hashbuf);
|
||||
|
||||
memmove(tag, hashbuf, CREDENTIAL_TAG_SIZE);
|
||||
}
|
||||
|
||||
void ctap_flush_state(int backup)
|
||||
{
|
||||
authenticator_write_state(&STATE, 0);
|
||||
if (backup)
|
||||
{
|
||||
authenticator_write_state(&STATE, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t auth_data_update_count(CTAP_authDataHeader * authData)
|
||||
{
|
||||
uint32_t count = ctap_atomic_count( 0 );
|
||||
@ -292,19 +302,34 @@ static uint32_t auth_data_update_count(CTAP_authDataHeader * authData)
|
||||
}
|
||||
uint8_t * byte = (uint8_t*) &authData->signCount;
|
||||
|
||||
*byte++ = (count >> 24) & 0xff;
|
||||
*byte++ = (count >> 16) & 0xff;
|
||||
*byte++ = (count >> 8) & 0xff;
|
||||
*byte++ = (count >> 0) & 0xff;
|
||||
*byte++ = (count >> 8) & 0xff;
|
||||
*byte++ = (count >> 16) & 0xff;
|
||||
*byte++ = (count >> 24) & 0xff;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, int len, CTAP_userEntity * user, uint8_t credtype, int32_t algtype, int32_t * sz)
|
||||
static void ctap_increment_rk_store()
|
||||
{
|
||||
STATE.rk_stored++;
|
||||
ctap_flush_state(1);
|
||||
}
|
||||
|
||||
static int is_matching_rk(CTAP_residentKey * rk, CTAP_residentKey * rk2)
|
||||
{
|
||||
return (memcmp(rk->id.rpIdHash, rk2->id.rpIdHash, 32) == 0) &&
|
||||
(memcmp(rk->user.id, rk2->user.id, rk->user.id_size) == 0) &&
|
||||
(rk->user.id_size == rk2->user.id_size);
|
||||
}
|
||||
|
||||
|
||||
static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, int len, CTAP_userEntity * user, uint8_t credtype, int32_t algtype, int32_t * sz, int store)
|
||||
{
|
||||
CborEncoder cose_key;
|
||||
int auth_data_sz, ret;
|
||||
uint32_t count;
|
||||
CTAP_residentKey rk, rk2;
|
||||
CTAP_authData * authData = (CTAP_authData *)auth_data_buf;
|
||||
|
||||
uint8_t * cose_key_buf = auth_data_buf + sizeof(CTAP_authData);
|
||||
@ -347,29 +372,56 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
|
||||
cbor_encoder_init(&cose_key, cose_key_buf, len - sizeof(CTAP_authData), 0);
|
||||
|
||||
memmove(authData->attest.aaguid, CTAP_AAGUID, 16);
|
||||
authData->attest.credLenL = CREDENTIAL_ID_SIZE & 0x00FF;
|
||||
authData->attest.credLenH = (CREDENTIAL_ID_SIZE & 0xFF00) >> 8;
|
||||
authData->attest.credLenL = sizeof(CredentialId) & 0x00FF;
|
||||
authData->attest.credLenH = (sizeof(CredentialId) & 0xFF00) >> 8;
|
||||
|
||||
#if CREDENTIAL_ID_SIZE != 168
|
||||
#error "need to double check credential ID layout"
|
||||
#else
|
||||
memset((uint8_t*)&authData->attest.credential, 0, sizeof(struct Credential));
|
||||
memset((uint8_t*)&authData->attest.id, 0, sizeof(CredentialId));
|
||||
|
||||
ctap_generate_rng(authData->attest.credential.nonce, CREDENTIAL_NONCE_SIZE);
|
||||
ctap_generate_rng(authData->attest.id.nonce, CREDENTIAL_NONCE_SIZE);
|
||||
|
||||
memmove(&authData->attest.credential.enc.user, user, sizeof(CTAP_userEntity));
|
||||
authData->attest.credential.enc.count = count;
|
||||
authData->attest.id.count = count;
|
||||
|
||||
memmove(authData->attest.id.rpIdHash, authData->head.rpIdHash, 32);
|
||||
|
||||
// Make a tag we can later check to make sure this is a token we made
|
||||
make_auth_tag(authData->attest.credential.nonce, user, count, authData->attest.credential.tag);
|
||||
make_auth_tag(authData->head.rpIdHash, authData->attest.id.nonce, count, authData->attest.id.tag);
|
||||
|
||||
crypto_aes256_init(CRYPTO_TRANSPORT_KEY, NULL);
|
||||
crypto_aes256_encrypt((uint8_t*)&authData->attest.credential.enc, CREDENTIAL_ENC_SIZE);
|
||||
// resident key
|
||||
if (store)
|
||||
{
|
||||
memmove(&rk.id, &authData->attest.id, sizeof(CredentialId));
|
||||
memmove(&rk.user, user, sizeof(CTAP_userEntity));
|
||||
|
||||
ctap_generate_cose_key(&cose_key, (uint8_t*)&authData->attest.credential, sizeof(struct Credential), credtype, algtype);
|
||||
int index = STATE.rk_stored;
|
||||
int i;
|
||||
for (i = 0; i < index; i++)
|
||||
{
|
||||
ctap_load_rk(i, &rk2);
|
||||
if (is_matching_rk(&rk, &rk2))
|
||||
{
|
||||
ctap_overwrite_rk(i, &rk);
|
||||
goto done_rk;
|
||||
}
|
||||
}
|
||||
if (index >= ctap_rk_size())
|
||||
{
|
||||
printf2(TAG_ERR, "Out of memory for resident keys\r\n");
|
||||
return CTAP2_ERR_KEY_STORE_FULL;
|
||||
}
|
||||
ctap_increment_rk_store();
|
||||
ctap_store_rk(index, &rk);
|
||||
dump_hex1(TAG_GREEN, rk.id.rpIdHash, 32);
|
||||
}
|
||||
done_rk:
|
||||
|
||||
// DELETE
|
||||
//crypto_aes256_init(CRYPTO_TRANSPORT_KEY, NULL);
|
||||
//crypto_aes256_encrypt((uint8_t*)&authData->attest.credential.user, CREDENTIAL_ENC_SIZE);
|
||||
printf1(TAG_GREEN, "MADE credId: "); dump_hex1(TAG_GREEN, (uint8_t*) &authData->attest.id, sizeof(CredentialId));
|
||||
|
||||
ctap_generate_cose_key(&cose_key, (uint8_t*)&authData->attest.id, sizeof(CredentialId), credtype, algtype);
|
||||
|
||||
auth_data_sz = sizeof(CTAP_authData) + cbor_encoder_get_buffer_size(&cose_key, cose_key_buf);
|
||||
#endif
|
||||
|
||||
}
|
||||
else
|
||||
@ -489,9 +541,9 @@ int ctap_authenticate_credential(struct rpId * rp, CTAP_credentialDescriptor * d
|
||||
{
|
||||
uint8_t tag[16];
|
||||
|
||||
make_auth_tag(desc->credential.nonce, &desc->credential.enc.user, desc->credential.enc.count, tag);
|
||||
make_auth_tag(desc->credential.id.rpIdHash, desc->credential.id.nonce, desc->credential.id.count, tag);
|
||||
|
||||
return (memcmp(desc->credential.tag, tag, CREDENTIAL_TAG_SIZE) == 0);
|
||||
return (memcmp(desc->credential.id.tag, tag, CREDENTIAL_TAG_SIZE) == 0);
|
||||
}
|
||||
|
||||
|
||||
@ -536,7 +588,7 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
|
||||
return CTAP2_ERR_INVALID_OPTION;
|
||||
}
|
||||
|
||||
crypto_aes256_init(CRYPTO_TRANSPORT_KEY, NULL);
|
||||
// crypto_aes256_init(CRYPTO_TRANSPORT_KEY, NULL);
|
||||
for (i = 0; i < MC.excludeListSize; i++)
|
||||
{
|
||||
ret = parse_credential_descriptor(&MC.excludeList, excl_cred);
|
||||
@ -546,8 +598,10 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
|
||||
}
|
||||
check_retr(ret);
|
||||
|
||||
crypto_aes256_reset_iv(NULL);
|
||||
crypto_aes256_decrypt((uint8_t*)& excl_cred->credential.enc, CREDENTIAL_ENC_SIZE);
|
||||
printf1(TAG_GREEN, "checking credId: "); dump_hex1(TAG_GREEN, (uint8_t*) &excl_cred->credential.id, sizeof(CredentialId));
|
||||
// DELETE
|
||||
// crypto_aes256_reset_iv(NULL);
|
||||
// crypto_aes256_decrypt((uint8_t*)& excl_cred->credential.enc, CREDENTIAL_ENC_SIZE);
|
||||
if (ctap_authenticate_credential(&MC.rp, excl_cred))
|
||||
{
|
||||
printf1(TAG_MC, "Cred %d failed!\r\n",i);
|
||||
@ -564,7 +618,8 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
|
||||
int32_t auth_data_sz;
|
||||
|
||||
ret = ctap_make_auth_data(&MC.rp, &map, auth_data_buf, sizeof(auth_data_buf),
|
||||
&MC.user, MC.publicKeyCredentialType, MC.COSEAlgorithmIdentifier, &auth_data_sz);
|
||||
&MC.user, MC.publicKeyCredentialType, MC.COSEAlgorithmIdentifier, &auth_data_sz, MC.rk);
|
||||
|
||||
check_retr(ret);
|
||||
|
||||
crypto_ecc256_load_attestation_key();
|
||||
@ -620,7 +675,7 @@ static uint8_t ctap_add_credential_descriptor(CborEncoder * map, CTAP_credential
|
||||
ret = cbor_encode_text_string(&desc, "id", 2);
|
||||
check_ret(ret);
|
||||
|
||||
ret = cbor_encode_byte_string(&desc, (uint8_t*)&cred->credential, sizeof(struct Credential));
|
||||
ret = cbor_encode_byte_string(&desc, (uint8_t*)&cred->credential.id, sizeof(CredentialId));
|
||||
check_ret(ret);
|
||||
}
|
||||
|
||||
@ -636,9 +691,15 @@ uint8_t ctap_add_user_entity(CborEncoder * map, CTAP_userEntity * user)
|
||||
int ret = cbor_encode_int(map, RESP_publicKeyCredentialUserEntity);
|
||||
check_ret(ret);
|
||||
|
||||
ret = cbor_encoder_create_map(map, &entity, 2);
|
||||
int dispname = (user->name[0] != 0);
|
||||
|
||||
if (dispname)
|
||||
ret = cbor_encoder_create_map(map, &entity, 2);
|
||||
else
|
||||
ret = cbor_encoder_create_map(map, &entity, 1);
|
||||
check_ret(ret);
|
||||
|
||||
printf1(TAG_GREEN,"id_size: %d\r\n", user->id_size);
|
||||
{
|
||||
ret = cbor_encode_text_string(&entity, "id", 2);
|
||||
check_ret(ret);
|
||||
@ -647,9 +708,9 @@ uint8_t ctap_add_user_entity(CborEncoder * map, CTAP_userEntity * user)
|
||||
check_ret(ret);
|
||||
}
|
||||
|
||||
|
||||
if (dispname)
|
||||
{
|
||||
ret = cbor_encode_text_string(&entity, "displayName", 11);
|
||||
ret = cbor_encode_text_string(&entity, "name", 4);
|
||||
check_ret(ret);
|
||||
|
||||
ret = cbor_encode_text_stringz(&entity, (const char *)user->name);
|
||||
@ -666,7 +727,26 @@ static int cred_cmp_func(const void * _a, const void * _b)
|
||||
{
|
||||
CTAP_credentialDescriptor * a = (CTAP_credentialDescriptor * )_a;
|
||||
CTAP_credentialDescriptor * b = (CTAP_credentialDescriptor * )_b;
|
||||
return b->credential.enc.count - a->credential.enc.count;
|
||||
return b->credential.id.count - a->credential.id.count;
|
||||
}
|
||||
|
||||
static void add_existing_user_info(CTAP_credentialDescriptor * cred)
|
||||
{
|
||||
CTAP_residentKey rk;
|
||||
int index = STATE.rk_stored;
|
||||
int i;
|
||||
for (i = 0; i < index; i++)
|
||||
{
|
||||
ctap_load_rk(i, &rk);
|
||||
if (is_matching_rk(&rk, (CTAP_residentKey *)&cred->credential))
|
||||
{
|
||||
printf1(TAG_GREEN, "found rk match for allowList item (%d)\r\n", i);
|
||||
memmove(&cred->credential.user, &rk.user, sizeof(CTAP_userEntity));
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
printf1(TAG_GREEN, "NO rk match for allowList item \r\n");
|
||||
}
|
||||
|
||||
// @return the number of valid credentials
|
||||
@ -675,23 +755,52 @@ int ctap_filter_invalid_credentials(CTAP_getAssertion * GA)
|
||||
{
|
||||
int i;
|
||||
int count = 0;
|
||||
crypto_aes256_init(CRYPTO_TRANSPORT_KEY, NULL);
|
||||
|
||||
uint8_t rpIdHash[32];
|
||||
CTAP_residentKey rk;
|
||||
|
||||
for (i = 0; i < GA->credLen; i++)
|
||||
{
|
||||
crypto_aes256_reset_iv(NULL);
|
||||
crypto_aes256_decrypt((uint8_t*)&GA->creds[i].credential.enc, CREDENTIAL_ENC_SIZE);
|
||||
if (! ctap_authenticate_credential(&GA->rp, &GA->creds[i]))
|
||||
{
|
||||
printf1(TAG_GA, "CRED #%d is invalid\n", GA->creds[i].credential.enc.count);
|
||||
GA->creds[i].credential.enc.count = 0; // invalidate
|
||||
printf1(TAG_GA, "CRED #%d is invalid\n", GA->creds[i].credential.id.count);
|
||||
GA->creds[i].credential.id.count = 0; // invalidate
|
||||
}
|
||||
else
|
||||
{
|
||||
// add user info if it exists
|
||||
add_existing_user_info(&GA->creds[i]);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
// No allowList, so use all matching RK's matching rpId
|
||||
if (!GA->credLen)
|
||||
{
|
||||
crypto_sha256_init();
|
||||
crypto_sha256_update(GA->rp.id,GA->rp.size);
|
||||
crypto_sha256_final(rpIdHash);
|
||||
|
||||
printf1(TAG_GREEN, "true rpIdHash: "); dump_hex1(TAG_GREEN, rpIdHash, 32);
|
||||
for(i = 0; i < STATE.rk_stored; i++)
|
||||
{
|
||||
ctap_load_rk(i, &rk);
|
||||
printf1(TAG_GREEN, "rpIdHash%d: ", i); dump_hex1(TAG_GREEN, rk.id.rpIdHash, 32);
|
||||
if (memcmp(rk.id.rpIdHash, rpIdHash, 32) == 0)
|
||||
{
|
||||
printf1(TAG_GA, "RK %d is a rpId match!\r\n", i);
|
||||
if (count == ALLOW_LIST_MAX_SIZE-1)
|
||||
{
|
||||
printf2(TAG_ERR, "not enough ram allocated for matching RK's (%d)\r\n", count);
|
||||
break;
|
||||
}
|
||||
GA->creds[count].type = PUB_KEY_CRED_PUB_KEY;
|
||||
memmove(&(GA->creds[count].credential), &rk, sizeof(CTAP_residentKey));
|
||||
count++;
|
||||
}
|
||||
}
|
||||
GA->credLen = count;
|
||||
}
|
||||
|
||||
printf1(TAG_GA, "qsort length: %d\n", GA->credLen);
|
||||
qsort(GA->creds, GA->credLen, sizeof(CTAP_credentialDescriptor), cred_cmp_func);
|
||||
return count;
|
||||
@ -728,24 +837,24 @@ static CTAP_credentialDescriptor * pop_credential()
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ctap_end_get_assertion(CborEncoder * map, CTAP_credentialDescriptor * cred, uint8_t * auth_data_buf, uint8_t * clientDataHash)
|
||||
// adds 2 to map, or 3 if add_user is true
|
||||
uint8_t ctap_end_get_assertion(CborEncoder * map, CTAP_credentialDescriptor * cred, uint8_t * auth_data_buf, uint8_t * clientDataHash, int add_user)
|
||||
{
|
||||
int ret;
|
||||
uint8_t sigbuf[64];
|
||||
uint8_t sigder[72];
|
||||
|
||||
//ret = ctap_add_user_entity(map, &cred->credential.enc.user);
|
||||
//check_retr(ret);
|
||||
|
||||
// Re-encrypt the credential
|
||||
crypto_aes256_init(CRYPTO_TRANSPORT_KEY, NULL);
|
||||
crypto_aes256_encrypt((uint8_t*)&cred->credential.enc, CREDENTIAL_ENC_SIZE);
|
||||
//
|
||||
if (add_user)
|
||||
{
|
||||
printf1(TAG_GREEN, "adding user details to output\r\n");
|
||||
ret = ctap_add_user_entity(map, &cred->credential.user);
|
||||
check_retr(ret);
|
||||
}
|
||||
|
||||
ret = ctap_add_credential_descriptor(map, cred);
|
||||
check_retr(ret);
|
||||
|
||||
crypto_ecc256_load_key((uint8_t*)&cred->credential, sizeof(struct Credential), NULL, 0);
|
||||
crypto_ecc256_load_key((uint8_t*)&cred->credential.id, sizeof(CredentialId), NULL, 0);
|
||||
|
||||
int sigder_sz = ctap_calculate_signature(auth_data_buf, sizeof(CTAP_authDataHeader), clientDataHash, auth_data_buf, sigbuf, sigder);
|
||||
|
||||
@ -772,9 +881,19 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
|
||||
}
|
||||
|
||||
auth_data_update_count(authData);
|
||||
int add_user_info = cred->credential.user.id_size && getAssertionState.user_verified;
|
||||
|
||||
if (getAssertionState.user_verified)
|
||||
{
|
||||
printf1(TAG_GREEN, "adding user info to assertion response\r\n");
|
||||
ret = cbor_encoder_create_map(encoder, &map, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf1(TAG_GREEN, "NOT adding user info to assertion response\r\n");
|
||||
ret = cbor_encoder_create_map(encoder, &map, 3);
|
||||
}
|
||||
|
||||
ret = cbor_encoder_create_map(encoder, &map, 3);
|
||||
check_ret(ret);
|
||||
|
||||
{
|
||||
@ -784,7 +903,7 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
|
||||
check_ret(ret);
|
||||
}
|
||||
|
||||
ret = ctap_end_get_assertion(&map, cred, (uint8_t *)authData, getAssertionState.clientDataHash);
|
||||
ret = ctap_end_get_assertion(&map, cred, (uint8_t *)authData, getAssertionState.clientDataHash, add_user_info);
|
||||
check_retr(ret);
|
||||
|
||||
ret = cbor_encoder_close_container(encoder, &map);
|
||||
@ -816,6 +935,7 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
|
||||
{
|
||||
ret = verify_pin_auth(GA.pinAuth, GA.clientDataHash);
|
||||
check_retr(ret);
|
||||
getAssertionState.user_verified = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -824,16 +944,29 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
|
||||
return CTAP2_ERR_MISSING_PARAMETER;
|
||||
}
|
||||
CborEncoder map;
|
||||
if (GA.credLen > 1)
|
||||
ret = cbor_encoder_create_map(encoder, &map, 4);
|
||||
else
|
||||
ret = cbor_encoder_create_map(encoder, &map, 3);
|
||||
check_ret(ret);
|
||||
|
||||
ret = ctap_make_auth_data(&GA.rp, &map, auth_data_buf, sizeof(auth_data_buf), NULL, 0,0,NULL);
|
||||
check_retr(ret);
|
||||
int map_size = 3;
|
||||
|
||||
printf1(TAG_GA, "ALLOW_LIST has %d creds\n", GA.credLen);
|
||||
int validCredCount = ctap_filter_invalid_credentials(&GA);
|
||||
|
||||
int add_user_info = GA.creds[validCredCount - 1].credential.user.id_size && getAssertionState.user_verified;
|
||||
if (validCredCount > 1)
|
||||
{
|
||||
map_size += 1;
|
||||
}
|
||||
|
||||
if (add_user_info)
|
||||
{
|
||||
map_size += 1;
|
||||
}
|
||||
|
||||
ret = cbor_encoder_create_map(encoder, &map, map_size);
|
||||
check_ret(ret);
|
||||
|
||||
ret = ctap_make_auth_data(&GA.rp, &map, auth_data_buf, sizeof(auth_data_buf), NULL, 0,0,NULL, 0);
|
||||
check_retr(ret);
|
||||
|
||||
/*for (int j = 0; j < GA.credLen; j++)*/
|
||||
/*{*/
|
||||
/*printf1(TAG_GA,"CRED ID (# %d): ", GA.creds[j].credential.enc.count);*/
|
||||
@ -851,7 +984,6 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
|
||||
// Decrypt here
|
||||
|
||||
//
|
||||
int validCredCount = ctap_filter_invalid_credentials(&GA);
|
||||
if (validCredCount > 0)
|
||||
{
|
||||
save_credential_list((CTAP_authDataHeader*)auth_data_buf, GA.clientDataHash, GA.creds, validCredCount-1); // skip last one
|
||||
@ -862,25 +994,30 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
|
||||
return CTAP2_ERR_NO_CREDENTIALS;
|
||||
}
|
||||
|
||||
// if only one account for this RP, null out the user details
|
||||
if (validCredCount < 2)
|
||||
{
|
||||
printf1(TAG_GREEN, "Only one account, nulling out user details on response\r\n");
|
||||
memset(&GA.creds[0].credential.user.name, 0, USER_NAME_LIMIT);
|
||||
}
|
||||
|
||||
printf1(TAG_GA,"resulting order of creds:\n");
|
||||
for (int j = 0; j < GA.credLen; j++)
|
||||
{
|
||||
printf1(TAG_GA,"CRED ID (# %d)\n", GA.creds[j].credential.enc.count);
|
||||
printf1(TAG_GA,"CRED ID (# %d)\n", GA.creds[j].credential.id.count);
|
||||
}
|
||||
|
||||
if (validCredCount > 1)
|
||||
{
|
||||
if (GA.credLen > 1)
|
||||
{
|
||||
ret = cbor_encode_int(&map, RESP_numberOfCredentials);
|
||||
check_ret(ret);
|
||||
ret = cbor_encode_int(&map, validCredCount);
|
||||
check_ret(ret);
|
||||
}
|
||||
ret = cbor_encode_int(&map, RESP_numberOfCredentials);
|
||||
check_ret(ret);
|
||||
ret = cbor_encode_int(&map, validCredCount);
|
||||
check_ret(ret);
|
||||
}
|
||||
|
||||
CTAP_credentialDescriptor * cred = &GA.creds[validCredCount - 1];
|
||||
|
||||
ret = ctap_end_get_assertion(&map, cred, auth_data_buf, GA.clientDataHash);
|
||||
ret = ctap_end_get_assertion(&map, cred, auth_data_buf, GA.clientDataHash, add_user_info);
|
||||
check_retr(ret);
|
||||
|
||||
ret = cbor_encoder_close_container(encoder, &map);
|
||||
@ -1320,14 +1457,7 @@ done:
|
||||
return status;
|
||||
}
|
||||
|
||||
void ctap_flush_state(int backup)
|
||||
{
|
||||
authenticator_write_state(&STATE, 0);
|
||||
if (backup)
|
||||
{
|
||||
authenticator_write_state(&STATE, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void ctap_state_init()
|
||||
{
|
||||
@ -1339,6 +1469,9 @@ static void ctap_state_init()
|
||||
STATE.is_initialized = INITIALIZED_MARKER;
|
||||
STATE.remaining_tries = PIN_LOCKOUT_ATTEMPTS;
|
||||
STATE.is_pin_set = 0;
|
||||
STATE.rk_stored = 0;
|
||||
|
||||
ctap_reset_rk();
|
||||
}
|
||||
|
||||
void ctap_init()
|
||||
@ -1597,6 +1730,7 @@ int8_t ctap_load_key(uint8_t index, uint8_t * key)
|
||||
void ctap_reset()
|
||||
{
|
||||
ctap_state_init();
|
||||
|
||||
authenticator_write_state(&STATE, 0);
|
||||
authenticator_write_state(&STATE, 1);
|
||||
|
||||
|
26
fido2/ctap.h
26
fido2/ctap.h
@ -111,12 +111,11 @@
|
||||
#define USER_NAME_LIMIT 65 // Must be minimum of 64 bytes but can be more.
|
||||
#define CTAP_MAX_MESSAGE_SIZE 1200
|
||||
|
||||
#define CREDENTIAL_RK_FLASH_PAD 2 // size of RK should be 8-byte aligned to store in flash easily.
|
||||
#define CREDENTIAL_TAG_SIZE 16
|
||||
#define CREDENTIAL_NONCE_SIZE 8
|
||||
#define CREDENTIAL_NONCE_SIZE (16 + CREDENTIAL_RK_FLASH_PAD)
|
||||
#define CREDENTIAL_COUNTER_SIZE (4)
|
||||
#define CREDENTIAL_ENC_SIZE 144 // pad to multiple of 16 bytes
|
||||
#define CREDENTIAL_PAD_SIZE (CREDENTIAL_ENC_SIZE - (USER_ID_MAX_SIZE + USER_NAME_LIMIT + CREDENTIAL_COUNTER_SIZE + 1))
|
||||
#define CREDENTIAL_ID_SIZE (CREDENTIAL_TAG_SIZE + CREDENTIAL_NONCE_SIZE + CREDENTIAL_ENC_SIZE)
|
||||
#define CREDENTIAL_ENC_SIZE 176 // pad to multiple of 16 bytes
|
||||
|
||||
#define PUB_KEY_CRED_PUB_KEY 0x01
|
||||
#define PUB_KEY_CRED_UNKNOWN 0x3F
|
||||
@ -143,22 +142,27 @@ typedef struct
|
||||
uint8_t name[USER_NAME_LIMIT];
|
||||
}__attribute__((packed)) CTAP_userEntity;
|
||||
|
||||
struct Credential {
|
||||
typedef struct {
|
||||
uint8_t tag[CREDENTIAL_TAG_SIZE];
|
||||
uint8_t nonce[CREDENTIAL_NONCE_SIZE];
|
||||
struct {
|
||||
CTAP_userEntity user;
|
||||
uint32_t count;
|
||||
uint8_t _pad[CREDENTIAL_PAD_SIZE];
|
||||
} __attribute__((packed)) enc;
|
||||
uint8_t rpIdHash[32];
|
||||
uint32_t count;
|
||||
}__attribute__((packed)) CredentialId;
|
||||
|
||||
struct Credential {
|
||||
CredentialId id;
|
||||
CTAP_userEntity user;
|
||||
};
|
||||
|
||||
typedef struct Credential CTAP_residentKey;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t aaguid[16];
|
||||
uint8_t credLenH;
|
||||
uint8_t credLenL;
|
||||
struct Credential credential;
|
||||
CredentialId id;
|
||||
} __attribute__((packed)) CTAP_attestHeader;
|
||||
|
||||
typedef struct
|
||||
|
@ -150,6 +150,7 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
|
||||
return CTAP2_ERR_LIMIT_EXCEEDED;
|
||||
}
|
||||
MC->user.id_size = sz;
|
||||
printf1(TAG_GREEN,"parsed id_size: %d\r\n", MC->user.id_size);
|
||||
check_ret(ret);
|
||||
}
|
||||
else if (strcmp((const char *)key, "name") == 0)
|
||||
@ -737,9 +738,9 @@ uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor *
|
||||
return CTAP2_ERR_MISSING_PARAMETER;
|
||||
}
|
||||
|
||||
buflen = sizeof(struct Credential);
|
||||
cbor_value_copy_byte_string(&val, (uint8_t*)&cred->credential, &buflen, NULL);
|
||||
if (buflen != CREDENTIAL_ID_SIZE)
|
||||
buflen = sizeof(CredentialId);
|
||||
cbor_value_copy_byte_string(&val, (uint8_t*)&cred->credential.id, &buflen, NULL);
|
||||
if (buflen != sizeof(CredentialId))
|
||||
{
|
||||
printf2(TAG_ERR,"Ignoring credential is incorrect length\n");
|
||||
//return CTAP2_ERR_CBOR_UNEXPECTED_TYPE; // maybe just skip it instead of fail?
|
||||
|
@ -82,4 +82,12 @@ extern int ctap_user_verification(uint8_t arg);
|
||||
// data is HID_MESSAGE_SIZE long in bytes
|
||||
extern void ctaphid_write_block(uint8_t * data);
|
||||
|
||||
|
||||
// Resident key
|
||||
void ctap_reset_rk();
|
||||
uint32_t ctap_rk_size();
|
||||
void ctap_store_rk(int index,CTAP_residentKey * rk);
|
||||
void ctap_load_rk(int index,CTAP_residentKey * rk);
|
||||
void ctap_overwrite_rk(int index,CTAP_residentKey * rk);
|
||||
|
||||
#endif
|
||||
|
@ -47,6 +47,7 @@ struct logtag tagtable[] = {
|
||||
{TAG_CTAP,"CTAP"},
|
||||
{TAG_U2F,"U2F"},
|
||||
{TAG_DUMP,"DUMP"},
|
||||
{TAG_DUMP2,"DUMP2"},
|
||||
{TAG_HID,"HID"},
|
||||
{TAG_USB,"USB"},
|
||||
{TAG_GREEN,"[1;32mDEBUG[0m"},
|
||||
|
@ -54,6 +54,7 @@ typedef enum
|
||||
TAG_USB = (1 << 13),
|
||||
TAG_WALLET = (1 << 14),
|
||||
TAG_STOR = (1 << 15),
|
||||
TAG_DUMP2 = (1 << 16),
|
||||
|
||||
TAG_FILENO = (1<<31)
|
||||
} LOG_TAG;
|
||||
|
@ -43,6 +43,8 @@ typedef struct
|
||||
int pin_code_length;
|
||||
int8_t remaining_tries;
|
||||
|
||||
uint16_t rk_stored;
|
||||
|
||||
uint16_t key_lens[MAX_KEYS];
|
||||
uint8_t key_space[KEY_SPACE_BYTES];
|
||||
} AuthenticatorState;
|
||||
|
@ -33,7 +33,7 @@ void hw_init(void);
|
||||
#define SOLO_BUTTON_PORT GPIOA
|
||||
#define SOLO_BUTTON_PIN LL_GPIO_PIN_0
|
||||
|
||||
#define SKIP_BUTTON_CHECK_WITH_DELAY 0
|
||||
#define SKIP_BUTTON_CHECK_WITH_DELAY 1
|
||||
#define SKIP_BUTTON_CHECK_FAST 0
|
||||
|
||||
#endif
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "fifo.h"
|
||||
#include "log.h"
|
||||
#include "ctaphid.h"
|
||||
#include "ctap.h"
|
||||
|
||||
|
||||
#define PAGE_SIZE 2048
|
||||
@ -26,11 +27,15 @@
|
||||
#define STATE2_PAGE (PAGES - 2)
|
||||
#define STATE1_PAGE (PAGES - 1)
|
||||
|
||||
#define RK_NUM_PAGES 10
|
||||
#define RK_START_PAGE (PAGES - 14)
|
||||
#define RK_END_PAGE (PAGES - 14 + RK_NUM_PAGES)
|
||||
|
||||
|
||||
#define APPLICATION_START_PAGE (0)
|
||||
#define APPLICATION_START_ADDR flash_addr(APPLICATION_START_PAGE)
|
||||
|
||||
#define APPLICATION_END_PAGE ((PAGES - 9)) // 119 is NOT included in application
|
||||
#define APPLICATION_END_PAGE ((PAGES - 19)) // 119 is NOT included in application
|
||||
#define APPLICATION_END_ADDR (flash_addr(APPLICATION_END_PAGE)-4) // NOT included in application
|
||||
|
||||
#define AUTH_WORD_ADDR (flash_addr(APPLICATION_END_PAGE)-4)
|
||||
@ -113,8 +118,8 @@ int usbhid_recv(uint8_t * msg)
|
||||
if (fifo_hidmsg_size())
|
||||
{
|
||||
fifo_hidmsg_take(msg);
|
||||
printf1(TAG_DUMP,">> ");
|
||||
dump_hex1(TAG_DUMP,msg, HID_PACKET_SIZE);
|
||||
printf1(TAG_DUMP2,">> ");
|
||||
dump_hex1(TAG_DUMP2,msg, HID_PACKET_SIZE);
|
||||
return HID_PACKET_SIZE;
|
||||
}
|
||||
return 0;
|
||||
@ -123,8 +128,8 @@ int usbhid_recv(uint8_t * msg)
|
||||
void usbhid_send(uint8_t * msg)
|
||||
{
|
||||
|
||||
printf1(TAG_DUMP,"<< ");
|
||||
dump_hex1(TAG_DUMP, msg, HID_PACKET_SIZE);
|
||||
printf1(TAG_DUMP2,"<< ");
|
||||
dump_hex1(TAG_DUMP2, msg, HID_PACKET_SIZE);
|
||||
while (PCD_GET_EP_TX_STATUS(USB, HID_EPIN_ADDR & 0x0f) == USB_EP_TX_VALID)
|
||||
;
|
||||
USBD_LL_Transmit(&Solo_USBD_Device, HID_EPIN_ADDR, msg, HID_PACKET_SIZE);
|
||||
@ -411,6 +416,79 @@ int ctap_user_verification(uint8_t arg)
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ctap_reset_rk()
|
||||
{
|
||||
int i;
|
||||
printf1(TAG_GREEN, "resetting RK \r\n");
|
||||
for(i = 0; i < RK_NUM_PAGES; i++)
|
||||
{
|
||||
flash_erase_page(RK_START_PAGE + i);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ctap_rk_size()
|
||||
{
|
||||
return RK_NUM_PAGES * (PAGE_SIZE / sizeof(CTAP_residentKey));
|
||||
}
|
||||
|
||||
void ctap_store_rk(int index,CTAP_residentKey * rk)
|
||||
{
|
||||
int page_offset = (sizeof(CTAP_residentKey) * index) / PAGE_SIZE;
|
||||
uint32_t addr = flash_addr(page_offset + RK_START_PAGE) + ((sizeof(CTAP_residentKey)*index) % PAGE_SIZE);
|
||||
|
||||
printf1(TAG_GREEN, "storing RK %d @ %04x\r\n", index,addr);
|
||||
|
||||
if (page_offset < RK_NUM_PAGES)
|
||||
{
|
||||
flash_write(addr, (uint8_t*)rk, sizeof(CTAP_residentKey));
|
||||
//dump_hex1(TAG_GREEN,rk,sizeof(CTAP_residentKey));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf2(TAG_ERR,"Out of bounds reading index %d for rk\n", index);
|
||||
}
|
||||
}
|
||||
|
||||
void ctap_load_rk(int index,CTAP_residentKey * rk)
|
||||
{
|
||||
int page_offset = (sizeof(CTAP_residentKey) * index) / PAGE_SIZE;
|
||||
uint32_t addr = flash_addr(page_offset + RK_START_PAGE) + ((sizeof(CTAP_residentKey)*index) % PAGE_SIZE);
|
||||
|
||||
printf1(TAG_GREEN, "reading RK %d @ %04x\r\n", index, addr);
|
||||
if (page_offset < RK_NUM_PAGES)
|
||||
{
|
||||
uint32_t * ptr = (uint32_t *)addr;
|
||||
memmove((uint8_t*)rk,ptr,sizeof(CTAP_residentKey));
|
||||
//dump_hex1(TAG_GREEN,rk,sizeof(CTAP_residentKey));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf2(TAG_ERR,"Out of bounds reading index %d for rk\n", index);
|
||||
}
|
||||
}
|
||||
|
||||
void ctap_overwrite_rk(int index,CTAP_residentKey * rk)
|
||||
{
|
||||
uint8_t tmppage[PAGE_SIZE];
|
||||
int page_offset = (sizeof(CTAP_residentKey) * index) / PAGE_SIZE;
|
||||
int page = page_offset + RK_START_PAGE;
|
||||
|
||||
printf1(TAG_GREEN, "overwriting RK %d\r\n", index);
|
||||
if (page_offset < RK_NUM_PAGES)
|
||||
{
|
||||
memmove(tmppage, (uint8_t*)flash_addr(page), PAGE_SIZE);
|
||||
|
||||
memmove(tmppage + (sizeof(CTAP_residentKey) * index) % PAGE_SIZE, rk, sizeof(CTAP_residentKey));
|
||||
flash_erase_page(page);
|
||||
flash_write(flash_addr(page), tmppage, ((sizeof(CTAP_residentKey) * (index + 1)) % PAGE_SIZE) );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf2(TAG_ERR,"Out of bounds reading index %d for rk\n", index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _Error_Handler(char *file, int line)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user