Skip to content

Commit

Permalink
add SM2 settings for ctap
Browse files Browse the repository at this point in the history
  • Loading branch information
dangfan committed Dec 25, 2023
1 parent a784504 commit 0ddb93b
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 111 deletions.
19 changes: 7 additions & 12 deletions applets/ctap/cose-key.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,18 @@

#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_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_KEY_CRV_SM2 9

#define COSE_ALG_ES256 (-7)
#define COSE_ALG_EDDSA (-8)
#define COSE_ALG_ECDH_ES_HKDF_256 (-25)
#define COSE_ALG_SM2 (-48)

#define COSE_KEY_ES256_SIZE 77
#define COSE_KEY_ECDH_ES_HKDF_257_SIZE 78
#define COSE_KEY_EDDSA_SIZE 42
#define COSE_ALG_ES256 -7
#define COSE_ALG_EDDSA -8
#define COSE_ALG_ECDH_ES_HKDF_256 -25

#endif // CANOKEY_CORE_FIDO2_COSE_KEY_H_
7 changes: 7 additions & 0 deletions applets/ctap/ctap-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#define PIN_CTR_ATTR 0x03
#define KH_KEY_ATTR 0x04
#define HE_KEY_ATTR 0x05
#define SM2_ATTR 0x06
#define DC_FILE "ctap_dc"
#define DC_GENERAL_ATTR 0x00
#define DC_META_FILE "ctap_dm"
Expand Down Expand Up @@ -364,6 +365,12 @@ typedef struct {
uint8_t pin_uv_auth_param[SHA256_DIGEST_LENGTH];
} CTAP_large_blobs;

typedef struct {
uint8_t enabled;
int32_t curve_id;
int32_t algo_id;
} CTAP_sm2_attr;

