diff --git a/Makefile b/Makefile index 1004e5f..507e600 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,8 @@ efm8prog: flashefm8.exe -part EFM8UB10F8G -sn 440105518 -erase flashefm8.exe -part EFM8UB10F8G -sn 440105518 -upload '.\efm8\Keil 8051 v9.53 - Debug\efm8.hex' +efm32com: + cd '.\efm32\GNU ARM v7.2.1 - Debug' && $(MAKE) all efm32prog: cd '.\efm32\GNU ARM v7.2.1 - Debug' && $(MAKE) all commander flash '.\efm32\GNU ARM v7.2.1 - Debug\EFM32.hex' -s 440121060 diff --git a/efm32/src/crypto.c b/efm32/src/crypto.c index 23bd865..8bab5f0 100644 --- a/efm32/src/crypto.c +++ b/efm32/src/crypto.c @@ -6,9 +6,11 @@ #include #include +#include "em_adc.h" + #include "util.h" #include "crypto.h" -#include "em_adc.h" +#include "device.h" #include "sha256.h" @@ -33,7 +35,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; +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" @@ -186,9 +188,13 @@ void crypto_ecc256_init() void crypto_load_external_key(uint8_t * key, int len) { _signing_key = key; - _external_key_len = len; + _key_len = len; +} +void crypto_ecc256_load_attestation_key() +{ + _signing_key = attestation_key; + _key_len = 32; } - @@ -274,48 +280,75 @@ void crypto_ecc256_sign(uint8_t * data, int len, uint8_t * sig) } +#if 1 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) + 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); } +#else +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 */ + + mbedtls_ecp_group_init( &grp ); + mbedtls_mpi_init( &d ); + mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_ID); + 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); +} + + + +#endif /* * Generate a keypair with configurable base point */ @@ -528,6 +561,7 @@ 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_ecc256_make_key_pair(uint8_t * pubkey, uint8_t * privkey) diff --git a/efm32/src/device.c b/efm32/src/device.c index 2c913d5..03a0efd 100644 --- a/efm32/src/device.c +++ b/efm32/src/device.c @@ -14,10 +14,12 @@ #include "em_adc.h" #include "em_cmu.h" +#include "InitDevice.h" #include "cbor.h" #include "log.h" #include "ctaphid.h" #include "util.h" +#include "app.h" #define MSG_AVAIL_PIN gpioPortC,9 #define RDY_PIN gpioPortC,10 @@ -63,7 +65,6 @@ void ctaphid_write_block(uint8_t * data) void heartbeat() { - static int beat = 0; GPIO_PinOutToggle(gpioPortF,4); GPIO_PinOutToggle(gpioPortF,5); @@ -199,6 +200,46 @@ void init_adc() ADC0->SINGLECTRLX |= _ADC_SINGLECTRLX_VINATT_MASK; ADC0->SINGLEFIFOCLEAR = ADC_SINGLEFIFOCLEAR_SINGLEFIFOCLEAR; } + +static uint8_t _STATE1[sizeof(AuthenticatorState)]; +static uint8_t _STATE2[sizeof(AuthenticatorState)]; + +void authenticator_read_state(AuthenticatorState * state) +{ + memmove(state,_STATE1,sizeof(AuthenticatorState)); +} + +void authenticator_read_backup_state(AuthenticatorState * state ) +{ + memmove(state,_STATE2,sizeof(AuthenticatorState)); +} + +void authenticator_write_state(AuthenticatorState * state, int backup) +{ + if (! backup) + { + memmove(_STATE1,state,sizeof(AuthenticatorState)); + } + else + { + memmove(_STATE2,state,sizeof(AuthenticatorState)); + } +} + +// Return 1 yes backup is init'd, else 0 +int authenticator_is_backup_initialized() +{ + uint8_t header[16]; + AuthenticatorState * state = (AuthenticatorState*) _STATE2; + return state->is_initialized == INITIALIZED_MARKER; +} + + + +uint8_t adc_rng(void); + + + void device_init(void) { /* Chip errata */ diff --git a/fido2/ctap.c b/fido2/ctap.c index 315f62c..e7b5b02 100644 --- a/fido2/ctap.c +++ b/fido2/ctap.c @@ -1404,7 +1404,6 @@ static uint16_t key_addr_offset(int index) 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; diff --git a/fido2/main.c b/fido2/main.c index 7daaed7..f7d801c 100644 --- a/fido2/main.c +++ b/fido2/main.c @@ -27,7 +27,7 @@ int main(int argc, char * argv[]) // TAG_GEN| /*TAG_MC |*/ /*TAG_GA |*/ - TAG_WALLET | + /*TAG_WALLET |*/ TAG_STOR | /*TAG_CP |*/ // TAG_CTAP| @@ -35,7 +35,7 @@ int main(int argc, char * argv[]) /*TAG_U2F|*/ /*TAG_PARSE |*/ // TAG_TIME| -// TAG_DUMP| + /*TAG_DUMP|*/ /*TAG_GREEN|*/ /*TAG_RED|*/ TAG_ERR diff --git a/fido2/u2f.c b/fido2/u2f.c index b2ea06d..a1a0071 100644 --- a/fido2/u2f.c +++ b/fido2/u2f.c @@ -37,7 +37,9 @@ void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp) { if (req->p1 == U2F_AUTHENTICATE_CHECK) { - if (memcmp(auth->chal, CHALLENGE_PIN, 32) == 0) // Pin requests + + + if (is_wallet_device((uint8_t *) &auth->kh, auth->khl)) // Pin requests { rcode = U2F_SW_CONDITIONS_NOT_SATISFIED; } @@ -45,13 +47,15 @@ void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp) { rcode = U2F_SW_WRONG_DATA; } + printf1(TAG_WALLET,"Ignoring U2F request\n"); goto end; } else { - if (memcmp(auth->chal, CHALLENGE_PIN, 32) != 0) // Pin requests + if ( ! is_wallet_device((uint8_t *) &auth->kh, auth->khl)) // Pin requests { rcode = U2F_SW_WRONG_PAYLOAD; + printf1(TAG_WALLET,"Ignoring U2F request\n"); goto end; } rcode = bridge_u2f_to_wallet(auth->chal, auth->app, auth->khl, (uint8_t*)&auth->kh); diff --git a/fido2/wallet.c b/fido2/wallet.c index 40d9046..eb5effd 100644 --- a/fido2/wallet.c +++ b/fido2/wallet.c @@ -32,8 +32,11 @@ typedef enum MBEDTLS_ECP_DP_SECP224K1, /*!< 224-bits "Koblitz" curve */ MBEDTLS_ECP_DP_SECP256K1, /*!< 256-bits "Koblitz" curve */ } mbedtls_ecp_group_id; +#else +#include "ecp.h" #endif + typedef enum { WalletSign = 0x10, @@ -44,14 +47,23 @@ typedef enum WalletRng = 0x15, } WalletOperation; +int is_wallet_device(uint8_t * kh, int len) +{ + wallet_request * req = (wallet_request *) kh; + + if (len < WALLET_MIN_LENGTH) + return 0; + + return memcmp(req->tag, WALLET_TAG, sizeof(WALLET_TAG)-1) == 0; +} // return 1 if hash is valid, 0 otherwise int check_pinhash(uint8_t * pinAuth, uint8_t * msg, uint8_t len) { uint8_t hmac[32]; crypto_sha256_hmac_init(PIN_TOKEN, PIN_TOKEN_SIZE, hmac); - crypto_sha256_update(msg, 4); - crypto_sha256_update(msg+ 4 + 16, len - 4 - 16); + crypto_sha256_update(msg, 8); + crypto_sha256_update(msg+ 8 + 16, len - 8 - 16); crypto_sha256_hmac_final(PIN_TOKEN, PIN_TOKEN_SIZE, hmac); return (memcmp(pinAuth, hmac, 16) == 0); @@ -404,7 +416,7 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui break; case WalletVersion: - u2f_response_writeback(WALLET_VERSION, sizeof(WALLET_VERSION)-1); + u2f_response_writeback((uint8_t*)WALLET_VERSION, sizeof(WALLET_VERSION)-1); break; case WalletRng: printf1(TAG_WALLET,"WalletRng\n"); diff --git a/fido2/wallet.h b/fido2/wallet.h index 358a54a..4423c4f 100644 --- a/fido2/wallet.h +++ b/fido2/wallet.h @@ -54,14 +54,16 @@ // Returns public key OR pinAuth // Only response to this challenge to prevent interference -#define CHALLENGE_PIN "\xf6\xa2\x3c\xa4\x0a\xf9\xda\xd4\x5f\xdc\xba\x7d\xc9\xde\xcb\xed\xb5\x84\x64\x3a\x4c\x9f\x44\xc2\x04\xb0\x17\xd7\xf4\x3e\xe0\x3f" +#define WALLET_TAG "\x8C\x27\x90\xf6" + +#define WALLET_MIN_LENGTH (4 + 4 + 16) #define WALLET_VERSION "WALLET_V1.0" -#define MAX_CHALLENGE_SIZE 233 -#define MAX_KEYID_SIZE 232 +#define MAX_CHALLENGE_SIZE 229 +#define MAX_KEYID_SIZE 228 -#define MAX_PAYLOAD_SIZE (255 - 16 - 4) +#define MAX_PAYLOAD_SIZE (255 - 16 - 4 - 4) typedef struct { @@ -69,14 +71,17 @@ typedef struct uint8_t p1; uint8_t p2; uint8_t numArgs; + uint8_t tag[4]; uint8_t pinAuth[16]; uint8_t payload[MAX_PAYLOAD_SIZE]; }__attribute__((packed)) wallet_request; - int16_t bridge_u2f_to_wallet(uint8_t * chal, uint8_t * appid, uint8_t klen, uint8_t * keyh); +// return 1 if request is a wallet request +int is_wallet_device(uint8_t * req, int len); + void wallet_init(); #endif /* WALLET_H_ */ diff --git a/pc/device.c b/pc/device.c index 300a72d..b96da85 100644 --- a/pc/device.c +++ b/pc/device.c @@ -327,11 +327,11 @@ int authenticator_is_backup_initialized() } // Return 1 yes backup is init'd, else 0 -int authenticator_is_initialized() -{ +/*int authenticator_is_initialized()*/ +/*{*/ -} +/*}*/ void authenticator_initialize() { diff --git a/web/js/u2f-api.js b/web/js/u2f-api.js index 2c7da47..3d14d1e 100644 --- a/web/js/u2f-api.js +++ b/web/js/u2f-api.js @@ -185,7 +185,6 @@ u2f.GetJsApiVersionResponse; * @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback */ u2f.getMessagePort = function(callback) { - console.log("getMessagePort"); if (typeof chrome != 'undefined' && chrome.runtime) { // The actual message here does not matter, but we need to get a reply // for the callback to run. Thus, send an empty signature request @@ -198,15 +197,12 @@ u2f.getMessagePort = function(callback) { if (!chrome.runtime.lastError) { // We are on a whitelisted origin and can talk directly // with the extension. - console.log("talk with ext"); u2f.getChromeRuntimePort_(callback); } else { // chrome.runtime was available, but we couldn't message // the extension directly, use iframe - console.log("talk with ext from iframe"); u2f.getIframePort_(callback); - console.log("setup"); } }); } else if (u2f.isAndroidChrome_()) { @@ -412,7 +408,6 @@ u2f.WrappedAuthenticatorPort_.prototype.postMessage = function(message) { u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ + ';S.request=' + encodeURIComponent(JSON.stringify(message)) + ';end'; - console.log(intentUrl); document.location = intentUrl; }; @@ -641,7 +636,6 @@ u2f.sign = function(appId, challenge, registeredKeys, callback, opt_timeoutSecon u2f.getApiVersion( function (response) { js_api_version = response['js_api_version'] === undefined ? 0 : response['js_api_version']; - console.log("Extension JS API Version: ", js_api_version); u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds); }); } else { @@ -687,7 +681,6 @@ u2f.register = function(appId, registerRequests, registeredKeys, callback, opt_t u2f.getApiVersion( function (response) { js_api_version = response['js_api_version'] === undefined ? 0: response['js_api_version']; - console.log("Extension JS API Version: ", js_api_version); u2f.sendRegisterRequest(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds); }); diff --git a/web/js/wallet.js b/web/js/wallet.js index d9e9bbd..1fb559d 100644 --- a/web/js/wallet.js +++ b/web/js/wallet.js @@ -1,4 +1,4 @@ -DEVELOPMENT = 1; +DEVELOPMENT = 0; 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)}; @@ -313,10 +313,16 @@ function send_msg_u2f(data, func, timeout) { timeout = timeout || 5; var appid = window.location.origin; - var chal = string2websafe('AABBCC'); + //var chal = string2websafe('AABBCC'); var chal = array2websafe(hex2array('d1cd7357bcedc03fcec112fe5a7f3f890292ff6f758978928b736ce1e63479e5')); + var args = { + type: 'navigator.id.getAssertion', + challenge: chal, + origin: appid + }; + var keyHandle = array2websafe(data); var key = { @@ -358,7 +364,7 @@ function formatRequest(cmd, p1, p2, pinAuth, args) { for (i = 0; i < args.length; i+=1) { argslen += args[i].length + 1 } - var len = 16 + 4 + argslen; + var len = 16 + 4 + 4 +argslen; if (len > 255) { @@ -372,13 +378,20 @@ function formatRequest(cmd, p1, p2, pinAuth, args) { array[2] = p2 & 0xff; array[3] = (args.length) & 0xff; + array[4] = 0x8C; // Wallet tag. To not interfere with U2F devices. + array[5] = 0x27; + array[6] = 0x90; + array[7] = 0xf6; + + var offset = 8; + if (pinAuth) { for (i = 0; i < 16; i += 1) { - array[4 + i] = pinAuth[i]; + array[offset + i] = pinAuth[i]; } } - var offset = 4 + i; + offset = offset + i; for (i = 0; i < args.length; i += 1) { array[offset] = args[i].length; @@ -406,6 +419,7 @@ function computePinAuth(pinToken, cmd,p1,p2,args) if (args && args.length) hmac.update([args.length]); else hmac.update([0]); + hmac.update([0x8c,0x27,0x90,0xf6]); if (args) { for (i = 0; i < args.length; i++) @@ -1125,10 +1139,41 @@ async function run_tests() { TEST(entropy > 7.99, 'Rng has good entropy: ' + entropy); } + async function benchmark() + { + var d = new Date(); + var t1,t2,i; + var ec = new EC('secp256k1'); + var key = ec.genKeyPair(); + var priv = key.getPrivate('hex'); + + var wif = key2wif(priv); // convert to wif + + // Corrupt 1 byte + var b = (wif[32] == 'A') ? 'B' : 'A'; + var badwif = wif.substring(0, 32) + b + wif.substring(32+1); + var chal = string2challenge('abc'); + + var p = await dev.register(wif); + TEST(p.status == 'CTAP1_SUCCESS', 'Wallet accepts good WIF key'); + + + for (i = 0; i < 10; i++) + { + t1 = performance.now(); + p = await dev.sign({challenge: chal}); + t2 = performance.now(); + var ver = key.verify(chal, p.sig); + TEST(ver && p.status == 'CTAP1_SUCCESS', 'Wallet returns signature ('+(t2-t1)+' ms)'); + } + + } + await device_start_over(); - await test_pin(); - await test_crypto(); - await test_rng(); + //await test_pin(); + //await test_crypto(); + //await test_rng(); + await benchmark(); }