From eaa7e15499d00b4024b02a94dd7e6b1bc8a0ca8e Mon Sep 17 00:00:00 2001 From: Conor Patrick Date: Wed, 11 Jul 2018 21:55:20 -0400 Subject: [PATCH] all core functionality *works* --- efm32/src/crypto.c | 51 +++++++++- fido2/crypto.c | 80 ++++++++++++++- fido2/crypto.h | 2 + fido2/ctap.c | 243 +++++++++++++++++++++++++++++++++++++++------ fido2/ctap.h | 14 ++- fido2/device.h | 12 +++ fido2/log.c | 1 + fido2/log.h | 1 + fido2/main.c | 1 + fido2/storage.h | 38 +++++++ fido2/wallet.c | 133 ++++++++++++++++++++++++- pc/app.h | 2 + pc/device.c | 192 +++++++++++++++++++++++++++++++++++ tools/ctap_test.py | 2 - web/js/wallet.js | 141 +++++++++++++++++++++++--- 15 files changed, 850 insertions(+), 63 deletions(-) create mode 100644 fido2/storage.h diff --git a/efm32/src/crypto.c b/efm32/src/crypto.c index 9c002fa..23bd865 100644 --- a/efm32/src/crypto.c +++ b/efm32/src/crypto.c @@ -33,6 +33,7 @@ static mbedtls_ctr_drbg_context ctr_drbg; static const struct uECC_Curve_t * _es256_curve = NULL; static const uint8_t * _signing_key = NULL; +static int _external_key_len = 0; // Secrets for testing only static uint8_t master_secret[32] = "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff" @@ -182,11 +183,15 @@ void crypto_ecc256_init() } -void crypto_ecc256_load_attestation_key() +void crypto_load_external_key(uint8_t * key, int len) { - _signing_key = attestation_key; + _signing_key = key; + _external_key_len = len; } + + + /** * \brief Import a point from unsigned binary data * @@ -269,6 +274,48 @@ void crypto_ecc256_sign(uint8_t * data, int len, uint8_t * sig) } +void crypto_ecdsa_sign(uint8_t * data, int len, uint8_t * sig, int MBEDTLS_ECP_ID) +{ + mbedtls_ecp_group grp; /*!< Elliptic curve and base point */ + mbedtls_mpi d; /*!< our secret value */ +//#define CRYPTO_ENABLE CMU->HFBUSCLKEN0 |= CMU_HFBUSCLKEN0_CRYPTO; \ +// CRYPTO->IFC = _CRYPTO_IFC_MASK; \ +// CRYPTO->CMD = CRYPTO_CMD_SEQSTOP; \ +// CRYPTO->CTRL = CRYPTO_CTRL_DMA0RSEL_DDATA0; \ +// CRYPTO->SEQCTRL = 0; \ +// CRYPTO->SEQCTRLB = 0 +// +//#define CRYPTO_DISABLE \ +// CRYPTO->IEN = 0; \ +// CMU->HFBUSCLKEN0 &= ~CMU_HFBUSCLKEN0_CRYPTO; +// CRYPTO_DISABLE; +// CRYPTO_ENABLE; +// mbedtls_ecp_group_init( &grp ); +// mbedtls_mpi_init( &d ); +// mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP256R1); +// mbedtls_mpi_read_binary(&d, _signing_key, 32); +// +// mbedtls_mpi r,s; +// mbedtls_mpi_init(&r); +// mbedtls_mpi_init(&s); +// +// printf("signing..\n"); +// dump_hex(data,len); +// mbedtls_ecdsa_sign_det( &grp, &r, &s, &d, +// data, 32, MBEDTLS_MD_SHA256 );// Issue: this will freeze on 13th iteration.. +// printf("signed\n"); +// +// mbedtls_mpi_write_binary(&r,sig,32); +// mbedtls_mpi_write_binary(&s,sig+32,32); + + if ( uECC_sign(_signing_key, data, len, sig, _es256_curve) == 0) + { + printf("error, uECC failed\n"); + exit(1); + } + +} + /* * Generate a keypair with configurable base point */ diff --git a/fido2/crypto.c b/fido2/crypto.c index d11efe3..3a52097 100644 --- a/fido2/crypto.c +++ b/fido2/crypto.c @@ -17,6 +17,27 @@ #include "uECC.h" #include "aes.h" #include "ctap.h" +#include "device.h" +#include "app.h" + +#ifdef USING_PC +typedef enum +{ + MBEDTLS_ECP_DP_NONE = 0, + MBEDTLS_ECP_DP_SECP192R1, /*!< 192-bits NIST curve */ + MBEDTLS_ECP_DP_SECP224R1, /*!< 224-bits NIST curve */ + MBEDTLS_ECP_DP_SECP256R1, /*!< 256-bits NIST curve */ + MBEDTLS_ECP_DP_SECP384R1, /*!< 384-bits NIST curve */ + MBEDTLS_ECP_DP_SECP521R1, /*!< 521-bits NIST curve */ + MBEDTLS_ECP_DP_BP256R1, /*!< 256-bits Brainpool curve */ + MBEDTLS_ECP_DP_BP384R1, /*!< 384-bits Brainpool curve */ + MBEDTLS_ECP_DP_BP512R1, /*!< 512-bits Brainpool curve */ + MBEDTLS_ECP_DP_CURVE25519, /*!< Curve25519 */ + MBEDTLS_ECP_DP_SECP192K1, /*!< 192-bits "Koblitz" curve */ + MBEDTLS_ECP_DP_SECP224K1, /*!< 224-bits "Koblitz" curve */ + MBEDTLS_ECP_DP_SECP256K1, /*!< 256-bits "Koblitz" curve */ +} mbedtls_ecp_group_id; +#endif const uint8_t attestation_cert_der[]; @@ -29,6 +50,7 @@ const uint16_t attestation_key_size; static SHA256_CTX sha256_ctx; static const struct uECC_Curve_t * _es256_curve = NULL; static const uint8_t * _signing_key = NULL; +static int _key_len = 0; // Secrets for testing only static uint8_t master_secret[32] = "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff" @@ -136,6 +158,7 @@ void crypto_ecc256_init() void crypto_ecc256_load_attestation_key() { _signing_key = attestation_key; + _key_len = 32; } void crypto_ecc256_sign(uint8_t * data, int len, uint8_t * sig) @@ -147,6 +170,55 @@ void crypto_ecc256_sign(uint8_t * data, int len, uint8_t * sig) } } +void crypto_ecc256_load_key(uint8_t * data, int len, uint8_t * data2, int len2) +{ + static uint8_t privkey[32]; + generate_private_key(data,len,data2,len2,privkey); + _signing_key = privkey; + _key_len = 32; +} + +void crypto_ecdsa_sign(uint8_t * data, int len, uint8_t * sig, int MBEDTLS_ECP_ID) +{ + + const struct uECC_Curve_t * curve = NULL; + + switch(MBEDTLS_ECP_ID) + { + case MBEDTLS_ECP_DP_SECP192R1: + curve = uECC_secp192r1(); + if (_key_len != 24) goto fail; + break; + case MBEDTLS_ECP_DP_SECP224R1: + curve = uECC_secp224r1(); + if (_key_len != 28) goto fail; + break; + case MBEDTLS_ECP_DP_SECP256R1: + curve = uECC_secp256r1(); + if (_key_len != 32) goto fail; + break; + case MBEDTLS_ECP_DP_SECP256K1: + curve = uECC_secp256k1(); + if (_key_len != 32) goto fail; + break; + default: + printf("error, invalid ECDSA alg specifier\n"); + exit(1); + } + + if ( uECC_sign(_signing_key, data, len, sig, curve) == 0) + { + printf("error, uECC failed\n"); + exit(1); + } + return; + +fail: + printf("error, invalid key length\n"); + exit(1); + +} + void generate_private_key(uint8_t * data, int len, uint8_t * data2, int len2, uint8_t * privkey) { crypto_sha256_hmac_init(CRYPTO_MASTER_KEY, 0, privkey); @@ -171,13 +243,13 @@ void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8 memmove(y,pubkey+32,32); } -void crypto_ecc256_load_key(uint8_t * data, int len, uint8_t * data2, int len2) +void crypto_load_external_key(uint8_t * key, int len) { - static uint8_t privkey[32]; - generate_private_key(data,len,data2,len2,privkey); - _signing_key = privkey; + _signing_key = key; + _key_len = len; } + void crypto_ecc256_make_key_pair(uint8_t * pubkey, uint8_t * privkey) { if (uECC_make_key(pubkey, privkey, _es256_curve) != 1) diff --git a/fido2/crypto.h b/fido2/crypto.h index ac7bed0..0cea5d0 100644 --- a/fido2/crypto.h +++ b/fido2/crypto.h @@ -19,7 +19,9 @@ void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8 void crypto_ecc256_load_key(uint8_t * data, int len, uint8_t * data2, int len2); void crypto_ecc256_load_attestation_key(); +void crypto_load_external_key(uint8_t * key, int len); void crypto_ecc256_sign(uint8_t * data, int len, uint8_t * sig); +void crypto_ecdsa_sign(uint8_t * data, int len, uint8_t * sig, int MBEDTLS_ECP_ID); void generate_private_key(uint8_t * data, int len, uint8_t * data2, int len2, uint8_t * privkey); diff --git a/fido2/ctap.c b/fido2/ctap.c index f48d06d..b062c1b 100644 --- a/fido2/ctap.c +++ b/fido2/ctap.c @@ -15,15 +15,15 @@ #include "app.h" #include "wallet.h" +#include "device.h" #define PIN_TOKEN_SIZE 16 uint8_t PIN_TOKEN[PIN_TOKEN_SIZE]; uint8_t KEY_AGREEMENT_PUB[64]; static uint8_t KEY_AGREEMENT_PRIV[32]; -static uint8_t PIN_CODE_SET = 0; -uint8_t PIN_CODE[NEW_PIN_ENC_MAX_SIZE]; static uint8_t PIN_CODE_HASH[32]; -static uint8_t DEVICE_LOCKOUT = 0; + +AuthenticatorState STATE; static struct { CTAP_authDataHeader authData; @@ -766,7 +766,7 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length) return ret; } - if (PIN_CODE_SET == 1 && GA.pinAuthPresent == 0) + if (ctap_is_pin_set() && GA.pinAuthPresent == 0) { printf2(TAG_ERR,"pinAuth is required\n"); return CTAP2_ERR_PIN_REQUIRED; @@ -851,6 +851,14 @@ uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platfor return CTAP1_ERR_OTHER; } + if (ctap_is_pin_set()) // Check first, prevent SCA + { + if (ctap_device_locked()) + { + return CTAP2_ERR_OPERATION_DENIED; + } + } + crypto_ecc256_shared_secret(platform_pubkey, KEY_AGREEMENT_PRIV, shared_secret); crypto_sha256_init(); @@ -898,6 +906,10 @@ uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platfor if (ctap_is_pin_set()) { + if (ctap_device_locked()) + { + return CTAP2_ERR_OPERATION_DENIED; + } crypto_aes256_reset_iv(NULL); crypto_aes256_decrypt(pinHashEnc, 16); if (memcmp(pinHashEnc, PIN_CODE_HASH, 16) != 0) @@ -1111,7 +1123,7 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp) case CTAP_CLIENT_PIN: if (ctap_device_locked()) { - status = CTAP2_ERR_NOT_ALLOWED; + status = CTAP2_ERR_OPERATION_DENIED; goto done; } break; @@ -1207,12 +1219,69 @@ done: return status; } +void ctap_flush_state(int backup) +{ + authenticator_write_state(&STATE, 0); + if (backup) + { + authenticator_write_state(&STATE, 1); + } +} + +static void ctap_state_init() +{ + // Set to 0xff instead of 0x00 to be easier on flash + memset(&STATE, 0xff, sizeof(AuthenticatorState)); + STATE.is_initialized = INITIALIZED_MARKER; + STATE.remaining_tries = PIN_LOCKOUT_ATTEMPTS; + STATE.is_pin_set = 0; +} + void ctap_init() { crypto_ecc256_init(); - ctap_reset_pin_attempts(); - DEVICE_LOCKOUT = 0; + authenticator_read_state(&STATE); + + if (STATE.is_initialized == INITIALIZED_MARKER) + { + printf1(TAG_STOR,"Auth state is initialized\n"); + } + else + { + printf1(TAG_STOR,"Auth state is NOT initialized. Initializing..\n"); + if (authenticator_is_backup_initialized()) + { + printf1(TAG_ERR,"Warning: memory corruption detected. restoring from backup..\n"); + authenticator_read_backup_state(&STATE); + authenticator_write_state(&STATE, 0); + } + else + { + ctap_state_init(); + authenticator_write_state(&STATE, 0); + authenticator_write_state(&STATE, 1); + + } + } + + if (ctap_is_pin_set()) + { + printf1(TAG_STOR,"pin code: \"%s\"\n", STATE.pin_code); + crypto_sha256_init(); + crypto_sha256_update(STATE.pin_code, strnlen(STATE.pin_code, NEW_PIN_ENC_MAX_SIZE)); + crypto_sha256_final(PIN_CODE_HASH); + printf1(TAG_STOR, "attempts_left: %d\n", STATE.remaining_tries); + } + else + { + printf1(TAG_STOR,"pin not set.\n"); + } + if (ctap_device_locked()) + { + printf1(TAG_ERR, "DEVICE LOCKED!\n"); + } + if (ctap_generate_rng(PIN_TOKEN, PIN_TOKEN_SIZE) != 1) { @@ -1230,12 +1299,12 @@ void ctap_init() uint8_t ctap_is_pin_set() { - return PIN_CODE_SET == 1; + return STATE.is_pin_set == 1; } uint8_t ctap_pin_matches(uint8_t * pin, int len) { - return memcmp(pin, PIN_CODE, len) == 0; + return memcmp(pin, STATE.pin_code, len) == 0; } @@ -1247,30 +1316,35 @@ void ctap_update_pin(uint8_t * pin, int len) printf2(TAG_ERR, "Update pin fail length\n"); exit(1); } - memset(PIN_CODE,0,sizeof(PIN_CODE)); - memmove(PIN_CODE, pin, len); + memset(STATE.pin_code, 0, NEW_PIN_ENC_MAX_SIZE); + memmove(STATE.pin_code, pin, len); crypto_sha256_init(); - crypto_sha256_update(PIN_CODE, len); + crypto_sha256_update(STATE.pin_code, len); crypto_sha256_final(PIN_CODE_HASH); - PIN_CODE_SET = 1; + STATE.is_pin_set = 1; - printf1(TAG_CTAP, "New pin set: %s\n", PIN_CODE); + printf1(TAG_CTAP, "New pin set: %s\n", STATE.pin_code); } -// TODO flash -static int8_t _flash_tries; uint8_t ctap_decrement_pin_attempts() { - if (_flash_tries > 0) + if (STATE.remaining_tries > 0) { - _flash_tries--; - printf1(TAG_CP, "ATTEMPTS left: %d\n", _flash_tries); + STATE.remaining_tries--; + ctap_flush_state(0); + printf1(TAG_CP, "ATTEMPTS left: %d\n", STATE.remaining_tries); + + if (STATE.remaining_tries == 0) + { + memset(PIN_TOKEN,0,sizeof(PIN_TOKEN)); + memset(PIN_CODE_HASH,0,sizeof(PIN_CODE_HASH)); + printf1(TAG_CP, "Device locked!\n"); + } } else { - DEVICE_LOCKOUT = 1; printf1(TAG_CP, "Device locked!\n"); return -1; } @@ -1279,17 +1353,18 @@ uint8_t ctap_decrement_pin_attempts() int8_t ctap_device_locked() { - return DEVICE_LOCKOUT == 1; + return STATE.remaining_tries == 0; } int8_t ctap_leftover_pin_attempts() { - return _flash_tries; + return STATE.remaining_tries; } void ctap_reset_pin_attempts() { - _flash_tries = 8; + STATE.remaining_tries = PIN_LOCKOUT_ATTEMPTS; + ctap_flush_state(0); } void ctap_reset_state() @@ -1297,15 +1372,119 @@ void ctap_reset_state() memset(&getAssertionState, 0, sizeof(getAssertionState)); } -void ctap_reset() +uint16_t ctap_keys_stored() { - _flash_tries = 8; - PIN_CODE_SET = 0; - DEVICE_LOCKOUT = 0; - ctap_reset_state(); - memset(PIN_CODE,0,sizeof(PIN_CODE)); - memset(PIN_CODE_HASH,0,sizeof(PIN_CODE_HASH)); - crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV); - crypto_reset_master_secret(); + int total = 0; + int i; + for (i = 0; i < MAX_KEYS; i++) + { + if (STATE.key_lens[i] != 0xffff) + { + total += 1; + } + else + { + break; + } + } + return total; +} + +static uint16_t key_addr_offset(int index) +{ + uint16_t offset = 0; + int i; + for (i = 0; i < index; i++) + { + if (STATE.key_lens[i] != 0xffff) offset += STATE.key_lens[i]; + } + return offset; +} + +uint16_t ctap_key_len(uint8_t index) +{ + int i = ctap_keys_stored(); + uint16_t offset; + if (i >= MAX_KEYS || index >= MAX_KEYS) + { + return 0; + } + if (STATE.key_lens[index] == 0xffff) return 0; + return STATE.key_lens[index]; + +} + +int8_t ctap_store_key(uint8_t index, uint8_t * key, uint16_t len) +{ + int i = ctap_keys_stored(); + uint16_t offset; + if (i >= MAX_KEYS || index >= MAX_KEYS || !len) + { + return ERR_NO_KEY_SPACE; + } + + if (STATE.key_lens[index] != 0xffff) + { + return ERR_KEY_SPACE_TAKEN; + } + + offset = key_addr_offset(index); + + if ((offset + len) > KEY_SPACE_BYTES) + { + return ERR_NO_KEY_SPACE; + } + + STATE.key_lens[index] = len; + + memmove(STATE.key_space + offset, key, len); + + ctap_flush_state(0); + ctap_flush_state(1); + + return 0; +} + +int8_t ctap_load_key(uint8_t index, uint8_t * key) +{ + int i = ctap_keys_stored(); + uint16_t offset; + uint16_t len; + if (i >= MAX_KEYS || index >= MAX_KEYS) + { + return ERR_NO_KEY_SPACE; + } + + if (STATE.key_lens[index] == 0) + { + return ERR_KEY_SPACE_EMPTY; + } + + offset = key_addr_offset(index); + len = ctap_key_len(index); + + if ((offset + len) > KEY_SPACE_BYTES) + { + return ERR_NO_KEY_SPACE; + } + + memmove(key, STATE.key_space + offset, len); + + return 0; +} + + + +void ctap_reset() +{ + ctap_state_init(); + authenticator_write_state(&STATE, 0); + authenticator_write_state(&STATE, 1); + + ctap_reset_state(); + memset(PIN_CODE_HASH,0,sizeof(PIN_CODE_HASH)); + crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV); + + crypto_reset_master_secret(); // Not sure what the significance of this is?? } diff --git a/fido2/ctap.h b/fido2/ctap.h index 138002d..8e5514a 100644 --- a/fido2/ctap.h +++ b/fido2/ctap.h @@ -2,7 +2,6 @@ #define _CTAP_H #include "cbor.h" -#include "device.h" #define CTAP_MAKE_CREDENTIAL 0x01 #define CTAP_GET_ASSERTION 0x02 @@ -109,6 +108,8 @@ #define CTAP_RESPONSE_BUFFER_SIZE 1024 +#define PIN_LOCKOUT_ATTEMPTS 8 + typedef struct { uint8_t id[USER_ID_MAX_SIZE]; @@ -260,10 +261,19 @@ uint8_t ctap_pin_matches(uint8_t * pin, int len); void ctap_reset(); int8_t ctap_device_locked(); +// Key storage API + +// Return length of key at index. 0 if not exist. +uint16_t ctap_key_len(uint8_t index); + +// See error codes in storage.h +int8_t ctap_store_key(uint8_t index, uint8_t * key, uint16_t len); +int8_t ctap_load_key(uint8_t index, uint8_t * key); +uint16_t ctap_key_len(uint8_t index); + #define PIN_TOKEN_SIZE 16 extern uint8_t PIN_TOKEN[PIN_TOKEN_SIZE]; extern uint8_t KEY_AGREEMENT_PUB[64]; -extern uint8_t PIN_CODE[NEW_PIN_ENC_MAX_SIZE]; #endif diff --git a/fido2/device.h b/fido2/device.h index 861f62f..2b5edab 100644 --- a/fido2/device.h +++ b/fido2/device.h @@ -1,6 +1,8 @@ #ifndef _DEVICE_H #define _DEVICE_H +#include "storage.h" + void device_init(); uint32_t millis(); @@ -20,6 +22,16 @@ void main_loop_delay(); void heartbeat(); +void authenticator_read_state(AuthenticatorState * ); + +void authenticator_read_backup_state(AuthenticatorState * ); + +// Return 1 yes backup is init'd, else 0 +//void authenticator_initialize() +int authenticator_is_backup_initialized(); + +void authenticator_write_state(AuthenticatorState *, int backup); + // Test for user presence // Return 1 for user is present, 0 user not present diff --git a/fido2/log.c b/fido2/log.c index e45a9e0..dc2c588 100644 --- a/fido2/log.c +++ b/fido2/log.c @@ -32,6 +32,7 @@ struct logtag tagtable[] = { {TAG_RED,"DEBUG"}, {TAG_TIME,"TIME"}, {TAG_WALLET,"WALLET"}, + {TAG_STOR,"STOR"}, }; diff --git a/fido2/log.h b/fido2/log.h index 7910796..2f3461c 100644 --- a/fido2/log.h +++ b/fido2/log.h @@ -26,6 +26,7 @@ typedef enum TAG_HID = (1 << 12), TAG_USB = (1 << 13), TAG_WALLET = (1 << 14), + TAG_STOR = (1 << 15), TAG_FILENO = (1<<31) } LOG_TAG; diff --git a/fido2/main.c b/fido2/main.c index 0afdd2a..7daaed7 100644 --- a/fido2/main.c +++ b/fido2/main.c @@ -28,6 +28,7 @@ int main(int argc, char * argv[]) /*TAG_MC |*/ /*TAG_GA |*/ TAG_WALLET | + TAG_STOR | /*TAG_CP |*/ // TAG_CTAP| // TAG_HID| diff --git a/fido2/storage.h b/fido2/storage.h new file mode 100644 index 0000000..f3a386e --- /dev/null +++ b/fido2/storage.h @@ -0,0 +1,38 @@ +#ifndef _STORAGE_H +#define _STORAGE_H + +#include "ctap.h" + +#define KEY_SPACE_BYTES 128 +#define MAX_KEYS (KEY_SPACE_BYTES/1) + +#define BACKUP_MARKER 0x5A +#define INITIALIZED_MARKER 0xA5 + +#define ERR_NO_KEY_SPACE (-1) +#define ERR_KEY_SPACE_TAKEN (-2) +#define ERR_KEY_SPACE_EMPTY (-2) + +typedef struct +{ + // Pin information + uint8_t is_initialized; + uint8_t is_pin_set; + uint8_t pin_code[NEW_PIN_ENC_MAX_SIZE]; + uint8_t remaining_tries; + + uint16_t key_lens[MAX_KEYS]; + uint8_t key_space[KEY_SPACE_BYTES]; +} AuthenticatorState; + + +typedef struct +{ + uint32_t addr; + uint8_t * filename; + uint32_t count; +} AuthenticatorCounter; + +extern AuthenticatorState STATE; + +#endif diff --git a/fido2/wallet.c b/fido2/wallet.c index 19678ca..7c3c7ae 100644 --- a/fido2/wallet.c +++ b/fido2/wallet.c @@ -5,12 +5,34 @@ * Author: conor */ #include "wallet.h" +#include "app.h" #include "ctap.h" #include "ctap_errors.h" #include "crypto.h" #include "u2f.h" #include "log.h" #include "util.h" +#include "storage.h" +#include "device.h" + +#ifdef USING_PC +typedef enum +{ + MBEDTLS_ECP_DP_NONE = 0, + MBEDTLS_ECP_DP_SECP192R1, /*!< 192-bits NIST curve */ + MBEDTLS_ECP_DP_SECP224R1, /*!< 224-bits NIST curve */ + MBEDTLS_ECP_DP_SECP256R1, /*!< 256-bits NIST curve */ + MBEDTLS_ECP_DP_SECP384R1, /*!< 384-bits NIST curve */ + MBEDTLS_ECP_DP_SECP521R1, /*!< 521-bits NIST curve */ + MBEDTLS_ECP_DP_BP256R1, /*!< 256-bits Brainpool curve */ + MBEDTLS_ECP_DP_BP384R1, /*!< 384-bits Brainpool curve */ + MBEDTLS_ECP_DP_BP512R1, /*!< 512-bits Brainpool curve */ + MBEDTLS_ECP_DP_CURVE25519, /*!< Curve25519 */ + MBEDTLS_ECP_DP_SECP192K1, /*!< 192-bits "Koblitz" curve */ + MBEDTLS_ECP_DP_SECP224K1, /*!< 224-bits "Koblitz" curve */ + MBEDTLS_ECP_DP_SECP256K1, /*!< 256-bits "Koblitz" curve */ +} mbedtls_ecp_group_id; +#endif typedef enum { @@ -43,9 +65,7 @@ void wallet_init() // TODO dont leave this printf1(TAG_WALLET,"Wallet is ready\n"); - /*ctap_update_pin("1234", 4);*/ - } int8_t wallet_pin(uint8_t subcmd, uint8_t * pinAuth, uint8_t * arg1, uint8_t * arg2, uint8_t * arg3, int len) @@ -58,6 +78,11 @@ int8_t wallet_pin(uint8_t subcmd, uint8_t * pinAuth, uint8_t * arg1, uint8_t * a case CP_cmdGetKeyAgreement: printf1(TAG_WALLET,"cmdGetKeyAgreement\n"); + if ( ctap_device_locked() ) + { + return CTAP2_ERR_OPERATION_DENIED; + } + u2f_response_writeback(KEY_AGREEMENT_PUB,sizeof(KEY_AGREEMENT_PUB)); printf1(TAG_WALLET,"pubkey: "); dump_hex1(TAG_WALLET,KEY_AGREEMENT_PUB,64); @@ -80,17 +105,22 @@ int8_t wallet_pin(uint8_t subcmd, uint8_t * pinAuth, uint8_t * arg1, uint8_t * a if (ret != 0) return ret; - printf1(TAG_WALLET,"Success. Pin = %s\n",PIN_CODE); + printf1(TAG_WALLET,"Success. Pin = %s\n", STATE.pin_code); break; case CP_cmdChangePin: printf1(TAG_WALLET,"cmdChangePin\n"); - if (! ctap_is_pin_set()) + if (! ctap_is_pin_set() ) { return CTAP2_ERR_PIN_NOT_SET; } + if ( ctap_device_locked() ) + { + return CTAP2_ERR_OPERATION_DENIED; + } + //pinEnc // plat_pubkey // pinHashEnc ret = ctap_update_pin_if_verified( arg2, len, arg1, pinAuth, arg3); if (ret != 0) @@ -100,6 +130,11 @@ int8_t wallet_pin(uint8_t subcmd, uint8_t * pinAuth, uint8_t * arg1, uint8_t * a case CP_cmdGetPinToken: printf1(TAG_WALLET,"cmdGetPinToken\n"); + if ( ctap_device_locked() ) + { + return CTAP2_ERR_OPERATION_DENIED; + } + ret = ctap_add_pin_if_verified(pinTokenEnc, arg1, pinAuth); // pubkey, pinHashEnc if (ret != 0) return ret; @@ -132,6 +167,12 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui uint8_t * args[5] = {NULL,NULL,NULL,NULL,NULL}; uint8_t lens[5]; + uint8_t key[256]; + uint8_t shasum[32]; + uint8_t chksum[4]; + + int keysize = sizeof(key); + for (i = 0; i < sizeof(sig); i++) { @@ -177,7 +218,7 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui printf1(TAG_WALLET,"WalletSign\n"); printf1(TAG_WALLET,"pinAuth:"); dump_hex1(TAG_WALLET, req->pinAuth, 16); - if (args[0] == NULL) + if (args[0] == NULL || lens[0] == 0) { ret = CTAP2_ERR_MISSING_PARAMETER; printf2(TAG_ERR,"Missing parameter for WalletSign\n"); @@ -208,9 +249,91 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui { printf1(TAG_WALLET,"Warning: no pin is set. Ignoring pinAuth\n"); } + + + ret = ctap_load_key(0, key); + + if (ret != 0) + { + ret = CTAP2_ERR_NO_CREDENTIALS; + goto cleanup; + } + + keysize = ctap_key_len(0); + + crypto_load_external_key(key, keysize); + crypto_ecdsa_sign(args[0], lens[0], sig, MBEDTLS_ECP_DP_SECP256R1); + + u2f_response_writeback(sig,64); + break; case WalletRegister: printf1(TAG_WALLET,"WalletRegister\n"); + if (args[0] == NULL) + { + ret = CTAP2_ERR_MISSING_PARAMETER; + printf2(TAG_ERR,"Missing parameter for WalletReg\n"); + goto cleanup; + } + if (lens[0] < 8 || lens[0] > keysize) + { + ret = CTAP1_ERR_INVALID_LENGTH; + printf2(TAG_ERR,"Invalid length for WalletReg\n"); + goto cleanup; + } + if (ctap_is_pin_set()) + { + if (check_pinhash(req->pinAuth, msg_buf, reqlen)) + { + printf1(TAG_WALLET,"pinAuth is valid\n"); + } + else + { + printf1(TAG_WALLET,"pinAuth is NOT valid\n"); + ret = CTAP2_ERR_PIN_AUTH_INVALID; + goto cleanup; + } + } + else + { + printf1(TAG_WALLET,"Warning: no pin is set. Ignoring pinAuth\n"); + } + + memmove(chksum, args[0] + lens[0] - 4, 4); + lens[0] -= 4; + + // perform integrity check + printf1(TAG_WALLET,"shasum on [%d]: ",lens[0]); dump_hex1(TAG_WALLET, args[0], lens[0]); + crypto_sha256_init(); + crypto_sha256_update(args[0], lens[0]); + crypto_sha256_final(shasum); + crypto_sha256_init(); + crypto_sha256_update(shasum, 32); + crypto_sha256_final(shasum); + + if (memcmp(shasum, chksum, 4) != 0) + { + ret = CTAP2_ERR_CREDENTIAL_NOT_VALID; + printf2(TAG_ERR,"Integrity fail for WalletReg\n"); + dump_hex1(TAG_ERR, chksum, sizeof(chksum)); + goto cleanup; + } + + // drop the first byte + args[0]++; + lens[0]--; + + printf1(TAG_WALLET,"adding key [%d]: ",lens[0]); dump_hex1(TAG_WALLET, args[0], lens[0]); + + ret = ctap_store_key(0, args[0], lens[0]); + + if (ret == ERR_NO_KEY_SPACE || ret == ERR_KEY_SPACE_TAKEN) + { + ret = CTAP2_ERR_KEY_STORE_FULL; + goto cleanup; + } + + break; case WalletPin: printf1(TAG_WALLET,"WalletPin\n"); diff --git a/pc/app.h b/pc/app.h index 301ae7d..30c2fc5 100644 --- a/pc/app.h +++ b/pc/app.h @@ -10,6 +10,8 @@ #define USING_DEV_BOARD +#define USING_PC + #define BRIDGE_TO_WALLET void printing_init(); diff --git a/pc/device.c b/pc/device.c index 184656c..300a72d 100644 --- a/pc/device.c +++ b/pc/device.c @@ -15,6 +15,7 @@ #include "log.h" +void authenticator_initialize(); int udp_server() { @@ -135,9 +136,13 @@ void usbhid_close() udp_close(serverfd); } + void device_init() { usbhid_init(); + + authenticator_initialize(); + } @@ -206,6 +211,193 @@ int ctap_generate_rng(uint8_t * dst, size_t num) } +const char * state_file = "authenticator_state.bin"; +const char * backup_file = "authenticator_state2.bin"; + +void authenticator_read_state(AuthenticatorState * state) +{ + FILE * f; + int ret; + + f = fopen(state_file, "rb"); + if (f== NULL) + { + perror("fopen"); + exit(1); + } + + ret = fread(state, 1, sizeof(AuthenticatorState), f); + fclose(f); + if(ret != sizeof(AuthenticatorState)) + { + perror("fwrite"); + exit(1); + } + +} + +void authenticator_read_backup_state(AuthenticatorState * state ) +{ + FILE * f; + int ret; + + f = fopen(backup_file, "rb"); + if (f== NULL) + { + perror("fopen"); + exit(1); + } + + ret = fread(state, 1, sizeof(AuthenticatorState), f); + fclose(f); + if(ret != sizeof(AuthenticatorState)) + { + perror("fwrite"); + exit(1); + } +} + +void authenticator_write_state(AuthenticatorState * state, int backup) +{ + FILE * f; + int ret; + + if (! backup) + { + f = fopen(state_file, "wb+"); + if (f== NULL) + { + perror("fopen"); + exit(1); + } + ret = fwrite(state, 1, sizeof(AuthenticatorState), f); + fclose(f); + if (ret != sizeof(AuthenticatorState)) + { + perror("fwrite"); + exit(1); + } + } + else + { + + f = fopen(backup_file, "wb+"); + if (f== NULL) + { + perror("fopen"); + exit(1); + } + ret = fwrite(state, 1, sizeof(AuthenticatorState), f); + fclose(f); + if (ret != sizeof(AuthenticatorState)) + { + perror("fwrite"); + exit(1); + } + } +} + +// Return 1 yes backup is init'd, else 0 +int authenticator_is_backup_initialized() +{ + uint8_t header[16]; + AuthenticatorState * state = (AuthenticatorState*) header; + FILE * f; + int ret; + uint8_t * mem; + + printf("state file exists\n"); + f = fopen(backup_file, "rb"); + if (f== NULL) + { + printf("Warning, backup file doesn't exist\n"); + return 0; + } + + ret = fread(header, 1, sizeof(header), f); + fclose(f); + if(ret != sizeof(header)) + { + perror("fwrite"); + exit(1); + } + + return state->is_initialized == INITIALIZED_MARKER; + +} + +// Return 1 yes backup is init'd, else 0 +int authenticator_is_initialized() +{ + + +} + +void authenticator_initialize() +{ + uint8_t header[16]; + FILE * f; + int ret; + uint8_t * mem; + if (access(state_file, F_OK) != -1) + { + printf("state file exists\n"); + f = fopen(state_file, "rb"); + if (f== NULL) + { + perror("fopen"); + exit(1); + } + + ret = fread(header, 1, sizeof(header), f); + fclose(f); + if(ret != sizeof(header)) + { + perror("fwrite"); + exit(1); + } + } + else + { + printf("state file does not exist, creating it\n"); + f = fopen(state_file, "wb+"); + if (f== NULL) + { + perror("fopen"); + exit(1); + } + mem = malloc(sizeof(AuthenticatorState)); + memset(mem,0xff,sizeof(AuthenticatorState)); + ret = fwrite(mem, 1, sizeof(AuthenticatorState), f); + free(mem); + fclose(f); + if (ret != sizeof(AuthenticatorState)) + { + perror("fwrite"); + exit(1); + } + + f = fopen(backup_file, "wb+"); + if (f== NULL) + { + perror("fopen"); + exit(1); + } + mem = malloc(sizeof(AuthenticatorState)); + memset(mem,0xff,sizeof(AuthenticatorState)); + ret = fwrite(mem, 1, sizeof(AuthenticatorState), f); + free(mem); + fclose(f); + if (ret != sizeof(AuthenticatorState)) + { + perror("fwrite"); + exit(1); + } + + } +} + + diff --git a/tools/ctap_test.py b/tools/ctap_test.py index 31643fc..120f229 100644 --- a/tools/ctap_test.py +++ b/tools/ctap_test.py @@ -36,8 +36,6 @@ class Packet(object): def FromWireFormat(pkt_size,data): return Packet(data) - - class Tester(): def __init__(self,): self.origin = 'https://examplo.org' diff --git a/web/js/wallet.js b/web/js/wallet.js index e1d160e..a3fd470 100644 --- a/web/js/wallet.js +++ b/web/js/wallet.js @@ -1,5 +1,8 @@ DEVELOPMENT = 1; +var to_b58 = function(B){var A="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";var d=[],s="",i,j,c,n;for(i in B){j=0,c=B[i];s+=c||s.length^i?"":1;while(j in d||c){n=d[j];n=n?n*256+c:c;c=n/58|0;d[j]=n%58;j++}}while(j--)s+=A[d[j]];return s}; +var from_b58 = function(S){var A="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";var d=[],b=[],i,j,c,n;for(i in S){j=0,c=A.indexOf(S[i]);if(c<0)throw new Error('Invald b58 character');c||b.length^i?i:b.push(0);while(j in d||c){n=d[j];n=n?n*58+c:c;c=n>>8;d[j]=n%256;j++}}while(j--)b.push(d[j]);return new Uint8Array(b)}; + function hex(byteArray, join) { if (join === undefined) join = ' '; return Array.from(byteArray, function(byte) { @@ -67,6 +70,10 @@ function hex2array(string) return arr; } +function array2hex(buffer) { + return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join(''); +} + // https://stackoverflow.com/questions/18729405/how-to-convert-utf8-string-to-byte-array function toUTF8Array(str) { var utf8 = []; @@ -356,13 +363,32 @@ function signRequestFormat(sigAlg,pinToken,challenge,keyid) { var cmd = CMD.sign; var p1 = sigAlg; var p2 = 0; + + if (typeof(challenge) == 'string') + { + challenge = websafe2array(challenge); + } + var args = [challenge]; if (keyid) args.push(keyid) var pinAuth = computePinAuth(pinToken,cmd,p1,p2,args); - console.log(hex(pinAuth)); var req = formatRequest(cmd,p1,p2,pinAuth,args); - console.log('',req); + + return req; +} + +// @wifkey is wif key in base58 format string +function registerRequestFormat(wifkey, pinToken) { + + var cmd = CMD.register; + var p1 = 0; + var p2 = 0; + var keyarr = from_b58(wifkey); + var args = [keyarr]; + + var pinAuth = computePinAuth(pinToken,cmd,p1,p2,args); + var req = formatRequest(cmd,p1,p2,pinAuth,args); return req; } @@ -396,7 +422,6 @@ var get_shared_secret_ = function(func) { send_msg(req, function(resp){ var i; - console.log('getKeyAgreement response:', resp); var devicePubkeyHex = '04'+hex(resp.data,''); var devicePubkey = self.ecp256.keyFromPublic(devicePubkeyHex,'hex'); @@ -424,7 +449,6 @@ var authenticate_ = function(pin, func){ hash = sha256.create(); hash.update(toUTF8Array(pin)); pinHash = hash.array().slice(0,16); - console.log('pinHash:', hex(pinHash)); var iv = new Uint8Array(16); iv.fill(0); @@ -432,7 +456,6 @@ var authenticate_ = function(pin, func){ var aesCbc = new aesjs.ModeOfOperation.cbc(this.shared_secret, iv); pinHashEnc = aesCbc.encrypt(pinHash); - console.log('pinenc:', hex(pinHashEnc)); var ourPubkey = this.platform_keypair.getPublic(undefined, 'hex'); var ourPubkeyBytes = hex2array(ourPubkey.slice(2,ourPubkey.length)); @@ -440,12 +463,10 @@ var authenticate_ = function(pin, func){ var req = pinRequestFormat(PIN.getPinToken, pinHashEnc, ourPubkeyBytes); - console.log('pinTokenReq',req); var self = this; send_msg(req, function(resp){ - console.log('getPinToken:', resp); var aesCbc = new aesjs.ModeOfOperation.cbc(self.shared_secret, iv); var pinTokenEnc = resp.data; var pinToken = aesCbc.decrypt(pinTokenEnc); @@ -491,8 +512,6 @@ var set_pin_ = function(pin, func, failAuth){ var pinBytesPadded = pin2bytes(pin); var encLen = pinBytesPadded.length; - console.log('encrypted len: ',encLen); - var iv = new Uint8Array(16); iv.fill(0); @@ -539,7 +558,6 @@ var change_pin_ = function(curpin, newpin, func, failAuth){ var pinBytesPadded = pin2bytes(newpin); var encLen = pinBytesPadded.length; - console.log('encrypted len: ',encLen); var iv = new Uint8Array(16); iv.fill(0); @@ -595,6 +613,21 @@ var sign_ = function(obj, func){ }); }; +var register_ = function(wifkey, func){ + + if (!wifkey) + throw new Error("No key provided"); + + + var req = registerRequestFormat(wifkey,this.pinToken); + + send_msg(req, function(resp){ + if (func) func(resp); + }); +}; + + + function WalletDevice() { var self = this; this.shared_secret = null; @@ -623,6 +656,39 @@ function WalletDevice() { this.change_pin = change_pin_; this.get_retries = get_retries_; + + this.register = register_; +} + +// @key input private key in hex string format +function key2wif(key) +{ + //2 + key = '0x80' + key; + + bin = hex2array(key); + + //3 + var hash = sha256.create(); + hash.update(bin); + bin = hash.array(); + + //4 + hash = sha256.create(); + hash.update(bin); + bin = hash.array(); + + // 5 + var chksum = bin.slice(0,4); + + // 6 + key = key + array2hex(chksum); + + // 7 + key = hex2array(key); + key = to_b58(key); + + return key; } @@ -637,14 +703,18 @@ function run_tests() { dev.is_pin_set(function(bool){ if (bool) { - console.log('Pin is set. Changing it again..'); - dev.change_pin(pin,pin2,function(succ){ - console.log('Pin set to ' + pin2,succ); + console.log('Pin is set. '); + //dev.change_pin(pin,pin2,function(succ){ + //console.log('Pin set to ' + pin2,succ); - dev.get_retries(function(num){ - console.log("Have "+num+" attempts to get pin right"); - }); + //dev.get_retries(function(num){ + //console.log("Have "+num+" attempts to get pin right"); + //}); + //}); + dev.authenticate(pin, function(){ + + t2(); }); } else { @@ -657,6 +727,45 @@ function run_tests() { }); + function t2 () + { + var ec = new EC('p256'); + var key = ec.genKeyPair(); + + var priv = key.getPrivate('hex'); + + // convert to wif + priv = key2wif(priv); + + var chal = 'ogfhriodghdro;igh'; + + var hash = sha256.create(); + hash.update(chal); + chal = hash.array(); + + + dev.register(priv, function(resp){ + console.log('register response', resp); + dev.sign({challenge: chal}, function(resp){ + + var r = resp.data.slice(0,32); + var s = resp.data.slice(32,64); + + r = array2hex(r); + s = array2hex(s); + + var sig = {r: r, s: s}; + + console.log('sign response', resp); + + var ver = key.verify(chal, sig); + + console.log("verify: ",ver); + + }); + }); + } + }