int u2f_register(const CAPDU *capdu, RAPDU *rapdu);
int u2f_authenticate(const CAPDU *capdu, RAPDU *rapdu);
int u2f_version(const CAPDU *capdu, RAPDU *rapdu);
Expand Down
2 changes: 1 addition & 1 deletion applets/ctap/ctap-parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ uint8_t parse_cose_key(CborValue *val, uint8_t *public_key) {
if (cbor_value_get_type(&map) != CborIntegerType) return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
ret = cbor_value_get_int_checked(&map, &key);
CHECK_CBOR_RET(ret);
if (key != COSE_ALG_ES256 && key != COSE_ALG_ECDH_ES_HKDF_256) return CTAP2_ERR_UNHANDLED_REQUEST;
if (key != COSE_ALG_ECDH_ES_HKDF_256) return CTAP2_ERR_UNHANDLED_REQUEST;
++parsed_keys;
break;

Expand Down
182 changes: 102 additions & 80 deletions applets/ctap/ctap.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#include "ctap-parser.h"
#include "secret.h"
#include "u2f.h"
#include <aes.h>
#include <block-cipher.h>
#include <cbor.h>
#include <common.h>
Expand Down Expand Up @@ -59,12 +58,15 @@ static const uint8_t aaguid[] = {0x24, 0x4e, 0xb2, 0x9e, 0xe0, 0x90, 0x4e, 0x49,

// pin & command states
static uint8_t consecutive_pin_counter, last_cmd;
// SM2 attr
CTAP_sm2_attr ctap_sm2_attr;

uint8_t ctap_install(uint8_t reset) {
consecutive_pin_counter = 3;
last_cmd = CTAP_INVALID_CMD;
cp_initialize();
if (!reset && get_file_size(CTAP_CERT_FILE) >= 0) {
if (!reset && get_file_size(LB_FILE) >= 0) {
if (read_attr(CTAP_CERT_FILE, SM2_ATTR, &ctap_sm2_attr, sizeof(ctap_sm2_attr)) < 0) return CTAP2_ERR_UNHANDLED_REQUEST;
DBG_MSG("CTAP initialized\n");
return 0;
}
Expand All @@ -79,6 +81,10 @@ uint8_t ctap_install(uint8_t reset) {
if (write_attr(CTAP_CERT_FILE, KH_KEY_ATTR, kh_key, sizeof(kh_key)) < 0) return CTAP2_ERR_UNHANDLED_REQUEST;
random_buffer(kh_key, sizeof(kh_key));
if (write_attr(CTAP_CERT_FILE, HE_KEY_ATTR, kh_key, sizeof(kh_key)) < 0) return CTAP2_ERR_UNHANDLED_REQUEST;
ctap_sm2_attr.enabled = 0;
ctap_sm2_attr.curve_id = 9; // An unused one. See https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves
ctap_sm2_attr.algo_id = -48; // An unused one. See https://www.iana.org/assignments/cose/cose.xhtml#algorithms
if (write_attr(CTAP_CERT_FILE, SM2_ATTR, &ctap_sm2_attr, sizeof(ctap_sm2_attr)) < 0) return CTAP2_ERR_UNHANDLED_REQUEST;
memcpy(kh_key,
(uint8_t[]) {0x80, 0x76, 0xbe, 0x8b, 0x52, 0x8d, 0x00, 0x75, 0xf7, 0xaa, 0xe9, 0x8d, 0x6f, 0xa5, 0x7a, 0x6d,
0x3c}, 17);
Expand All @@ -98,72 +104,70 @@ int ctap_install_cert(const CAPDU *capdu, RAPDU *rapdu) {
return write_file(CTAP_CERT_FILE, DATA, 0, LC, 1);
}

static void build_cose_key(uint8_t *data, uint8_t ecdh) {
// format public key as
// A5
// 01 02
// 03 26 (ecdsa) or 03 38 18 (ecdh)
// 20 01
// 21 58 20 x
// 22 58 20 y
if (ecdh) {
memmove(data + 46, data + 32, 32);
memmove(data + 11, data, 32);
} else {
memmove(data + 45, data + 32, 32);
memmove(data + 10, data, 32);
}
data[0] = 0xA5;
data[1] = 0x01;
data[2] = 0x02;
data[3] = 0x03;
if (ecdh) {
data[4] = 0x38;
data[5] = 0x18;
data[6] = 0x20;
data[7] = 0x01;
data[8] = 0x21;
data[9] = 0x58;
data[10] = 0x20;
data[43] = 0x22;
data[44] = 0x58;
data[45] = 0x20;
} else {
data[4] = 0x26;
data[5] = 0x20;
data[6] = 0x01;
data[7] = 0x21;
data[8] = 0x58;
data[9] = 0x20;
data[42] = 0x22;
data[43] = 0x58;
data[44] = 0x20;
}
static int build_ecdsa_cose_key(uint8_t *data, int algo, int curve) {
uint8_t buf[80];
CborEncoder encoder, map_encoder;

cbor_encoder_init(&encoder, buf, sizeof(buf), 0);
CborError ret = cbor_encoder_create_map(&encoder, &map_encoder, 5);
CHECK_CBOR_RET(ret);
ret = cbor_encode_int(&map_encoder, COSE_KEY_LABEL_KTY);
CHECK_CBOR_RET(ret);
ret = cbor_encode_int(&map_encoder, COSE_KEY_KTY_EC2);
CHECK_CBOR_RET(ret);
ret = cbor_encode_int(&map_encoder, COSE_KEY_LABEL_ALG);
CHECK_CBOR_RET(ret);
ret = cbor_encode_int(&map_encoder, algo);
CHECK_CBOR_RET(ret);
ret = cbor_encode_int(&map_encoder, COSE_KEY_LABEL_CRV);
CHECK_CBOR_RET(ret);
ret = cbor_encode_int(&map_encoder, curve);
CHECK_CBOR_RET(ret);
ret = cbor_encode_int(&map_encoder, COSE_KEY_LABEL_X);
CHECK_CBOR_RET(ret);
ret = cbor_encode_byte_string(&map_encoder, data, 32);
CHECK_CBOR_RET(ret);
ret = cbor_encode_int(&map_encoder, COSE_KEY_LABEL_Y);
CHECK_CBOR_RET(ret);
ret = cbor_encode_byte_string(&map_encoder, data + 32, 32);
CHECK_CBOR_RET(ret);
ret = cbor_encoder_close_container(&encoder, &map_encoder);
CHECK_CBOR_RET(ret);

const int len = cbor_encoder_get_buffer_size(&encoder, buf);
memcpy(data, buf, len);
return len;
}

static void build_ed25519_cose_key(uint8_t *data) {
// A4 # map(4)
// 01 # unsigned(1) kty =
// 01 # unsigned(2) OKP (1)
// 03 # unsigned(3) alg =
// 27 # negative(7) EdDSA (-8)
// 20 # negative(0) crv =
// 06 # unsigned(6) Ed25519 (6)
// 21 # negative(1) x =
// 58 20 # bytes(32) [bstr]
// (32 bytes x)

memmove(data + 10, data, 32);
data[0] = 0xa4;
data[1] = 0x01;
data[2] = 0x01;
data[3] = 0x03;
data[4] = 0x27;
data[5] = 0x20;
data[6] = 0x06;
data[7] = 0x21;
data[8] = 0x58;
data[9] = 0x20;
static int build_ed25519_cose_key(uint8_t *data) {
uint8_t buf[50];
CborEncoder encoder, map_encoder;

cbor_encoder_init(&encoder, buf, sizeof(buf), 0);
CborError ret = cbor_encoder_create_map(&encoder, &map_encoder, 4);
CHECK_CBOR_RET(ret);
ret = cbor_encode_int(&map_encoder, COSE_KEY_LABEL_KTY);
CHECK_CBOR_RET(ret);
ret = cbor_encode_int(&map_encoder, COSE_KEY_KTY_OKP);
CHECK_CBOR_RET(ret);
ret = cbor_encode_int(&map_encoder, COSE_KEY_LABEL_ALG);
CHECK_CBOR_RET(ret);
ret = cbor_encode_int(&map_encoder, COSE_ALG_EDDSA);
CHECK_CBOR_RET(ret);
ret = cbor_encode_int(&map_encoder, COSE_KEY_LABEL_CRV);
CHECK_CBOR_RET(ret);
ret = cbor_encode_int(&map_encoder, COSE_KEY_CRV_ED25519);
CHECK_CBOR_RET(ret);
ret = cbor_encode_int(&map_encoder, COSE_KEY_LABEL_X);
CHECK_CBOR_RET(ret);
ret = cbor_encode_byte_string(&map_encoder, data, 32);
CHECK_CBOR_RET(ret);
ret = cbor_encoder_close_container(&encoder, &map_encoder);
CHECK_CBOR_RET(ret);

const int len = cbor_encoder_get_buffer_size(&encoder, buf);
memcpy(data, buf, len);
return len;
}

int ctap_consistency_check(void) {
Expand Down Expand Up @@ -259,16 +263,18 @@ uint8_t ctap_make_auth_data(uint8_t *rp_id_hash, uint8_t *buf, uint8_t flags, co
DBG_MSG("Fail to generate a key handle\n");
return CTAP2_ERR_UNHANDLED_REQUEST;
}
int cose_key_size;
if (alg_type == COSE_ALG_ES256) {
build_cose_key(ad->at.public_key, 0);
outLen += sizeof(ad->at) - sizeof(ad->at.public_key) + COSE_KEY_ES256_SIZE;
cose_key_size = build_ecdsa_cose_key(ad->at.public_key, COSE_ALG_ES256, COSE_KEY_CRV_P256);
} else if (alg_type == COSE_ALG_EDDSA) {
build_ed25519_cose_key(ad->at.public_key);
outLen += sizeof(ad->at) - sizeof(ad->at.public_key) + COSE_KEY_EDDSA_SIZE;
cose_key_size = build_ed25519_cose_key(ad->at.public_key);
} else if (alg_type == ctap_sm2_attr.algo_id) {
cose_key_size = build_ecdsa_cose_key(ad->at.public_key, ctap_sm2_attr.algo_id, ctap_sm2_attr.curve_id);
} else {
DBG_MSG("Unknown algorithm type\n");
return CTAP2_ERR_UNHANDLED_REQUEST;
}
outLen += sizeof(ad->at) - sizeof(ad->at.public_key) + cose_key_size;
}
if (flags & FLAGS_ED) {
if (*len < outLen + extension_size) {
Expand Down Expand Up @@ -1326,14 +1332,14 @@ static uint8_t ctap_get_info(CborEncoder *encoder) {
// algorithms
ret = cbor_encode_int(&map, GI_RESP_ALGORITHMS);
CHECK_CBOR_RET(ret);
ret = cbor_encoder_create_array(&map, &array, 2);
ret = cbor_encoder_create_array(&map, &array, ctap_sm2_attr.enabled ? 3 : 2);
CHECK_CBOR_RET(ret);
ret = cbor_encoder_create_map(&array, &sub_map, 2);
CHECK_CBOR_RET(ret);
{
ret = cbor_encode_text_stringz(&sub_map, "alg");
CHECK_CBOR_RET(ret);
ret = cbor_encode_int(&sub_map, -7); // ES256 (P-256)
ret = cbor_encode_int(&sub_map, COSE_ALG_ES256);
CHECK_CBOR_RET(ret);
ret = cbor_encode_text_stringz(&sub_map, "type");
CHECK_CBOR_RET(ret);
Expand All @@ -1347,7 +1353,7 @@ static uint8_t ctap_get_info(CborEncoder *encoder) {
{
ret = cbor_encode_text_stringz(&sub_map, "alg");
CHECK_CBOR_RET(ret);
ret = cbor_encode_int(&sub_map, -8); // EdDSA
ret = cbor_encode_int(&sub_map, COSE_ALG_EDDSA);
CHECK_CBOR_RET(ret);
ret = cbor_encode_text_stringz(&sub_map, "type");
CHECK_CBOR_RET(ret);
Expand All @@ -1356,6 +1362,22 @@ static uint8_t ctap_get_info(CborEncoder *encoder) {
}
ret = cbor_encoder_close_container(&array, &sub_map);
CHECK_CBOR_RET(ret);
if (ctap_sm2_attr.enabled) {
ret = cbor_encoder_create_map(&array, &sub_map, 2);
CHECK_CBOR_RET(ret);
{
ret = cbor_encode_text_stringz(&sub_map, "alg");
CHECK_CBOR_RET(ret);
ret = cbor_encode_int(&sub_map, ctap_sm2_attr.algo_id);
CHECK_CBOR_RET(ret);
ret = cbor_encode_text_stringz(&sub_map, "type");
CHECK_CBOR_RET(ret);
ret = cbor_encode_text_stringz(&sub_map, "public-key");
CHECK_CBOR_RET(ret);
}
ret = cbor_encoder_close_container(&array, &sub_map);
CHECK_CBOR_RET(ret);
}
ret = cbor_encoder_close_container(&map, &array);
CHECK_CBOR_RET(ret);

Expand Down Expand Up @@ -1392,7 +1414,7 @@ static uint8_t ctap_client_pin(CborEncoder *encoder, const uint8_t *params, size
uint8_t iv[16], buf[PIN_ENC_SIZE_P2 + PIN_HASH_SIZE_P2], i;
memzero(iv, sizeof(iv));
uint8_t *ptr;
int err, retries;
int err, retries, cose_key_size;
switch (cp.sub_command) {
case CP_CMD_GET_PIN_RETRIES:
DBG_MSG("Subcommand Get Pin Retries\n");
Expand All @@ -1417,8 +1439,8 @@ static uint8_t ctap_client_pin(CborEncoder *encoder, const uint8_t *params, size
CHECK_CBOR_RET(ret);
ptr = key_map.data.ptr - 1;
cp_get_public_key(ptr);
build_cose_key(ptr, 1);
key_map.data.ptr = ptr + MAX_COSE_KEY_SIZE;
cose_key_size = build_ecdsa_cose_key(ptr, COSE_ALG_ECDH_ES_HKDF_256, COSE_KEY_CRV_P256);
key_map.data.ptr = ptr + cose_key_size;
ret = cbor_encoder_close_container(&map, &key_map);
CHECK_CBOR_RET(ret);
ret = cbor_encoder_close_container(encoder, &map);
Expand Down Expand Up @@ -1809,11 +1831,11 @@ static uint8_t ctap_credential_management(CborEncoder *encoder, const uint8_t *p
uint8_t *ptr = sub_map.data.ptr - 1;
memcpy(ptr, key.pub, PUBLIC_KEY_LENGTH[key_type]);
if (dc.credential_id.alg_type == COSE_ALG_ES256) {
build_cose_key(ptr, 0);
sub_map.data.ptr = ptr + COSE_KEY_ES256_SIZE;
int cose_key_size = build_ecdsa_cose_key(ptr, COSE_ALG_ES256, COSE_KEY_CRV_P256);
sub_map.data.ptr = ptr + cose_key_size;
} else if (dc.credential_id.alg_type == COSE_ALG_EDDSA) {
build_ed25519_cose_key(ptr);
sub_map.data.ptr = ptr + COSE_KEY_EDDSA_SIZE;
int cose_key_size = build_ed25519_cose_key(ptr);
sub_map.data.ptr = ptr + cose_key_size;
}
ret = cbor_encoder_close_container(&map, &sub_map);
CHECK_CBOR_RET(ret);
Expand Down
Loading

0 comments on commit 0ddb93b

Please sign in to comment.