Merge pull request #149 from solokeys/hmac-secret

Hmac-secret
This commit is contained in:
Nicolas Stalder 2019-03-22 22:05:41 +01:00 committed by GitHub
commit e21172fff8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 741 additions and 243 deletions

View File

@ -33,7 +33,6 @@ static int8_t PIN_BOOT_ATTEMPTS_LEFT = PIN_BOOT_ATTEMPTS;
AuthenticatorState STATE;
static void ctap_reset_key_agreement();
static struct {
@ -69,6 +68,8 @@ uint8_t verify_pin_auth(uint8_t * pinAuth, uint8_t * clientDataHash)
}
uint8_t ctap_get_info(CborEncoder * encoder)
{
int ret;
@ -77,16 +78,14 @@ uint8_t ctap_get_info(CborEncoder * encoder)
CborEncoder options;
CborEncoder pins;
const int number_of_versions = 2;
ret = cbor_encoder_create_map(encoder, &map, 5);
ret = cbor_encoder_create_map(encoder, &map, 6);
check_ret(ret);
{
ret = cbor_encode_uint(&map, RESP_versions); // versions key
check_ret(ret);
{
ret = cbor_encoder_create_array(&map, &array, number_of_versions);
ret = cbor_encoder_create_array(&map, &array, 2);
check_ret(ret);
{
ret = cbor_encode_text_stringz(&array, "U2F_V2");
@ -98,6 +97,19 @@ uint8_t ctap_get_info(CborEncoder * encoder)
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);
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);
}
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;
int auth_data_sz, ret;
unsigned int auth_data_sz = sizeof(CTAP_authDataHeader);
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);
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));
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);
if (credtype != 0)
if (credInfo != NULL)
{
// add attestedCredentialData
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);
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);
// resident key
if (store)
if (credInfo->rk)
{
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 i;
@ -410,29 +526,19 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
}
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);
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);
}
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;
}
@ -559,7 +665,7 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
CTAP_makeCredential MC;
int ret;
unsigned int i;
uint8_t auth_data_buf[300];
uint8_t auth_data_buf[310];
CTAP_credentialDescriptor * excl_cred = (CTAP_credentialDescriptor *) auth_data_buf;
uint8_t * sigbuf = auth_data_buf + 32;
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;
ret = cbor_encoder_create_map(encoder, &map, 3);
check_ret(ret);
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.rk);
uint32_t auth_data_sz = sizeof(auth_data_buf);
ret = ctap_make_auth_data(&MC.rp, &map, auth_data_buf, &auth_data_sz,
&MC.credInfo);
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();
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);
@ -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.authData, head, sizeof(CTAP_authDataHeader));
memmove(getAssertionState.creds, creds, sizeof(CTAP_credentialDescriptor) * (count));
}
getAssertionState.count = count;
printf1(TAG_GA,"saved %d credentials\n",count);
@ -916,7 +1042,6 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
CborEncoder map;
CTAP_authDataHeader authData;
memmove(&authData, &getAssertionState.authData, sizeof(CTAP_authDataHeader));
// CTAP_authDataHeader * authData = &getAssertionState.authData;
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);
}
check_ret(ret);
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);
}
// if only one account for this RP, null out the user details
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)
{
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);
if (ret != 0)
@ -1015,47 +1142,15 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
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);
check_ret(ret);
#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(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
if (validCredCount == 0)
{
printf2(TAG_ERR,"Error, no authentic credential\n");
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];
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);
check_retr(ret);
@ -1406,7 +1542,6 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
pkt_raw++;
length--;
uint8_t * buf = resp->data;
cbor_encoder_init(&encoder, buf, resp->data_size, 0);

View File

