Skip to content

Commit

Permalink
Implement ctap_parse_client_pin (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
pokusew committed Jan 15, 2025
1 parent be633f6 commit 8b85baf
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 38 deletions.
175 changes: 143 additions & 32 deletions core/ctap_parse.c
Original file line number Diff line number Diff line change
@@ -1,40 +1,157 @@
#include "ctap_parse.h"
#include <cbor.h>

uint8_t parse_fixed_byte_string(CborValue *map, uint8_t *dst, unsigned int len) {
uint8_t parse_fixed_byte_string(const CborValue *value, uint8_t *buffer, size_t expected_length) {

size_t sz;
CborError err;

if (!cbor_value_is_byte_string(value)) {
printf("parse_fixed_byte_string: not a byte string" nl);
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
}

size_t length = expected_length;
// If the byte string does not fit into the buffer of the given length
// the cbor_value_copy_byte_string returns an error and does NOT update the length value.
cbor_decoding_check(cbor_value_copy_byte_string(value, buffer, &length, NULL));
// On success, the cbor_value_copy_byte_string updates the length value to the number
// of bytes copied to the buffer. From the described contract, it is clear that the following must hold:
// length <= expected_length
assert(length <= expected_length);
if (length != expected_length) {
printf("parse_fixed_byte_string: invalid length: actual %zu < expected %zu" nl, length, expected_length);
return CTAP1_ERR_INVALID_LENGTH; // TODO: Use CTAP2_ERR_CBOR_UNEXPECTED_TYPE?
}

return CTAP2_OK;

}

uint8_t parse_byte_string(
const CborValue *value,
uint8_t *buffer,
size_t *length,
size_t min_length,
size_t max_length
) {

assert(min_length <= max_length);

CborError err;

if (!cbor_value_is_byte_string(map)) {
printf("error, CborByteStringType expected" nl);
if (!cbor_value_is_byte_string(value)) {
printf("parse_fixed_byte_string: not a byte string" nl);
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
}

sz = len;
cbor_decoding_check(cbor_value_copy_byte_string(map, dst, &sz, NULL));
if (sz != len) {
printf("error byte string is different length (%d vs %zu)" nl, len, sz);
return CTAP1_ERR_INVALID_LENGTH;
size_t actual_length;
cbor_decoding_check(cbor_value_get_string_length(value, &actual_length));
if (actual_length > max_length || actual_length < min_length) {
return CTAP1_ERR_INVALID_LENGTH; // TODO: Use CTAP2_ERR_CBOR_UNEXPECTED_TYPE?
}

*length = actual_length;
cbor_decoding_check(cbor_value_copy_byte_string(value, buffer, length, NULL));
assert(*length == actual_length);

return CTAP2_OK;

}

uint8_t ctap_parse_client_pin(const uint8_t *request, size_t length, CTAP_clientPIN *cp) {
uint8_t parse_cose_key(CborValue *it, COSE_Key *cose) {

uint8_t ret;
CborError err;

int key;
CborValue map;
size_t map_length;
size_t sz;
int key;

bool pubkey_x_parsed = false;
bool pubkey_y_parsed = false;
cose->kty = 0;
cose->crv = 0;

if (!cbor_value_is_map(it)) {
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
}

cbor_decoding_check(cbor_value_enter_container(it, &map));

cbor_decoding_check(cbor_value_get_map_length(it, &map_length));

printf("COSE_Key map has %zu elements" nl, map_length);

for (size_t i = 0; i < map_length; i++) {
// read the current key
if (!cbor_value_is_integer(&map)) {
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
}
cbor_decoding_check(cbor_value_get_int_checked(&map, &key));
// advance to the corresponding value
cbor_decoding_check(cbor_value_advance(&map));
// parse the value according to the key
switch (key) {
case COSE_KEY_LABEL_KTY:
printf("COSE_KEY_LABEL_KTY" nl);
if (!cbor_value_is_integer(&map)) {
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
}
cbor_decoding_check(cbor_value_get_int_checked(&map, &cose->kty));
break;
case COSE_KEY_LABEL_ALG:
printf("COSE_KEY_LABEL_ALG" nl);
break;
case COSE_KEY_LABEL_CRV:
printf("COSE_KEY_LABEL_CRV" nl);
if (!cbor_value_is_integer(&map)) {
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
}
cbor_decoding_check(cbor_value_get_int_checked(&map, &cose->crv));
break;
case COSE_KEY_LABEL_X:
printf("COSE_KEY_LABEL_X" nl);
if ((ret = parse_fixed_byte_string(&map, cose->pubkey.x, 32)) != CTAP2_OK) {
return ret;
}
pubkey_x_parsed = 1;
break;
case COSE_KEY_LABEL_Y:
printf("COSE_KEY_LABEL_Y" nl);
if ((ret = parse_fixed_byte_string(&map, cose->pubkey.y, 32)) != CTAP2_OK) {
return ret;
}
pubkey_y_parsed = 1;
break;
default:
printf("warning: unrecognized cose key option %d" nl, key);
}

// advance to the next map key
cbor_decoding_check(cbor_value_advance(&map));

}

// validate
if (pubkey_x_parsed == 0 || pubkey_y_parsed == 0 || cose->kty == 0 || cose->crv == 0) {
return CTAP2_ERR_MISSING_PARAMETER;
}

return CTAP2_OK;

}

uint8_t ctap_parse_client_pin(const uint8_t *request, size_t length, CTAP_clientPIN *cp) {

CborParser parser;
CborValue it;

uint8_t ret;
CborError err;

CborValue map;
size_t map_length;
int key;

memset(cp, 0, sizeof(CTAP_clientPIN));

Expand Down Expand Up @@ -84,10 +201,9 @@ uint8_t ctap_parse_client_pin(const uint8_t *request, size_t length, CTAP_client
break;
case CTAP_clientPIN_keyAgreement:
printf("CTAP_clientPIN_keyAgreement" nl);
// TODO
// if ((ret = parse_cose_key(&map, &cp->keyAgreement)) != CTAP2_OK) {
// return ret;
// }
if ((ret = parse_cose_key(&map, &cp->keyAgreement)) != CTAP2_OK) {
return ret;
}
cp->keyAgreementPresent = true;
break;
case CTAP_clientPIN_pinUvAuthParam:
Expand All @@ -99,21 +215,16 @@ uint8_t ctap_parse_client_pin(const uint8_t *request, size_t length, CTAP_client
break;
case CTAP_clientPIN_newPinEnc:
printf("CTAP_clientPIN_newPinEnc" nl);
// TODO
// 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 || sz < NEW_PIN_ENC_MIN_SIZE) {
// return CTAP2_ERR_PIN_POLICY_VIOLATION;
// }
//
// 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;
// }
if ((
ret = parse_byte_string(
&map,
cp->newPinEnc,
&cp->newPinEncSize,
NEW_PIN_ENC_MIN_SIZE,
NEW_PIN_ENC_MAX_SIZE
)) != CTAP2_OK) {
return ret;
}
break;
case CTAP_clientPIN_pinHashEnc:
printf("CTAP_clientPIN_pinHashEnc" nl);
Expand All @@ -123,7 +234,7 @@ uint8_t ctap_parse_client_pin(const uint8_t *request, size_t length, CTAP_client
cp->pinHashEncPresent = true;
break;
default:
printf("Unknown key %d" nl, key);
printf("ctap_parse_client_pin: unknown key %d" nl, key);
}

// advance to the next map key
Expand Down
30 changes: 24 additions & 6 deletions core/ctap_parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,22 @@ typedef struct COSE_Key {
int crv;
} COSE_Key;

#define COSE_KEY_LABEL_KTY 1
#define COSE_KEY_LABEL_ALG 3
#define COSE_KEY_LABEL_CRV -1
#define COSE_KEY_LABEL_X -2
#define COSE_KEY_LABEL_Y -3

#define COSE_KEY_KTY_OKP 1
#define COSE_KEY_KTY_EC2 2

#define COSE_KEY_CRV_P256 1
#define COSE_KEY_CRV_ED25519 6

#define COSE_ALG_ES256 -7
#define COSE_ALG_EDDSA -8
#define COSE_ALG_ECDH_ES_HKDF_256 -25

// request (message)
// CTAPHID_CBOR
// CTAP command byte
Expand All @@ -52,10 +68,12 @@ typedef struct COSE_Key {
// CTAP status code
// n bytes of CBOR encoded data

#define NEW_PIN_ENC_MAX_SIZE 256 // includes NULL terminator
// pinUvAuthProtocol 1 = 64 bytes
// pinUvAuthProtocol 2 = 80 bytes (16 + 64)
// NEW_PIN_ENC_MAX_SIZE = max(64, 80)
// NEW_PIN_ENC_MIN_SIZE = max(64, 80)
#define NEW_PIN_ENC_MAX_SIZE 80
#define NEW_PIN_ENC_MIN_SIZE 64
#define NEW_PIN_MAX_SIZE 64
#define NEW_PIN_MIN_SIZE 4

// Command
// code (one byte)
Expand All @@ -71,11 +89,11 @@ typedef struct CTAP_clientPIN {
int subCommand;
COSE_Key keyAgreement;
bool keyAgreementPresent;
uint8_t pinUvAuthParam[16];
uint8_t pinUvAuthParam[16]; // TODO: check size v1 vs v2
bool pinUvAuthParamPresent;
uint8_t newPinEnc[NEW_PIN_ENC_MAX_SIZE];
int newPinEncSize;
uint8_t pinHashEnc[16];
size_t newPinEncSize;
uint8_t pinHashEnc[16]; // TODO: check size v1 vs v2
bool pinHashEncPresent;
int permissions;
bool permissionsPresent;
Expand Down

0 comments on commit 8b85baf

Please sign in to comment.