combination of refactoring and get_assertion support

This commit is contained in:
Conor Patrick 2018-05-12 23:23:03 -04:00
parent 381a9c53b0
commit 48e7feac1d
5 changed files with 558 additions and 85 deletions

View File

@ -43,6 +43,10 @@ void crypto_sha256_update(uint8_t * data, size_t len)
sha256_update(&sha256_ctx, data, len); sha256_update(&sha256_ctx, data, len);
} }
void crypto_sha256_update_secret()
{
sha256_update(&sha256_ctx, master_secret, 32);
}
void crypto_sha256_final(uint8_t * hash) void crypto_sha256_final(uint8_t * hash)
{ {
@ -71,24 +75,24 @@ void crypto_ecc256_sign(uint8_t * data, int len, uint8_t * sig)
} }
} }
static void generate_private_key(uint8_t * rpId, int len1, uint8_t * entropy, int len2, uint8_t * privkey) void generate_private_key(uint8_t * data, int len, uint8_t * data2, int len2, uint8_t * privkey)
{ {
// poor man's hmac // poor man's hmac
crypto_sha256_init(); crypto_sha256_init();
crypto_sha256_update(rpId, len1); crypto_sha256_update(data, len);
crypto_sha256_update(entropy, len2); crypto_sha256_update(data2, len2);
crypto_sha256_update(master_secret, 32); crypto_sha256_update(master_secret, 32);
crypto_sha256_final(privkey); crypto_sha256_final(privkey);
} }
/*int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve);*/ /*int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve);*/
void crypto_ecc256_derive_public_key(uint8_t * rpId, int len1, uint8_t * entropy, int len2, uint8_t * x, uint8_t * y) void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8_t * y)
{ {
uint8_t privkey[32]; uint8_t privkey[32];
uint8_t pubkey[64]; uint8_t pubkey[64];
generate_private_key(rpId,len1,entropy,len2,privkey); generate_private_key(data,len,NULL,0,privkey);
memset(pubkey,0,sizeof(pubkey)); memset(pubkey,0,sizeof(pubkey));
uECC_compute_public_key(privkey, pubkey, _es256_curve); uECC_compute_public_key(privkey, pubkey, _es256_curve);
@ -96,10 +100,10 @@ void crypto_ecc256_derive_public_key(uint8_t * rpId, int len1, uint8_t * entropy
memmove(y,pubkey+32,32); memmove(y,pubkey+32,32);
} }
void crypto_ecc256_load_key(uint8_t * rpId, int len1, uint8_t * entropy, int len2) void crypto_ecc256_load_key(uint8_t * data, int len)
{ {
static uint8_t privkey[32]; static uint8_t privkey[32];
generate_private_key(rpId,len1,entropy,len2,privkey); generate_private_key(data,len,NULL,0,privkey);
_signing_key = privkey; _signing_key = privkey;
} }

View File

@ -6,16 +6,20 @@
void crypto_sha256_init(); void crypto_sha256_init();
void crypto_sha256_update(uint8_t * data, size_t len); void crypto_sha256_update(uint8_t * data, size_t len);
void crypto_sha256_update_secret();
void crypto_sha256_final(uint8_t * hash); void crypto_sha256_final(uint8_t * hash);
void crypto_ecc256_init(); void crypto_ecc256_init();
void crypto_ecc256_derive_public_key(uint8_t * rpId, int len1, uint8_t * entropy, int len2, uint8_t * x, uint8_t * y); void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8_t * y);
void crypto_ecc256_load_key(uint8_t * rpId, int len1, uint8_t * entropy, int len2); void crypto_ecc256_load_key(uint8_t * data, int len);
void crypto_ecc256_load_attestation_key(); void crypto_ecc256_load_attestation_key();
void crypto_ecc256_sign(uint8_t * data, int len, uint8_t * sig); void crypto_ecc256_sign(uint8_t * data, int len, uint8_t * sig);
void generate_private_key(uint8_t * data, int len, uint8_t * data2, int len2, uint8_t * privkey);
extern const uint8_t attestation_cert_der[]; extern const uint8_t attestation_cert_der[];
extern const uint16_t attestation_cert_der_size; extern const uint16_t attestation_cert_der_size;

523
ctap.c
View File