@ -54,6 +54,13 @@
#define CP_getKeyAgreement 0x07
#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_extensions 0x2
#define RESP_aaguid 0x3
@ -142,9 +149,13 @@ struct Credential {
CredentialId id;
CTAP_userEntity user;
};
typedef struct Credential CTAP_residentKey;
typedef struct
{
uint8_t type;
struct Credential credential;
} CTAP_credentialDescriptor;
typedef struct
{
@ -181,34 +192,62 @@ struct rpId
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
{
uint32_t paramsParsed;
uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
struct rpId rp;
CTAP_userEntity user;
uint8_t publicKeyCredentialType;
int32_t COSEAlgorithmIdentifier;
CTAP_credInfo credInfo;
CborValue excludeList;
size_t excludeListSize;
uint8_t rk;
uint8_t uv;
uint8_t up;
uint8_t pinAuth[16];
uint8_t pinAuthPresent;
int pinProtocol;
CTAP_extensions extensions;
} CTAP_makeCredential;
typedef struct
{
uint8_t type;
struct Credential credential;
} CTAP_credentialDescriptor;
typedef struct
{
@ -230,22 +269,16 @@ typedef struct
CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE];
uint8_t allowListPresent;
CTAP_extensions extensions;
} CTAP_getAssertion;
typedef struct
{
int pinProtocol;
int subCommand;
struct
{
struct{
uint8_t x[32];
uint8_t y[32];
} pubkey;
int kty;
int crv;
} keyAgreement;
COSE_key keyAgreement;
uint8_t keyAgreementPresent;
uint8_t pinAuth[16];
uint8_t pinAuthPresent;

View File

@ -128,14 +128,14 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
}
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)
{
printf2(TAG_ERR,"Error, USER_ID is too large\n");
return CTAP2_ERR_LIMIT_EXCEEDED;
}
MC->user.id_size = sz;
printf1(TAG_GREEN,"parsed id_size: %d\r\n", MC->user.id_size);
MC->credInfo.user.id_size = sz;
printf1(TAG_GREEN,"parsed id_size: %d\r\n", MC->credInfo.user.id_size);
check_ret(ret);
}
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;
}
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)
{ // Just truncate the name it's okay
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)
{
@ -161,12 +161,12 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
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)
{ // Just truncate the name it's okay
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)
{
@ -176,12 +176,12 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
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)
{ // Just truncate the name it's okay
check_ret(ret);
}
MC->user.icon[ICON_LIMIT - 1] = 0;
MC->credInfo.user.icon[ICON_LIMIT - 1] = 0;
}
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)
{
MC->publicKeyCredentialType = cred_type;
MC->COSEAlgorithmIdentifier = alg_type;
MC->credInfo.publicKeyCredentialType = cred_type;
MC->credInfo.COSEAlgorithmIdentifier = alg_type;
MC->paramsParsed |= PARAM_pubKeyCredParams;
return 0;
}
@ -556,6 +556,154 @@ uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8_t * up)
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)
{
int ret;
@ -631,8 +779,8 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
ret = parse_user(MC, &map);
printf1(TAG_MC," ID: "); dump_hex1(TAG_MC, MC->user.id, MC->user.id_size);
printf1(TAG_MC," name: %s\n", MC->user.name);
printf1(TAG_MC," ID: "); dump_hex1(TAG_MC, MC->credInfo.user.id, MC->credInfo.user.id_size);
printf1(TAG_MC," name: %s\n", MC->credInfo.user.name);
break;
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);
printf1(TAG_MC," cred_type: 0x%02x\n", MC->publicKeyCredentialType);
printf1(TAG_MC," alg_type: %d\n", MC->COSEAlgorithmIdentifier);
printf1(TAG_MC," cred_type: 0x%02x\n", MC->credInfo.publicKeyCredentialType);
printf1(TAG_MC," alg_type: %d\n", MC->credInfo.COSEAlgorithmIdentifier);
break;
case MC_excludeList:
@ -665,11 +813,13 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
{
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
ret = ctap_parse_extensions(&map, &MC->extensions);
check_retr(ret);
break;
case MC_options:
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);
break;
case MC_pinAuth:
@ -886,6 +1036,8 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
break;
case GA_extensions:
printf1(TAG_GA,"GA_extensions\n");
ret = ctap_parse_extensions(&map, &GA->extensions);
check_retr(ret);
break;
case GA_options:
@ -940,15 +1092,15 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
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;
size_t map_length;
int ret,key;
unsigned int i;
int xkey = 0,ykey = 0;
*kty = 0;
*crv = 0;
cose->kty = 0;
cose->crv = 0;
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");
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);
}
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");
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);
}
else
@ -1011,14 +1163,14 @@ uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int
break;
case COSE_KEY_LABEL_X:
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);
xkey = 1;
break;
case COSE_KEY_LABEL_Y:
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);
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);
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;
}
@ -1110,7 +1262,7 @@ uint8_t ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length
break;
case CP_keyAgreement:
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);
CP->keyAgreementPresent = 1;
break;

