From 19b1f08a8994a88cb3f7273331abf4b0d683152c Mon Sep 17 00:00:00 2001 From: Radoslav Gerganov Date: Tue, 17 Mar 2020 17:23:18 +0200 Subject: [PATCH] PoC: Add vendor command for signing an arbitrary SHA256 hash This patch adds new CTAP2 vendor command with command value 0x50. The command arguments are credentialId and user specified SHA256 hash. It returns a DER encoded signature of the given hash, using the key which corresponds to the specified credentialId. Example request: {1: , 2: {"id": , "type": "public-key"}, 3: [pinAuth]} Example response: {1: } Issue: #395 --- fido2/ctap.c | 46 +++++++++++++++++++++++++++++++ fido2/ctap.h | 11 ++++++++ fido2/ctap_parse.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++ fido2/ctap_parse.h | 1 + fido2/log.c | 1 + fido2/log.h | 1 + 6 files changed, 127 insertions(+) diff --git a/fido2/ctap.c b/fido2/ctap.c index 3ccb9cbf..0fee1c88 100644 --- a/fido2/ctap.c +++ b/fido2/ctap.c @@ -1354,6 +1354,45 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder) return 0; } +uint8_t ctap_sign_hash(CborEncoder * encoder, uint8_t * request, int length) +{ + CTAP_signHash SH; + CborEncoder map; + uint8_t sigbuf[64]; + uint8_t sigder[72]; + + int ret = ctap_parse_sign_hash(&SH, request, length); + if (ret != 0) + { + printf2(TAG_ERR,"error, ctap_parse_sign_hash failed\n"); + return ret; + } + if (ctap_is_pin_set() == 1) + { + ret = verify_pin_auth(SH.pinAuth, SH.clientDataHash); + check_retr(ret); + } + ret = ctap2_user_presence_test(CTAP2_UP_DELAY_MS); + check_retr(ret); + ret = cbor_encoder_create_map(encoder, &map, 1); + check_ret(ret); + + unsigned int cred_size = get_credential_id_size(&SH.cred); + crypto_ecc256_load_key((uint8_t*)&SH.cred.credential.id, cred_size, NULL, 0); + crypto_ecc256_sign(SH.clientDataHash, CLIENT_DATA_HASH_SIZE, sigbuf); + int sigder_sz = ctap_encode_der_sig(sigbuf,sigder); + printf1(TAG_SH,"der sig [%d]: ", sigder_sz); dump_hex1(TAG_SH, sigder, sigder_sz); + + ret = cbor_encode_int(&map, 1); + check_ret(ret); + ret = cbor_encode_byte_string(&map, sigder, sigder_sz); + check_ret(ret); + + ret = cbor_encoder_close_container(encoder, &map); + check_ret(ret); + return 0; +} + uint8_t ctap_cred_metadata(CborEncoder * encoder) { CborEncoder map; @@ -2226,6 +2265,7 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp) { case CTAP_MAKE_CREDENTIAL: case CTAP_GET_ASSERTION: + case CTAP_SOLO_SIGN: case CTAP_CBOR_CRED_MGMT: case CTAP_CBOR_CRED_MGMT_PRE: if (ctap_device_locked()) @@ -2309,6 +2349,12 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp) status = CTAP2_ERR_NOT_ALLOWED; } break; + case CTAP_SOLO_SIGN: + printf1(TAG_CTAP,"CTAP_SOLO_SIGN\n"); + status = ctap_sign_hash(&encoder, pkt_raw, length); + resp->length = cbor_encoder_get_buffer_size(&encoder, buf); + dump_hex1(TAG_DUMP, buf, resp->length); + break; case CTAP_CBOR_CRED_MGMT: case CTAP_CBOR_CRED_MGMT_PRE: printf1(TAG_CTAP,"CTAP_CBOR_CRED_MGMT_PRE\n"); diff --git a/fido2/ctap.h b/fido2/ctap.h index db98c27b..4f148b38 100644 --- a/fido2/ctap.h +++ b/fido2/ctap.h @@ -19,6 +19,7 @@ #define CTAP_CBOR_CRED_MGMT 0x0A #define CTAP_VENDOR_FIRST 0x40 #define CTAP_CBOR_CRED_MGMT_PRE 0x41 +#define CTAP_SOLO_SIGN 0x50 #define CTAP_VENDOR_LAST 0xBF #define MC_clientDataHash 0x01 @@ -39,6 +40,9 @@ #define GA_pinAuth 0x06 #define GA_pinProtocol 0x07 +#define SH_clientDataHash 0x01 +#define SH_credential 0x02 +#define SH_pinAuth 0x03 #define CM_cmd 0x01 #define CM_cmdMetadata 0x01 #define CM_cmdRPBegin 0x02 @@ -319,6 +323,13 @@ typedef struct } CTAP_getAssertion; +typedef struct +{ + uint8_t pinAuth[16]; + uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE]; + CTAP_credentialDescriptor cred; +} CTAP_signHash; + typedef struct { int cmd; diff --git a/fido2/ctap_parse.c b/fido2/ctap_parse.c index a6e53f24..86dc56fd 100644 --- a/fido2/ctap_parse.c +++ b/fido2/ctap_parse.c @@ -1007,6 +1007,73 @@ uint8_t parse_allow_list(CTAP_getAssertion * GA, CborValue * it) return 0; } +uint8_t ctap_parse_sign_hash(CTAP_signHash * SH, uint8_t * request, int length) +{ + int key; + size_t i, map_length; + CborParser parser; + CborValue it,map; + + memset(SH, 0, sizeof(CTAP_signHash)); + int 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_SH, "SH 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); + + switch(key) + { + case SH_clientDataHash: + printf1(TAG_SH, "SH_clientHash\n"); + + ret = parse_fixed_byte_string(&map, SH->clientDataHash, CLIENT_DATA_HASH_SIZE); + check_retr(ret); + + printf1(TAG_SH," "); dump_hex1(TAG_SH, SH->clientDataHash, CLIENT_DATA_HASH_SIZE); + break; + case SH_credential: + printf1(TAG_SH, "SH_credential\n"); + ret = parse_credential_descriptor(&map, &SH->cred); + check_ret(ret); + break; + case SH_pinAuth: + printf1(TAG_SH, "SH_pinAuth\n"); + ret = parse_fixed_byte_string(&map, SH->pinAuth, 16); + check_retr(ret); + break; + } + ret = cbor_value_advance(&map); + check_ret(ret); + } + return 0; +} + static uint8_t parse_cred_mgmt_subcommandparams(CborValue * val, CTAP_credMgmt * CM) { size_t map_length; diff --git a/fido2/ctap_parse.h b/fido2/ctap_parse.h index e0be8071..da6dbc2c 100644 --- a/fido2/ctap_parse.h +++ b/fido2/ctap_parse.h @@ -35,6 +35,7 @@ uint8_t parse_cose_key(CborValue * it, COSE_key * cose); uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length); uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int length); +uint8_t ctap_parse_sign_hash(CTAP_signHash * SH, uint8_t * request, int length); uint8_t ctap_parse_cred_mgmt(CTAP_credMgmt * CM, uint8_t * request, int length); uint8_t ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length); uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor * cred); diff --git a/fido2/log.c b/fido2/log.c index bb9a41ad..3a151f28 100644 --- a/fido2/log.c +++ b/fido2/log.c @@ -52,6 +52,7 @@ struct logtag tagtable[] = { {TAG_NFC_APDU, "NAPDU"}, {TAG_CCID, "CCID"}, {TAG_CM, "CRED_MGMT"}, + {TAG_SH, "SH"}, }; diff --git a/fido2/log.h b/fido2/log.h index d415255f..4ac78dc2 100644 --- a/fido2/log.h +++ b/fido2/log.h @@ -49,6 +49,7 @@ typedef enum TAG_NFC_APDU = (1 << 20), TAG_CCID = (1 << 21), TAG_CM = (1 << 22), + TAG_SH = (1 << 23), TAG_NO_TAG = (1UL << 30), TAG_FILENO = (1UL << 31)