From 9209bff54ff569edff7c9edced0b44a0bc28a2c8 Mon Sep 17 00:00:00 2001 From: Conor Patrick Date: Sat, 26 May 2018 15:29:37 -0400 Subject: [PATCH] u2f support added --- ctap.c | 15 +++--- ctap.h | 9 +++- ctaphid.c | 15 +++--- ctaphid.h | 3 ++ log.c | 1 + log.h | 7 +-- main.c | 1 + u2f.c | 136 +++++++++++++++++++++++++++++++----------------------- u2f.h | 14 +----- 9 files changed, 112 insertions(+), 89 deletions(-) diff --git a/ctap.c b/ctap.c index 7158edb..6362af0 100644 --- a/ctap.c +++ b/ctap.c @@ -1056,24 +1056,25 @@ uint8_t ctap_client_pin(CborEncoder * encoder, uint8_t * request, int length) return 0; } +void ctap_response_init(CTAP_RESPONSE * resp) +{ + memset(resp, 0, sizeof(CTAP_RESPONSE)); + resp->data_size = CTAP_RESPONSE_BUFFER_SIZE; +} uint8_t ctap_handle_packet(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp) { + CborEncoder encoder; uint8_t status = 0; uint8_t cmd = *pkt_raw; pkt_raw++; length--; - static uint8_t buf[1024]; - memset(buf,0,sizeof(buf)); + uint8_t * buf = resp->data; - resp->data = buf; - resp->length = 0; - - CborEncoder encoder; - cbor_encoder_init(&encoder, buf, sizeof(buf), 0); + cbor_encoder_init(&encoder, buf, resp->data_size, 0); 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.h b/ctap.h index 59370ad..e37dd13 100644 --- a/ctap.h +++ b/ctap.h @@ -67,7 +67,6 @@ #define RESP_pinToken 0x02 #define RESP_retries 0x03 - #define PARAM_clientDataHash (1 << 0) #define PARAM_rp (1 << 1) #define PARAM_user (1 << 2) @@ -107,6 +106,8 @@ #define NEW_PIN_ENC_MAX_SIZE 256 // includes NULL terminator +#define CTAP_RESPONSE_BUFFER_SIZE 1024 + typedef struct { uint8_t id[USER_ID_MAX_SIZE]; @@ -147,7 +148,8 @@ typedef struct typedef struct { - uint8_t * data; + uint8_t data[CTAP_RESPONSE_BUFFER_SIZE]; + uint16_t data_size; uint16_t length; } CTAP_RESPONSE; @@ -231,8 +233,11 @@ typedef struct } CTAP_clientPin; +void ctap_response_init(CTAP_RESPONSE * resp); + uint8_t ctap_handle_packet(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp); + // Run ctap related power-up procedures (init pinToken, generate shared secret) void ctap_init(); diff --git a/ctaphid.c b/ctaphid.c index 666f2cb..47473d0 100644 --- a/ctaphid.c +++ b/ctaphid.c @@ -386,7 +386,7 @@ start_over: init_resp.version_major = 0;//? init_resp.version_minor = 0;//? init_resp.build_version = 0;//? - init_resp.capabilities = CAPABILITY_WINK | CAPABILITY_CBOR; + init_resp.capabilities = CTAP_CAPABILITIES; ctaphid_write(&wb,&init_resp,sizeof(CTAPHID_INIT_RESPONSE)); ctaphid_write(&wb,NULL,0); @@ -436,6 +436,7 @@ start_over: return; } + ctap_response_init(&ctap_resp); status = ctap_handle_packet(ctap_buffer, buffer_len(), &ctap_resp); ctaphid_write_buffer_init(&wb); @@ -449,7 +450,7 @@ start_over: break; case CTAPHID_MSG: - printf("CTAPHID_CBOR\n"); + printf("CTAPHID_MSG\n"); if (buffer_len() == 0) { printf("Error,invalid 0 length field for MSG/U2F packet\n"); @@ -458,14 +459,14 @@ start_over: return; } + ctap_response_init(&ctap_resp); + u2f_request((struct u2f_request_apdu*)ctap_buffer, &ctap_resp); + ctaphid_write_buffer_init(&wb); - u2f_request(ctap_buffer); - wb.cid = active_cid; - wb.cmd = CTAPHID_CBOR; - wb.bcnt = (ctap_resp.length+1); + wb.cmd = CTAPHID_MSG; + wb.bcnt = (ctap_resp.length); - ctaphid_write(&wb, &status, 1); ctaphid_write(&wb, ctap_resp.data, ctap_resp.length); ctaphid_write(&wb, NULL, 0); break; diff --git a/ctaphid.h b/ctaphid.h index e641da9..64bc3ad 100644 --- a/ctaphid.h +++ b/ctaphid.h @@ -35,6 +35,9 @@ #define CAPABILITY_CBOR 0x04 #define CAPABILITY_NMSG 0x08 +//#define CTAP_CAPABILITIES (CAPABILITY_WINK | CAPABILITY_CBOR) +#define CTAP_CAPABILITIES (CAPABILITY_WINK ) + typedef struct { uint32_t cid; diff --git a/log.c b/log.c index 46d5d24..4c26e60 100644 --- a/log.c +++ b/log.c @@ -23,6 +23,7 @@ struct logtag tagtable[] = { {TAG_ERR,"ERR"}, {TAG_PARSE,"PARSE"}, {TAG_CTAP,"CTAP"}, + {TAG_U2F,"U2F"}, {TAG_DUMP,"DUMP"}, {TAG_GREEN,"\x1b[32mDEBUG\x1b[0m"}, {TAG_RED,"\x1b[31mDEBUG\x1b[0m"}, diff --git a/log.h b/log.h index 645ebf9..6be59aa 100644 --- a/log.h +++ b/log.h @@ -16,9 +16,10 @@ typedef enum TAG_ERR = (1 << 3), TAG_PARSE= (1 << 4), TAG_CTAP = (1 << 5), - TAG_DUMP = (1 << 6), - TAG_GREEN = (1 << 7), - TAG_RED= (1 << 8), + TAG_U2F = (1 << 6), + TAG_DUMP = (1 << 7), + TAG_GREEN = (1 << 8), + TAG_RED= (1 << 9), TAG_FILENO = (1<<31) } LOG_TAG; diff --git a/main.c b/main.c index d73f835..c8affda 100644 --- a/main.c +++ b/main.c @@ -27,6 +27,7 @@ int main(int argc, char * argv[]) TAG_GA | TAG_CP | TAG_CTAP | + TAG_U2F| TAG_PARSE | TAG_DUMP| TAG_GREEN| diff --git a/u2f.c b/u2f.c index 4d62ea2..65f16e7 100644 --- a/u2f.c +++ b/u2f.c @@ -1,83 +1,98 @@ +#include #include "u2f.h" #include "ctap.h" #include "crypto.h" +#include "log.h" // void u2f_response_writeback(uint8_t * buf, uint8_t len); static int16_t u2f_register(struct u2f_register_request * req); static int16_t u2f_version(); static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t control); +static int8_t u2f_response_writeback(const uint8_t * buf, uint16_t len); -void u2f_request(struct u2f_request_apdu * req) +static CTAP_RESPONSE * _u2f_resp = NULL; + +void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp) { - uint16_t * rcode = (uint16_t *)req; + uint16_t rcode; uint32_t len = ((req->LC3) | ((uint32_t)req->LC2 << 8) | ((uint32_t)req->LC1 << 16)); + uint8_t byte; + + _u2f_resp = resp; if (req->cla != 0) { - u2f_response_set_length(2); - *rcode = U2F_SW_CLASS_NOT_SUPPORTED; + printf1(TAG_U2F, "CLA not zero\n"); + rcode = U2F_SW_CLASS_NOT_SUPPORTED; goto end; } switch(req->ins) { case U2F_REGISTER: + printf1(TAG_U2F, "U2F_REGISTER\n"); if (len != 64) { - u2f_response_set_length(2); - *rcode = U2F_SW_WRONG_LENGTH; + rcode = U2F_SW_WRONG_LENGTH; } else { - *rcode = u2f_register((struct u2f_register_request*)req->payload); + rcode = u2f_register((struct u2f_register_request*)req->payload); } break; case U2F_AUTHENTICATE: - *rcode = u2f_authenticate((struct u2f_authenticate_request*)req->payload, req->p1); + printf1(TAG_U2F, "U2F_AUTHENTICATE\n"); + rcode = u2f_authenticate((struct u2f_authenticate_request*)req->payload, req->p1); break; case U2F_VERSION: + printf1(TAG_U2F, "U2F_VERSION\n"); if (len) { - u2f_response_set_length(2); - *rcode = U2F_SW_WRONG_LENGTH; + rcode = U2F_SW_WRONG_LENGTH; } else { - *rcode = u2f_version(); + rcode = u2f_version(); } break; case U2F_VENDOR_FIRST: case U2F_VENDOR_LAST: - *rcode = U2F_SW_NO_ERROR; + printf1(TAG_U2F, "U2F_VENDOR\n"); + rcode = U2F_SW_NO_ERROR; break; default: - u2f_response_set_length(2); - *rcode = U2F_SW_INS_NOT_SUPPORTED; + printf1(TAG_ERR, "Error, unknown U2F command\n"); + rcode = U2F_SW_INS_NOT_SUPPORTED; break; } + end: - u2f_response_writeback((uint8_t*)rcode,2); - u2f_response_flush(); + if (rcode != U2F_SW_NO_ERROR) + { + printf1(TAG_U2F,"U2F Error code %04x\n", rcode); + ctap_response_init(_u2f_resp); + } + + byte = (rcode & 0xff00)>>8; + u2f_response_writeback(&byte,1); + byte = rcode & 0xff; + u2f_response_writeback(&byte,1); + + printf1(TAG_U2F,"u2f resp: "); dump_hex1(TAG_U2F, _u2f_resp->data, _u2f_resp->length); } -void u2f_response_writeback(const uint8_t * buf, uint16_t len) +static int8_t u2f_response_writeback(const uint8_t * buf, uint16_t len) { - -} - -// Set total length of U2F response. Must be done before any writebacks -extern void u2f_response_set_length(uint16_t len) -{ - -} - -// u2f_response_flush callback when u2f finishes and will -// indicate when all buffer data, if any, should be written -extern void u2f_response_flush() -{ - + if ((_u2f_resp->length + len) > _u2f_resp->data_size) + { + printf2(TAG_ERR, "Not enough space for U2F response, writeback\n"); + exit(1); + } + memmove(_u2f_resp->data + _u2f_resp->length, buf, len); + _u2f_resp->length += len; + return 0; } @@ -119,7 +134,7 @@ static void dump_signature_der(uint8_t * sig) } static int8_t u2f_load_key(struct u2f_key_handle * kh, uint8_t * appid) { - crypto_ecc256_load_key((uint8_t*)kh, U2F_KEY_HANDLE_SIZE, appid, U2F_APPLICATION_SIZE); + crypto_ecc256_load_key((uint8_t*)kh, U2F_KEY_HANDLE_SIZE, NULL, 0); return 0; } @@ -133,28 +148,36 @@ static void u2f_make_auth_tag(struct u2f_key_handle * kh, uint8_t * appid, uint8 memmove(tag, hashbuf, CREDENTIAL_TAG_SIZE); } - -static int8_t u2f_appid_eq(struct u2f_key_handle * kh, uint8_t * appid) -{ - uint8_t tag[U2F_KEY_HANDLE_TAG_SIZE]; - u2f_make_auth_tag(kh, appid, kh->tag); - if (memcmp(kh->tag, tag, U2F_KEY_HANDLE_TAG_SIZE) == 0) - return 0; - else - return -1; -} - - - static int8_t u2f_new_keypair(struct u2f_key_handle * kh, uint8_t * appid, uint8_t * pubkey) { ctap_generate_rng(kh->key, U2F_KEY_HANDLE_KEY_SIZE); u2f_make_auth_tag(kh, appid, kh->tag); + crypto_ecc256_derive_public_key((uint8_t*)kh, U2F_KEY_HANDLE_SIZE, pubkey, pubkey+32); return 0; } + +static int8_t u2f_appid_eq(struct u2f_key_handle * kh, uint8_t * appid) +{ + uint8_t tag[U2F_KEY_HANDLE_TAG_SIZE]; + u2f_make_auth_tag(kh, appid, tag); + if (memcmp(kh->tag, tag, U2F_KEY_HANDLE_TAG_SIZE) == 0) + { + return 0; + } + else + { + printf1(TAG_U2F, "key handle + appid not authentic\n"); + printf1(TAG_U2F, "calc tag: \n"); dump_hex1(TAG_U2F,tag, U2F_KEY_HANDLE_TAG_SIZE); + printf1(TAG_U2F, "inp tag: \n"); dump_hex1(TAG_U2F,kh->tag, U2F_KEY_HANDLE_TAG_SIZE); + return -1; + } +} + + + static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t control) { @@ -165,7 +188,6 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c if (control == U2F_AUTHENTICATE_CHECK) { - u2f_response_set_length(2); if (u2f_appid_eq(&req->kh, req->app) == 0) { return U2F_SW_CONDITIONS_NOT_SATISFIED; @@ -183,15 +205,13 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c ) { - u2f_response_set_length(2); return U2F_SW_WRONG_PAYLOAD; } - if (ctap_user_presence_test()) + if (ctap_user_presence_test() == 0) { - u2f_response_set_length(2); return U2F_SW_CONDITIONS_NOT_SATISFIED; } @@ -206,11 +226,9 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c crypto_sha256_final(hash); + printf1(TAG_U2F, "sha256: "); dump_hex1(TAG_U2F,hash,32); crypto_ecc256_sign(hash, 32, sig); - - u2f_response_set_length(7 + get_signature_length(sig)); - u2f_response_writeback(&up,1); u2f_response_writeback((uint8_t *)&count,4); dump_signature_der(sig); @@ -230,15 +248,13 @@ static int16_t u2f_register(struct u2f_register_request * req) const uint16_t attest_size = attestation_cert_der_size; - if (ctap_user_presence_test()) + if ( ! ctap_user_presence_test()) { - u2f_response_set_length(2); return U2F_SW_CONDITIONS_NOT_SATISFIED; } if ( u2f_new_keypair(&key_handle, req->app, pubkey) == -1) { - u2f_response_set_length(2); return U2F_SW_INSUFFICIENT_MEMORY; } @@ -254,9 +270,12 @@ static int16_t u2f_register(struct u2f_register_request * req) crypto_sha256_final(hash); crypto_ecc256_load_attestation_key(); + + /*printf("check key handle size: %d vs %d\n", U2F_KEY_HANDLE_SIZE, sizeof(struct u2f_key_handle));*/ + + printf1(TAG_U2F, "sha256: "); dump_hex1(TAG_U2F,hash,32); crypto_ecc256_sign(hash, 32, sig); - u2f_response_set_length(69 + get_signature_length((uint8_t*)req) + U2F_KEY_HANDLE_SIZE + attest_size); i[0] = 0x5; u2f_response_writeback(i,2); u2f_response_writeback(pubkey,64); @@ -266,7 +285,9 @@ static int16_t u2f_register(struct u2f_register_request * req) u2f_response_writeback(attestation_cert_der,attest_size); - dump_signature_der((uint8_t*)req); + dump_signature_der(sig); + + /*printf1(TAG_U2F, "dersig: "); dump_hex1(TAG_U2F,sig,74);*/ return U2F_SW_NO_ERROR; @@ -275,7 +296,6 @@ static int16_t u2f_register(struct u2f_register_request * req) static int16_t u2f_version() { const char version[] = "U2F_V2"; - u2f_response_set_length(2 + sizeof(version)-1); u2f_response_writeback(version, sizeof(version)-1); return U2F_SW_NO_ERROR; } diff --git a/u2f.h b/u2f.h index 8a7c6d3..e3003b1 100644 --- a/u2f.h +++ b/u2f.h @@ -2,6 +2,7 @@ #define _U2F_H_ #include +#include "ctap.h" #define U2F_EC_FMT_UNCOMPRESSED 0x04 @@ -89,24 +90,13 @@ struct u2f_authenticate_request // u2f_request send a U2F message to U2F protocol // @req U2F message -void u2f_request(struct u2f_request_apdu* req); +void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp); ////////////////////////////////////////////////////////////////// /* Platform specific functions that must be implemented by user */ ////////////////////////////////////////////////////////////////// -// u2f_response_writeback callback for u2f to send back response data -// @buf data to write back -// @len length of buf in bytes -extern void u2f_response_writeback(const uint8_t * buf, uint16_t len); - -// Set total length of U2F response. Must be done before any writebacks -extern void u2f_response_set_length(uint16_t len); - -// u2f_response_flush callback when u2f finishes and will -// indicate when all buffer data, if any, should be written -extern void u2f_response_flush(); #endif /* U2F_H_ */