View File

@ -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_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);

View File

@ -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 \
-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
ECC_CFLAGS = $(CFLAGS) -DuECC_PLATFORM=5 -DuECC_OPTIMIZATION_LEVEL=4 -DuECC_SQUARE_FUNC=1 -DuECC_SUPPORT_COMPRESSED_POINT=0

View File

@ -39,7 +39,7 @@ int _write (int fd, const void *buf, unsigned long int len)
// logbuflen += len;
// Send out USB serial
CDC_Transmit_FS(buf, len);
CDC_Transmit_FS(data, len);
// if (res == USBD_OK)
// logbuflen = 0;
#endif

View File

@ -25,6 +25,9 @@ from fido2.ctap2 import ES256, PinProtocolV1
from fido2.utils import Timeout, sha256, hmac_sha256
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.client import SoloClient
@ -44,6 +47,17 @@ def VerifyAttestation(attest, data):
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):
def __init__(self, data):
l = len(data)
@ -153,6 +167,43 @@ class Tester:
elif data[0] != err:
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):
amt = 1000
pingdata = os.urandom(amt)
@ -723,6 +774,168 @@ class Tester:
except CtapError as 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,):
creds = []
@ -738,46 +951,6 @@ class Tester:
key_params = [{"type": "public-key", "alg": ES256.ALGORITHM}]
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():
if self.is_sim:
print("Sending restart command...")
@ -788,7 +961,7 @@ class Tester:
input()
self.find_device()
testReset()
self.testReset()
with Test("Get info"):
info = self.ctap.get_info()
@ -804,7 +977,7 @@ class Tester:
for x in info.options:
assert info.options[x] in [True, False]
prev_reg = testMC(
prev_reg = self.testMC(
"Send MC request, expect success",
cdh,
rp,
@ -826,7 +999,7 @@ class Tester:
}
]
prev_auth = testGA(
prev_auth = self.testGA(
"Send GA request, expect success",
rp["id"],
cdh,
@ -847,7 +1020,7 @@ class Tester:
assert prev_auth.user == None
assert prev_auth.number_of_credentials == None
testGA(
self.testGA(
"Send GA request with empty allow_list, expect NO_CREDENTIALS",
rp["id"],
cdh,
@ -860,7 +1033,7 @@ class Tester:
badid[len(badid) // 2] = badid[len(badid) // 2] ^ 1
badid = bytes(badid)
testGA(
self.testGA(
"Send GA request with corrupt credId in allow_list, expect NO_CREDENTIALS",
rp["id"],
cdh,
@ -868,7 +1041,7 @@ class Tester:
expectedError=CtapError.ERR.NO_CREDENTIALS,
)
testMC(
self.testMC(
"Send MC request with missing clientDataHash, expect error",
None,
rp,
@ -877,7 +1050,7 @@ class Tester:
expectedError=CtapError.ERR.MISSING_PARAMETER,
)
testMC(
self.testMC(
"Send MC request with integer for clientDataHash, expect error",
5,
rp,
@ -885,7 +1058,7 @@ class Tester:
key_params,
)
testMC(
self.testMC(
"Send MC request with missing user, expect error",
cdh,
rp,
@ -894,7 +1067,7 @@ class Tester:
expectedError=CtapError.ERR.MISSING_PARAMETER,
)
testMC(
self.testMC(
"Send MC request with bytearray user, expect error",
cdh,
rp,
@ -902,7 +1075,7 @@ class Tester:
key_params,
)
testMC(
self.testMC(
"Send MC request with missing RP, expect error",
cdh,
None,
@ -911,7 +1084,7 @@ class Tester:
expectedError=CtapError.ERR.MISSING_PARAMETER,
)
testMC(
self.testMC(
"Send MC request with bytearray RP, expect error",
cdh,
b"1234abcd",
@ -919,7 +1092,7 @@ class Tester:
key_params,
)
testMC(
self.testMC(
"Send MC request with missing pubKeyCredParams, expect error",
cdh,
rp,
@ -928,7 +1101,7 @@ class Tester:
expectedError=CtapError.ERR.MISSING_PARAMETER,
)
testMC(
self.testMC(
"Send MC request with incorrect pubKeyCredParams, expect error",
cdh,
rp,
@ -936,7 +1109,7 @@ class Tester:
b"2356",
)
testMC(
self.testMC(
"Send MC request with incorrect excludeList, expect error",
cdh,
rp,
@ -945,7 +1118,7 @@ class Tester:
other={"exclude_list": 8},
)
testMC(
self.testMC(
"Send MC request with incorrect extensions, expect error",
cdh,
rp,
@ -954,7 +1127,7 @@ class Tester:
other={"extensions": 8},
)
testMC(
self.testMC(
"Send MC request with incorrect options, expect error",
cdh,
rp,
@ -963,7 +1136,7 @@ class Tester:
other={"options": 8},
)
testMC(
self.testMC(
"Send MC request with bad RP.name",
cdh,
{"id": self.host, "name": 8, "icon": "icon"},
@ -971,7 +1144,7 @@ class Tester:
key_params,
)
testMC(
self.testMC(
"Send MC request with bad RP.id",
cdh,
{"id": 8, "name": "name", "icon": "icon"},
@ -979,7 +1152,7 @@ class Tester:
key_params,
)
testMC(
self.testMC(
"Send MC request with bad RP.icon",
cdh,
{"id": self.host, "name": "name", "icon": 8},
@ -987,7 +1160,7 @@ class Tester:
key_params,
)
testMC(
self.testMC(
"Send MC request with bad user.name",
cdh,
rp,
@ -995,7 +1168,7 @@ class Tester:
key_params,
)
testMC(
self.testMC(
"Send MC request with bad user.id",
cdh,
rp,
@ -1003,7 +1176,7 @@ class Tester:
key_params,
)
testMC(
self.testMC(
"Send MC request with bad user.displayName",
cdh,
rp,
@ -1011,7 +1184,7 @@ class Tester:
key_params,
)
testMC(
self.testMC(
"Send MC request with bad user.icon",
cdh,
rp,
@ -1019,7 +1192,7 @@ class Tester:
key_params,
)
testMC(
self.testMC(
"Send MC request with non-map pubKeyCredParams item",
cdh,
rp,
@ -1027,7 +1200,7 @@ class Tester:
["wrong"],
)
testMC(
self.testMC(
"Send MC request with pubKeyCredParams item missing type field",
cdh,
rp,
@ -1036,7 +1209,7 @@ class Tester:
expectedError=CtapError.ERR.MISSING_PARAMETER,
)
testMC(
self.testMC(
"Send MC request with pubKeyCredParams item with bad type field",
cdh,
rp,
@ -1044,7 +1217,7 @@ class Tester:
[{"alg": ES256.ALGORITHM, "type": b"public-key"}],
)
testMC(
self.testMC(
"Send MC request with pubKeyCredParams item missing alg",
cdh,
rp,
@ -1053,7 +1226,7 @@ class Tester:
expectedError=CtapError.ERR.MISSING_PARAMETER,
)
testMC(
self.testMC(
"Send MC request with pubKeyCredParams item with bad alg",
cdh,
rp,
@ -1061,7 +1234,7 @@ class Tester:
[{"alg": "7", "type": "public-key"}],
)
testMC(
self.testMC(
"Send MC request with pubKeyCredParams item with bogus alg, expect UNSUPPORTED_ALGORITHM",
cdh,
rp,
@ -1070,7 +1243,7 @@ class Tester:
expectedError=CtapError.ERR.UNSUPPORTED_ALGORITHM,
)
testMC(
self.testMC(
"Send MC request with pubKeyCredParams item with bogus type, expect UNSUPPORTED_ALGORITHM",
cdh,
rp,
@ -1079,7 +1252,7 @@ class Tester:
expectedError=CtapError.ERR.UNSUPPORTED_ALGORITHM,
)
testMC(
self.testMC(
"Send MC request with excludeList item with bogus type, expect SUCCESS",
cdh,
rp,
@ -1089,7 +1262,7 @@ class Tester:
other={"exclude_list": [{"id": b"1234", "type": "rot13"}]},
)
testMC(
self.testMC(
"Send MC request with excludeList with bad item, expect error",
cdh,
rp,
@ -1098,7 +1271,7 @@ class Tester:
other={"exclude_list": ["1234"]},
)
testMC(
self.testMC(
"Send MC request with excludeList with item missing type field, expect error",
cdh,
rp,
@ -1107,7 +1280,7 @@ class Tester:
other={"exclude_list": [{"id": b"1234"}]},
)
testMC(
self.testMC(
"Send MC request with excludeList with item missing id field, expect error",
cdh,
rp,
@ -1116,7 +1289,7 @@ class Tester:
other={"exclude_list": [{"type": "public-key"}]},
)
testMC(
self.testMC(
"Send MC request with excludeList with item containing bad id field, expect error",
cdh,
rp,
@ -1125,7 +1298,7 @@ class Tester:
other={"exclude_list": [{"type": "public-key", "id": "1234"}]},
)
testMC(
self.testMC(
"Send MC request with excludeList with item containing bad type field, expect error",
cdh,
rp,
@ -1134,7 +1307,7 @@ class Tester:
other={"exclude_list": [{"type": b"public-key", "id": b"1234"}]},
)
testMC(
self.testMC(
"Send MC request with excludeList containing previous registration, expect CREDENTIAL_EXCLUDED",
cdh,
rp,
@ -1151,7 +1324,7 @@ class Tester:
expectedError=CtapError.ERR.CREDENTIAL_EXCLUDED,
)
testMC(
self.testMC(
"Send MC request with unknown option, expect SUCCESS",
cdh,
rp,
@ -1163,7 +1336,7 @@ class Tester:
if "uv" in info.options:
if info.options["uv"]:
testMC(
self.testMC(
"Send MC request with uv set to true, expect SUCCESS",
cdh,
rp,
@ -1174,7 +1347,7 @@ class Tester:
)
if "up" in info.options:
if info.options["up"]:
testMC(
self.testMC(
"Send MC request with up set to true, expect INVALID_OPTION",
cdh,
rp,
@ -1184,7 +1357,7 @@ class Tester:
expectedError=CtapError.ERR.INVALID_OPTION,
)
testGA(
self.testGA(
"Send GA request with missing RPID, expect MISSING_PARAMETER",
None,
cdh,
@ -1192,14 +1365,14 @@ class Tester:
expectedError=CtapError.ERR.MISSING_PARAMETER,
)
testGA(
self.testGA(
"Send GA request with bad RPID, expect error",
{"type": "wrong"},
cdh,
allow_list,
)
testGA(
self.testGA(
"Send GA request with missing clientDataHash, expect MISSING_PARAMETER",
rp["id"],
None,
@ -1207,28 +1380,28 @@ class Tester:
expectedError=CtapError.ERR.MISSING_PARAMETER,
)
testGA(
self.testGA(
"Send GA request with bad clientDataHash, expect error",
rp["id"],
{"type": "wrong"},
allow_list,
)
testGA(
self.testGA(
"Send GA request with bad allow_list, expect error",
rp["id"],
cdh,
{"type": "wrong"},
)
testGA(
self.testGA(
"Send GA request with bad item in allow_list, expect error",
rp["id"],
cdh,
allow_list + ["wrong"],
)
testGA(
self.testGA(
"Send GA request with unknown option, expect SUCCESS",
rp["id"],
cdh,
@ -1239,7 +1412,7 @@ class Tester:
if "uv" in info.options:
if info.options["uv"]:
res = testGA(
res = self.testGA(
"Send GA request with uv set to true, expect SUCCESS",
rp["id"],
cdh,
@ -1251,7 +1424,7 @@ class Tester:
assert res.auth_data.flags & (1 << 2)
if "up" in info.options:
if info.options["up"]:
res = testGA(
res = self.testGA(
"Send GA request with up set to true, expect SUCCESS",
rp["id"],
cdh,
@ -1262,7 +1435,7 @@ class Tester:
with Test("Check that UP flag is set in response"):
assert res.auth_data.flags & 1
testGA(
self.testGA(
"Send GA request with bogus type item in allow_list, expect SUCCESS",
rp["id"],
cdh,
@ -1270,38 +1443,38 @@ class Tester:
expectedError=CtapError.ERR.SUCCESS,
)
testGA(
self.testGA(
"Send GA request with item missing type field in allow_list, expect error",
rp["id"],
cdh,
allow_list + [{"id": b"1234"}],
)
testGA(
self.testGA(
"Send GA request with item containing bad type field in allow_list, expect error",
rp["id"],
cdh,
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",
rp["id"],
cdh,
allow_list + [{"type": b"public-key", "id": 42}],
)
testGA(
self.testGA(
"Send GA request with item missing id in allow_list, expect error",
rp["id"],
cdh,
allow_list + [{"type": b"public-key"}],
)
testReset()
self.testReset()
def testRk(pin_code=None):
testGA(
self.testGA(
"Send GA request with reset auth, expect NO_CREDENTIALS",
rp["id"],
cdh,
@ -1316,7 +1489,7 @@ class Tester:
pin_token = self.client.pin_protocol.get_pin_token(pin_code)
pin_auth = hmac_sha256(pin_token, cdh)[:16]
testMC(
self.testMC(
"Send MC request with rk option set to true, expect SUCCESS",
cdh,
rp,
@ -1331,7 +1504,7 @@ class Tester:
options["uv"] = False
for i, x in enumerate([user1, user2, user3]):
testMC(
self.testMC(
"Send MC request with rk option set to true, expect SUCCESS %d/3"
% (i + 1),
cdh,
@ -1342,7 +1515,7 @@ class Tester:
expectedError=CtapError.ERR.SUCCESS,
)
auth1 = testGA(
auth1 = self.testGA(
"Send GA request with no allow_list, expect SUCCESS",
rp2["id"],
cdh,
@ -1383,7 +1556,7 @@ class Tester:
testRk("1234567890")
# PinProtocolV1
res = testCP(
res = self.testCP(
"Test getKeyAgreement, expect SUCCESS",
pin_protocol,
PinProtocolV1.CMD.GET_KEY_AGREEMENT,
@ -1405,7 +1578,7 @@ class Tester:
pin_token = self.client.pin_protocol.get_pin_token(pin2)
pin_auth = hmac_sha256(pin_token, cdh)[:16]
res_mc = testMC(
res_mc = self.testMC(
"Send MC request with new pin auth",
cdh,
rp,
@ -1418,7 +1591,7 @@ class Tester:
with Test("Check UV flag is set"):
assert res_mc.auth_data.flags & (1 << 2)
res_ga = testGA(
res_ga = self.testGA(
"Send GA request with no allow_list, expect SUCCESS",
rp["id"],
cdh,
@ -1435,12 +1608,12 @@ class Tester:
with Test("Check UV flag is set"):
assert res_ga.auth_data.flags & (1 << 2)
testReset()
self.testReset()
with Test("Setting pin code, expect SUCCESS"):
self.client.pin_protocol.set_pin(pin1)
testReset()
self.testReset()
# print("Setting pin code <4 bytes, expect POLICY_VIOLATION ")
# try:
@ -1486,7 +1659,7 @@ class Tester:
except CtapError as e:
print(e)
res_mc = testMC(
res_mc = self.testMC(
"Send MC request with no pin_auth, expect PIN_REQUIRED",
cdh,
rp,
@ -1495,14 +1668,14 @@ class Tester:
expectedError=CtapError.ERR.PIN_REQUIRED,
)
res_mc = testGA(
res_mc = self.testGA(
"Send GA request with no pin_auth, expect PIN_REQUIRED",
rp["id"],
cdh,
expectedError=CtapError.ERR.PIN_REQUIRED,
)
res = testCP(
res = self.testCP(
"Test getRetries, expect SUCCESS",
pin_protocol,
PinProtocolV1.CMD.GET_RETRIES,
@ -1520,7 +1693,7 @@ class Tester:
pin_wrong = "".join(pin_wrong)
for i in range(1, 3):
testPP(
self.testPP(
"Get pin_token with wrong pin code, expect PIN_INVALID (%d/2)" % i,
pin_wrong,
expectedError=CtapError.ERR.PIN_INVALID,
@ -1531,7 +1704,7 @@ class Tester:
print("Pass")
for i in range(1, 3):
testPP(
self.testPP(
"Get pin_token with wrong pin code, expect PIN_AUTH_BLOCKED %d/2" % i,
pin_wrong,
expectedError=CtapError.ERR.PIN_AUTH_BLOCKED,
@ -1543,7 +1716,7 @@ class Tester:
pin_token = self.client.pin_protocol.get_pin_token(pin1)
pin_auth = hmac_sha256(pin_token, cdh)[:16]
res_mc = testMC(
res_mc = self.testMC(
"Send MC request with correct pin_auth",
cdh,
rp,
@ -1563,7 +1736,7 @@ class Tester:
err = CtapError.ERR.PIN_AUTH_BLOCKED
elif i >= 9:
err = CtapError.ERR.PIN_BLOCKED
testPP(
self.testPP(
"Lock out authentictor and check correct error codes %d/9" % i,
pin_wrong,
expectedError=err,
@ -1580,7 +1753,7 @@ class Tester:
if err == CtapError.ERR.PIN_AUTH_BLOCKED:
reboot()
res_mc = testMC(
res_mc = self.testMC(
"Send MC request with correct pin_auth, expect PIN_BLOCKED",
cdh,
rp,
@ -1592,13 +1765,13 @@ class Tester:
reboot()
testPP(
self.testPP(
"Get pin_token with correct pin code, expect PIN_BLOCKED",
pin1,
expectedError=CtapError.ERR.PIN_BLOCKED,
)
testReset()
self.testReset()
print("Done")
@ -1690,14 +1863,9 @@ class Tester:
entropy = b""
while len(entropy) < total:
entropy += sc.get_rng()
total = len(entropy)
with Test("Test entropy is close to perfect"):
sum = 0.0
for x in range(0, 256):
freq = entropy.count(x)
p = freq / total
sum -= p * math.log2(p)
sum = shannon_entropy(entropy)
assert sum > 7.98
print("Entropy is %.5f bits per byte." % sum)
@ -1710,6 +1878,11 @@ class Tester:
except ApduError:
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,):
sc = SoloClient()
sc.find_device(self.dev)
@ -1831,8 +2004,10 @@ def test_find_brute_force():
if __name__ == "__main__":
tests = ("solo", "u2f", "fido2", "fido2-ext", "rk", "hid", "ping", "bootloader")
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)
t = Tester()
@ -1856,6 +2031,9 @@ if __name__ == "__main__":
t.test_fido2()
t.test_fido2_other()
if "fido2-ext" in sys.argv:
t.test_extensions()
if "rk" in sys.argv:
t.test_rk()