diff --git a/ctap.c b/ctap.c index 2fbbf8f..260be5f 100644 --- a/ctap.c +++ b/ctap.c @@ -6,6 +6,7 @@ #include "cbor.h" #include "ctap.h" +#include "ctap_parse.h" #include "ctap_errors.h" #include "cose_key.h" #include "crypto.h" @@ -13,13 +14,6 @@ #include "log.h" -#define check_ret(r) _check_ret(r,__LINE__, __FILE__);\ - if ((r) != CborNoError) return CTAP2_ERR_CBOR_PARSING; - -#define check_retr(r) _check_ret(r,__LINE__, __FILE__);\ - if ((r) != CborNoError) return r; - - #define PIN_TOKEN_SIZE 16 static uint8_t PIN_TOKEN[PIN_TOKEN_SIZE]; static uint8_t KEY_AGREEMENT_PUB[64]; @@ -29,94 +23,6 @@ static uint8_t PIN_CODE[NEW_PIN_ENC_MAX_SIZE]; static uint8_t PIN_CODE_HASH[32]; static uint8_t DEVICE_LOCKOUT = 0; -static CborEncoder * _ENCODER; -static void _check_ret(CborError ret, int line, const char * filename) -{ - if (ret != CborNoError) - { - printf1(TAG_ERR,"CborError: 0x%x: %s: %d: %s\n", ret, filename, line, cbor_error_string(ret)); - /*exit(1);*/ - } -} - -static const char * cbor_value_get_type_string(const CborValue *value) -{ - switch(cbor_value_get_type(value)) - { - case CborIntegerType: - return "CborIntegerType"; - break; - case CborByteStringType: - return "CborByteStringType"; - break; - case CborTextStringType: - return "CborTextStringType"; - break; - case CborArrayType: - return "CborArrayType"; - break; - case CborMapType: - return "CborMapType"; - break; - case CborTagType: - return "CborTagType"; - break; - case CborSimpleType: - return "CborSimpleType"; - break; - case CborBooleanType: - return "CborBooleanType"; - break; - case CborNullType: - return "CborNullType"; - break; - case CborUndefinedType: - return "CborUndefinedType"; - break; - case CborHalfFloatType: - return "CborHalfFloatType"; - break; - case CborFloatType: - return "CborFloatType"; - break; - case CborDoubleType: - return "CborDoubleType"; - break; - } - return "Invalid type"; -} - -/*static CborError cbor_value_map_find_value_by_int(const CborValue *map, const int key, CborValue * element)*/ -/*{*/ - /*size_t sz;*/ - /*CborValue ckey, it;*/ - /*int rkey;*/ - /*int ret = cbor_value_get_map_length(map, &sz);*/ - /*check_ret(ret);*/ - - /*cbor_value_enter_container(map, &it);*/ - - /*int i;*/ - /*for (i = 0; i < sz; i++)*/ - /*{*/ - /*if (cbor_value_get_type(&it) == CborIntegerType)*/ - /*{*/ - /*ret = cbor_value_advance(&it);*/ - /*check_ret(ret);*/ - /*ret = cbor_value_get_int_checked(&it, &rkey);*/ - /*check_ret(ret);*/ - /*ret = cbor_value_advance(&it);*/ - /*check_ret(ret);*/ - /*}*/ - /*else*/ - /*{*/ - /*cbor_value_advance(&it);*/ - /*cbor_value_advance(&it);*/ - /*}*/ - /*}*/ - - /*return CborNoError;*/ -/*}*/ uint8_t verify_pin_auth(uint8_t * pinAuth, uint8_t * clientDataHash) { @@ -254,541 +160,6 @@ uint8_t ctap_get_info(CborEncoder * encoder) } -static uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val) -{ - size_t sz, map_length; - uint8_t key[8]; - int ret; - 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) != CborTextStringType) - { - printf2(TAG_ERR,"Error, expecting text string type for user 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\n"); - return CTAP2_ERR_LIMIT_EXCEEDED; - } - check_ret(ret); - key[sizeof(key) - 1] = 0; - - ret = cbor_value_advance(&map); - check_ret(ret); - - if (strcmp(key, "id") == 0) - { - - if (cbor_value_get_type(&map) != CborByteStringType) - { - printf2(TAG_ERR,"Error, expecting byte string type for rp map value\n"); - return CTAP2_ERR_INVALID_CBOR_TYPE; - } - - sz = USER_ID_MAX_SIZE; - ret = cbor_value_copy_byte_string(&map, MC->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; - check_ret(ret); - } - else if (strcmp(key, "name") == 0) - { - sz = USER_NAME_LIMIT; - ret = cbor_value_copy_text_string(&map, MC->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; - } - else - { - printf1(TAG_PARSE,"ignoring key %s for user map\n", key); - } - - ret = cbor_value_advance(&map); - check_ret(ret); - - } - - MC->paramsParsed |= PARAM_user; - - return 0; -} - -static int parse_pub_key_cred_param(CborValue * val, uint8_t * cred_type, int32_t * alg_type) -{ - CborValue map; - CborValue cred; - CborValue alg; - int ret; - uint8_t type_str[16]; - size_t sz = sizeof(type_str); - - if (cbor_value_get_type(val) != CborMapType) - { - printf2(TAG_ERR,"error, expecting map type, got %s\n", cbor_value_get_type_string(val)); - return CTAP2_ERR_INVALID_CBOR_TYPE; - } - - ret = cbor_value_map_find_value(val, "type", &cred); - check_ret(ret); - ret = cbor_value_map_find_value(val, "alg", &alg); - check_ret(ret); - - if (cbor_value_get_type(&cred) != CborTextStringType) - { - printf2(TAG_ERR,"Error, parse_pub_key could not find credential param\n"); - return CTAP2_ERR_MISSING_PARAMETER; - } - if (cbor_value_get_type(&alg) != CborIntegerType) - { - printf2(TAG_ERR,"Error, parse_pub_key could not find alg param\n"); - return CTAP2_ERR_MISSING_PARAMETER; - } - - ret = cbor_value_copy_text_string(&cred, type_str, &sz, NULL); - check_ret(ret); - - type_str[sizeof(type_str) - 1] = 0; - - if (strcmp(type_str, "public-key") == 0) - { - *cred_type = PUB_KEY_CRED_PUB_KEY; - } - else - { - *cred_type = PUB_KEY_CRED_UNKNOWN; - } - - ret = cbor_value_get_int_checked(&alg, alg_type); - check_ret(ret); - - return 0; -} - -// Check if public key credential+algorithm type is supported -static int pub_key_cred_param_supported(uint8_t cred, int32_t alg) -{ - if (cred == PUB_KEY_CRED_PUB_KEY) - { - if (alg == COSE_ALG_ES256) - { - return CREDENTIAL_IS_SUPPORTED; - } - } - - return CREDENTIAL_NOT_SUPPORTED; -} - -static uint8_t parse_pub_key_cred_params(CTAP_makeCredential * MC, CborValue * val) -{ - size_t sz, arr_length; - uint8_t cred_type; - int32_t alg_type; - uint8_t key[8]; - int ret; - int i; - CborValue arr; - - - if (cbor_value_get_type(val) != CborArrayType) - { - printf2(TAG_ERR,"error, expecting array type\n"); - return CTAP2_ERR_INVALID_CBOR_TYPE; - } - - ret = cbor_value_enter_container(val,&arr); - check_ret(ret); - - ret = cbor_value_get_array_length(val, &arr_length); - check_ret(ret); - - for (i = 0; i < arr_length; i++) - { - if ((ret = parse_pub_key_cred_param(&arr, &cred_type, &alg_type)) == 0) - { - if (pub_key_cred_param_supported(cred_type, alg_type) == CREDENTIAL_IS_SUPPORTED) - { - MC->publicKeyCredentialType = cred_type; - MC->COSEAlgorithmIdentifier = alg_type; - MC->paramsParsed |= PARAM_pubKeyCredParams; - return 0; - } - } - else - { - // Continue? fail? - return ret; - } - ret = cbor_value_advance(&arr); - check_ret(ret); - } - - printf2(TAG_ERR,"Error, no public key credential parameters are supported!\n"); - return CTAP2_ERR_UNSUPPORTED_ALGORITHM; -} - -uint8_t parse_fixed_byte_string(CborValue * map, uint8_t * dst, int len) -{ - size_t sz; - int ret; - if (cbor_value_get_type(map) == CborByteStringType) - { - sz = len; - ret = cbor_value_copy_byte_string(map, dst, &sz, NULL); - check_ret(ret); - if (sz != len) - { - return CTAP1_ERR_OTHER; - } - } - else - { - return CTAP2_ERR_INVALID_CBOR_TYPE; - } - return 0; -} - - -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) - { - printf2(TAG_ERR,"Error, RP_ID is too large\n"); - return CTAP2_ERR_LIMIT_EXCEEDED; - } - check_ret(ret); - rp->id[DOMAIN_NAME_MAX_SIZE] = 0; // Extra byte defined in struct. - rp->size = sz; - return 0; -} - -static uint8_t parse_rp(struct rpId * rp, CborValue * val) -{ - size_t sz, map_length; - uint8_t key[8]; - int ret; - 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); - - rp->size = 0; - - for (i = 0; i < map_length; i++) - { - if (cbor_value_get_type(&map) != CborTextStringType) - { - printf2(TAG_ERR,"Error, expecting text string type for rp 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\n"); - return CTAP2_ERR_LIMIT_EXCEEDED; - } - check_ret(ret); - key[sizeof(key) - 1] = 0; - - ret = cbor_value_advance(&map); - check_ret(ret); - - if (cbor_value_get_type(&map) != CborTextStringType) - { - printf2(TAG_ERR,"Error, expecting text string type for rp map value\n"); - return CTAP2_ERR_INVALID_CBOR_TYPE; - } - - if (strcmp(key, "id") == 0) - { - ret = parse_rp_id(rp, &map); - if (ret != 0) - { - return ret; - } - } - else if (strcmp(key, "name") == 0) - { - sz = RP_NAME_LIMIT; - ret = cbor_value_copy_text_string(&map, rp->name, &sz, NULL); - if (ret != CborErrorOutOfMemory) - { // Just truncate the name it's okay - check_ret(ret); - } - rp->name[RP_NAME_LIMIT - 1] = 0; - } - else - { - printf1(TAG_PARSE,"ignoring key %s for RP map\n", key); - } - - ret = cbor_value_advance(&map); - check_ret(ret); - - } - if (rp->size == 0) - { - printf2(TAG_ERR,"Error, no RPID provided\n"); - return CTAP2_ERR_MISSING_PARAMETER; - } - - - return 0; -} - -static uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv) -{ - size_t sz, map_length; - uint8_t key[8]; - int ret; - int i; - _Bool b; - 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) != 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\n"); - return CTAP2_ERR_LIMIT_EXCEEDED; - } - check_ret(ret); - key[sizeof(key) - 1] = 0; - - ret = cbor_value_advance(&map); - check_ret(ret); - - if (cbor_value_get_type(&map) != CborBooleanType) - { - printf2(TAG_ERR,"Error, expecting text string type for rp map value\n"); - return CTAP2_ERR_INVALID_CBOR_TYPE; - } - - if (strcmp(key, "rk") == 0) - { - ret = cbor_value_get_boolean(&map, &b); - check_ret(ret); - *rk = b; - } - else if (strcmp(key, "uv") == 0) - { - ret = cbor_value_get_boolean(&map, &b); - check_ret(ret); - *uv = b; - } - else - { - printf1(TAG_PARSE,"ignoring key %s for option map\n", key); - } - - - - } -} - -static uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length) -{ - int ret; - int i; - int key; - size_t map_length; - size_t sz; - CborParser parser; - CborValue it,map; - - memset(MC, 0, sizeof(CTAP_makeCredential)); - ret = cbor_parser_init(request, length, CborValidateCanonicalFormat, &parser, &it); - check_retr(ret); - - CborType type = cbor_value_get_type(&it); - if (type != CborMapType) - { - printf2(TAG_ERR,"Error, expecting cbor map\n"); - return CTAP2_ERR_CBOR_UNEXPECTED_TYPE; - } - - ret = cbor_value_enter_container(&it,&map); - check_ret(ret); - - ret = cbor_value_get_map_length(&it, &map_length); - check_ret(ret); - - printf1(TAG_MC,"map has %d elements\n",map_length); - - for (i = 0; i < map_length; i++) - { - type = cbor_value_get_type(&map); - if (type != CborIntegerType) - { - printf2(TAG_ERR,"Error, expecting int for map key\n"); - return CTAP2_ERR_CBOR_UNEXPECTED_TYPE; - } - ret = cbor_value_get_int_checked(&map, &key); - check_ret(ret); - - ret = cbor_value_advance(&map); - check_ret(ret); - ret = 0; - - switch(key) - { - - case MC_clientDataHash: - printf1(TAG_MC,"CTAP_clientDataHash\n"); - - ret = parse_fixed_byte_string(&map, MC->clientDataHash, CLIENT_DATA_HASH_SIZE); - if (ret == 0) - { - MC->paramsParsed |= PARAM_clientDataHash; - } - - printf1(TAG_MC," "); dump_hex1(TAG_MC,MC->clientDataHash, 32); - break; - case MC_rp: - printf1(TAG_MC,"CTAP_rp\n"); - - ret = parse_rp(&MC->rp, &map); - if (ret == 0) - { - MC->paramsParsed |= PARAM_rp; - } - - - printf1(TAG_MC," ID: %s\n", MC->rp.id); - printf1(TAG_MC," name: %s\n", MC->rp.name); - break; - case MC_user: - printf1(TAG_MC,"CTAP_user\n"); - - 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); - - break; - case MC_pubKeyCredParams: - printf1(TAG_MC,"CTAP_pubKeyCredParams\n"); - - 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); - - break; - case MC_excludeList: - printf1(TAG_MC,"CTAP_excludeList\n"); - break; - case MC_extensions: - printf1(TAG_MC,"CTAP_extensions\n"); - break; - - case MC_options: - printf1(TAG_MC,"CTAP_options\n"); - ret = parse_options(&map, &MC->rk, &MC->uv); - check_retr(ret); - break; - case MC_pinAuth: - printf1(TAG_MC,"CTAP_pinAuth\n"); - - ret = parse_fixed_byte_string(&map, MC->pinAuth, 16); - check_retr(ret); - MC->pinAuthPresent = 1; - - break; - case MC_pinProtocol: - printf1(TAG_MC,"CTAP_pinProtocol\n"); - if (cbor_value_get_type(&map) == CborIntegerType) - { - ret = cbor_value_get_int_checked(&map, &MC->pinProtocol); - check_ret(ret); - } - else - { - return CTAP2_ERR_INVALID_CBOR_TYPE; - } - - break; - - default: - printf1(TAG_MC,"invalid key %d\n", key); - - } - if (ret != 0) - { - return ret; - } - - cbor_value_advance(&map); - check_ret(ret); - } - - return 0; -} static int ctap_add_cose_key(CborEncoder * cose_key, uint8_t * x, uint8_t * y, uint8_t credtype, int32_t algtype) { @@ -905,7 +276,6 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au authData->flags |= (1 << 6);//include attestation data 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; @@ -1094,87 +464,6 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt return CTAP1_ERR_SUCCESS; } -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) - { - printf2(TAG_ERR,"Error, expecting cbor array\n"); - return CTAP2_ERR_INVALID_CBOR_TYPE; - } - - 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) - { - printf1(TAG_PARSE,"Error, out of memory for allow list.\n"); - return CTAP2_ERR_TOO_MANY_ELEMENTS; - } - - GA->credLen += 1; - - if (cbor_value_get_type(&arr) != CborMapType) - { - printf2(TAG_ERR,"Error, CborMapType expected in allow_list\n"); - return CTAP2_ERR_INVALID_CBOR_TYPE; - } - - ret = cbor_value_map_find_value(&arr, "id", &val); - check_ret(ret); - - if (cbor_value_get_type(&val) != CborByteStringType) - { - printf2(TAG_ERR,"Error, No valid ID field (%s)\n", cbor_value_get_type_string(&val)); - return CTAP2_ERR_MISSING_PARAMETER; - } - - buflen = CREDENTIAL_ID_SIZE; - cbor_value_copy_byte_string(&val, GA->creds[i].credential.id, &buflen, NULL); - if (buflen != CREDENTIAL_ID_SIZE) - { - printf2(TAG_ERR,"Error, credential is incorrect length\n"); - return CTAP2_ERR_CBOR_UNEXPECTED_TYPE; // 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) - { - printf2(TAG_ERR,"Error, No valid type field\n"); - return CTAP2_ERR_MISSING_PARAMETER; - } - - 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) { @@ -1190,133 +479,6 @@ int ctap_authenticate_credential(struct rpId * rp, CTAP_credentialDescriptor * d 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) - { - printf2(TAG_ERR,"Error, expecting cbor map\n"); - return CTAP2_ERR_INVALID_CBOR_TYPE; - } - - ret = cbor_value_enter_container(&it,&map); - check_ret(ret); - - ret = cbor_value_get_map_length(&it, &map_length); - check_ret(ret); - - printf1(TAG_GA,"GA map has %d elements\n",map_length); - - for (i = 0; i < map_length; i++) - { - type = cbor_value_get_type(&map); - if (type != CborIntegerType) - { - printf2(TAG_ERR,"Error, expecting int for map key\n"); - return CTAP2_ERR_INVALID_CBOR_TYPE; - } - 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: - printf1(TAG_GA,"GA_clientDataHash\n"); - - ret = parse_fixed_byte_string(&map, GA->clientDataHash, CLIENT_DATA_HASH_SIZE); - check_retr(ret); - - printf1(TAG_GA," "); dump_hex1(TAG_GA, GA->clientDataHash, 32); - break; - case GA_rpId: - printf1(TAG_GA,"GA_rpId\n"); - - ret = parse_rp_id(&GA->rp, &map); - - printf1(TAG_GA," ID: %s\n", GA->rp.id); - break; - case GA_allowList: - printf1(TAG_GA,"GA_allowList\n"); - ret = parse_allow_list(GA, &map); - if (ret == 0) - { - for (j = 0; j < GA->credLen; j++) - { - printf1(TAG_GA,"CRED ID (# %d): ", GA->creds[j].credential.fields.count); - dump_hex1(TAG_GA, GA->creds[j].credential.id, CREDENTIAL_ID_SIZE); - 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"); - } - } - } - break; - case GA_extensions: - printf1(TAG_GA,"GA_extensions\n"); - break; - - case GA_options: - printf1(TAG_GA,"CTAP_options\n"); - ret = parse_options(&map, &GA->rk, &GA->uv); - check_retr(ret); - break; - case GA_pinAuth: - printf1(TAG_GA,"CTAP_pinAuth\n"); - - ret = parse_fixed_byte_string(&map, GA->pinAuth, 16); - check_retr(ret); - GA->pinAuthPresent = 1; - - break; - case GA_pinProtocol: - printf1(TAG_GA,"CTAP_pinProtocol\n"); - if (cbor_value_get_type(&map) == CborIntegerType) - { - ret = cbor_value_get_int_checked(&map, &GA->pinProtocol); - check_ret(ret); - } - else - { - return CTAP2_ERR_INVALID_CBOR_TYPE; - } - - break; - - } - if (ret != 0) - { - printf2(TAG_ERR,"error, parsing failed\n"); - return ret; - } - - cbor_value_advance(&map); - check_ret(ret); - } - - - return 0; -} static int pick_first_authentic_credential(CTAP_getAssertion * GA) { @@ -1465,239 +627,6 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length) } -uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int * crv) -{ - CborValue map; - size_t map_length; - size_t ptsz; - int i,ret,key; - int xkey = 0,ykey = 0; - *kty = 0; - *crv = 0; - - - CborType type = cbor_value_get_type(it); - if (type != CborMapType) - { - printf2(TAG_ERR,"Error, expecting cbor map\n"); - return CTAP2_ERR_INVALID_CBOR_TYPE; - } - - ret = cbor_value_enter_container(it,&map); - check_ret(ret); - - ret = cbor_value_get_map_length(it, &map_length); - check_ret(ret); - - printf1(TAG_PARSE,"cose key has %d elements\n",map_length); - - for (i = 0; i < map_length; i++) - { - if (cbor_value_get_type(&map) != CborIntegerType) - { - printf2(TAG_ERR,"Error, expecting int for map key\n"); - return CTAP2_ERR_INVALID_CBOR_TYPE; - } - - ret = cbor_value_get_int_checked(&map, &key); - check_ret(ret); - - ret = cbor_value_advance(&map); - check_ret(ret); - - switch(key) - { - case COSE_KEY_LABEL_KTY: - printf1(TAG_PARSE,"COSE_KEY_LABEL_KTY\n"); - if (cbor_value_get_type(&map) == CborIntegerType) - { - ret = cbor_value_get_int_checked(&map, kty); - check_ret(ret); - } - else - { - return CTAP2_ERR_INVALID_CBOR_TYPE; - } - break; - case COSE_KEY_LABEL_ALG: - printf1(TAG_PARSE,"COSE_KEY_LABEL_ALG\n"); - break; - case COSE_KEY_LABEL_CRV: - printf1(TAG_PARSE,"COSE_KEY_LABEL_CRV\n"); - if (cbor_value_get_type(&map) == CborIntegerType) - { - ret = cbor_value_get_int_checked(&map, crv); - check_ret(ret); - } - else - { - return CTAP2_ERR_INVALID_CBOR_TYPE; - } - break; - case COSE_KEY_LABEL_X: - printf1(TAG_PARSE,"COSE_KEY_LABEL_X\n"); - ret = parse_fixed_byte_string(&map, 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); - check_retr(ret); - ykey = 1; - - break; - default: - printf1(TAG_PARSE,"Warning, unrecognized cose key option %d\n", key); - } - - ret = cbor_value_advance(&map); - check_ret(ret); - } - if (xkey == 0 || ykey == 0 || *kty == 0 || *crv == 0) - { - return CTAP2_ERR_MISSING_PARAMETER; - } - return 0; -} - -int ctap_parse_client_pin(CTAP_clientPin * CP, 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(CP, 0, sizeof(CTAP_clientPin)); - ret = cbor_parser_init(request, length, CborValidateCanonicalFormat, &parser, &it); - check_ret(ret); - - CborType type = cbor_value_get_type(&it); - if (type != CborMapType) - { - printf2(TAG_ERR,"Error, expecting cbor map\n"); - return CTAP2_ERR_INVALID_CBOR_TYPE; - } - - ret = cbor_value_enter_container(&it,&map); - check_ret(ret); - - ret = cbor_value_get_map_length(&it, &map_length); - check_ret(ret); - - printf1(TAG_CP,"CP map has %d elements\n",map_length); - - for (i = 0; i < map_length; i++) - { - type = cbor_value_get_type(&map); - if (type != CborIntegerType) - { - printf2(TAG_ERR,"Error, expecting int for map key\n"); - return CTAP2_ERR_INVALID_CBOR_TYPE; - } - ret = cbor_value_get_int_checked(&map, &key); - check_ret(ret); - - ret = cbor_value_advance(&map); - check_ret(ret); - ret = 0; - - switch(key) - { - case CP_pinProtocol: - printf1(TAG_CP,"CP_pinProtocol\n"); - if (cbor_value_get_type(&map) == CborIntegerType) - { - cbor_value_get_int_checked(&map, &CP->pinProtocol); - check_ret(ret); - } - else - { - return CTAP2_ERR_INVALID_CBOR_TYPE; - } - break; - case CP_subCommand: - printf1(TAG_CP,"CP_subCommand\n"); - if (cbor_value_get_type(&map) == CborIntegerType) - { - cbor_value_get_int_checked(&map, &CP->subCommand); - check_ret(ret); - } - else - { - return CTAP2_ERR_INVALID_CBOR_TYPE; - } - - 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); - check_retr(ret); - CP->keyAgreementPresent = 1; - break; - case CP_pinAuth: - printf1(TAG_CP,"CP_pinAuth\n"); - - ret = parse_fixed_byte_string(&map, CP->pinAuth, 16); - check_retr(ret); - CP->pinAuthPresent = 1; - break; - case CP_newPinEnc: - printf1(TAG_CP,"CP_newPinEnc\n"); - if (cbor_value_get_type(&map) == CborByteStringType) - { - ret = cbor_value_calculate_string_length(&map, &sz); - check_ret(ret); - if (sz > NEW_PIN_ENC_MAX_SIZE) - { - return CTAP1_ERR_OTHER; - } - CP->newPinEncSize = sz; - sz = NEW_PIN_ENC_MAX_SIZE; - ret = cbor_value_copy_byte_string(&map, CP->newPinEnc, &sz, NULL); - check_ret(ret); - } - else - { - return CTAP2_ERR_INVALID_CBOR_TYPE; - } - - break; - case CP_pinHashEnc: - printf1(TAG_CP,"CP_pinHashEnc\n"); - - ret = parse_fixed_byte_string(&map, CP->pinHashEnc, 16); - check_retr(ret); - CP->pinHashEncPresent = 1; - - break; - case CP_getKeyAgreement: - printf1(TAG_CP,"CP_getKeyAgreement\n"); - ret = cbor_value_get_boolean(&map, &CP->getKeyAgreement); - check_ret(ret); - break; - case CP_getRetries: - printf1(TAG_CP,"CP_getRetries\n"); - ret = cbor_value_get_boolean(&map, &CP->getRetries); - check_ret(ret); - break; - default: - printf1(TAG_CP,"Unknown key %d\n", key); - } - - ret = cbor_value_advance(&map); - check_ret(ret); - - } - - - return 0; -} - 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]; @@ -1949,7 +878,6 @@ uint8_t ctap_handle_packet(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp) CborEncoder encoder; cbor_encoder_init(&encoder, buf, sizeof(buf), 0); - _ENCODER = &encoder; printf1(TAG_CTAP,"cbor input structure: %d bytes\n", length); printf1(TAG_DUMP,"cbor req: "); dump_hex1(TAG_DUMP, pkt_raw, length); diff --git a/ctap_parse.c b/ctap_parse.c new file mode 100644 index 0000000..47732e3 --- /dev/null +++ b/ctap_parse.c @@ -0,0 +1,1050 @@ +#include + +#include "cbor.h" + +#include "ctap.h" +#include "ctap_parse.h" +#include "ctap_errors.h" +#include "cose_key.h" +#include "util.h" +#include "log.h" + + +void _check_ret(CborError ret, int line, const char * filename) +{ + if (ret != CborNoError) + { + printf1(TAG_ERR,"CborError: 0x%x: %s: %d: %s\n", ret, filename, line, cbor_error_string(ret)); + /*exit(1);*/ + } +} + +const char * cbor_value_get_type_string(const CborValue *value) +{ + switch(cbor_value_get_type(value)) + { + case CborIntegerType: + return "CborIntegerType"; + break; + case CborByteStringType: + return "CborByteStringType"; + break; + case CborTextStringType: + return "CborTextStringType"; + break; + case CborArrayType: + return "CborArrayType"; + break; + case CborMapType: + return "CborMapType"; + break; + case CborTagType: + return "CborTagType"; + break; + case CborSimpleType: + return "CborSimpleType"; + break; + case CborBooleanType: + return "CborBooleanType"; + break; + case CborNullType: + return "CborNullType"; + break; + case CborUndefinedType: + return "CborUndefinedType"; + break; + case CborHalfFloatType: + return "CborHalfFloatType"; + break; + case CborFloatType: + return "CborFloatType"; + break; + case CborDoubleType: + return "CborDoubleType"; + break; + } + return "Invalid type"; +} + + +uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val) +{ + size_t sz, map_length; + uint8_t key[8]; + int ret; + 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) != CborTextStringType) + { + printf2(TAG_ERR,"Error, expecting text string type for user 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\n"); + return CTAP2_ERR_LIMIT_EXCEEDED; + } + check_ret(ret); + key[sizeof(key) - 1] = 0; + + ret = cbor_value_advance(&map); + check_ret(ret); + + if (strcmp(key, "id") == 0) + { + + if (cbor_value_get_type(&map) != CborByteStringType) + { + printf2(TAG_ERR,"Error, expecting byte string type for rp map value\n"); + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + + sz = USER_ID_MAX_SIZE; + ret = cbor_value_copy_byte_string(&map, MC->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; + check_ret(ret); + } + else if (strcmp(key, "name") == 0) + { + sz = USER_NAME_LIMIT; + ret = cbor_value_copy_text_string(&map, MC->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; + } + else + { + printf1(TAG_PARSE,"ignoring key %s for user map\n", key); + } + + ret = cbor_value_advance(&map); + check_ret(ret); + + } + + MC->paramsParsed |= PARAM_user; + + return 0; +} + + +int parse_pub_key_cred_param(CborValue * val, uint8_t * cred_type, int32_t * alg_type) +{ + CborValue map; + CborValue cred; + CborValue alg; + int ret; + uint8_t type_str[16]; + size_t sz = sizeof(type_str); + + if (cbor_value_get_type(val) != CborMapType) + { + printf2(TAG_ERR,"error, expecting map type, got %s\n", cbor_value_get_type_string(val)); + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + + ret = cbor_value_map_find_value(val, "type", &cred); + check_ret(ret); + ret = cbor_value_map_find_value(val, "alg", &alg); + check_ret(ret); + + if (cbor_value_get_type(&cred) != CborTextStringType) + { + printf2(TAG_ERR,"Error, parse_pub_key could not find credential param\n"); + return CTAP2_ERR_MISSING_PARAMETER; + } + if (cbor_value_get_type(&alg) != CborIntegerType) + { + printf2(TAG_ERR,"Error, parse_pub_key could not find alg param\n"); + return CTAP2_ERR_MISSING_PARAMETER; + } + + ret = cbor_value_copy_text_string(&cred, type_str, &sz, NULL); + check_ret(ret); + + type_str[sizeof(type_str) - 1] = 0; + + if (strcmp(type_str, "public-key") == 0) + { + *cred_type = PUB_KEY_CRED_PUB_KEY; + } + else + { + *cred_type = PUB_KEY_CRED_UNKNOWN; + } + + ret = cbor_value_get_int_checked(&alg, alg_type); + check_ret(ret); + + return 0; +} + +// Check if public key credential+algorithm type is supported +static int pub_key_cred_param_supported(uint8_t cred, int32_t alg) +{ + if (cred == PUB_KEY_CRED_PUB_KEY) + { + if (alg == COSE_ALG_ES256) + { + return CREDENTIAL_IS_SUPPORTED; + } + } + + return CREDENTIAL_NOT_SUPPORTED; +} + +uint8_t parse_pub_key_cred_params(CTAP_makeCredential * MC, CborValue * val) +{ + size_t sz, arr_length; + uint8_t cred_type; + int32_t alg_type; + uint8_t key[8]; + int ret; + int i; + CborValue arr; + + + if (cbor_value_get_type(val) != CborArrayType) + { + printf2(TAG_ERR,"error, expecting array type\n"); + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + + ret = cbor_value_enter_container(val,&arr); + check_ret(ret); + + ret = cbor_value_get_array_length(val, &arr_length); + check_ret(ret); + + for (i = 0; i < arr_length; i++) + { + if ((ret = parse_pub_key_cred_param(&arr, &cred_type, &alg_type)) == 0) + { + if (pub_key_cred_param_supported(cred_type, alg_type) == CREDENTIAL_IS_SUPPORTED) + { + MC->publicKeyCredentialType = cred_type; + MC->COSEAlgorithmIdentifier = alg_type; + MC->paramsParsed |= PARAM_pubKeyCredParams; + return 0; + } + } + else + { + // Continue? fail? + return ret; + } + ret = cbor_value_advance(&arr); + check_ret(ret); + } + + printf2(TAG_ERR,"Error, no public key credential parameters are supported!\n"); + return CTAP2_ERR_UNSUPPORTED_ALGORITHM; +} + +uint8_t parse_fixed_byte_string(CborValue * map, uint8_t * dst, int len) +{ + size_t sz; + int ret; + if (cbor_value_get_type(map) == CborByteStringType) + { + sz = len; + ret = cbor_value_copy_byte_string(map, dst, &sz, NULL); + check_ret(ret); + if (sz != len) + { + return CTAP1_ERR_OTHER; + } + } + else + { + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + return 0; +} + + +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) + { + printf2(TAG_ERR,"Error, RP_ID is too large\n"); + return CTAP2_ERR_LIMIT_EXCEEDED; + } + check_ret(ret); + rp->id[DOMAIN_NAME_MAX_SIZE] = 0; // Extra byte defined in struct. + rp->size = sz; + return 0; +} + +uint8_t parse_rp(struct rpId * rp, CborValue * val) +{ + size_t sz, map_length; + uint8_t key[8]; + int ret; + 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); + + rp->size = 0; + + for (i = 0; i < map_length; i++) + { + if (cbor_value_get_type(&map) != CborTextStringType) + { + printf2(TAG_ERR,"Error, expecting text string type for rp 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\n"); + return CTAP2_ERR_LIMIT_EXCEEDED; + } + check_ret(ret); + key[sizeof(key) - 1] = 0; + + ret = cbor_value_advance(&map); + check_ret(ret); + + if (cbor_value_get_type(&map) != CborTextStringType) + { + printf2(TAG_ERR,"Error, expecting text string type for rp map value\n"); + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + + if (strcmp(key, "id") == 0) + { + ret = parse_rp_id(rp, &map); + if (ret != 0) + { + return ret; + } + } + else if (strcmp(key, "name") == 0) + { + sz = RP_NAME_LIMIT; + ret = cbor_value_copy_text_string(&map, rp->name, &sz, NULL); + if (ret != CborErrorOutOfMemory) + { // Just truncate the name it's okay + check_ret(ret); + } + rp->name[RP_NAME_LIMIT - 1] = 0; + } + else + { + printf1(TAG_PARSE,"ignoring key %s for RP map\n", key); + } + + ret = cbor_value_advance(&map); + check_ret(ret); + + } + if (rp->size == 0) + { + printf2(TAG_ERR,"Error, no RPID provided\n"); + return CTAP2_ERR_MISSING_PARAMETER; + } + + + return 0; +} + +uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv) +{ + size_t sz, map_length; + uint8_t key[8]; + int ret; + int i; + _Bool b; + 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) != 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\n"); + return CTAP2_ERR_LIMIT_EXCEEDED; + } + check_ret(ret); + key[sizeof(key) - 1] = 0; + + ret = cbor_value_advance(&map); + check_ret(ret); + + if (cbor_value_get_type(&map) != CborBooleanType) + { + printf2(TAG_ERR,"Error, expecting text string type for rp map value\n"); + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + + if (strcmp(key, "rk") == 0) + { + ret = cbor_value_get_boolean(&map, &b); + check_ret(ret); + *rk = b; + } + else if (strcmp(key, "uv") == 0) + { + ret = cbor_value_get_boolean(&map, &b); + check_ret(ret); + *uv = b; + } + else + { + printf1(TAG_PARSE,"ignoring key %s for option map\n", key); + } + + + + } +} + +uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length) +{ + int ret; + int i; + int key; + size_t map_length; + size_t sz; + CborParser parser; + CborValue it,map; + + memset(MC, 0, sizeof(CTAP_makeCredential)); + ret = cbor_parser_init(request, length, CborValidateCanonicalFormat, &parser, &it); + check_retr(ret); + + CborType type = cbor_value_get_type(&it); + if (type != CborMapType) + { + printf2(TAG_ERR,"Error, expecting cbor map\n"); + return CTAP2_ERR_CBOR_UNEXPECTED_TYPE; + } + + ret = cbor_value_enter_container(&it,&map); + check_ret(ret); + + ret = cbor_value_get_map_length(&it, &map_length); + check_ret(ret); + + printf1(TAG_MC,"map has %d elements\n",map_length); + + for (i = 0; i < map_length; i++) + { + type = cbor_value_get_type(&map); + if (type != CborIntegerType) + { + printf2(TAG_ERR,"Error, expecting int for map key\n"); + return CTAP2_ERR_CBOR_UNEXPECTED_TYPE; + } + ret = cbor_value_get_int_checked(&map, &key); + check_ret(ret); + + ret = cbor_value_advance(&map); + check_ret(ret); + ret = 0; + + switch(key) + { + + case MC_clientDataHash: + printf1(TAG_MC,"CTAP_clientDataHash\n"); + + ret = parse_fixed_byte_string(&map, MC->clientDataHash, CLIENT_DATA_HASH_SIZE); + if (ret == 0) + { + MC->paramsParsed |= PARAM_clientDataHash; + } + + printf1(TAG_MC," "); dump_hex1(TAG_MC,MC->clientDataHash, 32); + break; + case MC_rp: + printf1(TAG_MC,"CTAP_rp\n"); + + ret = parse_rp(&MC->rp, &map); + if (ret == 0) + { + MC->paramsParsed |= PARAM_rp; + } + + + printf1(TAG_MC," ID: %s\n", MC->rp.id); + printf1(TAG_MC," name: %s\n", MC->rp.name); + break; + case MC_user: + printf1(TAG_MC,"CTAP_user\n"); + + 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); + + break; + case MC_pubKeyCredParams: + printf1(TAG_MC,"CTAP_pubKeyCredParams\n"); + + 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); + + break; + case MC_excludeList: + printf1(TAG_MC,"CTAP_excludeList\n"); + break; + case MC_extensions: + printf1(TAG_MC,"CTAP_extensions\n"); + break; + + case MC_options: + printf1(TAG_MC,"CTAP_options\n"); + ret = parse_options(&map, &MC->rk, &MC->uv); + check_retr(ret); + break; + case MC_pinAuth: + printf1(TAG_MC,"CTAP_pinAuth\n"); + + ret = parse_fixed_byte_string(&map, MC->pinAuth, 16); + check_retr(ret); + MC->pinAuthPresent = 1; + + break; + case MC_pinProtocol: + printf1(TAG_MC,"CTAP_pinProtocol\n"); + if (cbor_value_get_type(&map) == CborIntegerType) + { + ret = cbor_value_get_int_checked(&map, &MC->pinProtocol); + check_ret(ret); + } + else + { + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + + break; + + default: + printf1(TAG_MC,"invalid key %d\n", key); + + } + if (ret != 0) + { + return ret; + } + + cbor_value_advance(&map); + check_ret(ret); + } + + return 0; +} + +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) + { + printf2(TAG_ERR,"Error, expecting cbor array\n"); + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + + 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) + { + printf1(TAG_PARSE,"Error, out of memory for allow list.\n"); + return CTAP2_ERR_TOO_MANY_ELEMENTS; + } + + GA->credLen += 1; + + if (cbor_value_get_type(&arr) != CborMapType) + { + printf2(TAG_ERR,"Error, CborMapType expected in allow_list\n"); + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + + ret = cbor_value_map_find_value(&arr, "id", &val); + check_ret(ret); + + if (cbor_value_get_type(&val) != CborByteStringType) + { + printf2(TAG_ERR,"Error, No valid ID field (%s)\n", cbor_value_get_type_string(&val)); + return CTAP2_ERR_MISSING_PARAMETER; + } + + buflen = CREDENTIAL_ID_SIZE; + cbor_value_copy_byte_string(&val, GA->creds[i].credential.id, &buflen, NULL); + if (buflen != CREDENTIAL_ID_SIZE) + { + printf2(TAG_ERR,"Error, credential is incorrect length\n"); + return CTAP2_ERR_CBOR_UNEXPECTED_TYPE; // 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) + { + printf2(TAG_ERR,"Error, No valid type field\n"); + return CTAP2_ERR_MISSING_PARAMETER; + } + + 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; +} + + +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) + { + printf2(TAG_ERR,"Error, expecting cbor map\n"); + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + + ret = cbor_value_enter_container(&it,&map); + check_ret(ret); + + ret = cbor_value_get_map_length(&it, &map_length); + check_ret(ret); + + printf1(TAG_GA,"GA map has %d elements\n",map_length); + + for (i = 0; i < map_length; i++) + { + type = cbor_value_get_type(&map); + if (type != CborIntegerType) + { + printf2(TAG_ERR,"Error, expecting int for map key\n"); + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + 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: + printf1(TAG_GA,"GA_clientDataHash\n"); + + ret = parse_fixed_byte_string(&map, GA->clientDataHash, CLIENT_DATA_HASH_SIZE); + check_retr(ret); + + printf1(TAG_GA," "); dump_hex1(TAG_GA, GA->clientDataHash, 32); + break; + case GA_rpId: + printf1(TAG_GA,"GA_rpId\n"); + + ret = parse_rp_id(&GA->rp, &map); + + printf1(TAG_GA," ID: %s\n", GA->rp.id); + break; + case GA_allowList: + printf1(TAG_GA,"GA_allowList\n"); + ret = parse_allow_list(GA, &map); + if (ret == 0) + { + /*for (j = 0; j < GA->credLen; j++)*/ + /*{*/ + /*printf1(TAG_GA,"CRED ID (# %d): ", GA->creds[j].credential.fields.count);*/ + /*dump_hex1(TAG_GA, GA->creds[j].credential.id, CREDENTIAL_ID_SIZE);*/ + /*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");*/ + /*}*/ + /*}*/ + } + break; + case GA_extensions: + printf1(TAG_GA,"GA_extensions\n"); + break; + + case GA_options: + printf1(TAG_GA,"CTAP_options\n"); + ret = parse_options(&map, &GA->rk, &GA->uv); + check_retr(ret); + break; + case GA_pinAuth: + printf1(TAG_GA,"CTAP_pinAuth\n"); + + ret = parse_fixed_byte_string(&map, GA->pinAuth, 16); + check_retr(ret); + GA->pinAuthPresent = 1; + + break; + case GA_pinProtocol: + printf1(TAG_GA,"CTAP_pinProtocol\n"); + if (cbor_value_get_type(&map) == CborIntegerType) + { + ret = cbor_value_get_int_checked(&map, &GA->pinProtocol); + check_ret(ret); + } + else + { + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + + break; + + } + if (ret != 0) + { + printf2(TAG_ERR,"error, parsing failed\n"); + return ret; + } + + cbor_value_advance(&map); + check_ret(ret); + } + + + return 0; +} + +uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int * crv) +{ + CborValue map; + size_t map_length; + size_t ptsz; + int i,ret,key; + int xkey = 0,ykey = 0; + *kty = 0; + *crv = 0; + + + CborType type = cbor_value_get_type(it); + if (type != CborMapType) + { + printf2(TAG_ERR,"Error, expecting cbor map\n"); + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + + ret = cbor_value_enter_container(it,&map); + check_ret(ret); + + ret = cbor_value_get_map_length(it, &map_length); + check_ret(ret); + + printf1(TAG_PARSE,"cose key has %d elements\n",map_length); + + for (i = 0; i < map_length; i++) + { + if (cbor_value_get_type(&map) != CborIntegerType) + { + printf2(TAG_ERR,"Error, expecting int for map key\n"); + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + + ret = cbor_value_get_int_checked(&map, &key); + check_ret(ret); + + ret = cbor_value_advance(&map); + check_ret(ret); + + switch(key) + { + case COSE_KEY_LABEL_KTY: + printf1(TAG_PARSE,"COSE_KEY_LABEL_KTY\n"); + if (cbor_value_get_type(&map) == CborIntegerType) + { + ret = cbor_value_get_int_checked(&map, kty); + check_ret(ret); + } + else + { + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + break; + case COSE_KEY_LABEL_ALG: + printf1(TAG_PARSE,"COSE_KEY_LABEL_ALG\n"); + break; + case COSE_KEY_LABEL_CRV: + printf1(TAG_PARSE,"COSE_KEY_LABEL_CRV\n"); + if (cbor_value_get_type(&map) == CborIntegerType) + { + ret = cbor_value_get_int_checked(&map, crv); + check_ret(ret); + } + else + { + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + break; + case COSE_KEY_LABEL_X: + printf1(TAG_PARSE,"COSE_KEY_LABEL_X\n"); + ret = parse_fixed_byte_string(&map, 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); + check_retr(ret); + ykey = 1; + + break; + default: + printf1(TAG_PARSE,"Warning, unrecognized cose key option %d\n", key); + } + + ret = cbor_value_advance(&map); + check_ret(ret); + } + if (xkey == 0 || ykey == 0 || *kty == 0 || *crv == 0) + { + return CTAP2_ERR_MISSING_PARAMETER; + } + return 0; +} + +int ctap_parse_client_pin(CTAP_clientPin * CP, 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(CP, 0, sizeof(CTAP_clientPin)); + ret = cbor_parser_init(request, length, CborValidateCanonicalFormat, &parser, &it); + check_ret(ret); + + CborType type = cbor_value_get_type(&it); + if (type != CborMapType) + { + printf2(TAG_ERR,"Error, expecting cbor map\n"); + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + + ret = cbor_value_enter_container(&it,&map); + check_ret(ret); + + ret = cbor_value_get_map_length(&it, &map_length); + check_ret(ret); + + printf1(TAG_CP,"CP map has %d elements\n",map_length); + + for (i = 0; i < map_length; i++) + { + type = cbor_value_get_type(&map); + if (type != CborIntegerType) + { + printf2(TAG_ERR,"Error, expecting int for map key\n"); + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + ret = cbor_value_get_int_checked(&map, &key); + check_ret(ret); + + ret = cbor_value_advance(&map); + check_ret(ret); + ret = 0; + + switch(key) + { + case CP_pinProtocol: + printf1(TAG_CP,"CP_pinProtocol\n"); + if (cbor_value_get_type(&map) == CborIntegerType) + { + cbor_value_get_int_checked(&map, &CP->pinProtocol); + check_ret(ret); + } + else + { + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + break; + case CP_subCommand: + printf1(TAG_CP,"CP_subCommand\n"); + if (cbor_value_get_type(&map) == CborIntegerType) + { + cbor_value_get_int_checked(&map, &CP->subCommand); + check_ret(ret); + } + else + { + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + + 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); + check_retr(ret); + CP->keyAgreementPresent = 1; + break; + case CP_pinAuth: + printf1(TAG_CP,"CP_pinAuth\n"); + + ret = parse_fixed_byte_string(&map, CP->pinAuth, 16); + check_retr(ret); + CP->pinAuthPresent = 1; + break; + case CP_newPinEnc: + printf1(TAG_CP,"CP_newPinEnc\n"); + if (cbor_value_get_type(&map) == CborByteStringType) + { + ret = cbor_value_calculate_string_length(&map, &sz); + check_ret(ret); + if (sz > NEW_PIN_ENC_MAX_SIZE) + { + return CTAP1_ERR_OTHER; + } + CP->newPinEncSize = sz; + sz = NEW_PIN_ENC_MAX_SIZE; + ret = cbor_value_copy_byte_string(&map, CP->newPinEnc, &sz, NULL); + check_ret(ret); + } + else + { + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + + break; + case CP_pinHashEnc: + printf1(TAG_CP,"CP_pinHashEnc\n"); + + ret = parse_fixed_byte_string(&map, CP->pinHashEnc, 16); + check_retr(ret); + CP->pinHashEncPresent = 1; + + break; + case CP_getKeyAgreement: + printf1(TAG_CP,"CP_getKeyAgreement\n"); + ret = cbor_value_get_boolean(&map, &CP->getKeyAgreement); + check_ret(ret); + break; + case CP_getRetries: + printf1(TAG_CP,"CP_getRetries\n"); + ret = cbor_value_get_boolean(&map, &CP->getRetries); + check_ret(ret); + break; + default: + printf1(TAG_CP,"Unknown key %d\n", key); + } + + ret = cbor_value_advance(&map); + check_ret(ret); + + } + + + return 0; +} + + diff --git a/ctap_parse.h b/ctap_parse.h new file mode 100644 index 0000000..07710cd --- /dev/null +++ b/ctap_parse.h @@ -0,0 +1,34 @@ +#ifndef _CTAP_PARSE_H +#define _CTAP_PARSE_H + + +#define check_ret(r) _check_ret(r,__LINE__, __FILE__);\ + if ((r) != CborNoError) return CTAP2_ERR_CBOR_PARSING; + +#define check_retr(r) _check_ret(r,__LINE__, __FILE__);\ + if ((r) != CborNoError) return r; + + +extern void _check_ret(CborError ret, int line, const char * filename); + + +const char * cbor_value_get_type_string(const CborValue *value); + + +uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val); +int parse_pub_key_cred_param(CborValue * val, uint8_t * cred_type, int32_t * alg_type); +uint8_t parse_pub_key_cred_params(CTAP_makeCredential * MC, CborValue * val); +uint8_t parse_fixed_byte_string(CborValue * map, uint8_t * dst, int len); +int parse_rp_id(struct rpId * rp, CborValue * val); +uint8_t parse_rp(struct rpId * rp, CborValue * val); +uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv); + + +uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length); +int ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int length); +int ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length); + +int 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); + +#endif