pass fido2 tests

This commit is contained in:
Conor Patrick
2018-10-28 16:30:55 -04:00
parent 2f911368da
commit 2fd96f8e4b
23 changed files with 900 additions and 318 deletions

View File

@@ -26,6 +26,7 @@
#include "cbor.h"
#include "ctap.h"
#include "ctaphid.h"
#include "ctap_parse.h"
#include "ctap_errors.h"
#include "cose_key.h"
@@ -43,6 +44,7 @@ uint8_t PIN_TOKEN[PIN_TOKEN_SIZE];
uint8_t KEY_AGREEMENT_PUB[64];
static uint8_t KEY_AGREEMENT_PRIV[32];
static uint8_t PIN_CODE_HASH[32];
static int8_t PIN_BOOT_ATTEMPTS_LEFT = PIN_BOOT_ATTEMPTS;
AuthenticatorState STATE;
@@ -264,11 +266,11 @@ static int ctap_generate_cose_key(CborEncoder * cose_key, uint8_t * hmac_input,
return 0;
}
void make_auth_tag(struct rpId * rp, CTAP_userEntity * user, uint32_t count, uint8_t * tag)
void make_auth_tag(uint8_t * nonce, CTAP_userEntity * user, uint32_t count, uint8_t * tag)
{
uint8_t hashbuf[32];
crypto_sha256_hmac_init(NULL, 0, hashbuf);
crypto_sha256_update(rp->id, rp->size);
crypto_sha256_update(nonce, CREDENTIAL_NONCE_SIZE);
crypto_sha256_update(user->id, user->id_size);
crypto_sha256_update(user->name, strnlen((const char*)user->name, USER_NAME_LIMIT));
crypto_sha256_update((uint8_t*)&count, 4);
@@ -283,21 +285,19 @@ static uint32_t auth_data_update_count(CTAP_authDataHeader * authData)
if (count == 0) // count 0 will indicate invalid token
{
count = ctap_atomic_count( 0 );
}
uint8_t * byte = (uint8_t*) &authData->signCount;
*byte++ = count & 0xff;
count = count >> 8;
*byte++ = count & 0xff;
count = count >> 8;
*byte++ = count & 0xff;
count = count >> 8;
*byte++ = count & 0xff;
*byte++ = (count >> 0) & 0xff;
*byte++ = (count >> 8) & 0xff;
*byte++ = (count >> 16) & 0xff;
*byte++ = (count >> 24) & 0xff;
return count;
}
static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, int len, CTAP_userEntity * user, uint8_t credtype, int32_t algtype)
static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, int len, CTAP_userEntity * user, uint8_t credtype, int32_t algtype, int32_t * sz)
{
CborEncoder cose_key;
int auth_data_sz, ret;
@@ -318,10 +318,24 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
count = auth_data_update_count(&authData->head);
authData->head.flags = (ctap_user_presence_test() << 0);
device_set_status(CTAPHID_STATUS_UPNEEDED);
int but = ctap_user_presence_test();
if (!but)
{
return CTAP2_ERR_OPERATION_DENIED;
}
else if (but < 0) // Cancel
{
return CTAP2_ERR_KEEPALIVE_CANCEL;
}
device_set_status(CTAPHID_STATUS_PROCESSING);
authData->head.flags = (but << 0);
authData->head.flags |= (ctap_user_verification(0) << 2);
if (credtype != 0)
{
// add attestedCredentialData
@@ -338,19 +352,19 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
#else
memset((uint8_t*)&authData->attest.credential, 0, sizeof(struct Credential));
// Make a tag we can later check to make sure this is a token we made
make_auth_tag(rp, user, count, authData->attest.credential.tag);
ctap_generate_rng(authData->attest.credential.nonce, CREDENTIAL_NONCE_SIZE);
memmove(&authData->attest.credential.enc.user, user, sizeof(CTAP_userEntity)); //TODO encrypt this
authData->attest.credential.enc.count = count;
// Make a tag we can later check to make sure this is a token we made
make_auth_tag(authData->attest.credential.nonce, user, count, authData->attest.credential.tag);
crypto_aes256_init(CRYPTO_TRANSPORT_KEY, NULL);
crypto_aes256_encrypt((uint8_t*)&authData->attest.credential.enc, CREDENTIAL_ENC_SIZE);
ctap_generate_cose_key(&cose_key, (uint8_t*)&authData->attest.credential, sizeof(struct Credential), credtype, algtype);
printf1(TAG_MC,"COSE_KEY: "); dump_hex1(TAG_MC, cose_key_buf, 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);
#endif
@@ -367,7 +381,8 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
check_ret(ret);
}
return auth_data_sz;
if (sz) *sz = auth_data_sz;
return 0;
}
@@ -419,7 +434,6 @@ int ctap_calculate_signature(uint8_t * data, int datalen, uint8_t * clientDataHa
crypto_sha256_update(clientDataHash, CLIENT_DATA_HASH_SIZE);
crypto_sha256_final(hashbuf);
printf1(TAG_GREEN, "sha256: "); dump_hex1(TAG_DUMP,hashbuf,32);
crypto_ecc256_sign(hashbuf, 32, sigbuf);
return ctap_encode_der_sig(sigbuf,sigder);
@@ -471,13 +485,8 @@ uint8_t ctap_add_attest_statement(CborEncoder * map, uint8_t * sigder, int len)
int ctap_authenticate_credential(struct rpId * rp, CTAP_credentialDescriptor * desc)
{
uint8_t tag[16];
if (desc->type != PUB_KEY_CRED_PUB_KEY)
{
printf1(TAG_GA,"unsupported credential type: %d\n", desc->type);
return 0;
}
make_auth_tag(rp, &desc->credential.enc.user, desc->credential.enc.count, tag);
make_auth_tag(desc->credential.nonce, &desc->credential.enc.user, desc->credential.enc.count, tag);
return (memcmp(desc->credential.tag, tag, CREDENTIAL_TAG_SIZE) == 0);
}
@@ -519,6 +528,12 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
}
}
if (MC.up)
{
return CTAP2_ERR_INVALID_OPTION;
}
crypto_aes256_init(CRYPTO_TRANSPORT_KEY, NULL);
for (i = 0; i < MC.excludeListSize; i++)
{
ret = parse_credential_descriptor(&MC.excludeList, excl_cred);
@@ -528,8 +543,11 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
}
check_retr(ret);
crypto_aes256_reset_iv(NULL);
crypto_aes256_decrypt((uint8_t*)& excl_cred->credential.enc, CREDENTIAL_ENC_SIZE);
if (ctap_authenticate_credential(&MC.rp, excl_cred))
{
printf1(TAG_MC, "Cred %d failed!\r\n",i);
return CTAP2_ERR_CREDENTIAL_EXCLUDED;
}
@@ -540,9 +558,11 @@ 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;
int auth_data_sz = ctap_make_auth_data(&MC.rp, &map, auth_data_buf, sizeof(auth_data_buf),
&MC.user, MC.publicKeyCredentialType, MC.COSEAlgorithmIdentifier);
ret = ctap_make_auth_data(&MC.rp, &map, auth_data_buf, sizeof(auth_data_buf),
&MC.user, MC.publicKeyCredentialType, MC.COSEAlgorithmIdentifier, &auth_data_sz);
check_retr(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);
@@ -724,11 +744,6 @@ uint8_t ctap_end_get_assertion(CborEncoder * map, CTAP_credentialDescriptor * cr
crypto_ecc256_load_key((uint8_t*)&cred->credential, sizeof(struct Credential), NULL, 0);
/*printf1(TAG_GREEN,"auth_data_buf: "); dump_hex1(TAG_DUMP, auth_data_buf, sizeof(CTAP_authDataHeader));*/
/*printf1(TAG_GREEN,"clientdatahash: "); dump_hex1(TAG_DUMP, clientDataHash, 32);*/
/*printf1(TAG_GREEN,"credential: # %d\n", cred->credential.enc.count);*/
/*dump_hex1(TAG_DUMP, clientDataHash, 32);*/
int sigder_sz = ctap_calculate_signature(auth_data_buf, sizeof(CTAP_authDataHeader), clientDataHash, auth_data_buf, sigbuf, sigder);
{
@@ -801,12 +816,16 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
}
}
if (!GA.rp.size || !GA.clientDataHashPresent)
{
return CTAP2_ERR_MISSING_PARAMETER;
}
CborEncoder map;
ret = cbor_encoder_create_map(encoder, &map, 5);
check_ret(ret);
ctap_make_auth_data(&GA.rp, &map, auth_data_buf, sizeof(auth_data_buf), NULL, 0,0);
ret = ctap_make_auth_data(&GA.rp, &map, auth_data_buf, sizeof(auth_data_buf), NULL, 0,0,NULL);
check_retr(ret);
printf1(TAG_GA, "ALLOW_LIST has %d creds\n", GA.credLen);
/*for (int j = 0; j < GA.credLen; j++)*/
@@ -834,13 +853,13 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
else
{
printf2(TAG_ERR,"Error, no authentic credential\n");
return CTAP2_ERR_CREDENTIAL_NOT_VALID;
return CTAP2_ERR_NO_CREDENTIALS;
}
printf1(TAG_RED,"resulting order of creds:\n");
printf1(TAG_GA,"resulting order of creds:\n");
for (int j = 0; j < GA.credLen; j++)
{
printf1(TAG_RED,"CRED ID (# %d)\n", GA.creds[j].credential.enc.count);
printf1(TAG_GA,"CRED ID (# %d)\n", GA.creds[j].credential.enc.count);
}
{
@@ -861,6 +880,18 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
return 0;
}
// Return how many trailing zeros in a buffer
static int trailing_zeros(uint8_t * buf, int indx)
{
int c = 0;
while(0==buf[indx] && indx)
{
indx--;
c++;
}
return c;
}
uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platform_pubkey, uint8_t * pinAuth, uint8_t * pinHashEnc)
{
uint8_t shared_secret[32];
@@ -876,7 +907,11 @@ uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platfor
{
if (ctap_device_locked())
{
return CTAP2_ERR_OPERATION_DENIED;
return CTAP2_ERR_PIN_BLOCKED;
}
if (ctap_device_boot_locked())
{
return CTAP2_ERR_PIN_AUTH_BLOCKED;
}
}
@@ -911,25 +946,31 @@ uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platfor
crypto_aes256_decrypt(pinEnc, len);
printf1(TAG_CP,"new pin: %s\n", pinEnc);
ret = strnlen((const char *)pinEnc, NEW_PIN_ENC_MAX_SIZE);
if (ret == NEW_PIN_ENC_MAX_SIZE)
ret = trailing_zeros(pinEnc, NEW_PIN_ENC_MIN_SIZE - 1);
ret = NEW_PIN_ENC_MIN_SIZE - ret;
if (ret < NEW_PIN_MIN_SIZE || ret >= NEW_PIN_MAX_SIZE)
{
printf2(TAG_ERR,"No NULL terminator in new pin string\n");
return CTAP1_ERR_OTHER;
}
else if (ret < 4)
{
printf2(TAG_ERR,"new PIN is too short\n");
printf2(TAG_ERR,"new PIN is too short or too long [%d bytes]\n", ret);
return CTAP2_ERR_PIN_POLICY_VIOLATION;
}
else
{
printf1(TAG_CP,"new pin: %s [%d bytes]\n", pinEnc, ret);
dump_hex1(TAG_CP, pinEnc, ret);
}
if (ctap_is_pin_set())
{
if (ctap_device_locked())
{
return CTAP2_ERR_OPERATION_DENIED;
return CTAP2_ERR_PIN_BLOCKED;
}
if (ctap_device_boot_locked())
{
return CTAP2_ERR_PIN_AUTH_BLOCKED;
}
crypto_aes256_reset_iv(NULL);
crypto_aes256_decrypt(pinHashEnc, 16);
@@ -937,6 +978,10 @@ uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platfor
{
crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV);
ctap_decrement_pin_attempts();
if (ctap_device_boot_locked())
{
return CTAP2_ERR_PIN_AUTH_BLOCKED;
}
return CTAP2_ERR_PIN_INVALID;
}
else
@@ -976,6 +1021,10 @@ uint8_t ctap_add_pin_if_verified(uint8_t * pinTokenEnc, uint8_t * platform_pubke
// Generate new keyAgreement pair
crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV);
ctap_decrement_pin_attempts();
if (ctap_device_boot_locked())
{
return CTAP2_ERR_PIN_AUTH_BLOCKED;
}
return CTAP2_ERR_PIN_INVALID;
}
@@ -995,6 +1044,20 @@ uint8_t ctap_client_pin(CborEncoder * encoder, uint8_t * request, int length)
uint8_t pinTokenEnc[PIN_TOKEN_SIZE];
int ret = ctap_parse_client_pin(&CP,request,length);
switch(CP.subCommand)
{
case CP_cmdSetPin:
case CP_cmdChangePin:
case CP_cmdGetPinToken:
if (ctap_device_locked())
{
return CTAP2_ERR_PIN_BLOCKED;
}
if (ctap_device_boot_locked())
{
return CTAP2_ERR_PIN_AUTH_BLOCKED;
}
}
if (ret != 0)
{
@@ -1104,7 +1167,7 @@ uint8_t ctap_client_pin(CborEncoder * encoder, uint8_t * request, int length)
check_ret(ret);
}
if (num_map)
if (num_map || CP.getRetries)
{
ret = cbor_encoder_close_container(encoder, &map);
check_ret(ret);
@@ -1141,10 +1204,14 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
{
case CTAP_MAKE_CREDENTIAL:
case CTAP_GET_ASSERTION:
case CTAP_CLIENT_PIN:
if (ctap_device_locked())
{
status = CTAP2_ERR_OPERATION_DENIED;
status = CTAP2_ERR_PIN_BLOCKED;
goto done;
}
if (ctap_device_boot_locked())
{
status = CTAP2_ERR_PIN_AUTH_BLOCKED;
goto done;
}
break;
@@ -1153,6 +1220,7 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
switch(cmd)
{
case CTAP_MAKE_CREDENTIAL:
device_set_status(CTAPHID_STATUS_PROCESSING);
printf1(TAG_CTAP,"CTAP_MAKE_CREDENTIAL\n");
t1 = millis();
status = ctap_make_credential(&encoder, pkt_raw, length);
@@ -1164,6 +1232,7 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
resp->length = cbor_encoder_get_buffer_size(&encoder, buf);
break;
case CTAP_GET_ASSERTION:
device_set_status(CTAPHID_STATUS_PROCESSING);
printf1(TAG_CTAP,"CTAP_GET_ASSERTION\n");
t1 = millis();
status = ctap_get_assertion(&encoder, pkt_raw, length);
@@ -1190,6 +1259,7 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
case CTAP_CLIENT_PIN:
printf1(TAG_CTAP,"CTAP_CLIENT_PIN\n");
status = ctap_client_pin(&encoder, pkt_raw, length);
resp->length = cbor_encoder_get_buffer_size(&encoder, buf);
dump_hex1(TAG_DUMP, buf, cbor_encoder_get_buffer_size(&encoder, buf));
break;
@@ -1228,6 +1298,7 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
}
done:
device_set_status(CTAPHID_STATUS_IDLE);
getAssertionState.lastcmd = cmd;
if (status != CTAP1_ERR_SUCCESS)
@@ -1235,7 +1306,7 @@ done:
resp->length = 0;
}
printf1(TAG_CTAP,"cbor output structure: %d bytes\n", resp->length);
printf1(TAG_CTAP,"cbor output structure: %d bytes. Return 0x%02x\n", resp->length, status);
return status;
}
@@ -1267,6 +1338,8 @@ void ctap_init()
authenticator_read_state(&STATE);
device_set_status(CTAPHID_STATUS_IDLE);
if (STATE.is_initialized == INITIALIZED_MARKER)
{
printf1(TAG_STOR,"Auth state is initialized\n");
@@ -1295,7 +1368,7 @@ void ctap_init()
{
printf1(TAG_STOR,"pin code: \"%s\"\n", STATE.pin_code);
crypto_sha256_init();
crypto_sha256_update(STATE.pin_code, strnlen(STATE.pin_code, NEW_PIN_ENC_MAX_SIZE));
crypto_sha256_update(STATE.pin_code, STATE.pin_code_length);
crypto_sha256_final(PIN_CODE_HASH);
printf1(TAG_STOR, "attempts_left: %d\n", STATE.remaining_tries);
}
@@ -1335,14 +1408,15 @@ uint8_t ctap_pin_matches(uint8_t * pin, int len)
void ctap_update_pin(uint8_t * pin, int len)
{
// TODO this should go in flash
if (len > NEW_PIN_ENC_MAX_SIZE-1 || len < 4)
if (len > NEW_PIN_ENC_MIN_SIZE || len < 4)
{
printf2(TAG_ERR, "Update pin fail length\n");
exit(1);
}
memset(STATE.pin_code, 0, NEW_PIN_ENC_MAX_SIZE);
memset(STATE.pin_code, 0, NEW_PIN_ENC_MIN_SIZE);
memmove(STATE.pin_code, pin, len);
STATE.pin_code_length = len;
STATE.pin_code[NEW_PIN_ENC_MIN_SIZE - 1] = 0;
crypto_sha256_init();
crypto_sha256_update(STATE.pin_code, len);
@@ -1350,18 +1424,25 @@ void ctap_update_pin(uint8_t * pin, int len)
STATE.is_pin_set = 1;
authenticator_write_state(&STATE, 1);
authenticator_write_state(&STATE, 0);
printf1(TAG_CTAP, "New pin set: %s\n", STATE.pin_code);
}
uint8_t ctap_decrement_pin_attempts()
{
if (STATE.remaining_tries > 0)
if (PIN_BOOT_ATTEMPTS_LEFT > 0)
{
PIN_BOOT_ATTEMPTS_LEFT--;
}
if (! ctap_device_locked())
{
STATE.remaining_tries--;
ctap_flush_state(0);
printf1(TAG_CP, "ATTEMPTS left: %d\n", STATE.remaining_tries);
if (STATE.remaining_tries == 0)
if (ctap_device_locked())
{
memset(PIN_TOKEN,0,sizeof(PIN_TOKEN));
memset(PIN_CODE_HASH,0,sizeof(PIN_CODE_HASH));
@@ -1378,7 +1459,12 @@ uint8_t ctap_decrement_pin_attempts()
int8_t ctap_device_locked()
{
return STATE.remaining_tries == 0;
return STATE.remaining_tries <= 0;
}
int8_t ctap_device_boot_locked()
{
return PIN_BOOT_ATTEMPTS_LEFT <= 0;
}
int8_t ctap_leftover_pin_attempts()
@@ -1389,6 +1475,7 @@ int8_t ctap_leftover_pin_attempts()
void ctap_reset_pin_attempts()
{
STATE.remaining_tries = PIN_LOCKOUT_ATTEMPTS;
PIN_BOOT_ATTEMPTS_LEFT = PIN_BOOT_ATTEMPTS;
ctap_flush_state(0);
}