kopia lustrzana https://github.com/solokeys/solo1
1083 wiersze
30 KiB
C
1083 wiersze
30 KiB
C
/*
|
|
Copyright 2018 Conor Patrick
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
this software and associated documentation files (the "Software"), to deal in
|
|
the Software without restriction, including without limitation the rights to
|
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
of the Software, and to permit persons to whom the Software is furnished to do
|
|
so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
*/
|
|
#include <stdint.h>
|
|
|
|
#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;
|
|
default:
|
|
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, (char *)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((const char*)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((const char *)key, "name") == 0)
|
|
{
|
|
sz = USER_NAME_LIMIT;
|
|
ret = cbor_value_copy_text_string(&map, (char *)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;
|
|
}
|
|
|
|
|
|
uint8_t parse_pub_key_cred_param(CborValue * val, uint8_t * cred_type, int32_t * alg_type)
|
|
{
|
|
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, (char*)type_str, &sz, NULL);
|
|
check_ret(ret);
|
|
|
|
type_str[sizeof(type_str) - 1] = 0;
|
|
|
|
if (strcmp((const char*)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, (int*)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 arr_length;
|
|
uint8_t cred_type;
|
|
int32_t alg_type;
|
|
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;
|
|
}
|
|
|
|
|
|
uint8_t parse_rp_id(struct rpId * rp, CborValue * val)
|
|
{
|
|
size_t sz = DOMAIN_NAME_MAX_SIZE;
|
|
int ret = cbor_value_copy_text_string(val, (char*)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;
|
|
char 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, (char*)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;
|
|
char 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);
|
|
}
|
|
|
|
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
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");
|
|
if( cbor_value_get_type(&map) == CborArrayType )
|
|
{
|
|
ret = cbor_value_enter_container(&map, &MC->excludeList);
|
|
check_ret(ret);
|
|
|
|
ret = cbor_value_get_array_length(&map, &MC->excludeListSize);
|
|
check_ret(ret);
|
|
}
|
|
else
|
|
{
|
|
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
|
}
|
|
printf1(TAG_MC,"CTAP_excludeList done\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;
|
|
}
|
|
|
|
uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor * cred)
|
|
{
|
|
int ret;
|
|
size_t buflen;
|
|
char type[12];
|
|
CborValue val;
|
|
if (cbor_value_get_type(arr) != CborMapType)
|
|
{
|
|
printf2(TAG_ERR,"Error, CborMapType expected in credential\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 = sizeof(struct Credential);
|
|
cbor_value_copy_byte_string(&val, (uint8_t*)&cred->credential, &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)
|
|
{
|
|
cred->type = PUB_KEY_CRED_PUB_KEY;
|
|
}
|
|
else
|
|
{
|
|
cred->type = PUB_KEY_CRED_UNKNOWN;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint8_t parse_allow_list(CTAP_getAssertion * GA, CborValue * it)
|
|
{
|
|
CborValue arr;
|
|
size_t len;
|
|
int i,ret;
|
|
CTAP_credentialDescriptor * cred;
|
|
|
|
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;
|
|
cred = &GA->creds[i];
|
|
|
|
ret = parse_credential_descriptor(&arr,cred);
|
|
check_retr(ret);
|
|
|
|
ret = cbor_value_advance(&arr);
|
|
check_ret(ret);
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int length)
|
|
{
|
|
int ret;
|
|
int i;
|
|
int key;
|
|
size_t map_length;
|
|
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)
|
|
{
|
|
|
|
}
|
|
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;
|
|
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;
|
|
}
|
|
|
|
uint8_t ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length)
|
|
{
|
|
int ret;
|
|
int i;
|
|
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;
|
|
}
|
|
|
|
|