@ -11,11 +11,14 @@
#define check_ret(r) _check_ret(r,__LINE__, __FILE__) #define check_ret(r) _check_ret(r,__LINE__, __FILE__)
static CborEncoder * _ENCODER;
static void _check_ret(CborError ret, int line, const char * filename) static void _check_ret(CborError ret, int line, const char * filename)
{ {
if (ret != CborNoError) if (ret != CborNoError)
{ {
printf("CborError: 0x%x: %s: %d: %s\n", ret, filename, line, cbor_error_string(ret)); printf("CborError: 0x%x: %s: %d: %s\n", ret, filename, line, cbor_error_string(ret));
if (ret == 0x80000000)
printf(" need %d more bytes\n",cbor_encoder_get_extra_bytes_needed(_ENCODER));
exit(1); exit(1);
} }
} }
@ -105,7 +108,7 @@ void ctap_get_info(CborEncoder * encoder)
check_ret(ret); check_ret(ret);
} }
static int parse_client_data_hash(CTAP_makeCredential * MC, CborValue * val) static int parse_client_data_hash(uint8_t * clientDataHash, CborValue * val)
{ {
size_t sz; size_t sz;
int ret; int ret;
@ -121,11 +124,9 @@ static int parse_client_data_hash(CTAP_makeCredential * MC, CborValue * val)
printf("error, wrong size for client data hash\n"); printf("error, wrong size for client data hash\n");
return -1; return -1;
} }
ret = cbor_value_copy_byte_string(val, MC->clientDataHash, &sz, NULL); ret = cbor_value_copy_byte_string(val, clientDataHash, &sz, NULL);
check_ret(ret); check_ret(ret);
MC->paramsParsed |= PARAM_clientDataHash;
return 0; return 0;
} }
@ -183,24 +184,24 @@ static int 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->userId, &sz, NULL); ret = cbor_value_copy_byte_string(&map, MC->user.id, &sz, NULL);
if (ret == CborErrorOutOfMemory) if (ret == CborErrorOutOfMemory)
{ {
printf("Error, USER_ID is too large\n"); printf("Error, USER_ID is too large\n");
return -1; return -1;
} }
MC->userIdSize = sz; MC->user.id_size = sz;
check_ret(ret); check_ret(ret);
} }
else if (strcmp(key, "name") == 0) else if (strcmp(key, "name") == 0)
{ {
sz = USER_NAME_LIMIT; sz = USER_NAME_LIMIT;
ret = cbor_value_copy_text_string(&map, MC->userName, &sz, NULL); ret = cbor_value_copy_text_string(&map, MC->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->userName[USER_NAME_LIMIT - 1] = 0; MC->user.name[USER_NAME_LIMIT - 1] = 0;
} }
else else
{ {
@ -330,7 +331,22 @@ static int parse_pub_key_cred_params(CTAP_makeCredential * MC, CborValue * val)
} }
static int parse_rp(CTAP_makeCredential * MC, CborValue * val) static int parse_rp_id(struct rpId * rp, CborValue * val)
{
size_t sz = DOMAIN_NAME_MAX_SIZE;
int ret = cbor_value_copy_text_string(val, rp->id, &sz, NULL);
if (ret == CborErrorOutOfMemory)
{
printf("Error, RP_ID is too large\n");
return -1;
}
rp->id[DOMAIN_NAME_MAX_SIZE] = 0; // Extra byte defined in struct.
rp->size = sz;
check_ret(ret);
return 0;
}
static int parse_rp(struct rpId * rp, CborValue * val)
{ {
size_t sz, map_length; size_t sz, map_length;
uint8_t key[8]; uint8_t key[8];
@ -351,6 +367,8 @@ static int parse_rp(CTAP_makeCredential * MC, CborValue * val)
ret = cbor_value_get_map_length(val, &map_length); ret = cbor_value_get_map_length(val, &map_length);
check_ret(ret); check_ret(ret);
rp->size = 0;
for (i = 0; i < map_length; i++) for (i = 0; i < map_length; i++)
{ {
if (cbor_value_get_type(&map) != CborTextStringType) if (cbor_value_get_type(&map) != CborTextStringType)
@ -381,26 +399,21 @@ static int parse_rp(CTAP_makeCredential * MC, CborValue * val)
if (strcmp(key, "id") == 0) if (strcmp(key, "id") == 0)
{ {
sz = DOMAIN_NAME_MAX_SIZE; ret = parse_rp_id(rp, &map);
ret = cbor_value_copy_text_string(&map, MC->rpId, &sz, NULL); if (ret != 0)
if (ret == CborErrorOutOfMemory)
{ {
printf("Error, RP_ID is too large\n"); return ret;
return -1;
} }
MC->rpId[DOMAIN_NAME_MAX_SIZE] = 0; // Extra byte defined in struct.
MC->rpIdSize = sz;
check_ret(ret);
} }
else if (strcmp(key, "name") == 0) else if (strcmp(key, "name") == 0)
{ {
sz = RP_NAME_LIMIT; sz = RP_NAME_LIMIT;
ret = cbor_value_copy_text_string(&map, MC->rpName, &sz, NULL); ret = cbor_value_copy_text_string(&map, rp->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->rpName[RP_NAME_LIMIT - 1] = 0; rp->name[RP_NAME_LIMIT - 1] = 0;
} }
else else
{ {
@ -411,8 +424,11 @@ static int parse_rp(CTAP_makeCredential * MC, CborValue * val)
check_ret(ret); check_ret(ret);
} }
if (rp->size == 0)
MC->paramsParsed |= PARAM_rp; {
printf("Error, no RPID provided\n");
return -1;
}
return 0; return 0;
@ -460,6 +476,7 @@ static int ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * en
ret = cbor_value_advance(&map); ret = cbor_value_advance(&map);
check_ret(ret); check_ret(ret);
ret = 0;
switch(key) switch(key)
{ {
@ -467,25 +484,34 @@ static int ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * en
case MC_clientDataHash: case MC_clientDataHash:
printf("CTAP_clientDataHash\n"); printf("CTAP_clientDataHash\n");
ret = parse_client_data_hash(MC, &map); ret = parse_client_data_hash(MC->clientDataHash, &map);
if (ret == 0)
{
MC->paramsParsed |= PARAM_clientDataHash;
}
printf(" "); dump_hex(MC->clientDataHash, 32); printf(" "); dump_hex(MC->clientDataHash, 32);
break; break;
case MC_rp: case MC_rp:
printf("CTAP_rp\n"); printf("CTAP_rp\n");
ret = parse_rp(MC, &map); ret = parse_rp(&MC->rp, &map);
if (ret == 0)
{
MC->paramsParsed |= PARAM_rp;
}
printf(" ID: %s\n", MC->rpId);
printf(" name: %s\n", MC->rpName); printf(" ID: %s\n", MC->rp.id);
printf(" name: %s\n", MC->rp.name);
break; break;
case MC_user: case MC_user:
printf("CTAP_user\n"); printf("CTAP_user\n");
ret = parse_user(MC, &map); ret = parse_user(MC, &map);
printf(" ID: "); dump_hex(MC->userId, MC->userIdSize); printf(" ID: "); dump_hex(MC->user.id, MC->user.id_size);
printf(" name: %s\n", MC->userName); printf(" name: %s\n", MC->user.name);
break; break;
case MC_pubKeyCredParams: case MC_pubKeyCredParams:
@ -528,29 +554,30 @@ static int ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * en
return 0; return 0;
} }
static int ctap_generate_cose_key(CTAP_makeCredential * MC, CborEncoder * cose_key, uint8_t * rpId, int l1, uint8_t * entropy, int l2) static int ctap_generate_cose_key(CborEncoder * cose_key, uint8_t * hmac_input, int len, uint8_t credtype, int32_t algtype)
{ {
uint8_t x[32], y[32]; uint8_t x[32], y[32];
int ret; int ret;
CborEncoder map; CborEncoder map;
ret = cbor_encoder_create_map(cose_key, &map, 5); ret = cbor_encoder_create_map(cose_key, &map, 5);
int extra = cbor_encoder_get_extra_bytes_needed(&map);
printf(" extra? %d\n", extra);
check_ret(ret); check_ret(ret);
if (MC->publicKeyCredentialType != PUB_KEY_CRED_PUB_KEY) if (credtype != PUB_KEY_CRED_PUB_KEY)
{ {
printf("Error, pubkey credential type not supported\n"); printf("Error, pubkey credential type not supported\n");
return -1; return -1;
} }
switch(MC->COSEAlgorithmIdentifier) switch(algtype)
{ {
case COSE_ALG_ES256: case COSE_ALG_ES256:
crypto_ecc256_init(); crypto_ecc256_init();
crypto_ecc256_derive_public_key(rpId, l1, crypto_ecc256_derive_public_key(hmac_input, len, x, y);
entropy, l2, x, y);
break; break;
default: default:
printf("Error, COSE alg %d not supported\n", MC->COSEAlgorithmIdentifier); printf("Error, COSE alg %d not supported\n", algtype);
return -1; return -1;
} }
@ -564,7 +591,7 @@ static int ctap_generate_cose_key(CTAP_makeCredential * MC, CborEncoder * cose_k
{ {
ret = cbor_encode_int(&map, COSE_KEY_LABEL_ALG); ret = cbor_encode_int(&map, COSE_KEY_LABEL_ALG);
check_ret(ret); check_ret(ret);
ret = cbor_encode_int(&map, MC->COSEAlgorithmIdentifier); ret = cbor_encode_int(&map, algtype);
check_ret(ret); check_ret(ret);
} }
@ -594,60 +621,92 @@ static int ctap_generate_cose_key(CTAP_makeCredential * MC, CborEncoder * cose_k
check_ret(ret); check_ret(ret);
} }
static int ctap_make_auth_data(CTAP_makeCredential * MC, CborEncoder * map, uint8_t * auth_data_buf, int len) void make_auth_tag(struct rpId * rp, CTAP_userEntity * user, uint32_t count, uint8_t * tag)
{
uint8_t hashbuf[32];
crypto_sha256_init();
crypto_sha256_update(rp->id, rp->size);
crypto_sha256_update(user->id, user->id_size);
crypto_sha256_update(user->name, strnlen(user->name, USER_NAME_LIMIT));
crypto_sha256_update((uint8_t*)&count, 4);
crypto_sha256_update_secret();
crypto_sha256_final(hashbuf);
memmove(tag, hashbuf, CREDENTIAL_TAG_SIZE);
}
static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, int len, CTAP_userEntity * user, uint8_t credtype, int32_t algtype)
{ {
CborEncoder cose_key; CborEncoder cose_key;
int auth_data_sz, ret; int auth_data_sz, ret;
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);
cbor_encoder_init(&cose_key, cose_key_buf, len - sizeof(CTAP_authData), 0);
if((sizeof(CTAP_authData) - sizeof(CTAP_attestHeader)) > len)
{
printf("assertion fail, auth_data_buf must be at least %d bytes\n", sizeof(CTAP_authData) - sizeof(CTAP_attestHeader));
exit(1);
}
crypto_sha256_init(); crypto_sha256_init();
crypto_sha256_update(MC->rpId, MC->rpIdSize); crypto_sha256_update(rp->id, rp->size);
crypto_sha256_final(authData->rpIdHash); crypto_sha256_final(authData->rpIdHash);
authData->flags = (ctap_user_presence_test() << 0); authData->flags = (ctap_user_presence_test() << 0);
authData->flags |= (ctap_user_verification(0) << 2); authData->flags |= (ctap_user_verification(0) << 2);
authData->flags |= (1 << 6);//include attestation data
authData->signCount = ctap_atomic_count(); authData->signCount = ctap_atomic_count( 0 );
memmove(authData->attest.aaguid, CTAP_AAGUID, 16); if (credtype != 0)
authData->attest.credLenL = CREDENTIAL_ID_SIZE & 0x00FF; {
authData->attest.credLenH = (CREDENTIAL_ID_SIZE & 0xFF00) >> 8; // add attestedCredentialData
authData->flags |= (1 << 6);//include attestation data
#if CREDENTIAL_ID_SIZE != 48 cbor_encoder_init(&cose_key, cose_key_buf, len - sizeof(CTAP_authData), 0);
_ENCODER = &cose_key;
memmove(authData->attest.aaguid, CTAP_AAGUID, 16);
authData->attest.credLenL = CREDENTIAL_ID_SIZE & 0x00FF;
authData->attest.credLenH = (CREDENTIAL_ID_SIZE & 0xFF00) >> 8;
#if CREDENTIAL_ID_SIZE != 150
#error "need to update credential ID layout" #error "need to update credential ID layout"
#else #else
memmove(authData->attest.credentialId, authData->rpIdHash, 16); memset(authData->attest.credential.id, 0, CREDENTIAL_ID_SIZE);
ctap_generate_rng(authData->attest.credentialId + 16, 32);
ctap_generate_cose_key(MC, &cose_key, authData->attest.credentialId, 16, // Make a tag we can later check to make sure this is a token we made
authData->attest.credentialId, 32); make_auth_tag(rp, user, authData->signCount, authData->attest.credential.fields.tag);
printf("COSE_KEY: "); dump_hex(cose_key_buf, cbor_encoder_get_buffer_size(&cose_key, cose_key_buf)); memmove(&authData->attest.credential.fields.user, user, sizeof(CTAP_userEntity)); //TODO encrypt this
auth_data_sz = sizeof(CTAP_authData) + cbor_encoder_get_buffer_size(&cose_key, cose_key_buf); authData->attest.credential.fields.count = authData->signCount;
ctap_generate_cose_key(&cose_key, authData->attest.credential.id, CREDENTIAL_ID_SIZE, credtype, algtype);
printf("COSE_KEY: "); dump_hex(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 #endif
}
else
{
auth_data_sz = sizeof(CTAP_authData) - sizeof(CTAP_attestHeader);
}
{ {
ret = cbor_encode_int(map,RESP_authData); ret = cbor_encode_int(map,RESP_authData);
check_ret(ret); check_ret(ret);
ret = cbor_encode_byte_string(map, auth_data_buf, auth_data_sz); ret = cbor_encode_byte_string(map, auth_data_buf, auth_data_sz);
check_ret(ret); check_ret(ret);
} }
{
ret = cbor_encode_int(map,RESP_fmt);
check_ret(ret);
ret = cbor_encode_text_stringz(map, "packed");
check_ret(ret);
}
return auth_data_sz; return auth_data_sz;
} }
// require load_key prior to this
// @data data to hash before signature // @data data to hash before signature
// @clientDataHash for signature // @clientDataHash for signature
// @tmp buffer for hash. (can be same as data if data >= 32 bytes) // @tmp buffer for hash. (can be same as data if data >= 32 bytes)
@ -662,7 +721,6 @@ 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_load_attestation_key();
crypto_ecc256_sign(hashbuf, 32, sigbuf); crypto_ecc256_sign(hashbuf, 32, sigbuf);
/*printf("signature hash: "); dump_hex(hashbuf, 32);*/ /*printf("signature hash: "); dump_hex(hashbuf, 32);*/
@ -735,7 +793,7 @@ void ctap_make_credential(CborEncoder * encoder, uint8_t * request, int length)
{ {
CTAP_makeCredential MC; CTAP_makeCredential MC;
int ret; int ret;
uint8_t auth_data_buf[200]; uint8_t auth_data_buf[300];
uint8_t * hashbuf = auth_data_buf + 0; uint8_t * hashbuf = auth_data_buf + 0;
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;
@ -757,18 +815,360 @@ void ctap_make_credential(CborEncoder * encoder, uint8_t * request, int length)
ret = cbor_encoder_create_map(encoder, &map, 3); ret = cbor_encoder_create_map(encoder, &map, 3);
check_ret(ret); check_ret(ret);
int auth_data_sz = ctap_make_auth_data(&MC, &map, auth_data_buf, sizeof(auth_data_buf)); int auth_data_sz = ctap_make_auth_data(&MC.rp, &map, auth_data_buf, sizeof(auth_data_buf),
&MC.user, MC.publicKeyCredentialType, MC.COSEAlgorithmIdentifier);
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);
printf("der sig [%d]: ", sigder_sz); dump_hex(sigder, sigder_sz); printf("der sig [%d]: ", sigder_sz); dump_hex(sigder, sigder_sz);
ctap_add_attest_statement(&map, sigder, sigder_sz); ctap_add_attest_statement(&map, sigder, sigder_sz);
{
ret = cbor_encode_int(&map,RESP_fmt);
check_ret(ret);
ret = cbor_encode_text_stringz(&map, "packed");
check_ret(ret);
}
ret = cbor_encoder_close_container(encoder, &map); ret = cbor_encoder_close_container(encoder, &map);
check_ret(ret); check_ret(ret);
} }
static int parse_allow_list(CTAP_getAssertion * GA, CborValue * it)
{
CborValue arr, val;
size_t len,buflen;
uint8_t type[12];
int i,ret;
if (cbor_value_get_type(it) != CborArrayType)
{
printf("Error, expecting cbor array\n");
return -1;
}
ret = cbor_value_enter_container(it,&arr);
check_ret(ret);
ret = cbor_value_get_array_length(it, &len);
check_ret(ret);
GA->credLen = 0;
for(i = 0; i < len; i++)
{
if (i >= ALLOW_LIST_MAX_SIZE)
{
printf("Warning, out of memory for allow list, truncating.\n");
break;
}
GA->credLen += 1;
if (cbor_value_get_type(&arr) != CborMapType)
{
printf("Error, CborMapType expected in allow_list\n");
return -1;
}
ret = cbor_value_map_find_value(&arr, "id", &val);
check_ret(ret);
if (cbor_value_get_type(&val) != CborByteStringType)
{
printf("Error, No valid ID field (%s)\n", cbor_value_get_type_string(&val));
return -1;
}
buflen = CREDENTIAL_ID_SIZE;
cbor_value_copy_byte_string(&val, GA->creds[i].credential.id, &buflen, NULL);
if (buflen != CREDENTIAL_ID_SIZE)
{
printf("Error, credential is incorrect length\n");
return -1; // maybe just skip it instead of fail?
}
ret = cbor_value_map_find_value(&arr, "type", &val);
check_ret(ret);
if (cbor_value_get_type(&val) != CborTextStringType)
{
printf("Error, No valid type field\n");
return -1;
}
buflen = sizeof(type);
cbor_value_copy_text_string(&val, type, &buflen, NULL);
if (strcmp(type, "public-key") == 0)
{
GA->creds[i].type = PUB_KEY_CRED_PUB_KEY;
}
else
{
GA->creds[i].type = PUB_KEY_CRED_UNKNOWN;
}
ret = cbor_value_advance(&arr);
check_ret(ret);
}
return 0;
}
// Return 1 if credential belongs to this token
int ctap_authenticate_credential(struct rpId * rp, CTAP_credentialDescriptor * desc)
{
uint8_t tag[16];
if (desc->type != PUB_KEY_CRED_PUB_KEY)
{
printf("unsupported credential type: %d\n", desc->type);
return 0;
}
make_auth_tag(rp, &desc->credential.fields.user, desc->credential.fields.count, tag);
return (memcmp(desc->credential.fields.tag, tag, CREDENTIAL_TAG_SIZE) == 0);
}
int ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int length)
{
int ret;
int i,j;
int key;
size_t map_length;
size_t sz;
CborParser parser;
CborValue it,map;
memset(GA, 0, sizeof(CTAP_getAssertion));
ret = cbor_parser_init(request, length, CborValidateCanonicalFormat, &parser, &it);
check_ret(ret);
CborType type = cbor_value_get_type(&it);
if (type != CborMapType)
{
printf("Error, expecting cbor map\n");
return -1;
}
ret = cbor_value_enter_container(&it,&map);
check_ret(ret);
ret = cbor_value_get_map_length(&it, &map_length);
check_ret(ret);
printf("GA map has %d elements\n",map_length);
for (i = 0; i < map_length; i++)
{
type = cbor_value_get_type(&map);
if (type != CborIntegerType)
{
printf("Error, expecting int for map key\n");
}
ret = cbor_value_get_int_checked(&map, &key);
check_ret(ret);
ret = cbor_value_advance(&map);
check_ret(ret);
ret = 0;
switch(key)
{
case GA_clientDataHash:
printf("GA_clientDataHash\n");
ret = parse_client_data_hash(GA->clientDataHash, &map);
printf(" "); dump_hex(GA->clientDataHash, 32);
break;
case GA_rpId:
printf("GA_rpId\n");
ret = parse_rp_id(&GA->rp, &map);
printf(" ID: %s\n", GA->rp.id);
break;
case GA_allowList:
printf("GA_allowList\n");
ret = parse_allow_list(GA, &map);
if (ret == 0)
{
for (j = 0; j < GA->credLen; j++)
{
printf("CRED ID (# %d): ", GA->creds[j].credential.fields.count);
dump_hex(GA->creds[j].credential.id, CREDENTIAL_ID_SIZE);
if (ctap_authenticate_credential(&GA->rp, &GA->creds[j])) // warning encryption will break this
{
printf(" Authenticated.\n");
}
else
{
printf(" NOT authentic.\n");
}
}
}
break;
case GA_extensions:
printf("GA_extensions\n");
break;
case GA_options:
printf("GA_options\n");
break;
case GA_pinAuth:
printf("GA_pinAuth\n");
break;
case GA_pinProtocol:
printf("GA_pinProtocol\n");
break;
default:
printf("invalid key %d\n", key);
}
if (ret != 0)
{
printf("error, parsing failed\n");
return ret;
}
cbor_value_advance(&map);
check_ret(ret);
}
return 0;
}
static int pick_first_authentic_credential(CTAP_getAssertion * GA)
{
int i;
for (i = 0; i < GA->credLen; i++)
{
if (ctap_authenticate_credential(&GA->rp, &GA->creds[i]))
{
return i;
}
}
return -1;
}
static void ctap_add_credential_descriptor(CborEncoder * map, CTAP_credentialDescriptor * cred)
{
CborEncoder desc;
int ret = cbor_encode_int(map, RESP_credential);
check_ret(ret);
ret = cbor_encoder_create_map(map, &desc, 2);
check_ret(ret);
{
ret = cbor_encode_text_string(&desc, "type", 4);
check_ret(ret);
ret = cbor_encode_int(&desc, cred->type);
check_ret(ret);
}
{
ret = cbor_encode_text_string(&desc, "id", 2);
check_ret(ret);
ret = cbor_encode_byte_string(&desc, cred->credential.id, CREDENTIAL_ID_SIZE);
check_ret(ret);
}
ret = cbor_encoder_close_container(map, &desc);
check_ret(ret);
}
void ctap_add_user_entity(CborEncoder * map, CTAP_userEntity * user)
{
CborEncoder entity;
int ret = cbor_encode_int(map, RESP_publicKeyCredentialUserEntity);
check_ret(ret);
ret = cbor_encoder_create_map(map, &entity, 2);
check_ret(ret);
{
ret = cbor_encode_text_string(&entity, "id", 2);
check_ret(ret);
ret = cbor_encode_byte_string(&entity, user->id, user->id_size);
check_ret(ret);
}
{
ret = cbor_encode_text_string(&entity, "displayName", 11);
check_ret(ret);
ret = cbor_encode_text_stringz(&entity, user->name);
check_ret(ret);
}
ret = cbor_encoder_close_container(map, &entity);
check_ret(ret);
}
void ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
{
CTAP_getAssertion GA;
uint8_t auth_data_buf[32 + 1 + 4];
uint8_t sigbuf[64];
uint8_t sigder[72];
int ret = ctap_parse_get_assertion(&GA,request,length);
if (ret != 0)
{
printf("error, parse_get_assertion failed\n");
return;
}
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);
int pick = pick_first_authentic_credential(&GA); // TODO let this handle decryption? lazy?
if (pick == -1)
{
printf("Error, no authentic credential\n");
return;
}
ctap_add_credential_descriptor(&map, &GA.creds[pick]);
ctap_add_user_entity(&map, &GA.creds[pick].credential.fields.user);
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);
check_ret(ret);
ret = cbor_encode_byte_string(&map, sigbuf, 64);
check_ret(ret);
}
{
ret = cbor_encode_int(&map, RESP_numberOfCredentials);
check_ret(ret);
ret = cbor_encode_int(&map, GA.credLen);
check_ret(ret);
}
ret = cbor_encoder_close_container(encoder, &map);
check_ret(ret);
}
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)
{ {
@ -784,6 +1184,7 @@ uint8_t ctap_handle_packet(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
CborEncoder encoder; CborEncoder encoder;
cbor_encoder_init(&encoder, buf, sizeof(buf), 0); cbor_encoder_init(&encoder, buf, sizeof(buf), 0);
_ENCODER = &encoder;
printf("cbor req: "); dump_hex(pkt_raw, length - 1); printf("cbor req: "); dump_hex(pkt_raw, length - 1);
@ -798,6 +1199,10 @@ uint8_t ctap_handle_packet(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
break; break;
case CTAP_GET_ASSERTION: case CTAP_GET_ASSERTION:
printf("CTAP_GET_ASSERTION\n"); printf("CTAP_GET_ASSERTION\n");
ctap_get_assertion(&encoder, pkt_raw, length - 1);
resp->length = cbor_encoder_get_buffer_size(&encoder, buf);
printf("cbor [%d]: \n", cbor_encoder_get_buffer_size(&encoder, buf)); dump_hex(buf, cbor_encoder_get_buffer_size(&encoder, buf));
break; break;
case CTAP_CANCEL: case CTAP_CANCEL:
printf("CTAP_CANCEL\n"); printf("CTAP_CANCEL\n");

80
ctap.h
View File

@ -44,6 +44,12 @@
#define RESP_authData 0x02 #define RESP_authData 0x02
#define RESP_attStmt 0x03 #define RESP_attStmt 0x03
#define RESP_credential 0x01
#define RESP_signature 0x03
#define RESP_publicKeyCredentialUserEntity 0x04
#define RESP_numberOfCredentials 0x05
#define PARAM_clientDataHash (1 << 0) #define PARAM_clientDataHash (1 << 0)
@ -67,7 +73,9 @@
#define USER_ID_MAX_SIZE 64 #define USER_ID_MAX_SIZE 64
#define USER_NAME_LIMIT 65 // Must be minimum of 64 bytes but can be more. #define USER_NAME_LIMIT 65 // Must be minimum of 64 bytes but can be more.
#define CREDENTIAL_ID_SIZE 48 #define CREDENTIAL_TAG_SIZE 16
#define CREDENTIAL_COUNTER_SIZE (4)
#define CREDENTIAL_ID_SIZE (CREDENTIAL_TAG_SIZE + USER_ID_MAX_SIZE + USER_NAME_LIMIT + CREDENTIAL_COUNTER_SIZE + 1)
#define PUB_KEY_CRED_PUB_KEY 0x01 #define PUB_KEY_CRED_PUB_KEY 0x01
#define PUB_KEY_CRED_UNKNOWN 0x3F #define PUB_KEY_CRED_UNKNOWN 0x3F
@ -75,13 +83,30 @@
#define CREDENTIAL_IS_SUPPORTED 1 #define CREDENTIAL_IS_SUPPORTED 1
#define CREDENTIAL_NOT_SUPPORTED 0 #define CREDENTIAL_NOT_SUPPORTED 0
#define ALLOW_LIST_MAX_SIZE 20
typedef struct
{
uint8_t id[USER_ID_MAX_SIZE];
uint8_t id_size;
uint8_t name[USER_NAME_LIMIT];
} CTAP_userEntity;
union _credential {
struct {
uint8_t tag[CREDENTIAL_TAG_SIZE];
CTAP_userEntity user;
uint32_t count;
}__attribute__((packed)) fields;
uint8_t id[CREDENTIAL_ID_SIZE];
};
typedef struct typedef struct
{ {
uint8_t aaguid[16]; uint8_t aaguid[16];
uint8_t credLenH; uint8_t credLenH;
uint8_t credLenL; uint8_t credLenL;
uint8_t credentialId[CREDENTIAL_ID_SIZE]; union _credential credential;
} __attribute__((packed)) CTAP_attestHeader; } __attribute__((packed)) CTAP_attestHeader;
@ -93,26 +118,25 @@ typedef struct
CTAP_attestHeader attest; CTAP_attestHeader attest;
} __attribute__((packed)) CTAP_authData; } __attribute__((packed)) CTAP_authData;
typedef struct typedef struct
{ {
uint8_t * data; uint8_t * data;
uint16_t length; uint16_t length;
} CTAP_RESPONSE; } CTAP_RESPONSE;
struct rpId
{
uint8_t id[DOMAIN_NAME_MAX_SIZE + 1]; // extra for NULL termination
size_t size;
uint8_t name[RP_NAME_LIMIT];
};
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;
uint8_t rpId[DOMAIN_NAME_MAX_SIZE + 1]; // extra for NULL termination CTAP_userEntity user;
size_t rpIdSize;
uint8_t rpName[RP_NAME_LIMIT];
uint8_t userId[USER_ID_MAX_SIZE];
uint8_t userIdSize;
uint8_t userName[USER_NAME_LIMIT];
uint8_t publicKeyCredentialType; uint8_t publicKeyCredentialType;
int32_t COSEAlgorithmIdentifier; int32_t COSEAlgorithmIdentifier;
@ -121,6 +145,33 @@ typedef struct
} CTAP_makeCredential; } CTAP_makeCredential;
typedef struct
{
uint8_t type;
union _credential credential;
} CTAP_credentialDescriptor;
typedef struct
{
uint32_t paramsParsed;
uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
struct rpId rp;
CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE];
int credLen;
//uint8_t userId[USER_ID_MAX_SIZE];
//uint8_t userIdSize;
//uint8_t userName[USER_NAME_LIMIT];
//uint8_t publicKeyCredentialType;
//int32_t COSEAlgorithmIdentifier;
//uint8_t pinProtocol;
} CTAP_getAssertion;
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);
// Test for user presence // Test for user presence
@ -130,8 +181,9 @@ extern int ctap_user_presence_test();
// Generate @num bytes of random numbers to @dest // Generate @num bytes of random numbers to @dest
extern int ctap_generate_rng(uint8_t * dst, size_t num); extern int ctap_generate_rng(uint8_t * dst, size_t num);
// Increment atomic counter and return it // Increment atomic counter and return it.
extern uint32_t ctap_atomic_count(); // Must support two counters, @sel selects counter0 or counter1.
uint32_t ctap_atomic_count(int sel);
// Verify the user // Verify the user
// return 1 if user is verified, 0 if not // return 1 if user is verified, 0 if not

View File

@ -30,10 +30,18 @@ int ctap_user_verification(uint8_t arg)
} }
uint32_t ctap_atomic_count() uint32_t ctap_atomic_count(int sel)
{ {
static uint32_t counter = 25; static uint32_t counter1 = 25;
return counter++; static uint32_t counter2 = 25;
if (sel == 0)
{
return counter1++;
}
else
{
return counter2++;
}
} }
int ctap_generate_rng(uint8_t * dst, size_t num) int ctap_generate_rng(uint8_t * dst, size_t num)