get_next_assertion works

This commit is contained in:
Conor Patrick 2018-05-23 23:30:08 -04:00
parent a21c8058e1
commit 644ceaef63
6 changed files with 195 additions and 46 deletions

228
ctap.c
View File

@ -24,9 +24,13 @@ static uint8_t PIN_CODE_HASH[32];
static uint8_t DEVICE_LOCKOUT = 0; static uint8_t DEVICE_LOCKOUT = 0;
static struct { static struct {
CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE]; CTAP_authDataHeader authData;
uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE-1];
uint8_t lastcmd; uint8_t lastcmd;
uint32_t count; uint32_t count;
uint32_t index;
uint32_t time;
} getAssertionState; } getAssertionState;
uint8_t verify_pin_auth(uint8_t * pinAuth, uint8_t * clientDataHash) uint8_t verify_pin_auth(uint8_t * pinAuth, uint8_t * clientDataHash)
@ -249,6 +253,17 @@ void make_auth_tag(struct rpId * rp, CTAP_userEntity * user, uint32_t count, uin
memmove(tag, hashbuf, CREDENTIAL_TAG_SIZE); memmove(tag, hashbuf, CREDENTIAL_TAG_SIZE);
} }
static uint32_t auth_data_update_count(CTAP_authDataHeader * authData)
{
uint32_t count = ctap_atomic_count( 0 );
if (count == 0) // count 0 will indicate invalid token
{
count = ctap_atomic_count( 0 );
}
authData->signCount = ntohl(count);
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)
{ {
CborEncoder cose_key; CborEncoder cose_key;
@ -258,7 +273,7 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
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_authData) - sizeof(CTAP_attestHeader)) > 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);
@ -266,18 +281,18 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
crypto_sha256_init(); crypto_sha256_init();
crypto_sha256_update(rp->id, rp->size); crypto_sha256_update(rp->id, rp->size);
crypto_sha256_final(authData->rpIdHash); crypto_sha256_final(authData->head.rpIdHash);
authData->flags = (ctap_user_presence_test() << 0); count = auth_data_update_count(&authData->head);
authData->flags |= (ctap_user_verification(0) << 2);
authData->head.flags = (ctap_user_presence_test() << 0);
authData->head.flags |= (ctap_user_verification(0) << 2);
count = ctap_atomic_count( 0 );
authData->signCount = ntohl(count);
if (credtype != 0) if (credtype != 0)
{ {
// add attestedCredentialData // add attestedCredentialData
authData->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);
@ -286,7 +301,7 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
authData->attest.credLenH = (CREDENTIAL_ID_SIZE & 0xFF00) >> 8; authData->attest.credLenH = (CREDENTIAL_ID_SIZE & 0xFF00) >> 8;
#if CREDENTIAL_ID_SIZE != 150 #if CREDENTIAL_ID_SIZE != 150
#error "need to update credential ID layout" #error "need to double check credential ID layout"
#else #else
memset(authData->attest.credential.id, 0, CREDENTIAL_ID_SIZE); memset(authData->attest.credential.id, 0, CREDENTIAL_ID_SIZE);
@ -307,7 +322,7 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
} }
else else
{ {
auth_data_sz = sizeof(CTAP_authData) - sizeof(CTAP_attestHeader); auth_data_sz = sizeof(CTAP_authDataHeader);
} }
{ {
@ -317,7 +332,6 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
check_ret(ret); check_ret(ret);
} }
return auth_data_sz; return auth_data_sz;
} }
@ -336,11 +350,9 @@ int ctap_calculate_signature(uint8_t * data, int datalen, uint8_t * clientDataHa
crypto_sha256_update(clientDataHash, CLIENT_DATA_HASH_SIZE); crypto_sha256_update(clientDataHash, CLIENT_DATA_HASH_SIZE);
crypto_sha256_final(hashbuf); crypto_sha256_final(hashbuf);
crypto_ecc256_sign(hashbuf, 32, sigbuf); printf1(TAG_GREEN, "sha256: "); dump_hex1(TAG_DUMP,hashbuf,32);
/*printf1("signature hash: "); dump_hex(hashbuf, 32);*/ crypto_ecc256_sign(hashbuf, 32, sigbuf);
/*printf1("R: "); dump_hex(sigbuf, 32);*/
/*printf1("S: "); dump_hex(sigbuf+32, 32);*/
// Need to caress into dumb der format .. // Need to caress into dumb der format ..
uint8_t pad_s = (sigbuf[32] & 0x80) == 0x80; uint8_t pad_s = (sigbuf[32] & 0x80) == 0x80;
@ -439,8 +451,6 @@ 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);
@ -489,7 +499,7 @@ static int pick_first_authentic_credential(CTAP_getAssertion * GA)
int i; int i;
for (i = 0; i < GA->credLen; i++) for (i = 0; i < GA->credLen; i++)
{ {
if (ctap_authenticate_credential(&GA->rp, &GA->creds[i])) if (GA->creds[i].credential.fields.count != 0)
{ {
return i; return i;
} }
@ -559,13 +569,132 @@ uint8_t ctap_add_user_entity(CborEncoder * map, CTAP_userEntity * user)
return 0; return 0;
} }
uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length) static int cred_cmp_func(const void * _a, const void * _b)
{ {
CTAP_getAssertion GA; CTAP_credentialDescriptor * a = (CTAP_credentialDescriptor * )_a;
uint8_t auth_data_buf[32 + 1 + 4]; CTAP_credentialDescriptor * b = (CTAP_credentialDescriptor * )_b;
return b->credential.fields.count - a->credential.fields.count;
}
// @return the number of valid credentials
// sorts the credentials. Most recent creds will be first, invalid ones last.
int ctap_filter_invalid_credentials(CTAP_getAssertion * GA)
{
int i;
int count = 0;
for (i = 0; i < GA->credLen; i++)
{
if (! ctap_authenticate_credential(&GA->rp, &GA->creds[i]))
{
printf1(TAG_GA, "CRED #%d is invalid\n", GA->creds[i].credential.fields.count);
GA->creds[i].credential.fields.count = 0; // invalidate
}
else
{
count++;
}
}
printf("qsort length: %d\n", GA->credLen);
qsort(GA->creds, GA->credLen, sizeof(CTAP_credentialDescriptor), cred_cmp_func);
return count;
}
static void save_credential_list(CTAP_authDataHeader * head, uint8_t * clientDataHash, CTAP_credentialDescriptor * creds, uint32_t count)
{
if(count)
{
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);
}
static CTAP_credentialDescriptor * pop_credential()
{
if (getAssertionState.count > 0)
{
getAssertionState.count--;
return &getAssertionState.creds[getAssertionState.count];
}
else
{
return NULL;
}
}
uint8_t ctap_end_get_assertion(CborEncoder * map, CTAP_credentialDescriptor * cred, uint8_t * auth_data_buf, uint8_t * clientDataHash)
{
int ret;
uint8_t sigbuf[64]; uint8_t sigbuf[64];
uint8_t sigder[72]; uint8_t sigder[72];
ret = ctap_add_credential_descriptor(map, cred);
check_retr(ret);
ret = ctap_add_user_entity(map, &cred->credential.fields.user);
check_retr(ret);
crypto_ecc256_load_key(cred->credential.id, CREDENTIAL_ID_SIZE);
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.fields.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);
{
ret = cbor_encode_int(map, RESP_signature);
check_ret(ret);
ret = cbor_encode_byte_string(map, sigder, sigder_sz);
check_ret(ret);
}
return 0;
}
uint8_t ctap_get_next_assertion(CborEncoder * encoder)
{
int ret;
CborEncoder map;
CTAP_authDataHeader * authData = &getAssertionState.authData;
CTAP_credentialDescriptor * cred = pop_credential();
if (cred == NULL)
{
return CTAP2_ERR_NOT_ALLOWED;
}
auth_data_update_count(authData);
ret = cbor_encoder_create_map(encoder, &map, 4);
check_ret(ret);
{
ret = cbor_encode_int(&map,RESP_authData);
check_ret(ret);
ret = cbor_encode_byte_string(&map, (uint8_t *)authData, sizeof(CTAP_authDataHeader));
check_ret(ret);
}
ret = ctap_end_get_assertion(&map, cred, (uint8_t *)authData, getAssertionState.clientDataHash);
check_retr(ret);
ret = cbor_encoder_close_container(encoder, &map);
check_ret(ret);
return 0;
}
uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
{
CTAP_getAssertion GA;
uint8_t auth_data_buf[sizeof(CTAP_authDataHeader)];
int ret = ctap_parse_get_assertion(&GA,request,length); int ret = ctap_parse_get_assertion(&GA,request,length);
if (ret != 0) if (ret != 0)
@ -610,37 +739,38 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
} }
} }
int pick = pick_first_authentic_credential(&GA); // TODO let this handle decryption? lazy? // Decrypt here
if (pick == -1)
//
int validCredCount = ctap_filter_invalid_credentials(&GA);
if (validCredCount > 0)
{
save_credential_list((CTAP_authDataHeader*)auth_data_buf, GA.clientDataHash, GA.creds, validCredCount-1); // skip last one
}
else
{ {
printf2(TAG_ERR,"Error, no authentic credential\n"); printf2(TAG_ERR,"Error, no authentic credential\n");
return CTAP2_ERR_CREDENTIAL_NOT_VALID; return CTAP2_ERR_CREDENTIAL_NOT_VALID;
} }
ret = ctap_add_credential_descriptor(&map, &GA.creds[pick]); printf1(TAG_RED,"resulting order of creds:\n");
check_retr(ret); for (int j = 0; j < GA.credLen; j++)
ret = ctap_add_user_entity(&map, &GA.creds[pick].credential.fields.user);
check_retr(ret);
crypto_ecc256_load_key(GA.creds[pick].credential.id, CREDENTIAL_ID_SIZE);
int sigder_sz = ctap_calculate_signature(auth_data_buf, sizeof(auth_data_buf), GA.clientDataHash, auth_data_buf, sigbuf, sigder);
{ {
ret = cbor_encode_int(&map, RESP_signature); printf1(TAG_RED,"CRED ID (# %d)\n", GA.creds[j].credential.fields.count);
check_ret(ret);
ret = cbor_encode_byte_string(&map, sigder, sigder_sz);
check_ret(ret);
} }
{ {
ret = cbor_encode_int(&map, RESP_numberOfCredentials); ret = cbor_encode_int(&map, RESP_numberOfCredentials);
check_ret(ret); check_ret(ret);
ret = cbor_encode_int(&map, GA.credLen); ret = cbor_encode_int(&map, validCredCount);
check_ret(ret); check_ret(ret);
} }
CTAP_credentialDescriptor * cred = &GA.creds[validCredCount - 1];
ret = ctap_end_get_assertion(&map, cred, auth_data_buf, GA.clientDataHash);
check_retr(ret);
ret = cbor_encoder_close_container(encoder, &map); ret = cbor_encoder_close_container(encoder, &map);
check_ret(ret); check_ret(ret);
@ -882,13 +1012,6 @@ uint8_t ctap_client_pin(CborEncoder * encoder, uint8_t * request, int length)
} }
static void save_credential_list(CTAP_credentialDescriptor * creds, uint32_t count)
{
memmove(getAssertionState.creds, creds, sizeof(CTAP_credentialDescriptor) * count);
getAssertionState.count = count;
}
uint8_t ctap_handle_packet(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp) uint8_t ctap_handle_packet(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
{ {
@ -973,6 +1096,21 @@ uint8_t ctap_handle_packet(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
break; break;
case GET_NEXT_ASSERTION: case GET_NEXT_ASSERTION:
printf1(TAG_CTAP,"CTAP_NEXT_ASSERTION\n"); printf1(TAG_CTAP,"CTAP_NEXT_ASSERTION\n");
if (getAssertionState.lastcmd == CTAP_GET_ASSERTION)
{
status = ctap_get_next_assertion(&encoder);
resp->length = cbor_encoder_get_buffer_size(&encoder, buf);
dump_hex1(TAG_DUMP, buf, cbor_encoder_get_buffer_size(&encoder, buf));
if (status == 0)
{
cmd = CTAP_GET_ASSERTION; // allow for next assertion
}
}
else
{
printf2(TAG_ERR, "unwanted GET_NEXT_ASSERTION\n");
status = CTAP2_ERR_NOT_ALLOWED;
}
break; break;
default: default:
status = CTAP1_ERR_INVALID_COMMAND; status = CTAP1_ERR_INVALID_COMMAND;

6
ctap.h
View File

@ -126,12 +126,16 @@ typedef struct
union _credential credential; union _credential credential;
} __attribute__((packed)) CTAP_attestHeader; } __attribute__((packed)) CTAP_attestHeader;
typedef struct typedef struct
{ {
uint8_t rpIdHash[32]; uint8_t rpIdHash[32];
uint8_t flags; uint8_t flags;
uint32_t signCount; uint32_t signCount;
} __attribute__((packed)) CTAP_authDataHeader;
typedef struct
{
CTAP_authDataHeader head;
CTAP_attestHeader attest; CTAP_attestHeader attest;
} __attribute__((packed)) CTAP_authData; } __attribute__((packed)) CTAP_authData;

View File

@ -42,6 +42,7 @@ uint32_t ctap_atomic_count(int sel)
{ {
return counter2++; return counter2++;
} }
return 44;
} }
int ctap_generate_rng(uint8_t * dst, size_t num) int ctap_generate_rng(uint8_t * dst, size_t num)

