commit
e21172fff8
283
fido2/ctap.c
283
fido2/ctap.c
@ -33,7 +33,6 @@ static int8_t PIN_BOOT_ATTEMPTS_LEFT = PIN_BOOT_ATTEMPTS;
|
|||||||
|
|
||||||
AuthenticatorState STATE;
|
AuthenticatorState STATE;
|
||||||
|
|
||||||
|
|
||||||
static void ctap_reset_key_agreement();
|
static void ctap_reset_key_agreement();
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
@ -69,6 +68,8 @@ uint8_t verify_pin_auth(uint8_t * pinAuth, uint8_t * clientDataHash)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
uint8_t ctap_get_info(CborEncoder * encoder)
|
uint8_t ctap_get_info(CborEncoder * encoder)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@ -77,16 +78,14 @@ uint8_t ctap_get_info(CborEncoder * encoder)
|
|||||||
CborEncoder options;
|
CborEncoder options;
|
||||||
CborEncoder pins;
|
CborEncoder pins;
|
||||||
|
|
||||||
const int number_of_versions = 2;
|
ret = cbor_encoder_create_map(encoder, &map, 6);
|
||||||
|
|
||||||
ret = cbor_encoder_create_map(encoder, &map, 5);
|
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
{
|
{
|
||||||
|
|
||||||
ret = cbor_encode_uint(&map, RESP_versions); // versions key
|
ret = cbor_encode_uint(&map, RESP_versions); // versions key
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
{
|
{
|
||||||
ret = cbor_encoder_create_array(&map, &array, number_of_versions);
|
ret = cbor_encoder_create_array(&map, &array, 2);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
{
|
{
|
||||||
ret = cbor_encode_text_stringz(&array, "U2F_V2");
|
ret = cbor_encode_text_stringz(&array, "U2F_V2");
|
||||||
@ -98,6 +97,19 @@ uint8_t ctap_get_info(CborEncoder * encoder)
|
|||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = cbor_encode_uint(&map, RESP_extensions);
|
||||||
|
check_ret(ret);
|
||||||
|
{
|
||||||
|
ret = cbor_encoder_create_array(&map, &array, 1);
|
||||||
|
check_ret(ret);
|
||||||
|
{
|
||||||
|
ret = cbor_encode_text_stringz(&array, "hmac-secret");
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
ret = cbor_encoder_close_container(&map, &array);
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
|
||||||
ret = cbor_encode_uint(&map, RESP_aaguid);
|
ret = cbor_encode_uint(&map, RESP_aaguid);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
{
|
{
|
||||||
@ -311,18 +323,123 @@ static int is_matching_rk(CTAP_residentKey * rk, CTAP_residentKey * rk2)
|
|||||||
(rk->user.id_size == rk2->user.id_size);
|
(rk->user.id_size == rk2->user.id_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ctap_make_extensions(CTAP_extensions * ext, uint8_t * ext_encoder_buf, unsigned int * ext_encoder_buf_size)
|
||||||
|
{
|
||||||
|
CborEncoder extensions;
|
||||||
|
int ret;
|
||||||
|
uint8_t output[64];
|
||||||
|
uint8_t shared_secret[32];
|
||||||
|
uint8_t hmac[32];
|
||||||
|
uint8_t credRandom[32];
|
||||||
|
|
||||||
static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, unsigned int len, CTAP_userEntity * user, uint8_t credtype, int32_t algtype, int32_t * sz, int store)
|
if (ext->hmac_secret_present == EXT_HMAC_SECRET_PARSED)
|
||||||
|
{
|
||||||
|
printf1(TAG_CTAP, "Processing hmac-secret..\r\n");
|
||||||
|
|
||||||
|
crypto_ecc256_shared_secret((uint8_t*) &ext->hmac_secret.keyAgreement.pubkey,
|
||||||
|
KEY_AGREEMENT_PRIV,
|
||||||
|
shared_secret);
|
||||||
|
crypto_sha256_init();
|
||||||
|
crypto_sha256_update(shared_secret, 32);
|
||||||
|
crypto_sha256_final(shared_secret);
|
||||||
|
|
||||||
|
crypto_sha256_hmac_init(shared_secret, 32, hmac);
|
||||||
|
crypto_sha256_update(ext->hmac_secret.saltEnc, ext->hmac_secret.saltLen);
|
||||||
|
crypto_sha256_hmac_final(shared_secret, 32, hmac);
|
||||||
|
|
||||||
|
if (memcmp(ext->hmac_secret.saltAuth, hmac, 16) == 0)
|
||||||
|
{
|
||||||
|
printf1(TAG_CTAP, "saltAuth is valid\r\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf1(TAG_CTAP, "saltAuth is invalid\r\n");
|
||||||
|
return CTAP2_ERR_EXTENSION_FIRST;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate credRandom
|
||||||
|
crypto_sha256_hmac_init(CRYPTO_TRANSPORT_KEY, 0, credRandom);
|
||||||
|
crypto_sha256_update((uint8_t*)&ext->hmac_secret.credential->id, sizeof(CredentialId));
|
||||||
|
crypto_sha256_hmac_final(CRYPTO_TRANSPORT_KEY, 0, credRandom);
|
||||||
|
|
||||||
|
// Decrypt saltEnc
|
||||||
|
crypto_aes256_init(shared_secret, NULL);
|
||||||
|
crypto_aes256_decrypt(ext->hmac_secret.saltEnc, ext->hmac_secret.saltLen);
|
||||||
|
|
||||||
|
// Generate outputs
|
||||||
|
crypto_sha256_hmac_init(credRandom, 32, output);
|
||||||
|
crypto_sha256_update(ext->hmac_secret.saltEnc, 32);
|
||||||
|
crypto_sha256_hmac_final(credRandom, 32, output);
|
||||||
|
|
||||||
|
if (ext->hmac_secret.saltLen == 64)
|
||||||
|
{
|
||||||
|
crypto_sha256_hmac_init(credRandom, 32, output + 32);
|
||||||
|
crypto_sha256_update(ext->hmac_secret.saltEnc + 32, 32);
|
||||||
|
crypto_sha256_hmac_final(credRandom, 32, output + 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt for final output
|
||||||
|
crypto_aes256_init(shared_secret, NULL);
|
||||||
|
crypto_aes256_encrypt(output, ext->hmac_secret.saltLen);
|
||||||
|
|
||||||
|
// output
|
||||||
|
printf1(TAG_GREEN, "have %d bytes for Extenstions encoder\r\n",*ext_encoder_buf_size);
|
||||||
|
cbor_encoder_init(&extensions, ext_encoder_buf, *ext_encoder_buf_size, 0);
|
||||||
|
{
|
||||||
|
CborEncoder hmac_secret_map;
|
||||||
|
ret = cbor_encoder_create_map(&extensions, &hmac_secret_map, 1);
|
||||||
|
check_ret(ret);
|
||||||
|
{
|
||||||
|
ret = cbor_encode_text_stringz(&hmac_secret_map, "hmac-secret");
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
|
ret = cbor_encode_byte_string(&hmac_secret_map, output, ext->hmac_secret.saltLen);
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
ret = cbor_encoder_close_container(&extensions, &hmac_secret_map);
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
*ext_encoder_buf_size = cbor_encoder_get_buffer_size(&extensions, ext_encoder_buf);
|
||||||
|
}
|
||||||
|
else if (ext->hmac_secret_present == EXT_HMAC_SECRET_REQUESTED)
|
||||||
|
{
|
||||||
|
cbor_encoder_init(&extensions, ext_encoder_buf, *ext_encoder_buf_size, 0);
|
||||||
|
{
|
||||||
|
CborEncoder hmac_secret_map;
|
||||||
|
ret = cbor_encoder_create_map(&extensions, &hmac_secret_map, 1);
|
||||||
|
check_ret(ret);
|
||||||
|
{
|
||||||
|
ret = cbor_encode_text_stringz(&hmac_secret_map, "hmac-secret");
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
|
ret = cbor_encode_boolean(&hmac_secret_map, 1);
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
ret = cbor_encoder_close_container(&extensions, &hmac_secret_map);
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
*ext_encoder_buf_size = cbor_encoder_get_buffer_size(&extensions, ext_encoder_buf);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*ext_encoder_buf_size = 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, uint32_t * len, CTAP_credInfo * credInfo)
|
||||||
{
|
{
|
||||||
CborEncoder cose_key;
|
CborEncoder cose_key;
|
||||||
int auth_data_sz, ret;
|
|
||||||
|
unsigned int auth_data_sz = sizeof(CTAP_authDataHeader);
|
||||||
uint32_t count;
|
uint32_t count;
|
||||||
CTAP_residentKey rk, rk2;
|
CTAP_residentKey rk, rk2;
|
||||||
CTAP_authData * authData = (CTAP_authData *)auth_data_buf;
|
CTAP_authData * authData = (CTAP_authData *)auth_data_buf;
|
||||||
|
|
||||||
uint8_t * cose_key_buf = auth_data_buf + sizeof(CTAP_authData);
|
uint8_t * cose_key_buf = auth_data_buf + sizeof(CTAP_authData);
|
||||||
|
|
||||||
if((sizeof(CTAP_authDataHeader)) > len)
|
if((sizeof(CTAP_authDataHeader)) > *len)
|
||||||
{
|
{
|
||||||
printf1(TAG_ERR,"assertion fail, auth_data_buf must be at least %d bytes\n", sizeof(CTAP_authData) - sizeof(CTAP_attestHeader));
|
printf1(TAG_ERR,"assertion fail, auth_data_buf must be at least %d bytes\n", sizeof(CTAP_authData) - sizeof(CTAP_attestHeader));
|
||||||
exit(1);
|
exit(1);
|
||||||
@ -359,13 +476,12 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
|
|||||||
authData->head.flags |= (ctap_is_pin_set() << 2);
|
authData->head.flags |= (ctap_is_pin_set() << 2);
|
||||||
|
|
||||||
|
|
||||||
|
if (credInfo != NULL)
|
||||||
if (credtype != 0)
|
|
||||||
{
|
{
|
||||||
// add attestedCredentialData
|
// add attestedCredentialData
|
||||||
authData->head.flags |= (1 << 6);//include attestation data
|
authData->head.flags |= (1 << 6);//include attestation data
|
||||||
|
|
||||||
cbor_encoder_init(&cose_key, cose_key_buf, len - sizeof(CTAP_authData), 0);
|
cbor_encoder_init(&cose_key, cose_key_buf, *len - sizeof(CTAP_authData), 0);
|
||||||
|
|
||||||
memmove(authData->attest.aaguid, CTAP_AAGUID, 16);
|
memmove(authData->attest.aaguid, CTAP_AAGUID, 16);
|
||||||
authData->attest.credLenL = sizeof(CredentialId) & 0x00FF;
|
authData->attest.credLenL = sizeof(CredentialId) & 0x00FF;
|
||||||
@ -383,10 +499,10 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
|
|||||||
make_auth_tag(authData->head.rpIdHash, authData->attest.id.nonce, count, authData->attest.id.tag);
|
make_auth_tag(authData->head.rpIdHash, authData->attest.id.nonce, count, authData->attest.id.tag);
|
||||||
|
|
||||||
// resident key
|
// resident key
|
||||||
if (store)
|
if (credInfo->rk)
|
||||||
{
|
{
|
||||||
memmove(&rk.id, &authData->attest.id, sizeof(CredentialId));
|
memmove(&rk.id, &authData->attest.id, sizeof(CredentialId));
|
||||||
memmove(&rk.user, user, sizeof(CTAP_userEntity));
|
memmove(&rk.user, &credInfo->user, sizeof(CTAP_userEntity));
|
||||||
|
|
||||||
unsigned int index = STATE.rk_stored;
|
unsigned int index = STATE.rk_stored;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
@ -410,29 +526,19 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
|
|||||||
}
|
}
|
||||||
done_rk:
|
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));
|
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);
|
ctap_generate_cose_key(&cose_key, (uint8_t*)&authData->attest.id, sizeof(CredentialId), credInfo->publicKeyCredentialType, credInfo->COSEAlgorithmIdentifier);
|
||||||
|
|
||||||
auth_data_sz = sizeof(CTAP_authData) + cbor_encoder_get_buffer_size(&cose_key, cose_key_buf);
|
auth_data_sz = sizeof(CTAP_authData) + cbor_encoder_get_buffer_size(&cose_key, cose_key_buf);
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
auth_data_sz = sizeof(CTAP_authDataHeader);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
ret = cbor_encode_int(map,RESP_authData);
|
|
||||||
check_ret(ret);
|
|
||||||
ret = cbor_encode_byte_string(map, auth_data_buf, auth_data_sz);
|
|
||||||
check_ret(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sz) *sz = auth_data_sz;
|
|
||||||
|
|
||||||
|
|
||||||
|
*len = auth_data_sz;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -559,7 +665,7 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
|
|||||||
CTAP_makeCredential MC;
|
CTAP_makeCredential MC;
|
||||||
int ret;
|
int ret;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
uint8_t auth_data_buf[300];
|
uint8_t auth_data_buf[310];
|
||||||
CTAP_credentialDescriptor * excl_cred = (CTAP_credentialDescriptor *) auth_data_buf;
|
CTAP_credentialDescriptor * excl_cred = (CTAP_credentialDescriptor *) auth_data_buf;
|
||||||
uint8_t * sigbuf = auth_data_buf + 32;
|
uint8_t * sigbuf = auth_data_buf + 32;
|
||||||
uint8_t * sigder = auth_data_buf + 32 + 64;
|
uint8_t * sigder = auth_data_buf + 32 + 64;
|
||||||
@ -624,13 +730,32 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
|
|||||||
CborEncoder map;
|
CborEncoder map;
|
||||||
ret = cbor_encoder_create_map(encoder, &map, 3);
|
ret = cbor_encoder_create_map(encoder, &map, 3);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
int32_t auth_data_sz;
|
uint32_t auth_data_sz = sizeof(auth_data_buf);
|
||||||
|
|
||||||
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.rk);
|
|
||||||
|
|
||||||
|
ret = ctap_make_auth_data(&MC.rp, &map, auth_data_buf, &auth_data_sz,
|
||||||
|
&MC.credInfo);
|
||||||
check_retr(ret);
|
check_retr(ret);
|
||||||
|
|
||||||
|
{
|
||||||
|
unsigned int ext_encoder_buf_size = sizeof(auth_data_buf) - auth_data_sz;
|
||||||
|
uint8_t * ext_encoder_buf = auth_data_buf + auth_data_sz;
|
||||||
|
|
||||||
|
ret = ctap_make_extensions(&MC.extensions, ext_encoder_buf, &ext_encoder_buf_size);
|
||||||
|
check_retr(ret);
|
||||||
|
if (ext_encoder_buf_size)
|
||||||
|
{
|
||||||
|
((CTAP_authData *)auth_data_buf)->head.flags |= (1 << 7);
|
||||||
|
auth_data_sz += ext_encoder_buf_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ret = cbor_encode_int(&map,RESP_authData);
|
||||||
|
check_ret(ret);
|
||||||
|
ret = cbor_encode_byte_string(&map, auth_data_buf, auth_data_sz);
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
|
||||||
crypto_ecc256_load_attestation_key();
|
crypto_ecc256_load_attestation_key();
|
||||||
int sigder_sz = ctap_calculate_signature(auth_data_buf, auth_data_sz, MC.clientDataHash, auth_data_buf, sigbuf, sigder);
|
int sigder_sz = ctap_calculate_signature(auth_data_buf, auth_data_sz, MC.clientDataHash, auth_data_buf, sigbuf, sigder);
|
||||||
printf1(TAG_MC,"der sig [%d]: ", sigder_sz); dump_hex1(TAG_MC, sigder, sigder_sz);
|
printf1(TAG_MC,"der sig [%d]: ", sigder_sz); dump_hex1(TAG_MC, sigder, sigder_sz);
|
||||||
@ -852,6 +977,7 @@ static void save_credential_list(CTAP_authDataHeader * head, uint8_t * clientDat
|
|||||||
memmove(getAssertionState.clientDataHash, clientDataHash, CLIENT_DATA_HASH_SIZE);
|
memmove(getAssertionState.clientDataHash, clientDataHash, CLIENT_DATA_HASH_SIZE);
|
||||||
memmove(&getAssertionState.authData, head, sizeof(CTAP_authDataHeader));
|
memmove(&getAssertionState.authData, head, sizeof(CTAP_authDataHeader));
|
||||||
memmove(getAssertionState.creds, creds, sizeof(CTAP_credentialDescriptor) * (count));
|
memmove(getAssertionState.creds, creds, sizeof(CTAP_credentialDescriptor) * (count));
|
||||||
|
|
||||||
}
|
}
|
||||||
getAssertionState.count = count;
|
getAssertionState.count = count;
|
||||||
printf1(TAG_GA,"saved %d credentials\n",count);
|
printf1(TAG_GA,"saved %d credentials\n",count);
|
||||||
@ -916,7 +1042,6 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
|
|||||||
CborEncoder map;
|
CborEncoder map;
|
||||||
CTAP_authDataHeader authData;
|
CTAP_authDataHeader authData;
|
||||||
memmove(&authData, &getAssertionState.authData, sizeof(CTAP_authDataHeader));
|
memmove(&authData, &getAssertionState.authData, sizeof(CTAP_authDataHeader));
|
||||||
// CTAP_authDataHeader * authData = &getAssertionState.authData;
|
|
||||||
|
|
||||||
CTAP_credentialDescriptor * cred = pop_credential();
|
CTAP_credentialDescriptor * cred = pop_credential();
|
||||||
|
|
||||||
@ -939,6 +1064,7 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
|
|||||||
ret = cbor_encoder_create_map(encoder, &map, 3);
|
ret = cbor_encoder_create_map(encoder, &map, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
printf1(TAG_RED, "RPID hash: "); dump_hex1(TAG_RED, authData.rpIdHash, 32);
|
printf1(TAG_RED, "RPID hash: "); dump_hex1(TAG_RED, authData.rpIdHash, 32);
|
||||||
|
|
||||||
@ -949,6 +1075,7 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
|
|||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// if only one account for this RP, null out the user details
|
// if only one account for this RP, null out the user details
|
||||||
if (!getAssertionState.user_verified)
|
if (!getAssertionState.user_verified)
|
||||||
{
|
{
|
||||||
@ -969,7 +1096,7 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
|
|||||||
uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
|
uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
|
||||||
{
|
{
|
||||||
CTAP_getAssertion GA;
|
CTAP_getAssertion GA;
|
||||||
uint8_t auth_data_buf[sizeof(CTAP_authDataHeader)];
|
uint8_t auth_data_buf[sizeof(CTAP_authDataHeader) + 80];
|
||||||
int ret = ctap_parse_get_assertion(&GA,request,length);
|
int ret = ctap_parse_get_assertion(&GA,request,length);
|
||||||
|
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
@ -1015,47 +1142,15 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
|
|||||||
map_size += 1;
|
map_size += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GA.extensions.hmac_secret_present == EXT_HMAC_SECRET_PARSED)
|
||||||
|
{
|
||||||
|
printf1(TAG_GA, "hmac-secret is present\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
ret = cbor_encoder_create_map(encoder, &map, map_size);
|
ret = cbor_encoder_create_map(encoder, &map, map_size);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
|
|
||||||
#ifdef ENABLE_U2F_EXTENSIONS
|
if (validCredCount == 0)
|
||||||
if ( is_extension_request((uint8_t*)&GA.creds[validCredCount - 1].credential.id, sizeof(CredentialId)) )
|
|
||||||
{
|
|
||||||
ret = cbor_encode_int(&map,RESP_authData);
|
|
||||||
check_ret(ret);
|
|
||||||
memset(auth_data_buf,0,sizeof(auth_data_buf));
|
|
||||||
ret = cbor_encode_byte_string(&map, auth_data_buf, sizeof(auth_data_buf));
|
|
||||||
check_ret(ret);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
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);*/
|
|
||||||
/*dump_hex1(TAG_GA, (uint8_t*)&GA.creds[j].credential, sizeof(struct Credential));*/
|
|
||||||
/*if (ctap_authenticate_credential(&GA.rp, &GA.creds[j])) // warning encryption will break this*/
|
|
||||||
/*{*/
|
|
||||||
/*printf1(TAG_GA," Authenticated.\n");*/
|
|
||||||
/*}*/
|
|
||||||
/*else*/
|
|
||||||
/*{*/
|
|
||||||
/*printf1(TAG_GA," NOT authentic.\n");*/
|
|
||||||
/*}*/
|
|
||||||
/*}*/
|
|
||||||
|
|
||||||
// Decrypt here
|
|
||||||
|
|
||||||
//
|
|
||||||
if (validCredCount > 0)
|
|
||||||
{
|
|
||||||
save_credential_list((CTAP_authDataHeader*)auth_data_buf, GA.clientDataHash, GA.creds, validCredCount-1); // skip last one
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
printf2(TAG_ERR,"Error, no authentic credential\n");
|
printf2(TAG_ERR,"Error, no authentic credential\n");
|
||||||
return CTAP2_ERR_NO_CREDENTIALS;
|
return CTAP2_ERR_NO_CREDENTIALS;
|
||||||
@ -1085,6 +1180,47 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
|
|||||||
|
|
||||||
CTAP_credentialDescriptor * cred = &GA.creds[validCredCount - 1];
|
CTAP_credentialDescriptor * cred = &GA.creds[validCredCount - 1];
|
||||||
|
|
||||||
|
GA.extensions.hmac_secret.credential = &cred->credential;
|
||||||
|
|
||||||
|
#ifdef ENABLE_U2F_EXTENSIONS
|
||||||
|
if ( is_extension_request((uint8_t*)&GA.creds[validCredCount - 1].credential.id, sizeof(CredentialId)) )
|
||||||
|
{
|
||||||
|
ret = cbor_encode_int(&map,RESP_authData);
|
||||||
|
check_ret(ret);
|
||||||
|
memset(auth_data_buf,0,sizeof(CTAP_authDataHeader));
|
||||||
|
ret = cbor_encode_byte_string(&map, auth_data_buf, sizeof(CTAP_authDataHeader));
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
uint32_t len = sizeof(auth_data_buf);
|
||||||
|
ret = ctap_make_auth_data(&GA.rp, &map, auth_data_buf, &len, NULL);
|
||||||
|
check_retr(ret);
|
||||||
|
|
||||||
|
{
|
||||||
|
unsigned int ext_encoder_buf_size = sizeof(auth_data_buf) - len;
|
||||||
|
uint8_t * ext_encoder_buf = auth_data_buf + len;
|
||||||
|
|
||||||
|
ret = ctap_make_extensions(&GA.extensions, ext_encoder_buf, &ext_encoder_buf_size);
|
||||||
|
check_retr(ret);
|
||||||
|
if (ext_encoder_buf_size)
|
||||||
|
{
|
||||||
|
((CTAP_authData *)auth_data_buf)->head.flags |= (1 << 7);
|
||||||
|
len += ext_encoder_buf_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ret = cbor_encode_int(&map,RESP_authData);
|
||||||
|
check_ret(ret);
|
||||||
|
ret = cbor_encode_byte_string(&map, auth_data_buf, len);
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
save_credential_list((CTAP_authDataHeader*)auth_data_buf, GA.clientDataHash, GA.creds, validCredCount-1); // skip last one
|
||||||
|
|
||||||
ret = ctap_end_get_assertion(&map, cred, auth_data_buf, GA.clientDataHash, add_user_info);
|
ret = ctap_end_get_assertion(&map, cred, auth_data_buf, GA.clientDataHash, add_user_info);
|
||||||
check_retr(ret);
|
check_retr(ret);
|
||||||
|
|
||||||
@ -1406,7 +1542,6 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
|
|||||||
pkt_raw++;
|
pkt_raw++;
|
||||||
length--;
|
length--;
|
||||||
|
|
||||||
|
|
||||||
uint8_t * buf = resp->data;
|
uint8_t * buf = resp->data;
|
||||||
|
|
||||||
cbor_encoder_init(&encoder, buf, resp->data_size, 0);
|
cbor_encoder_init(&encoder, buf, resp->data_size, 0);
|
||||||
|
73
fido2/ctap.h
73
fido2/ctap.h
@ -54,6 +54,13 @@
|
|||||||
#define CP_getKeyAgreement 0x07
|
#define CP_getKeyAgreement 0x07
|
||||||
#define CP_getRetries 0x08
|
#define CP_getRetries 0x08
|
||||||
|
|
||||||
|
#define EXT_HMAC_SECRET_COSE_KEY 0x01
|
||||||
|
#define EXT_HMAC_SECRET_SALT_ENC 0x02
|
||||||
|
#define EXT_HMAC_SECRET_SALT_AUTH 0x03
|
||||||
|
|
||||||
|
#define EXT_HMAC_SECRET_REQUESTED 0x01
|
||||||
|
#define EXT_HMAC_SECRET_PARSED 0x02
|
||||||
|
|
||||||
#define RESP_versions 0x1
|
#define RESP_versions 0x1
|
||||||
#define RESP_extensions 0x2
|
#define RESP_extensions 0x2
|
||||||
#define RESP_aaguid 0x3
|
#define RESP_aaguid 0x3
|
||||||
@ -142,9 +149,13 @@ struct Credential {
|
|||||||
CredentialId id;
|
CredentialId id;
|
||||||
CTAP_userEntity user;
|
CTAP_userEntity user;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct Credential CTAP_residentKey;
|
typedef struct Credential CTAP_residentKey;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t type;
|
||||||
|
struct Credential credential;
|
||||||
|
} CTAP_credentialDescriptor;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@ -181,34 +192,62 @@ struct rpId
|
|||||||
uint8_t name[RP_NAME_LIMIT];
|
uint8_t name[RP_NAME_LIMIT];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
struct{
|
||||||
|
uint8_t x[32];
|
||||||
|
uint8_t y[32];
|
||||||
|
} pubkey;
|
||||||
|
|
||||||
|
int kty;
|
||||||
|
int crv;
|
||||||
|
} COSE_key;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t saltLen;
|
||||||
|
uint8_t saltEnc[64];
|
||||||
|
uint8_t saltAuth[32];
|
||||||
|
COSE_key keyAgreement;
|
||||||
|
struct Credential * credential;
|
||||||
|
} CTAP_hmac_secret;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t hmac_secret_present;
|
||||||
|
CTAP_hmac_secret hmac_secret;
|
||||||
|
} CTAP_extensions;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
CTAP_userEntity user;
|
||||||
|
uint8_t publicKeyCredentialType;
|
||||||
|
int32_t COSEAlgorithmIdentifier;
|
||||||
|
uint8_t rk;
|
||||||
|
} CTAP_credInfo;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint32_t paramsParsed;
|
uint32_t paramsParsed;
|
||||||
uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
|
uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
|
||||||
struct rpId rp;
|
struct rpId rp;
|
||||||
CTAP_userEntity user;
|
|
||||||
|
|
||||||
uint8_t publicKeyCredentialType;
|
CTAP_credInfo credInfo;
|
||||||
int32_t COSEAlgorithmIdentifier;
|
|
||||||
|
|
||||||
CborValue excludeList;
|
CborValue excludeList;
|
||||||
size_t excludeListSize;
|
size_t excludeListSize;
|
||||||
|
|
||||||
uint8_t rk;
|
|
||||||
uint8_t uv;
|
uint8_t uv;
|
||||||
uint8_t up;
|
uint8_t up;
|
||||||
|
|
||||||
uint8_t pinAuth[16];
|
uint8_t pinAuth[16];
|
||||||
uint8_t pinAuthPresent;
|
uint8_t pinAuthPresent;
|
||||||
int pinProtocol;
|
int pinProtocol;
|
||||||
|
CTAP_extensions extensions;
|
||||||
|
|
||||||
} CTAP_makeCredential;
|
} CTAP_makeCredential;
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
uint8_t type;
|
|
||||||
struct Credential credential;
|
|
||||||
} CTAP_credentialDescriptor;
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@ -230,22 +269,16 @@ typedef struct
|
|||||||
|
|
||||||
CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE];
|
CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE];
|
||||||
uint8_t allowListPresent;
|
uint8_t allowListPresent;
|
||||||
|
|
||||||
|
CTAP_extensions extensions;
|
||||||
|
|
||||||
} CTAP_getAssertion;
|
} CTAP_getAssertion;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
int pinProtocol;
|
int pinProtocol;
|
||||||
int subCommand;
|
int subCommand;
|
||||||
struct
|
COSE_key keyAgreement;
|
||||||
{
|
|
||||||
struct{
|
|
||||||
uint8_t x[32];
|
|
||||||
uint8_t y[32];
|
|
||||||
} pubkey;
|
|
||||||
|
|
||||||
int kty;
|
|
||||||
int crv;
|
|
||||||
} keyAgreement;
|
|
||||||
uint8_t keyAgreementPresent;
|
uint8_t keyAgreementPresent;
|
||||||
uint8_t pinAuth[16];
|
uint8_t pinAuth[16];
|
||||||
uint8_t pinAuthPresent;
|
uint8_t pinAuthPresent;
|
||||||
|
@ -128,14 +128,14 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
sz = USER_ID_MAX_SIZE;
|
sz = USER_ID_MAX_SIZE;
|
||||||
ret = cbor_value_copy_byte_string(&map, MC->user.id, &sz, NULL);
|
ret = cbor_value_copy_byte_string(&map, MC->credInfo.user.id, &sz, NULL);
|
||||||
if (ret == CborErrorOutOfMemory)
|
if (ret == CborErrorOutOfMemory)
|
||||||
{
|
{
|
||||||
printf2(TAG_ERR,"Error, USER_ID is too large\n");
|
printf2(TAG_ERR,"Error, USER_ID is too large\n");
|
||||||
return CTAP2_ERR_LIMIT_EXCEEDED;
|
return CTAP2_ERR_LIMIT_EXCEEDED;
|
||||||
}
|
}
|
||||||
MC->user.id_size = sz;
|
MC->credInfo.user.id_size = sz;
|
||||||
printf1(TAG_GREEN,"parsed id_size: %d\r\n", MC->user.id_size);
|
printf1(TAG_GREEN,"parsed id_size: %d\r\n", MC->credInfo.user.id_size);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
else if (strcmp((const char *)key, "name") == 0)
|
else if (strcmp((const char *)key, "name") == 0)
|
||||||
@ -146,12 +146,12 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
|
|||||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||||
}
|
}
|
||||||
sz = USER_NAME_LIMIT;
|
sz = USER_NAME_LIMIT;
|
||||||
ret = cbor_value_copy_text_string(&map, (char *)MC->user.name, &sz, NULL);
|
ret = cbor_value_copy_text_string(&map, (char *)MC->credInfo.user.name, &sz, NULL);
|
||||||
if (ret != CborErrorOutOfMemory)
|
if (ret != CborErrorOutOfMemory)
|
||||||
{ // Just truncate the name it's okay
|
{ // Just truncate the name it's okay
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
MC->user.name[USER_NAME_LIMIT - 1] = 0;
|
MC->credInfo.user.name[USER_NAME_LIMIT - 1] = 0;
|
||||||
}
|
}
|
||||||
else if (strcmp((const char *)key, "displayName") == 0)
|
else if (strcmp((const char *)key, "displayName") == 0)
|
||||||
{
|
{
|
||||||
@ -161,12 +161,12 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
|
|||||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||||
}
|
}
|
||||||
sz = DISPLAY_NAME_LIMIT;
|
sz = DISPLAY_NAME_LIMIT;
|
||||||
ret = cbor_value_copy_text_string(&map, (char *)MC->user.displayName, &sz, NULL);
|
ret = cbor_value_copy_text_string(&map, (char *)MC->credInfo.user.displayName, &sz, NULL);
|
||||||
if (ret != CborErrorOutOfMemory)
|
if (ret != CborErrorOutOfMemory)
|
||||||
{ // Just truncate the name it's okay
|
{ // Just truncate the name it's okay
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
MC->user.displayName[DISPLAY_NAME_LIMIT - 1] = 0;
|
MC->credInfo.user.displayName[DISPLAY_NAME_LIMIT - 1] = 0;
|
||||||
}
|
}
|
||||||
else if (strcmp((const char *)key, "icon") == 0)
|
else if (strcmp((const char *)key, "icon") == 0)
|
||||||
{
|
{
|
||||||
@ -176,12 +176,12 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
|
|||||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||||
}
|
}
|
||||||
sz = ICON_LIMIT;
|
sz = ICON_LIMIT;
|
||||||
ret = cbor_value_copy_text_string(&map, (char *)MC->user.icon, &sz, NULL);
|
ret = cbor_value_copy_text_string(&map, (char *)MC->credInfo.user.icon, &sz, NULL);
|
||||||
if (ret != CborErrorOutOfMemory)
|
if (ret != CborErrorOutOfMemory)
|
||||||
{ // Just truncate the name it's okay
|
{ // Just truncate the name it's okay
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
MC->user.icon[ICON_LIMIT - 1] = 0;
|
MC->credInfo.user.icon[ICON_LIMIT - 1] = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -305,8 +305,8 @@ uint8_t parse_pub_key_cred_params(CTAP_makeCredential * MC, CborValue * val)
|
|||||||
{
|
{
|
||||||
if (pub_key_cred_param_supported(cred_type, alg_type) == CREDENTIAL_IS_SUPPORTED)
|
if (pub_key_cred_param_supported(cred_type, alg_type) == CREDENTIAL_IS_SUPPORTED)
|
||||||
{
|
{
|
||||||
MC->publicKeyCredentialType = cred_type;
|
MC->credInfo.publicKeyCredentialType = cred_type;
|
||||||
MC->COSEAlgorithmIdentifier = alg_type;
|
MC->credInfo.COSEAlgorithmIdentifier = alg_type;
|
||||||
MC->paramsParsed |= PARAM_pubKeyCredParams;
|
MC->paramsParsed |= PARAM_pubKeyCredParams;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -556,6 +556,154 @@ uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8_t * up)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t ctap_parse_hmac_secret(CborValue * val, CTAP_hmac_secret * hs)
|
||||||
|
{
|
||||||
|
size_t map_length;
|
||||||
|
size_t salt_len;
|
||||||
|
uint8_t parsed_count = 0;
|
||||||
|
int key;
|
||||||
|
int ret;
|
||||||
|
unsigned int i;
|
||||||
|
CborValue map;
|
||||||
|
|
||||||
|
if (cbor_value_get_type(val) != CborMapType)
|
||||||
|
{
|
||||||
|
printf2(TAG_ERR,"error, wrong type\n");
|
||||||
|
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = cbor_value_enter_container(val,&map);
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
|
ret = cbor_value_get_map_length(val, &map_length);
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
|
for (i = 0; i < map_length; i++)
|
||||||
|
{
|
||||||
|
if (cbor_value_get_type(&map) != CborIntegerType)
|
||||||
|
{
|
||||||
|
printf2(TAG_ERR,"Error, expecting CborIntegerTypefor hmac-secret map key, got %s\n", cbor_value_get_type_string(&map));
|
||||||
|
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||||
|
}
|
||||||
|
ret = cbor_value_get_int(&map, &key);
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
|
ret = cbor_value_advance(&map);
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
|
switch(key)
|
||||||
|
{
|
||||||
|
case EXT_HMAC_SECRET_COSE_KEY:
|
||||||
|
ret = parse_cose_key(&map, &hs->keyAgreement);
|
||||||
|
check_retr(ret);
|
||||||
|
parsed_count++;
|
||||||
|
break;
|
||||||
|
case EXT_HMAC_SECRET_SALT_ENC:
|
||||||
|
salt_len = 64;
|
||||||
|
ret = cbor_value_copy_byte_string(&map, hs->saltEnc, &salt_len, NULL);
|
||||||
|
if ((salt_len != 32 && salt_len != 64) || ret == CborErrorOutOfMemory)
|
||||||
|
{
|
||||||
|
return CTAP1_ERR_INVALID_LENGTH;
|
||||||
|
}
|
||||||
|
check_ret(ret);
|
||||||
|
hs->saltLen = salt_len;
|
||||||
|
parsed_count++;
|
||||||
|
break;
|
||||||
|
case EXT_HMAC_SECRET_SALT_AUTH:
|
||||||
|
salt_len = 32;
|
||||||
|
ret = cbor_value_copy_byte_string(&map, hs->saltAuth, &salt_len, NULL);
|
||||||
|
check_ret(ret);
|
||||||
|
parsed_count++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = cbor_value_advance(&map);
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsed_count != 3)
|
||||||
|
{
|
||||||
|
printf2(TAG_ERR, "ctap_parse_hmac_secret missing parameter. Got %d.\r\n", parsed_count);
|
||||||
|
return CTAP2_ERR_MISSING_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t ctap_parse_extensions(CborValue * val, CTAP_extensions * ext)
|
||||||
|
{
|
||||||
|
CborValue map;
|
||||||
|
size_t sz, map_length;
|
||||||
|
char key[16];
|
||||||
|
int ret;
|
||||||
|
unsigned int i;
|
||||||
|
bool b;
|
||||||
|
|
||||||
|
if (cbor_value_get_type(val) != CborMapType)
|
||||||
|
{
|
||||||
|
printf2(TAG_ERR,"error, wrong type\n");
|
||||||
|
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = cbor_value_enter_container(val, &map);
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
|
ret = cbor_value_get_map_length(val, &map_length);
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
|
for (i = 0; i < map_length; i++)
|
||||||
|
{
|
||||||
|
if (cbor_value_get_type(&map) != CborTextStringType)
|
||||||
|
{
|
||||||
|
printf2(TAG_ERR,"Error, expecting text string type for options map key, got %s\n", cbor_value_get_type_string(&map));
|
||||||
|
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||||
|
}
|
||||||
|
sz = sizeof(key);
|
||||||
|
ret = cbor_value_copy_text_string(&map, key, &sz, NULL);
|
||||||
|
|
||||||
|
if (ret == CborErrorOutOfMemory)
|
||||||
|
{
|
||||||
|
printf2(TAG_ERR,"Error, rp map key is too large. Ignoring.\n");
|
||||||
|
cbor_value_advance(&map);
|
||||||
|
cbor_value_advance(&map);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
check_ret(ret);
|
||||||
|
key[sizeof(key) - 1] = 0;
|
||||||
|
|
||||||
|
ret = cbor_value_advance(&map);
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
|
|
||||||
|
if (strncmp(key, "hmac-secret",11) == 0)
|
||||||
|
{
|
||||||
|
if (cbor_value_get_type(&map) == CborBooleanType)
|
||||||
|
{
|
||||||
|
ret = cbor_value_get_boolean(&map, &b);
|
||||||
|
check_ret(ret);
|
||||||
|
if (b) ext->hmac_secret_present = EXT_HMAC_SECRET_REQUESTED;
|
||||||
|
printf1(TAG_CTAP, "set hmac_secret_present to %d\r\n", b);
|
||||||
|
}
|
||||||
|
else if (cbor_value_get_type(&map) == CborMapType)
|
||||||
|
{
|
||||||
|
ret = ctap_parse_hmac_secret(&map, &ext->hmac_secret);
|
||||||
|
check_retr(ret);
|
||||||
|
ext->hmac_secret_present = EXT_HMAC_SECRET_PARSED;
|
||||||
|
printf1(TAG_CTAP, "parsed hmac_secret request\r\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf1(TAG_RED, "warning: hmac_secret request ignored for being wrong type\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = cbor_value_advance(&map);
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length)
|
uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@ -631,8 +779,8 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
|
|||||||
|
|
||||||
ret = parse_user(MC, &map);
|
ret = parse_user(MC, &map);
|
||||||
|
|
||||||
printf1(TAG_MC," ID: "); dump_hex1(TAG_MC, MC->user.id, MC->user.id_size);
|
printf1(TAG_MC," ID: "); dump_hex1(TAG_MC, MC->credInfo.user.id, MC->credInfo.user.id_size);
|
||||||
printf1(TAG_MC," name: %s\n", MC->user.name);
|
printf1(TAG_MC," name: %s\n", MC->credInfo.user.name);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case MC_pubKeyCredParams:
|
case MC_pubKeyCredParams:
|
||||||
@ -640,8 +788,8 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
|
|||||||
|
|
||||||
ret = parse_pub_key_cred_params(MC, &map);
|
ret = parse_pub_key_cred_params(MC, &map);
|
||||||
|
|
||||||
printf1(TAG_MC," cred_type: 0x%02x\n", MC->publicKeyCredentialType);
|
printf1(TAG_MC," cred_type: 0x%02x\n", MC->credInfo.publicKeyCredentialType);
|
||||||
printf1(TAG_MC," alg_type: %d\n", MC->COSEAlgorithmIdentifier);
|
printf1(TAG_MC," alg_type: %d\n", MC->credInfo.COSEAlgorithmIdentifier);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case MC_excludeList:
|
case MC_excludeList:
|
||||||
@ -665,11 +813,13 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
|
|||||||
{
|
{
|
||||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||||
}
|
}
|
||||||
|
ret = ctap_parse_extensions(&map, &MC->extensions);
|
||||||
|
check_retr(ret);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MC_options:
|
case MC_options:
|
||||||
printf1(TAG_MC,"CTAP_options\n");
|
printf1(TAG_MC,"CTAP_options\n");
|
||||||
ret = parse_options(&map, &MC->rk, &MC->uv, &MC->up);
|
ret = parse_options(&map, &MC->credInfo.rk, &MC->uv, &MC->up);
|
||||||
check_retr(ret);
|
check_retr(ret);
|
||||||
break;
|
break;
|
||||||
case MC_pinAuth:
|
case MC_pinAuth:
|
||||||
@ -886,6 +1036,8 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
|
|||||||
break;
|
break;
|
||||||
case GA_extensions:
|
case GA_extensions:
|
||||||
printf1(TAG_GA,"GA_extensions\n");
|
printf1(TAG_GA,"GA_extensions\n");
|
||||||
|
ret = ctap_parse_extensions(&map, &GA->extensions);
|
||||||
|
check_retr(ret);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GA_options:
|
case GA_options:
|
||||||
@ -940,15 +1092,15 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int * crv)
|
uint8_t parse_cose_key(CborValue * it, COSE_key * cose)
|
||||||
{
|
{
|
||||||
CborValue map;
|
CborValue map;
|
||||||
size_t map_length;
|
size_t map_length;
|
||||||
int ret,key;
|
int ret,key;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
int xkey = 0,ykey = 0;
|
int xkey = 0,ykey = 0;
|
||||||
*kty = 0;
|
cose->kty = 0;
|
||||||
*crv = 0;
|
cose->crv = 0;
|
||||||
|
|
||||||
|
|
||||||
CborType type = cbor_value_get_type(it);
|
CborType type = cbor_value_get_type(it);
|
||||||
@ -986,7 +1138,7 @@ uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int
|
|||||||
printf1(TAG_PARSE,"COSE_KEY_LABEL_KTY\n");
|
printf1(TAG_PARSE,"COSE_KEY_LABEL_KTY\n");
|
||||||
if (cbor_value_get_type(&map) == CborIntegerType)
|
if (cbor_value_get_type(&map) == CborIntegerType)
|
||||||
{
|
{
|
||||||
ret = cbor_value_get_int_checked(&map, kty);
|
ret = cbor_value_get_int_checked(&map, &cose->kty);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1001,7 +1153,7 @@ uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int
|
|||||||
printf1(TAG_PARSE,"COSE_KEY_LABEL_CRV\n");
|
printf1(TAG_PARSE,"COSE_KEY_LABEL_CRV\n");
|
||||||
if (cbor_value_get_type(&map) == CborIntegerType)
|
if (cbor_value_get_type(&map) == CborIntegerType)
|
||||||
{
|
{
|
||||||
ret = cbor_value_get_int_checked(&map, crv);
|
ret = cbor_value_get_int_checked(&map, &cose->crv);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1011,14 +1163,14 @@ uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int
|
|||||||
break;
|
break;
|
||||||
case COSE_KEY_LABEL_X:
|
case COSE_KEY_LABEL_X:
|
||||||
printf1(TAG_PARSE,"COSE_KEY_LABEL_X\n");
|
printf1(TAG_PARSE,"COSE_KEY_LABEL_X\n");
|
||||||
ret = parse_fixed_byte_string(&map, x, 32);
|
ret = parse_fixed_byte_string(&map, cose->pubkey.x, 32);
|
||||||
check_retr(ret);
|
check_retr(ret);
|
||||||
xkey = 1;
|
xkey = 1;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case COSE_KEY_LABEL_Y:
|
case COSE_KEY_LABEL_Y:
|
||||||
printf1(TAG_PARSE,"COSE_KEY_LABEL_Y\n");
|
printf1(TAG_PARSE,"COSE_KEY_LABEL_Y\n");
|
||||||
ret = parse_fixed_byte_string(&map, y, 32);
|
ret = parse_fixed_byte_string(&map, cose->pubkey.y, 32);
|
||||||
check_retr(ret);
|
check_retr(ret);
|
||||||
ykey = 1;
|
ykey = 1;
|
||||||
|
|
||||||
@ -1030,7 +1182,7 @@ uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int
|
|||||||
ret = cbor_value_advance(&map);
|
ret = cbor_value_advance(&map);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
if (xkey == 0 || ykey == 0 || *kty == 0 || *crv == 0)
|
if (xkey == 0 || ykey == 0 || cose->kty == 0 || cose->crv == 0)
|
||||||
{
|
{
|
||||||
return CTAP2_ERR_MISSING_PARAMETER;
|
return CTAP2_ERR_MISSING_PARAMETER;
|
||||||
}
|
}
|
||||||
@ -1110,7 +1262,7 @@ uint8_t ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length
|
|||||||
break;
|
break;
|
||||||
case CP_keyAgreement:
|
case CP_keyAgreement:
|
||||||
printf1(TAG_CP,"CP_keyAgreement\n");
|
printf1(TAG_CP,"CP_keyAgreement\n");
|
||||||
ret = parse_cose_key(&map, CP->keyAgreement.pubkey.x, CP->keyAgreement.pubkey.y, &CP->keyAgreement.kty, &CP->keyAgreement.crv);
|
ret = parse_cose_key(&map, &CP->keyAgreement);
|
||||||
check_retr(ret);
|
check_retr(ret);
|
||||||
CP->keyAgreementPresent = 1;
|
CP->keyAgreementPresent = 1;
|
||||||
break;
|
break;
|
||||||
|
@ -30,7 +30,7 @@ uint8_t parse_rp(struct rpId * rp, CborValue * val);
|
|||||||
uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8_t * up);
|
uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8_t * up);
|
||||||
|
|
||||||
uint8_t parse_allow_list(CTAP_getAssertion * GA, CborValue * it);
|
uint8_t parse_allow_list(CTAP_getAssertion * GA, CborValue * it);
|
||||||
uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int * crv);
|
uint8_t parse_cose_key(CborValue * it, COSE_key * cose);
|
||||||
|
|
||||||
|
|
||||||
uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length);
|
uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length);
|
||||||
|
@ -46,7 +46,7 @@ DEFINES = -DDEBUG_LEVEL=$(DEBUG) -D$(CHIP) -DAES256=1 -DUSE_FULL_LL_DRIVER -DAP
|
|||||||
|
|
||||||
CFLAGS=$(INC) -c $(DEFINES) -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -fdata-sections -ffunction-sections \
|
CFLAGS=$(INC) -c $(DEFINES) -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -fdata-sections -ffunction-sections \
|
||||||
-fomit-frame-pointer $(HW) -g $(VERSION_FLAGS)
|
-fomit-frame-pointer $(HW) -g $(VERSION_FLAGS)
|
||||||
LDFLAGS_LIB=$(HW) $(SEARCH) -specs=nano.specs -specs=nosys.specs -Wl,--gc-sections -u _printf_float -lnosys
|
LDFLAGS_LIB=$(HW) $(SEARCH) -specs=nano.specs -specs=nosys.specs -Wl,--gc-sections -lnosys
|
||||||
LDFLAGS=$(HW) $(LDFLAGS_LIB) -T$(LDSCRIPT) -Wl,-Map=$(TARGET).map,--cref -Wl,-Bstatic -ltinycbor
|
LDFLAGS=$(HW) $(LDFLAGS_LIB) -T$(LDSCRIPT) -Wl,-Map=$(TARGET).map,--cref -Wl,-Bstatic -ltinycbor
|
||||||
|
|
||||||
ECC_CFLAGS = $(CFLAGS) -DuECC_PLATFORM=5 -DuECC_OPTIMIZATION_LEVEL=4 -DuECC_SQUARE_FUNC=1 -DuECC_SUPPORT_COMPRESSED_POINT=0
|
ECC_CFLAGS = $(CFLAGS) -DuECC_PLATFORM=5 -DuECC_OPTIMIZATION_LEVEL=4 -DuECC_SQUARE_FUNC=1 -DuECC_SUPPORT_COMPRESSED_POINT=0
|
||||||
|
@ -39,7 +39,7 @@ int _write (int fd, const void *buf, unsigned long int len)
|
|||||||
// logbuflen += len;
|
// logbuflen += len;
|
||||||
|
|
||||||
// Send out USB serial
|
// Send out USB serial
|
||||||
CDC_Transmit_FS(buf, len);
|
CDC_Transmit_FS(data, len);
|
||||||
// if (res == USBD_OK)
|
// if (res == USBD_OK)
|
||||||
// logbuflen = 0;
|
// logbuflen = 0;
|
||||||
#endif
|
#endif
|
||||||
|
@ -25,6 +25,9 @@ from fido2.ctap2 import ES256, PinProtocolV1
|
|||||||
from fido2.utils import Timeout, sha256, hmac_sha256
|
from fido2.utils import Timeout, sha256, hmac_sha256
|
||||||
from fido2.attestation import Attestation
|
from fido2.attestation import Attestation
|
||||||
|
|
||||||
|
from cryptography.hazmat.backends import default_backend
|
||||||
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||||
|
|
||||||
from solo.fido2 import force_udp_backend
|
from solo.fido2 import force_udp_backend
|
||||||
from solo.client import SoloClient
|
from solo.client import SoloClient
|
||||||
|
|
||||||
@ -44,6 +47,17 @@ def VerifyAttestation(attest, data):
|
|||||||
verifier().verify(attest.att_statement, attest.auth_data, data.hash)
|
verifier().verify(attest.att_statement, attest.auth_data, data.hash)
|
||||||
|
|
||||||
|
|
||||||
|
def shannon_entropy(data):
|
||||||
|
sum = 0.0
|
||||||
|
total = len(data)
|
||||||
|
for x in range(0, 256):
|
||||||
|
freq = data.count(x)
|
||||||
|
p = freq / total
|
||||||
|
if p > 0:
|
||||||
|
sum -= p * math.log2(p)
|
||||||
|
return sum
|
||||||
|
|
||||||
|
|
||||||
class Packet(object):
|
class Packet(object):
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
l = len(data)
|
l = len(data)
|
||||||
@ -153,6 +167,43 @@ class Tester:
|
|||||||
elif data[0] != err:
|
elif data[0] != err:
|
||||||
raise ValueError("Unexpected error: %02x" % data[0])
|
raise ValueError("Unexpected error: %02x" % data[0])
|
||||||
|
|
||||||
|
def testFunc(self, func, test, *args, **kwargs):
|
||||||
|
with Test(test):
|
||||||
|
res = None
|
||||||
|
expectedError = kwargs.get("expectedError", None)
|
||||||
|
otherArgs = kwargs.get("other", {})
|
||||||
|
try:
|
||||||
|
res = func(*args, **otherArgs)
|
||||||
|
if expectedError != CtapError.ERR.SUCCESS:
|
||||||
|
raise RuntimeError("Expected error to occur for test: %s" % test)
|
||||||
|
except CtapError as e:
|
||||||
|
if expectedError is not None:
|
||||||
|
if e.code != expectedError:
|
||||||
|
raise RuntimeError(
|
||||||
|
"Got error code 0x%x, expected %x" % (e.code, expectedError)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(e)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def testReset(self,):
|
||||||
|
print("Resetting Authenticator...")
|
||||||
|
self.ctap.reset()
|
||||||
|
|
||||||
|
def testMC(self, test, *args, **kwargs):
|
||||||
|
return self.testFunc(self.ctap.make_credential, test, *args, **kwargs)
|
||||||
|
|
||||||
|
def testGA(self, test, *args, **kwargs):
|
||||||
|
return self.testFunc(self.ctap.get_assertion, test, *args, **kwargs)
|
||||||
|
|
||||||
|
def testCP(self, test, *args, **kwargs):
|
||||||
|
return self.testFunc(self.ctap.client_pin, test, *args, **kwargs)
|
||||||
|
|
||||||
|
def testPP(self, test, *args, **kwargs):
|
||||||
|
return self.testFunc(
|
||||||
|
self.client.pin_protocol.get_pin_token, test, *args, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
def test_long_ping(self):
|
def test_long_ping(self):
|
||||||
amt = 1000
|
amt = 1000
|
||||||
pingdata = os.urandom(amt)
|
pingdata = os.urandom(amt)
|
||||||
@ -723,6 +774,168 @@ class Tester:
|
|||||||
except CtapError as e:
|
except CtapError as e:
|
||||||
print("Warning, reset failed: ", e)
|
print("Warning, reset failed: ", e)
|
||||||
|
|
||||||
|
def test_extensions(self,):
|
||||||
|
creds = []
|
||||||
|
exclude_list = []
|
||||||
|
rp = {"id": self.host, "name": "ExaRP"}
|
||||||
|
user = {"id": b"usee_od", "name": "AB User"}
|
||||||
|
challenge = "Y2hhbGxlbmdl"
|
||||||
|
pin_protocol = 1
|
||||||
|
key_params = [{"type": "public-key", "alg": ES256.ALGORITHM}]
|
||||||
|
cdh = b"123456789abcdef0123456789abcdef0"
|
||||||
|
|
||||||
|
salt1 = b"\x5a" * 32
|
||||||
|
salt2 = b"\x96" * 32
|
||||||
|
salt3 = b"\x03" * 32
|
||||||
|
|
||||||
|
# self.testReset()
|
||||||
|
|
||||||
|
with Test("Get info has hmac-secret"):
|
||||||
|
info = self.ctap.get_info()
|
||||||
|
assert "hmac-secret" in info.extensions
|
||||||
|
|
||||||
|
reg = self.testMC(
|
||||||
|
"Send MC with hmac-secret ext set to true, expect SUCCESS",
|
||||||
|
cdh,
|
||||||
|
rp,
|
||||||
|
user,
|
||||||
|
key_params,
|
||||||
|
expectedError=CtapError.ERR.SUCCESS,
|
||||||
|
other={"extensions": {"hmac-secret": True}, "options": {"rk": True}},
|
||||||
|
)
|
||||||
|
|
||||||
|
with Test("Check 'hmac-secret' is set to true in auth_data extensions"):
|
||||||
|
assert reg.auth_data.extensions
|
||||||
|
assert "hmac-secret" in reg.auth_data.extensions
|
||||||
|
assert reg.auth_data.extensions["hmac-secret"] == True
|
||||||
|
|
||||||
|
reg = self.testMC(
|
||||||
|
"Send MC with fake extension set to true, expect SUCCESS",
|
||||||
|
cdh,
|
||||||
|
rp,
|
||||||
|
user,
|
||||||
|
key_params,
|
||||||
|
expectedError=CtapError.ERR.SUCCESS,
|
||||||
|
other={"extensions": {"tetris": True}},
|
||||||
|
)
|
||||||
|
|
||||||
|
with Test("Get shared secret"):
|
||||||
|
key_agreement, shared_secret = (
|
||||||
|
self.client.pin_protocol._init_shared_secret()
|
||||||
|
)
|
||||||
|
cipher = Cipher(
|
||||||
|
algorithms.AES(shared_secret),
|
||||||
|
modes.CBC(b"\x00" * 16),
|
||||||
|
default_backend(),
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_salt_params(salts):
|
||||||
|
enc = cipher.encryptor()
|
||||||
|
salt_enc = b""
|
||||||
|
for salt in salts:
|
||||||
|
salt_enc += enc.update(salt)
|
||||||
|
salt_enc += enc.finalize()
|
||||||
|
|
||||||
|
salt_auth = hmac_sha256(shared_secret, salt_enc)[:16]
|
||||||
|
return salt_enc, salt_auth
|
||||||
|
|
||||||
|
for salt_list in ((salt1,), (salt1, salt2)):
|
||||||
|
salt_enc, salt_auth = get_salt_params(salt_list)
|
||||||
|
|
||||||
|
auth = self.testGA(
|
||||||
|
"Send GA request with %d salts hmac-secret, expect success"
|
||||||
|
% len(salt_list),
|
||||||
|
rp["id"],
|
||||||
|
cdh,
|
||||||
|
other={
|
||||||
|
"extensions": {
|
||||||
|
"hmac-secret": {1: key_agreement, 2: salt_enc, 3: salt_auth}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expectedError=CtapError.ERR.SUCCESS,
|
||||||
|
)
|
||||||
|
|
||||||
|
with Test(
|
||||||
|
"Check that hmac-secret is in auth_data extensions and has %d bytes"
|
||||||
|
% (len(salt_list) * 32)
|
||||||
|
):
|
||||||
|
ext = auth.auth_data.extensions
|
||||||
|
assert ext
|
||||||
|
assert "hmac-secret" in ext
|
||||||
|
assert type(ext["hmac-secret"]) == type(b"")
|
||||||
|
assert len(ext["hmac-secret"]) == len(salt_list) * 32
|
||||||
|
|
||||||
|
with Test("Check that shannon_entropy of hmac-secret is good"):
|
||||||
|
ext = auth.auth_data.extensions
|
||||||
|
dec = cipher.decryptor()
|
||||||
|
key = dec.update(ext["hmac-secret"]) + dec.finalize()
|
||||||
|
|
||||||
|
print(shannon_entropy(ext["hmac-secret"]))
|
||||||
|
if len(salt_list) == 1:
|
||||||
|
assert shannon_entropy(ext["hmac-secret"]) > 4.6
|
||||||
|
assert shannon_entropy(key) > 4.6
|
||||||
|
if len(salt_list) == 2:
|
||||||
|
assert shannon_entropy(ext["hmac-secret"]) > 5.4
|
||||||
|
assert shannon_entropy(key) > 5.4
|
||||||
|
|
||||||
|
salt_enc, salt_auth = get_salt_params((salt3,))
|
||||||
|
|
||||||
|
auth = self.testGA(
|
||||||
|
"Send GA request with hmac-secret missing keyAgreement, expect error",
|
||||||
|
rp["id"],
|
||||||
|
cdh,
|
||||||
|
other={"extensions": {"hmac-secret": {2: salt_enc, 3: salt_auth}}},
|
||||||
|
)
|
||||||
|
auth = self.testGA(
|
||||||
|
"Send GA request with hmac-secret missing saltAuth, expect MISSING_PARAMETER",
|
||||||
|
rp["id"],
|
||||||
|
cdh,
|
||||||
|
other={"extensions": {"hmac-secret": {1: key_agreement, 2: salt_enc}}},
|
||||||
|
expectedError=CtapError.ERR.MISSING_PARAMETER,
|
||||||
|
)
|
||||||
|
auth = self.testGA(
|
||||||
|
"Send GA request with hmac-secret missing saltEnc, expect MISSING_PARAMETER",
|
||||||
|
rp["id"],
|
||||||
|
cdh,
|
||||||
|
other={"extensions": {"hmac-secret": {1: key_agreement, 3: salt_auth}}},
|
||||||
|
expectedError=CtapError.ERR.MISSING_PARAMETER,
|
||||||
|
)
|
||||||
|
|
||||||
|
bad_auth = list(salt_auth[:])
|
||||||
|
bad_auth[len(bad_auth) // 2] = bad_auth[len(bad_auth) // 2] ^ 1
|
||||||
|
bad_auth = bytes(bad_auth)
|
||||||
|
|
||||||
|
auth = self.testGA(
|
||||||
|
"Send GA request with hmac-secret containing bad saltAuth, expect EXTENSION_FIRST",
|
||||||
|
rp["id"],
|
||||||
|
cdh,
|
||||||
|
other={
|
||||||
|
"extensions": {
|
||||||
|
"hmac-secret": {1: key_agreement, 2: salt_enc, 3: bad_auth}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expectedError=CtapError.ERR.EXTENSION_FIRST,
|
||||||
|
)
|
||||||
|
|
||||||
|
salt4 = b"\x5a" * 16
|
||||||
|
salt5 = b"\x96" * 64
|
||||||
|
for salt_list in ((salt4,), (salt4, salt5)):
|
||||||
|
salt_enc, salt_auth = get_salt_params(salt_list)
|
||||||
|
|
||||||
|
salt_auth = hmac_sha256(shared_secret, salt_enc)[:16]
|
||||||
|
auth = self.testGA(
|
||||||
|
"Send GA request with incorrect salt length %d, expect INVALID_LENGTH"
|
||||||
|
% len(salt_enc),
|
||||||
|
rp["id"],
|
||||||
|
cdh,
|
||||||
|
other={
|
||||||
|
"extensions": {
|
||||||
|
"hmac-secret": {1: key_agreement, 2: salt_enc, 3: salt_auth}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expectedError=CtapError.ERR.INVALID_LENGTH,
|
||||||
|
)
|
||||||
|
|
||||||
def test_fido2_other(self,):
|
def test_fido2_other(self,):
|
||||||
|
|
||||||
creds = []
|
creds = []
|
||||||
@ -738,46 +951,6 @@ class Tester:
|
|||||||
key_params = [{"type": "public-key", "alg": ES256.ALGORITHM}]
|
key_params = [{"type": "public-key", "alg": ES256.ALGORITHM}]
|
||||||
cdh = b"123456789abcdef0123456789abcdef0"
|
cdh = b"123456789abcdef0123456789abcdef0"
|
||||||
|
|
||||||
def testFunc(func, test, *args, **kwargs):
|
|
||||||
with Test(test):
|
|
||||||
res = None
|
|
||||||
expectedError = kwargs.get("expectedError", None)
|
|
||||||
otherArgs = kwargs.get("other", {})
|
|
||||||
try:
|
|
||||||
res = func(*args, **otherArgs)
|
|
||||||
if expectedError != CtapError.ERR.SUCCESS:
|
|
||||||
raise RuntimeError(
|
|
||||||
"Expected error to occur for test: %s" % test
|
|
||||||
)
|
|
||||||
except CtapError as e:
|
|
||||||
if expectedError is not None:
|
|
||||||
if e.code != expectedError:
|
|
||||||
raise RuntimeError(
|
|
||||||
"Got error code 0x%x, expected %x"
|
|
||||||
% (e.code, expectedError)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
print(e)
|
|
||||||
return res
|
|
||||||
|
|
||||||
def testReset():
|
|
||||||
print("Resetting Authenticator...")
|
|
||||||
self.ctap.reset()
|
|
||||||
|
|
||||||
def testMC(test, *args, **kwargs):
|
|
||||||
return testFunc(self.ctap.make_credential, test, *args, **kwargs)
|
|
||||||
|
|
||||||
def testGA(test, *args, **kwargs):
|
|
||||||
return testFunc(self.ctap.get_assertion, test, *args, **kwargs)
|
|
||||||
|
|
||||||
def testCP(test, *args, **kwargs):
|
|
||||||
return testFunc(self.ctap.client_pin, test, *args, **kwargs)
|
|
||||||
|
|
||||||
def testPP(test, *args, **kwargs):
|
|
||||||
return testFunc(
|
|
||||||
self.client.pin_protocol.get_pin_token, test, *args, **kwargs
|
|
||||||
)
|
|
||||||
|
|
||||||
def reboot():
|
def reboot():
|
||||||
if self.is_sim:
|
if self.is_sim:
|
||||||
print("Sending restart command...")
|
print("Sending restart command...")
|
||||||
@ -788,7 +961,7 @@ class Tester:
|
|||||||
input()
|
input()
|
||||||
self.find_device()
|
self.find_device()
|
||||||
|
|
||||||
testReset()
|
self.testReset()
|
||||||
|
|
||||||
with Test("Get info"):
|
with Test("Get info"):
|
||||||
info = self.ctap.get_info()
|
info = self.ctap.get_info()
|
||||||
@ -804,7 +977,7 @@ class Tester:
|
|||||||
for x in info.options:
|
for x in info.options:
|
||||||
assert info.options[x] in [True, False]
|
assert info.options[x] in [True, False]
|
||||||
|
|
||||||
prev_reg = testMC(
|
prev_reg = self.testMC(
|
||||||
"Send MC request, expect success",
|
"Send MC request, expect success",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -826,7 +999,7 @@ class Tester:
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
prev_auth = testGA(
|
prev_auth = self.testGA(
|
||||||
"Send GA request, expect success",
|
"Send GA request, expect success",
|
||||||
rp["id"],
|
rp["id"],
|
||||||
cdh,
|
cdh,
|
||||||
@ -847,7 +1020,7 @@ class Tester:
|
|||||||
assert prev_auth.user == None
|
assert prev_auth.user == None
|
||||||
assert prev_auth.number_of_credentials == None
|
assert prev_auth.number_of_credentials == None
|
||||||
|
|
||||||
testGA(
|
self.testGA(
|
||||||
"Send GA request with empty allow_list, expect NO_CREDENTIALS",
|
"Send GA request with empty allow_list, expect NO_CREDENTIALS",
|
||||||
rp["id"],
|
rp["id"],
|
||||||
cdh,
|
cdh,
|
||||||
@ -860,7 +1033,7 @@ class Tester:
|
|||||||
badid[len(badid) // 2] = badid[len(badid) // 2] ^ 1
|
badid[len(badid) // 2] = badid[len(badid) // 2] ^ 1
|
||||||
badid = bytes(badid)
|
badid = bytes(badid)
|
||||||
|
|
||||||
testGA(
|
self.testGA(
|
||||||
"Send GA request with corrupt credId in allow_list, expect NO_CREDENTIALS",
|
"Send GA request with corrupt credId in allow_list, expect NO_CREDENTIALS",
|
||||||
rp["id"],
|
rp["id"],
|
||||||
cdh,
|
cdh,
|
||||||
@ -868,7 +1041,7 @@ class Tester:
|
|||||||
expectedError=CtapError.ERR.NO_CREDENTIALS,
|
expectedError=CtapError.ERR.NO_CREDENTIALS,
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with missing clientDataHash, expect error",
|
"Send MC request with missing clientDataHash, expect error",
|
||||||
None,
|
None,
|
||||||
rp,
|
rp,
|
||||||
@ -877,7 +1050,7 @@ class Tester:
|
|||||||
expectedError=CtapError.ERR.MISSING_PARAMETER,
|
expectedError=CtapError.ERR.MISSING_PARAMETER,
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with integer for clientDataHash, expect error",
|
"Send MC request with integer for clientDataHash, expect error",
|
||||||
5,
|
5,
|
||||||
rp,
|
rp,
|
||||||
@ -885,7 +1058,7 @@ class Tester:
|
|||||||
key_params,
|
key_params,
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with missing user, expect error",
|
"Send MC request with missing user, expect error",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -894,7 +1067,7 @@ class Tester:
|
|||||||
expectedError=CtapError.ERR.MISSING_PARAMETER,
|
expectedError=CtapError.ERR.MISSING_PARAMETER,
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with bytearray user, expect error",
|
"Send MC request with bytearray user, expect error",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -902,7 +1075,7 @@ class Tester:
|
|||||||
key_params,
|
key_params,
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with missing RP, expect error",
|
"Send MC request with missing RP, expect error",
|
||||||
cdh,
|
cdh,
|
||||||
None,
|
None,
|
||||||
@ -911,7 +1084,7 @@ class Tester:
|
|||||||
expectedError=CtapError.ERR.MISSING_PARAMETER,
|
expectedError=CtapError.ERR.MISSING_PARAMETER,
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with bytearray RP, expect error",
|
"Send MC request with bytearray RP, expect error",
|
||||||
cdh,
|
cdh,
|
||||||
b"1234abcd",
|
b"1234abcd",
|
||||||
@ -919,7 +1092,7 @@ class Tester:
|
|||||||
key_params,
|
key_params,
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with missing pubKeyCredParams, expect error",
|
"Send MC request with missing pubKeyCredParams, expect error",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -928,7 +1101,7 @@ class Tester:
|
|||||||
expectedError=CtapError.ERR.MISSING_PARAMETER,
|
expectedError=CtapError.ERR.MISSING_PARAMETER,
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with incorrect pubKeyCredParams, expect error",
|
"Send MC request with incorrect pubKeyCredParams, expect error",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -936,7 +1109,7 @@ class Tester:
|
|||||||
b"2356",
|
b"2356",
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with incorrect excludeList, expect error",
|
"Send MC request with incorrect excludeList, expect error",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -945,7 +1118,7 @@ class Tester:
|
|||||||
other={"exclude_list": 8},
|
other={"exclude_list": 8},
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with incorrect extensions, expect error",
|
"Send MC request with incorrect extensions, expect error",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -954,7 +1127,7 @@ class Tester:
|
|||||||
other={"extensions": 8},
|
other={"extensions": 8},
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with incorrect options, expect error",
|
"Send MC request with incorrect options, expect error",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -963,7 +1136,7 @@ class Tester:
|
|||||||
other={"options": 8},
|
other={"options": 8},
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with bad RP.name",
|
"Send MC request with bad RP.name",
|
||||||
cdh,
|
cdh,
|
||||||
{"id": self.host, "name": 8, "icon": "icon"},
|
{"id": self.host, "name": 8, "icon": "icon"},
|
||||||
@ -971,7 +1144,7 @@ class Tester:
|
|||||||
key_params,
|
key_params,
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with bad RP.id",
|
"Send MC request with bad RP.id",
|
||||||
cdh,
|
cdh,
|
||||||
{"id": 8, "name": "name", "icon": "icon"},
|
{"id": 8, "name": "name", "icon": "icon"},
|
||||||
@ -979,7 +1152,7 @@ class Tester:
|
|||||||
key_params,
|
key_params,
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with bad RP.icon",
|
"Send MC request with bad RP.icon",
|
||||||
cdh,
|
cdh,
|
||||||
{"id": self.host, "name": "name", "icon": 8},
|
{"id": self.host, "name": "name", "icon": 8},
|
||||||
@ -987,7 +1160,7 @@ class Tester:
|
|||||||
key_params,
|
key_params,
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with bad user.name",
|
"Send MC request with bad user.name",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -995,7 +1168,7 @@ class Tester:
|
|||||||
key_params,
|
key_params,
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with bad user.id",
|
"Send MC request with bad user.id",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1003,7 +1176,7 @@ class Tester:
|
|||||||
key_params,
|
key_params,
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with bad user.displayName",
|
"Send MC request with bad user.displayName",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1011,7 +1184,7 @@ class Tester:
|
|||||||
key_params,
|
key_params,
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with bad user.icon",
|
"Send MC request with bad user.icon",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1019,7 +1192,7 @@ class Tester:
|
|||||||
key_params,
|
key_params,
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with non-map pubKeyCredParams item",
|
"Send MC request with non-map pubKeyCredParams item",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1027,7 +1200,7 @@ class Tester:
|
|||||||
["wrong"],
|
["wrong"],
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with pubKeyCredParams item missing type field",
|
"Send MC request with pubKeyCredParams item missing type field",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1036,7 +1209,7 @@ class Tester:
|
|||||||
expectedError=CtapError.ERR.MISSING_PARAMETER,
|
expectedError=CtapError.ERR.MISSING_PARAMETER,
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with pubKeyCredParams item with bad type field",
|
"Send MC request with pubKeyCredParams item with bad type field",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1044,7 +1217,7 @@ class Tester:
|
|||||||
[{"alg": ES256.ALGORITHM, "type": b"public-key"}],
|
[{"alg": ES256.ALGORITHM, "type": b"public-key"}],
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with pubKeyCredParams item missing alg",
|
"Send MC request with pubKeyCredParams item missing alg",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1053,7 +1226,7 @@ class Tester:
|
|||||||
expectedError=CtapError.ERR.MISSING_PARAMETER,
|
expectedError=CtapError.ERR.MISSING_PARAMETER,
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with pubKeyCredParams item with bad alg",
|
"Send MC request with pubKeyCredParams item with bad alg",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1061,7 +1234,7 @@ class Tester:
|
|||||||
[{"alg": "7", "type": "public-key"}],
|
[{"alg": "7", "type": "public-key"}],
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with pubKeyCredParams item with bogus alg, expect UNSUPPORTED_ALGORITHM",
|
"Send MC request with pubKeyCredParams item with bogus alg, expect UNSUPPORTED_ALGORITHM",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1070,7 +1243,7 @@ class Tester:
|
|||||||
expectedError=CtapError.ERR.UNSUPPORTED_ALGORITHM,
|
expectedError=CtapError.ERR.UNSUPPORTED_ALGORITHM,
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with pubKeyCredParams item with bogus type, expect UNSUPPORTED_ALGORITHM",
|
"Send MC request with pubKeyCredParams item with bogus type, expect UNSUPPORTED_ALGORITHM",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1079,7 +1252,7 @@ class Tester:
|
|||||||
expectedError=CtapError.ERR.UNSUPPORTED_ALGORITHM,
|
expectedError=CtapError.ERR.UNSUPPORTED_ALGORITHM,
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with excludeList item with bogus type, expect SUCCESS",
|
"Send MC request with excludeList item with bogus type, expect SUCCESS",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1089,7 +1262,7 @@ class Tester:
|
|||||||
other={"exclude_list": [{"id": b"1234", "type": "rot13"}]},
|
other={"exclude_list": [{"id": b"1234", "type": "rot13"}]},
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with excludeList with bad item, expect error",
|
"Send MC request with excludeList with bad item, expect error",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1098,7 +1271,7 @@ class Tester:
|
|||||||
other={"exclude_list": ["1234"]},
|
other={"exclude_list": ["1234"]},
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with excludeList with item missing type field, expect error",
|
"Send MC request with excludeList with item missing type field, expect error",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1107,7 +1280,7 @@ class Tester:
|
|||||||
other={"exclude_list": [{"id": b"1234"}]},
|
other={"exclude_list": [{"id": b"1234"}]},
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with excludeList with item missing id field, expect error",
|
"Send MC request with excludeList with item missing id field, expect error",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1116,7 +1289,7 @@ class Tester:
|
|||||||
other={"exclude_list": [{"type": "public-key"}]},
|
other={"exclude_list": [{"type": "public-key"}]},
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with excludeList with item containing bad id field, expect error",
|
"Send MC request with excludeList with item containing bad id field, expect error",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1125,7 +1298,7 @@ class Tester:
|
|||||||
other={"exclude_list": [{"type": "public-key", "id": "1234"}]},
|
other={"exclude_list": [{"type": "public-key", "id": "1234"}]},
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with excludeList with item containing bad type field, expect error",
|
"Send MC request with excludeList with item containing bad type field, expect error",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1134,7 +1307,7 @@ class Tester:
|
|||||||
other={"exclude_list": [{"type": b"public-key", "id": b"1234"}]},
|
other={"exclude_list": [{"type": b"public-key", "id": b"1234"}]},
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with excludeList containing previous registration, expect CREDENTIAL_EXCLUDED",
|
"Send MC request with excludeList containing previous registration, expect CREDENTIAL_EXCLUDED",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1151,7 +1324,7 @@ class Tester:
|
|||||||
expectedError=CtapError.ERR.CREDENTIAL_EXCLUDED,
|
expectedError=CtapError.ERR.CREDENTIAL_EXCLUDED,
|
||||||
)
|
)
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with unknown option, expect SUCCESS",
|
"Send MC request with unknown option, expect SUCCESS",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1163,7 +1336,7 @@ class Tester:
|
|||||||
|
|
||||||
if "uv" in info.options:
|
if "uv" in info.options:
|
||||||
if info.options["uv"]:
|
if info.options["uv"]:
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with uv set to true, expect SUCCESS",
|
"Send MC request with uv set to true, expect SUCCESS",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1174,7 +1347,7 @@ class Tester:
|
|||||||
)
|
)
|
||||||
if "up" in info.options:
|
if "up" in info.options:
|
||||||
if info.options["up"]:
|
if info.options["up"]:
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with up set to true, expect INVALID_OPTION",
|
"Send MC request with up set to true, expect INVALID_OPTION",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1184,7 +1357,7 @@ class Tester:
|
|||||||
expectedError=CtapError.ERR.INVALID_OPTION,
|
expectedError=CtapError.ERR.INVALID_OPTION,
|
||||||
)
|
)
|
||||||
|
|
||||||
testGA(
|
self.testGA(
|
||||||
"Send GA request with missing RPID, expect MISSING_PARAMETER",
|
"Send GA request with missing RPID, expect MISSING_PARAMETER",
|
||||||
None,
|
None,
|
||||||
cdh,
|
cdh,
|
||||||
@ -1192,14 +1365,14 @@ class Tester:
|
|||||||
expectedError=CtapError.ERR.MISSING_PARAMETER,
|
expectedError=CtapError.ERR.MISSING_PARAMETER,
|
||||||
)
|
)
|
||||||
|
|
||||||
testGA(
|
self.testGA(
|
||||||
"Send GA request with bad RPID, expect error",
|
"Send GA request with bad RPID, expect error",
|
||||||
{"type": "wrong"},
|
{"type": "wrong"},
|
||||||
cdh,
|
cdh,
|
||||||
allow_list,
|
allow_list,
|
||||||
)
|
)
|
||||||
|
|
||||||
testGA(
|
self.testGA(
|
||||||
"Send GA request with missing clientDataHash, expect MISSING_PARAMETER",
|
"Send GA request with missing clientDataHash, expect MISSING_PARAMETER",
|
||||||
rp["id"],
|
rp["id"],
|
||||||
None,
|
None,
|
||||||
@ -1207,28 +1380,28 @@ class Tester:
|
|||||||
expectedError=CtapError.ERR.MISSING_PARAMETER,
|
expectedError=CtapError.ERR.MISSING_PARAMETER,
|
||||||
)
|
)
|
||||||
|
|
||||||
testGA(
|
self.testGA(
|
||||||
"Send GA request with bad clientDataHash, expect error",
|
"Send GA request with bad clientDataHash, expect error",
|
||||||
rp["id"],
|
rp["id"],
|
||||||
{"type": "wrong"},
|
{"type": "wrong"},
|
||||||
allow_list,
|
allow_list,
|
||||||
)
|
)
|
||||||
|
|
||||||
testGA(
|
self.testGA(
|
||||||
"Send GA request with bad allow_list, expect error",
|
"Send GA request with bad allow_list, expect error",
|
||||||
rp["id"],
|
rp["id"],
|
||||||
cdh,
|
cdh,
|
||||||
{"type": "wrong"},
|
{"type": "wrong"},
|
||||||
)
|
)
|
||||||
|
|
||||||
testGA(
|
self.testGA(
|
||||||
"Send GA request with bad item in allow_list, expect error",
|
"Send GA request with bad item in allow_list, expect error",
|
||||||
rp["id"],
|
rp["id"],
|
||||||
cdh,
|
cdh,
|
||||||
allow_list + ["wrong"],
|
allow_list + ["wrong"],
|
||||||
)
|
)
|
||||||
|
|
||||||
testGA(
|
self.testGA(
|
||||||
"Send GA request with unknown option, expect SUCCESS",
|
"Send GA request with unknown option, expect SUCCESS",
|
||||||
rp["id"],
|
rp["id"],
|
||||||
cdh,
|
cdh,
|
||||||
@ -1239,7 +1412,7 @@ class Tester:
|
|||||||
|
|
||||||
if "uv" in info.options:
|
if "uv" in info.options:
|
||||||
if info.options["uv"]:
|
if info.options["uv"]:
|
||||||
res = testGA(
|
res = self.testGA(
|
||||||
"Send GA request with uv set to true, expect SUCCESS",
|
"Send GA request with uv set to true, expect SUCCESS",
|
||||||
rp["id"],
|
rp["id"],
|
||||||
cdh,
|
cdh,
|
||||||
@ -1251,7 +1424,7 @@ class Tester:
|
|||||||
assert res.auth_data.flags & (1 << 2)
|
assert res.auth_data.flags & (1 << 2)
|
||||||
if "up" in info.options:
|
if "up" in info.options:
|
||||||
if info.options["up"]:
|
if info.options["up"]:
|
||||||
res = testGA(
|
res = self.testGA(
|
||||||
"Send GA request with up set to true, expect SUCCESS",
|
"Send GA request with up set to true, expect SUCCESS",
|
||||||
rp["id"],
|
rp["id"],
|
||||||
cdh,
|
cdh,
|
||||||
@ -1262,7 +1435,7 @@ class Tester:
|
|||||||
with Test("Check that UP flag is set in response"):
|
with Test("Check that UP flag is set in response"):
|
||||||
assert res.auth_data.flags & 1
|
assert res.auth_data.flags & 1
|
||||||
|
|
||||||
testGA(
|
self.testGA(
|
||||||
"Send GA request with bogus type item in allow_list, expect SUCCESS",
|
"Send GA request with bogus type item in allow_list, expect SUCCESS",
|
||||||
rp["id"],
|
rp["id"],
|
||||||
cdh,
|
cdh,
|
||||||
@ -1270,38 +1443,38 @@ class Tester:
|
|||||||
expectedError=CtapError.ERR.SUCCESS,
|
expectedError=CtapError.ERR.SUCCESS,
|
||||||
)
|
)
|
||||||
|
|
||||||
testGA(
|
self.testGA(
|
||||||
"Send GA request with item missing type field in allow_list, expect error",
|
"Send GA request with item missing type field in allow_list, expect error",
|
||||||
rp["id"],
|
rp["id"],
|
||||||
cdh,
|
cdh,
|
||||||
allow_list + [{"id": b"1234"}],
|
allow_list + [{"id": b"1234"}],
|
||||||
)
|
)
|
||||||
|
|
||||||
testGA(
|
self.testGA(
|
||||||
"Send GA request with item containing bad type field in allow_list, expect error",
|
"Send GA request with item containing bad type field in allow_list, expect error",
|
||||||
rp["id"],
|
rp["id"],
|
||||||
cdh,
|
cdh,
|
||||||
allow_list + [{"type": b"public-key", "id": b"1234"}],
|
allow_list + [{"type": b"public-key", "id": b"1234"}],
|
||||||
)
|
)
|
||||||
|
|
||||||
testGA(
|
self.testGA(
|
||||||
"Send GA request with item containing bad id in allow_list, expect error",
|
"Send GA request with item containing bad id in allow_list, expect error",
|
||||||
rp["id"],
|
rp["id"],
|
||||||
cdh,
|
cdh,
|
||||||
allow_list + [{"type": b"public-key", "id": 42}],
|
allow_list + [{"type": b"public-key", "id": 42}],
|
||||||
)
|
)
|
||||||
|
|
||||||
testGA(
|
self.testGA(
|
||||||
"Send GA request with item missing id in allow_list, expect error",
|
"Send GA request with item missing id in allow_list, expect error",
|
||||||
rp["id"],
|
rp["id"],
|
||||||
cdh,
|
cdh,
|
||||||
allow_list + [{"type": b"public-key"}],
|
allow_list + [{"type": b"public-key"}],
|
||||||
)
|
)
|
||||||
|
|
||||||
testReset()
|
self.testReset()
|
||||||
|
|
||||||
def testRk(pin_code=None):
|
def testRk(pin_code=None):
|
||||||
testGA(
|
self.testGA(
|
||||||
"Send GA request with reset auth, expect NO_CREDENTIALS",
|
"Send GA request with reset auth, expect NO_CREDENTIALS",
|
||||||
rp["id"],
|
rp["id"],
|
||||||
cdh,
|
cdh,
|
||||||
@ -1316,7 +1489,7 @@ class Tester:
|
|||||||
pin_token = self.client.pin_protocol.get_pin_token(pin_code)
|
pin_token = self.client.pin_protocol.get_pin_token(pin_code)
|
||||||
pin_auth = hmac_sha256(pin_token, cdh)[:16]
|
pin_auth = hmac_sha256(pin_token, cdh)[:16]
|
||||||
|
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with rk option set to true, expect SUCCESS",
|
"Send MC request with rk option set to true, expect SUCCESS",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1331,7 +1504,7 @@ class Tester:
|
|||||||
options["uv"] = False
|
options["uv"] = False
|
||||||
|
|
||||||
for i, x in enumerate([user1, user2, user3]):
|
for i, x in enumerate([user1, user2, user3]):
|
||||||
testMC(
|
self.testMC(
|
||||||
"Send MC request with rk option set to true, expect SUCCESS %d/3"
|
"Send MC request with rk option set to true, expect SUCCESS %d/3"
|
||||||
% (i + 1),
|
% (i + 1),
|
||||||
cdh,
|
cdh,
|
||||||
@ -1342,7 +1515,7 @@ class Tester:
|
|||||||
expectedError=CtapError.ERR.SUCCESS,
|
expectedError=CtapError.ERR.SUCCESS,
|
||||||
)
|
)
|
||||||
|
|
||||||
auth1 = testGA(
|
auth1 = self.testGA(
|
||||||
"Send GA request with no allow_list, expect SUCCESS",
|
"Send GA request with no allow_list, expect SUCCESS",
|
||||||
rp2["id"],
|
rp2["id"],
|
||||||
cdh,
|
cdh,
|
||||||
@ -1383,7 +1556,7 @@ class Tester:
|
|||||||
testRk("1234567890")
|
testRk("1234567890")
|
||||||
|
|
||||||
# PinProtocolV1
|
# PinProtocolV1
|
||||||
res = testCP(
|
res = self.testCP(
|
||||||
"Test getKeyAgreement, expect SUCCESS",
|
"Test getKeyAgreement, expect SUCCESS",
|
||||||
pin_protocol,
|
pin_protocol,
|
||||||
PinProtocolV1.CMD.GET_KEY_AGREEMENT,
|
PinProtocolV1.CMD.GET_KEY_AGREEMENT,
|
||||||
@ -1405,7 +1578,7 @@ class Tester:
|
|||||||
pin_token = self.client.pin_protocol.get_pin_token(pin2)
|
pin_token = self.client.pin_protocol.get_pin_token(pin2)
|
||||||
pin_auth = hmac_sha256(pin_token, cdh)[:16]
|
pin_auth = hmac_sha256(pin_token, cdh)[:16]
|
||||||
|
|
||||||
res_mc = testMC(
|
res_mc = self.testMC(
|
||||||
"Send MC request with new pin auth",
|
"Send MC request with new pin auth",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1418,7 +1591,7 @@ class Tester:
|
|||||||
with Test("Check UV flag is set"):
|
with Test("Check UV flag is set"):
|
||||||
assert res_mc.auth_data.flags & (1 << 2)
|
assert res_mc.auth_data.flags & (1 << 2)
|
||||||
|
|
||||||
res_ga = testGA(
|
res_ga = self.testGA(
|
||||||
"Send GA request with no allow_list, expect SUCCESS",
|
"Send GA request with no allow_list, expect SUCCESS",
|
||||||
rp["id"],
|
rp["id"],
|
||||||
cdh,
|
cdh,
|
||||||
@ -1435,12 +1608,12 @@ class Tester:
|
|||||||
with Test("Check UV flag is set"):
|
with Test("Check UV flag is set"):
|
||||||
assert res_ga.auth_data.flags & (1 << 2)
|
assert res_ga.auth_data.flags & (1 << 2)
|
||||||
|
|
||||||
testReset()
|
self.testReset()
|
||||||
|
|
||||||
with Test("Setting pin code, expect SUCCESS"):
|
with Test("Setting pin code, expect SUCCESS"):
|
||||||
self.client.pin_protocol.set_pin(pin1)
|
self.client.pin_protocol.set_pin(pin1)
|
||||||
|
|
||||||
testReset()
|
self.testReset()
|
||||||
|
|
||||||
# print("Setting pin code <4 bytes, expect POLICY_VIOLATION ")
|
# print("Setting pin code <4 bytes, expect POLICY_VIOLATION ")
|
||||||
# try:
|
# try:
|
||||||
@ -1486,7 +1659,7 @@ class Tester:
|
|||||||
except CtapError as e:
|
except CtapError as e:
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
res_mc = testMC(
|
res_mc = self.testMC(
|
||||||
"Send MC request with no pin_auth, expect PIN_REQUIRED",
|
"Send MC request with no pin_auth, expect PIN_REQUIRED",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1495,14 +1668,14 @@ class Tester:
|
|||||||
expectedError=CtapError.ERR.PIN_REQUIRED,
|
expectedError=CtapError.ERR.PIN_REQUIRED,
|
||||||
)
|
)
|
||||||
|
|
||||||
res_mc = testGA(
|
res_mc = self.testGA(
|
||||||
"Send GA request with no pin_auth, expect PIN_REQUIRED",
|
"Send GA request with no pin_auth, expect PIN_REQUIRED",
|
||||||
rp["id"],
|
rp["id"],
|
||||||
cdh,
|
cdh,
|
||||||
expectedError=CtapError.ERR.PIN_REQUIRED,
|
expectedError=CtapError.ERR.PIN_REQUIRED,
|
||||||
)
|
)
|
||||||
|
|
||||||
res = testCP(
|
res = self.testCP(
|
||||||
"Test getRetries, expect SUCCESS",
|
"Test getRetries, expect SUCCESS",
|
||||||
pin_protocol,
|
pin_protocol,
|
||||||
PinProtocolV1.CMD.GET_RETRIES,
|
PinProtocolV1.CMD.GET_RETRIES,
|
||||||
@ -1520,7 +1693,7 @@ class Tester:
|
|||||||
pin_wrong = "".join(pin_wrong)
|
pin_wrong = "".join(pin_wrong)
|
||||||
|
|
||||||
for i in range(1, 3):
|
for i in range(1, 3):
|
||||||
testPP(
|
self.testPP(
|
||||||
"Get pin_token with wrong pin code, expect PIN_INVALID (%d/2)" % i,
|
"Get pin_token with wrong pin code, expect PIN_INVALID (%d/2)" % i,
|
||||||
pin_wrong,
|
pin_wrong,
|
||||||
expectedError=CtapError.ERR.PIN_INVALID,
|
expectedError=CtapError.ERR.PIN_INVALID,
|
||||||
@ -1531,7 +1704,7 @@ class Tester:
|
|||||||
print("Pass")
|
print("Pass")
|
||||||
|
|
||||||
for i in range(1, 3):
|
for i in range(1, 3):
|
||||||
testPP(
|
self.testPP(
|
||||||
"Get pin_token with wrong pin code, expect PIN_AUTH_BLOCKED %d/2" % i,
|
"Get pin_token with wrong pin code, expect PIN_AUTH_BLOCKED %d/2" % i,
|
||||||
pin_wrong,
|
pin_wrong,
|
||||||
expectedError=CtapError.ERR.PIN_AUTH_BLOCKED,
|
expectedError=CtapError.ERR.PIN_AUTH_BLOCKED,
|
||||||
@ -1543,7 +1716,7 @@ class Tester:
|
|||||||
pin_token = self.client.pin_protocol.get_pin_token(pin1)
|
pin_token = self.client.pin_protocol.get_pin_token(pin1)
|
||||||
pin_auth = hmac_sha256(pin_token, cdh)[:16]
|
pin_auth = hmac_sha256(pin_token, cdh)[:16]
|
||||||
|
|
||||||
res_mc = testMC(
|
res_mc = self.testMC(
|
||||||
"Send MC request with correct pin_auth",
|
"Send MC request with correct pin_auth",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1563,7 +1736,7 @@ class Tester:
|
|||||||
err = CtapError.ERR.PIN_AUTH_BLOCKED
|
err = CtapError.ERR.PIN_AUTH_BLOCKED
|
||||||
elif i >= 9:
|
elif i >= 9:
|
||||||
err = CtapError.ERR.PIN_BLOCKED
|
err = CtapError.ERR.PIN_BLOCKED
|
||||||
testPP(
|
self.testPP(
|
||||||
"Lock out authentictor and check correct error codes %d/9" % i,
|
"Lock out authentictor and check correct error codes %d/9" % i,
|
||||||
pin_wrong,
|
pin_wrong,
|
||||||
expectedError=err,
|
expectedError=err,
|
||||||
@ -1580,7 +1753,7 @@ class Tester:
|
|||||||
if err == CtapError.ERR.PIN_AUTH_BLOCKED:
|
if err == CtapError.ERR.PIN_AUTH_BLOCKED:
|
||||||
reboot()
|
reboot()
|
||||||
|
|
||||||
res_mc = testMC(
|
res_mc = self.testMC(
|
||||||
"Send MC request with correct pin_auth, expect PIN_BLOCKED",
|
"Send MC request with correct pin_auth, expect PIN_BLOCKED",
|
||||||
cdh,
|
cdh,
|
||||||
rp,
|
rp,
|
||||||
@ -1592,13 +1765,13 @@ class Tester:
|
|||||||
|
|
||||||
reboot()
|
reboot()
|
||||||
|
|
||||||
testPP(
|
self.testPP(
|
||||||
"Get pin_token with correct pin code, expect PIN_BLOCKED",
|
"Get pin_token with correct pin code, expect PIN_BLOCKED",
|
||||||
pin1,
|
pin1,
|
||||||
expectedError=CtapError.ERR.PIN_BLOCKED,
|
expectedError=CtapError.ERR.PIN_BLOCKED,
|
||||||
)
|
)
|
||||||
|
|
||||||
testReset()
|
self.testReset()
|
||||||
|
|
||||||
print("Done")
|
print("Done")
|
||||||
|
|
||||||
@ -1690,14 +1863,9 @@ class Tester:
|
|||||||
entropy = b""
|
entropy = b""
|
||||||
while len(entropy) < total:
|
while len(entropy) < total:
|
||||||
entropy += sc.get_rng()
|
entropy += sc.get_rng()
|
||||||
total = len(entropy)
|
|
||||||
|
|
||||||
with Test("Test entropy is close to perfect"):
|
with Test("Test entropy is close to perfect"):
|
||||||
sum = 0.0
|
sum = shannon_entropy(entropy)
|
||||||
for x in range(0, 256):
|
|
||||||
freq = entropy.count(x)
|
|
||||||
p = freq / total
|
|
||||||
sum -= p * math.log2(p)
|
|
||||||
assert sum > 7.98
|
assert sum > 7.98
|
||||||
print("Entropy is %.5f bits per byte." % sum)
|
print("Entropy is %.5f bits per byte." % sum)
|
||||||
|
|
||||||
@ -1710,6 +1878,11 @@ class Tester:
|
|||||||
except ApduError:
|
except ApduError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
sc.exchange = sc.exchange_fido2
|
||||||
|
with Test("Test Solo version and random commands with fido2 layer"):
|
||||||
|
assert len(sc.solo_version()) == 3
|
||||||
|
sc.get_rng()
|
||||||
|
|
||||||
def test_bootloader(self,):
|
def test_bootloader(self,):
|
||||||
sc = SoloClient()
|
sc = SoloClient()
|
||||||
sc.find_device(self.dev)
|
sc.find_device(self.dev)
|
||||||
@ -1831,8 +2004,10 @@ def test_find_brute_force():
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
tests = ("solo", "u2f", "fido2", "fido2-ext", "rk", "hid", "ping", "bootloader")
|
||||||
|
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
print("Usage: %s [sim] <[u2f]|[fido2]|[rk]|[hid]|[ping]>")
|
print(f"Usage: {sys.argv[0]} [sim] <{'|'.join(sorted(tests))}>")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
t = Tester()
|
t = Tester()
|
||||||
@ -1856,6 +2031,9 @@ if __name__ == "__main__":
|
|||||||
t.test_fido2()
|
t.test_fido2()
|
||||||
t.test_fido2_other()
|
t.test_fido2_other()
|
||||||
|
|
||||||
|
if "fido2-ext" in sys.argv:
|
||||||
|
t.test_extensions()
|
||||||
|
|
||||||
if "rk" in sys.argv:
|
if "rk" in sys.argv:
|
||||||
t.test_rk()
|
t.test_rk()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user