2
log.c
View File

@ -24,6 +24,8 @@ struct logtag tagtable[] = {
{TAG_PARSE,"PARSE"}, {TAG_PARSE,"PARSE"},
{TAG_CTAP,"CTAP"}, {TAG_CTAP,"CTAP"},
{TAG_DUMP,"DUMP"}, {TAG_DUMP,"DUMP"},
{TAG_GREEN,"\x1b[32mDEBUG\x1b[0m"},
{TAG_RED,"\x1b[31mDEBUG\x1b[0m"},
}; };
void LOG(uint32_t tag, const char * filename, int num, const char * fmt, ...) void LOG(uint32_t tag, const char * filename, int num, const char * fmt, ...)

2
log.h
View File

@ -17,6 +17,8 @@ typedef enum
TAG_PARSE= (1 << 4), TAG_PARSE= (1 << 4),
TAG_CTAP = (1 << 5), TAG_CTAP = (1 << 5),
TAG_DUMP = (1 << 6), TAG_DUMP = (1 << 6),
TAG_GREEN = (1 << 7),
TAG_RED= (1 << 8),
TAG_FILENO = (1<<31) TAG_FILENO = (1<<31)
} LOG_TAG; } LOG_TAG;

2
main.c
View File

@ -29,6 +29,8 @@ int main(int argc, char * argv[])
TAG_CTAP | TAG_CTAP |
TAG_PARSE | TAG_PARSE |
TAG_DUMP| TAG_DUMP|
TAG_GREEN|
TAG_RED|
TAG_ERR TAG_ERR
); );