From 7536c9338fe615e56fedb13105be49c3ee76ef02 Mon Sep 17 00:00:00 2001 From: Stephanie Stroka Date: Tue, 1 May 2018 18:18:13 +0200 Subject: [PATCH] Update pairing mechanism and add MAC --- src/CMakeLists.txt | 3 + src/aescbcb64.c | 192 ++++++++++++++++++++++++++ src/aescbcb64.h | 43 ++++++ src/commander.c | 327 ++++++--------------------------------------- src/commander.h | 5 - src/ecdh.c | 289 +++++++++++++++++++++++++++++++++++++++ src/ecdh.h | 39 ++++++ src/flags.h | 12 +- src/flash.c | 2 +- src/flash.h | 12 +- src/led.c | 36 ++--- src/led.h | 2 +- src/memory.c | 30 +++-- src/memory.h | 7 +- src/sharedsecret.c | 47 +++++++ src/sharedsecret.h | 33 +++++ src/utils.c | 3 + src/utils.h | 1 + tests/api.h | 12 +- tests/hmac_check.h | 106 +++++++++++++++ tests/tests_api.c | 311 ++++++++++++++++++++++++++++++++---------- tests/tests_unit.c | 44 ++++++ 22 files changed, 1151 insertions(+), 405 deletions(-) create mode 100644 src/aescbcb64.c create mode 100644 src/aescbcb64.h create mode 100644 src/ecdh.c create mode 100644 src/ecdh.h create mode 100644 src/sharedsecret.c create mode 100644 src/sharedsecret.h create mode 100644 tests/hmac_check.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3a339314..f088dc9c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,6 +9,8 @@ set(DBB-FIRMWARE-SOURCES aes.c + sharedsecret.c + aescbcb64.c base58.c base64.c pbkdf2.c @@ -31,6 +33,7 @@ set(DBB-FIRMWARE-SOURCES ataes132.c flash.c touch.c + ecdh.c ) if(USE_SECP256K1_LIB) diff --git a/src/aescbcb64.c b/src/aescbcb64.c new file mode 100644 index 00000000..db584492 --- /dev/null +++ b/src/aescbcb64.c @@ -0,0 +1,192 @@ +/* + + The MIT License (MIT) + + Copyright (c) 2015-2018 Douglas J. Bakkum + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include +#include +#include + +#include "aescbcb64.h" +#include "hmac.h" +#include "commander.h" +#include "sharedsecret.h" +#include "memory.h" +#include "base64.h" +#include "aes.h" +#include "sha2.h" +#include "random.h" +#include "flags.h" +#include "utils.h" + +// Must free() returned value +static uint8_t *aescbcb64_init_and_encrypt(const unsigned char *in, int inlen, + int *out_len, + const uint8_t *key) +{ + int pads; + int inpadlen = inlen + N_BLOCK - inlen % N_BLOCK; + unsigned char inpad[inpadlen]; + unsigned char enc[inpadlen]; + unsigned char iv[N_BLOCK]; + uint8_t *enc_cat = malloc(sizeof(uint8_t) * (inpadlen + + N_BLOCK)); // concatenating [ iv0 | enc ] + *out_len = inpadlen + N_BLOCK; + + aes_context ctx[1]; + + // Set cipher key + memset(ctx, 0, sizeof(ctx)); + aes_set_key(key, 32, ctx); + + // PKCS7 padding + memcpy(inpad, in, inlen); + for (pads = 0; pads < N_BLOCK - inlen % N_BLOCK; pads++ ) { + inpad[inlen + pads] = (N_BLOCK - inlen % N_BLOCK); + } + + // Make a random initialization vector + if (random_bytes((uint8_t *)iv, N_BLOCK, 0) == DBB_ERROR) { + commander_fill_report(cmd_str(CMD_random), NULL, DBB_ERR_MEM_ATAES); + utils_zero(inpad, inpadlen); + utils_zero(ctx, sizeof(ctx)); + return NULL; + } + memcpy(enc_cat, iv, N_BLOCK); + + // CBC encrypt multiple blocks + aes_cbc_encrypt(inpad, enc, inpadlen / N_BLOCK, iv, ctx); + memcpy(enc_cat + N_BLOCK, enc, inpadlen); + + utils_zero(inpad, inpadlen); + utils_zero(ctx, sizeof(ctx)); + return enc_cat; +} + + +// Must free() returned value (allocated inside base64() function) +char *aescbcb64_encrypt(const unsigned char *in, int inlen, int *out_b64len, + const uint8_t *key) +{ + int out_len; + uint8_t *enc_cat = aescbcb64_init_and_encrypt(in, inlen, &out_len, key); + // base64 encoding + char *b64; + b64 = base64(enc_cat, out_len, out_b64len); + free(enc_cat); + return b64; +} + +// Encrypts a given constant char array of length inlen using the AES algorithm with CBC mode, +// appends its SHA256 HMAC and base64 encodes the result. +// +// Must free() returned value +char *aescbcb64_hmac_encrypt(const unsigned char *in, int inlen, int *out_b64len, + const uint8_t *shared_secret) +{ + uint8_t encryption_key[SHA256_DIGEST_LENGTH]; + uint8_t authentication_key[SHA256_DIGEST_LENGTH]; + + sharedsecret_derive_keys(shared_secret, encryption_key, authentication_key); + + int encrypt_len; + uint8_t *encrypted = aescbcb64_init_and_encrypt(in, + inlen, + &encrypt_len, + encryption_key); + uint8_t hmac[SHA256_DIGEST_LENGTH]; + hmac_sha256(authentication_key, SHA256_DIGEST_LENGTH, encrypted, encrypt_len, hmac); + + uint8_t authenticated_encrypted_msg[encrypt_len + SHA256_DIGEST_LENGTH]; + memcpy(authenticated_encrypted_msg, encrypted, encrypt_len); + memcpy(authenticated_encrypted_msg + encrypt_len, hmac, SHA256_DIGEST_LENGTH); + + free(encrypted); + utils_zero(encryption_key, sizeof(encryption_key)); + utils_zero(authentication_key, sizeof(authentication_key)); + char *b64 = base64(authenticated_encrypted_msg, encrypt_len + SHA256_DIGEST_LENGTH, + out_b64len); + return b64; +} + +char *aescbcb64_init_and_decrypt(uint8_t *ub64, int ub64len, int *decrypt_len, + const uint8_t *key) +{ + *decrypt_len = 0; + + // Set cipher key + aes_context ctx[1]; + memset(ctx, 0, sizeof(ctx)); + aes_set_key(key, 32, ctx); + + unsigned char dec_pad[ub64len - N_BLOCK]; + aes_cbc_decrypt(ub64 + N_BLOCK, dec_pad, ub64len / N_BLOCK - 1, ub64, ctx); + + // Strip PKCS7 padding + int padlen = dec_pad[ub64len - N_BLOCK - 1]; + if (ub64len - N_BLOCK - padlen <= 0) { + utils_zero(dec_pad, sizeof(dec_pad)); + utils_zero(ctx, sizeof(ctx)); + return NULL; + } + char *dec = malloc(ub64len - N_BLOCK - padlen + 1); // +1 for null termination + if (!dec) { + utils_zero(dec_pad, sizeof(dec_pad)); + utils_zero(ctx, sizeof(ctx)); + return NULL; + } + memcpy(dec, dec_pad, ub64len - N_BLOCK - padlen); + dec[ub64len - N_BLOCK - padlen] = '\0'; + *decrypt_len = ub64len - N_BLOCK - padlen + 1; + utils_zero(dec_pad, sizeof(dec_pad)); + utils_zero(ctx, sizeof(ctx)); + return dec; +} + +// Must free() returned value +char *aescbcb64_decrypt(const unsigned char *in, int inlen, int *decrypt_len, + const uint8_t *key) +{ + if (!in || inlen == 0) { + return NULL; + } + + // Unbase64 + int ub64len; + unsigned char *ub64 = unbase64((const char *)in, inlen, &ub64len); + if (!ub64) { + return NULL; + } + if ((ub64len % N_BLOCK) || ub64len < N_BLOCK) { + free(ub64); + return NULL; + } + + char *ret = aescbcb64_init_and_decrypt(ub64, ub64len, decrypt_len, key); + memset(ub64, 0, ub64len); + free(ub64); + return ret; +} + + diff --git a/src/aescbcb64.h b/src/aescbcb64.h new file mode 100644 index 00000000..d31c0c60 --- /dev/null +++ b/src/aescbcb64.h @@ -0,0 +1,43 @@ +/* + + The MIT License (MIT) + + Copyright (c) 2015-2018 Douglas J. Bakkum + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef _AESCBCB64_H_ +#define _AESCBCB64_H_ + + +char *aescbcb64_hmac_encrypt(const unsigned char *in, int inlen, + int *out_b64len, const uint8_t *shared_secret); + +char *aescbcb64_init_and_decrypt(uint8_t *ub64, int ub64len, int *decrypt_len, + const uint8_t *key); + +char *aescbcb64_encrypt(const unsigned char *in, int inlen, + int *out_b64len, const uint8_t *key); + +char *aescbcb64_decrypt(const unsigned char *in, int inlen, + int *decrypt_len, const uint8_t *key); + +#endif diff --git a/src/commander.c b/src/commander.c index 97321d3d..19aeedef 100644 --- a/src/commander.c +++ b/src/commander.c @@ -41,10 +41,13 @@ #include "flags.h" #include "sha2.h" #include "aes.h" +#include "aescbcb64.h" +#include "hmac.h" #include "led.h" #include "ecc.h" #include "sd.h" #include "drivers/config/mcu.h" +#include "ecdh.h" #define BRACED(x) (strlens(x) ? (((x[0]) == '{') && ((x[strlens(x) - 1]) == '}')) : 0) @@ -57,104 +60,9 @@ static int REPORT_BUF_OVERFLOW = 0; __extension__ static char json_array[] = {[0 ... COMMANDER_ARRAY_MAX] = 0}; __extension__ static char json_report[] = {[0 ... COMMANDER_REPORT_SIZE] = 0}; __extension__ static char sign_command[] = {[0 ... COMMANDER_REPORT_SIZE] = 0}; -static char TFA_PIN[VERIFYPASS_LOCK_CODE_LEN * 2 + 1]; +static char TFA_PIN[TFA_PIN_LEN * 2 + 1]; static int TFA_VERIFY = 0; -// Must free() returned value (allocated inside base64() function) -char *aes_cbc_b64_encrypt(const unsigned char *in, int inlen, int *out_b64len, - const uint8_t *key) -{ - int pads; - int inpadlen = inlen + N_BLOCK - inlen % N_BLOCK; - unsigned char inpad[inpadlen]; - unsigned char enc[inpadlen]; - unsigned char iv[N_BLOCK]; - unsigned char enc_cat[inpadlen + N_BLOCK]; // concatenating [ iv0 | enc ] - aes_context ctx[1]; - - // Set cipher key - memset(ctx, 0, sizeof(ctx)); - aes_set_key(key, 32, ctx); - - // PKCS7 padding - memcpy(inpad, in, inlen); - for (pads = 0; pads < N_BLOCK - inlen % N_BLOCK; pads++ ) { - inpad[inlen + pads] = (N_BLOCK - inlen % N_BLOCK); - } - - // Make a random initialization vector - if (random_bytes((uint8_t *)iv, N_BLOCK, 0) == DBB_ERROR) { - commander_fill_report(cmd_str(CMD_random), NULL, DBB_ERR_MEM_ATAES); - utils_zero(inpad, inpadlen); - utils_zero(ctx, sizeof(ctx)); - return NULL; - } - memcpy(enc_cat, iv, N_BLOCK); - - // CBC encrypt multiple blocks - aes_cbc_encrypt( inpad, enc, inpadlen / N_BLOCK, iv, ctx ); - memcpy(enc_cat + N_BLOCK, enc, inpadlen); - - // base64 encoding - int b64len; - char *b64; - b64 = base64(enc_cat, inpadlen + N_BLOCK, &b64len); - *out_b64len = b64len; - utils_zero(inpad, inpadlen); - utils_zero(ctx, sizeof(ctx)); - return b64; -} - - -// Must free() returned value -char *aes_cbc_b64_decrypt(const unsigned char *in, int inlen, int *decrypt_len, - const uint8_t *key) -{ - *decrypt_len = 0; - - if (!in || inlen == 0) { - return NULL; - } - - // Unbase64 - int ub64len; - unsigned char *ub64 = unbase64((const char *)in, inlen, &ub64len); - if (!ub64 || (ub64len % N_BLOCK) || ub64len < N_BLOCK) { - free(ub64); - return NULL; - } - - // Set cipher key - aes_context ctx[1]; - memset(ctx, 0, sizeof(ctx)); - aes_set_key(key, 32, ctx); - - unsigned char dec_pad[ub64len - N_BLOCK]; - aes_cbc_decrypt(ub64 + N_BLOCK, dec_pad, ub64len / N_BLOCK - 1, ub64, ctx); - memset(ub64, 0, ub64len); - free(ub64); - - // Strip PKCS7 padding - int padlen = dec_pad[ub64len - N_BLOCK - 1]; - if (ub64len - N_BLOCK - padlen <= 0) { - utils_zero(dec_pad, sizeof(dec_pad)); - utils_zero(ctx, sizeof(ctx)); - return NULL; - } - char *dec = malloc(ub64len - N_BLOCK - padlen + 1); // +1 for null termination - if (!dec) { - utils_zero(dec_pad, sizeof(dec_pad)); - utils_zero(ctx, sizeof(ctx)); - return NULL; - } - memcpy(dec, dec_pad, ub64len - N_BLOCK - padlen); - dec[ub64len - N_BLOCK - padlen] = '\0'; - *decrypt_len = ub64len - N_BLOCK - padlen + 1; - utils_zero(dec_pad, sizeof(dec_pad)); - utils_zero(ctx, sizeof(ctx)); - return dec; -} - // // Reporting results // @@ -193,7 +101,8 @@ void commander_fill_report(const char *cmd, const char *msg, int flag) "\"%s\":{\"message\":\"%s\",\"code\":%s,\"command\":\"%s\"}", attr_str(ATTR_error), flag_msg(flag), flag_code(flag), cmd); } - } else if (flag == DBB_JSON_BOOL || flag == DBB_JSON_ARRAY || flag == DBB_JSON_NUMBER) { + } else if (flag == DBB_JSON_BOOL || flag == DBB_JSON_ARRAY || flag == DBB_JSON_NUMBER || + flag == DBB_JSON_OBJECT) { snprintf(p + strlens(json_report), COMMANDER_REPORT_SIZE - strlens(json_report), "\"%s\":%s", cmd, msg); } else { @@ -792,10 +701,11 @@ static void commander_process_random(yajl_val json_node) snprintf(echo_number, sizeof(echo_number), "{\"random\":\"%s\"}", utils_uint8_to_hex(number, sizeof(number))); - encoded_report = aes_cbc_b64_encrypt((unsigned char *)echo_number, - strlens(echo_number), - &encrypt_len, - memory_report_aeskey(PASSWORD_VERIFY)); + + encoded_report = aescbcb64_hmac_encrypt((unsigned char *) echo_number, + strlens(echo_number), + &encrypt_len, memory_report_aeskey(TFA_SHARED_SECRET)); + if (encoded_report) { commander_fill_report(cmd_str(CMD_echo), encoded_report, DBB_OK); free(encoded_report); @@ -806,155 +716,14 @@ static void commander_process_random(yajl_val json_node) } -static int commander_process_ecdh(int cmd, const uint8_t *pair_pubkey, - uint8_t *out_pubkey) -{ - uint8_t rand_privkey[32], ecdh_secret[32], rand_led, ret, i = 0; - - do { - if (random_bytes(rand_privkey, sizeof(rand_privkey), 0) == DBB_ERROR) { - commander_fill_report(cmd_str(cmd), NULL, DBB_ERR_MEM_ATAES); - return DBB_ERROR; - } - } while (!bitcoin_ecc.ecc_isValid(rand_privkey, ECC_SECP256k1)); - - if (bitcoin_ecc.ecc_ecdh(pair_pubkey, rand_privkey, ecdh_secret, ECC_SECP256k1)) { - commander_fill_report(cmd_str(cmd), NULL, DBB_ERR_KEY_ECDH); - return DBB_ERROR; - } - - // Use a 'second channel' LED blink code to avoid MITM - do { - if (random_bytes(&rand_led, sizeof(rand_led), 0) == DBB_ERROR) { - commander_fill_report(cmd_str(cmd), NULL, DBB_ERR_MEM_ATAES); - return DBB_ERROR; - } - - rand_led %= LED_MAX_CODE_BLINKS; - rand_led++; // min 1 blink - led_code(&rand_led, sizeof(rand_led)); - - // Xor ECDH secret - ecdh_secret[i % sizeof(ecdh_secret)] ^= rand_led; - i++; - } while ((touch_button_press(DBB_TOUCH_REJECT_TIMEOUT) == DBB_ERR_TOUCH_TIMEOUT) && - (i < LED_MAX_BLINK_SETS)); - - if (i == 0) { - // While loop not entered - commander_fill_report(cmd_str(CMD_touchbutton), NULL, DBB_ERR_TOUCH_ABORT); - return DBB_ERROR; - } - - // Save to eeprom - ret = commander_process_aes_key(utils_uint8_to_hex(ecdh_secret, 32), 64, - PASSWORD_VERIFY); - if (ret != DBB_OK) { - commander_fill_report(cmd_str(cmd), NULL, ret); - return ret; - } - - bitcoin_ecc.ecc_get_public_key33(rand_privkey, out_pubkey, ECC_SECP256k1); - utils_zero(rand_privkey, sizeof(rand_privkey)); - utils_zero(ecdh_secret, sizeof(ecdh_secret)); - utils_clear_buffers(); - return DBB_OK; -} - - -static void commander_process_verifypass(yajl_val json_node) +static void commander_process_ecdh(yajl_val json_node) { - uint8_t number[32] = {0}; - char *l, text[64 + 1]; - - const char *value_path[] = { cmd_str(CMD_verifypass), NULL }; - const char *value = YAJL_GET_STRING(yajl_tree_get(json_node, value_path, yajl_t_string)); - - const char *pair_pubkey_path[] = { cmd_str(CMD_verifypass), cmd_str(CMD_ecdh), NULL }; - const char *pair_pubkey = YAJL_GET_STRING(yajl_tree_get(json_node, pair_pubkey_path, - yajl_t_string)); - if (wallet_is_locked()) { - commander_fill_report(cmd_str(CMD_verifypass), NULL, DBB_ERR_IO_LOCKED); - return; - } - - if (strlens(value)) { - if (STREQ(value, attr_str(ATTR_export))) { - memcpy(text, utils_uint8_to_hex(memory_report_aeskey(PASSWORD_VERIFY), 32), 64 + 1); - utils_clear_buffers(); - int ret = sd_write(VERIFYPASS_FILENAME, text, NULL, - NULL, DBB_SD_REPLACE, CMD_verifypass); - - if (ret == DBB_OK) { - l = sd_load(VERIFYPASS_FILENAME, CMD_verifypass); - if (l) { - if (!MEMEQ(text, l, strlens(text))) { - commander_fill_report(cmd_str(CMD_verifypass), NULL, DBB_ERR_SD_CORRUPT_FILE); - } else { - commander_fill_report(cmd_str(CMD_verifypass), attr_str(ATTR_success), DBB_OK); - } - utils_zero(l, strlens(l)); - } - } - utils_zero(text, sizeof(text)); - return; - } - } - - if (strlens(value)) { - if (STREQ(value, attr_str(ATTR_create))) { - int status = touch_button_press(DBB_TOUCH_LONG); - if (status != DBB_TOUCHED) { - commander_fill_report(cmd_str(CMD_verifypass), NULL, status); - return; - } - - if (random_bytes(number, sizeof(number), 1) == DBB_ERROR) { - commander_fill_report(cmd_str(CMD_verifypass), NULL, DBB_ERR_MEM_ATAES); - return; - } - status = commander_process_aes_key(utils_uint8_to_hex(number, sizeof(number)), - sizeof(number) * 2, PASSWORD_VERIFY); - if (status != DBB_OK) { - commander_fill_report(cmd_str(CMD_verifypass), NULL, status); - return; - } - commander_fill_report(cmd_str(CMD_verifypass), attr_str(ATTR_success), DBB_OK); - return; - } - } - - if (strlens(pair_pubkey)) { - if (strlens(pair_pubkey) != 66) { - commander_fill_report(cmd_str(CMD_verifypass), NULL, DBB_ERR_KEY_ECDH_LEN); - return; - } - - uint8_t out_pubkey[33]; - if (commander_process_ecdh(CMD_verifypass, utils_hex_to_uint8(pair_pubkey), - out_pubkey) == DBB_OK) { - char msg[256]; - int encrypt_len; - char *enc = aes_cbc_b64_encrypt((const unsigned char *)VERIFYPASS_CRYPT_TEST, - strlens(VERIFYPASS_CRYPT_TEST), - &encrypt_len, - memory_report_aeskey(PASSWORD_VERIFY)); - if (enc) { - snprintf(msg, sizeof(msg), "{\"%s\":\"%s\", \"%s\":\"%s\"}", - cmd_str(CMD_ecdh), utils_uint8_to_hex(out_pubkey, sizeof(out_pubkey)), - cmd_str(CMD_ciphertext), enc); - commander_fill_report(cmd_str(CMD_verifypass), msg, DBB_JSON_ARRAY); - free(enc); - } else { - commander_clear_report(); - commander_fill_report(cmd_str(CMD_ecdh), NULL, DBB_ERR_MEM_ENCRYPT); - } - } + commander_fill_report(cmd_str(CMD_ecdh), NULL, DBB_ERR_IO_LOCKED); return; } - commander_fill_report(cmd_str(CMD_verifypass), NULL, DBB_ERR_IO_INVALID_CMD); + ecdh_dispatch_command(json_node); } @@ -975,11 +744,9 @@ static void commander_process_xpub(yajl_val json_node) commander_fill_report(cmd_str(CMD_xpub), xpub, DBB_OK); int encrypt_len; - char *encoded_report; - encoded_report = aes_cbc_b64_encrypt((unsigned char *)xpub, - strlens(xpub), - &encrypt_len, - memory_report_aeskey(PASSWORD_VERIFY)); + char *encoded_report = aescbcb64_hmac_encrypt((unsigned char *) xpub, strlens(xpub), + &encrypt_len, memory_report_aeskey(TFA_SHARED_SECRET)); + if (encoded_report) { commander_fill_report(cmd_str(CMD_echo), encoded_report, DBB_OK); free(encoded_report); @@ -1081,10 +848,11 @@ static void commander_process_device(yajl_val json_node) } int tfa_len; - char *tfa = aes_cbc_b64_encrypt((const unsigned char *)VERIFYPASS_CRYPT_TEST, - strlens(VERIFYPASS_CRYPT_TEST), - &tfa_len, - memory_report_aeskey(PASSWORD_VERIFY)); + + char *tfa = aescbcb64_hmac_encrypt((const unsigned char *)VERIFYPASS_CRYPT_TEST, + strlens(VERIFYPASS_CRYPT_TEST), + &tfa_len, + memory_report_aeskey(TFA_SHARED_SECRET)); if (!tfa) { commander_clear_report(); commander_fill_report(cmd_str(CMD_device), NULL, DBB_ERR_MEM_ENCRYPT); @@ -1125,9 +893,6 @@ static void commander_process_led(yajl_val json_node) } if (STREQ(value, attr_str(ATTR_blink))) { - led_blink(); - commander_fill_report(cmd_str(CMD_led), attr_str(ATTR_success), DBB_OK); - } else if (STREQ(value, attr_str(ATTR_abort))) { led_abort(); commander_fill_report(cmd_str(CMD_led), attr_str(ATTR_success), DBB_OK); } else { @@ -1334,8 +1099,8 @@ static int commander_process(int cmd, yajl_val json_node) commander_process_password(json_node); break; - case CMD_verifypass: - commander_process_verifypass(json_node); + case CMD_ecdh: + commander_process_ecdh(json_node); break; case CMD_led: @@ -1390,9 +1155,9 @@ static int commander_tfa_append_pin(void) { if (wallet_is_locked()) { // Create one-time PIN - uint8_t pin_b[VERIFYPASS_LOCK_CODE_LEN]; + uint8_t pin_b[TFA_PIN_LEN]; memset(TFA_PIN, 0, sizeof(TFA_PIN)); - if (random_bytes(pin_b, VERIFYPASS_LOCK_CODE_LEN, 0) == DBB_ERROR) { + if (random_bytes(pin_b, TFA_PIN_LEN, 0) == DBB_ERROR) { commander_fill_report(cmd_str(CMD_random), NULL, DBB_ERR_MEM_ATAES); return DBB_ERROR; } @@ -1401,7 +1166,7 @@ static int commander_tfa_append_pin(void) snprintf(TFA_PIN, sizeof(TFA_PIN), "0001"); #else snprintf(TFA_PIN, sizeof(TFA_PIN), "%s", - utils_uint8_to_hex(pin_b, VERIFYPASS_LOCK_CODE_LEN)); + utils_uint8_to_hex(pin_b, TFA_PIN_LEN)); #endif // Append PIN to echo @@ -1428,7 +1193,6 @@ static int commander_tfa_check_pin(yajl_val json_node) return DBB_OK; } - static int commander_echo_command(yajl_val json_node) { const char *meta_path[] = { cmd_str(CMD_sign), cmd_str(CMD_meta), NULL }; @@ -1515,12 +1279,9 @@ static int commander_echo_command(yajl_val json_node) return DBB_ERROR; } - int encrypt_len; - char *encoded_report; - encoded_report = aes_cbc_b64_encrypt((unsigned char *)json_report, - strlens(json_report), - &encrypt_len, - memory_report_aeskey(PASSWORD_VERIFY)); + int length; + char *encoded_report = aescbcb64_hmac_encrypt((unsigned char *) json_report, + strlens(json_report), &length, memory_report_aeskey(TFA_SHARED_SECRET)); commander_clear_report(); if (encoded_report) { commander_fill_report(cmd_str(CMD_echo), encoded_report, DBB_OK); @@ -1647,10 +1408,10 @@ static void commander_parse(char *command) } exit: - encoded_report = aes_cbc_b64_encrypt((unsigned char *)json_report, - strlens(json_report), - &encrypt_len, - memory_active_key_get()); + encoded_report = aescbcb64_encrypt((unsigned char *)json_report, + strlens(json_report), + &encrypt_len, + memory_active_key_get()); commander_clear_report(); if (encoded_report) { @@ -1674,13 +1435,13 @@ static uint8_t commander_find_active_key(const char *encrypted_command) key_std = memory_report_aeskey(PASSWORD_STAND); key_hdn = memory_report_aeskey(PASSWORD_HIDDEN); - cmd_std = aes_cbc_b64_decrypt((const unsigned char *)encrypted_command, - strlens(encrypted_command), - &len_std, key_std); + cmd_std = aescbcb64_decrypt((const unsigned char *)encrypted_command, + strlens(encrypted_command), + &len_std, key_std); - cmd_hdn = aes_cbc_b64_decrypt((const unsigned char *)encrypted_command, - strlens(encrypted_command), - &len_hdn, key_hdn); + cmd_hdn = aescbcb64_decrypt((const unsigned char *)encrypted_command, + strlens(encrypted_command), + &len_hdn, key_hdn); if (strlens(cmd_std)) { if (BRACED(cmd_std)) { @@ -1724,10 +1485,10 @@ static char *commander_decrypt(const char *encrypted_command) size_t json_object_len = 0; if (commander_find_active_key(encrypted_command) == DBB_OK) { - command = aes_cbc_b64_decrypt((const unsigned char *)encrypted_command, - strlens(encrypted_command), - &command_len, - memory_active_key_get()); + command = aescbcb64_decrypt((const unsigned char *)encrypted_command, + strlens(encrypted_command), + &command_len, + memory_active_key_get()); } err_count = memory_report_access_err_count(); diff --git a/src/commander.h b/src/commander.h index 9f8753e1..d9bb5444 100644 --- a/src/commander.h +++ b/src/commander.h @@ -33,11 +33,6 @@ #include "memory.h" -char *aes_cbc_b64_encrypt(const unsigned char *in, int inlen, int *out_b64len, - const uint8_t *key); -char *aes_cbc_b64_decrypt(const unsigned char *in, int inlen, int *decrypt_len, - const uint8_t *key); - void commander_clear_report(void); const char *commander_read_report(void); const char *commander_read_array(void); diff --git a/src/ecdh.c b/src/ecdh.c new file mode 100644 index 00000000..5a86db48 --- /dev/null +++ b/src/ecdh.c @@ -0,0 +1,289 @@ +/* + + The MIT License (MIT) + + Copyright (c) 2015-2018 Douglas J. Bakkum, Stephanie Stroka, Shift Cryptosecurity + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include +#include + +#include "ecdh.h" +#include "random.h" +#include "sd.h" +#include "touch.h" +#include "utils.h" +#include "commander.h" +#include "ecc.h" +#include "led.h" +#include "memory.h" +#include "flags.h" +#include "sha2.h" +#include "yajl/src/api/yajl_tree.h" + +#define SIZE_BYTE 8 + +#define SIZE_EC_POINT_COMPRESSED 33 +#define SIZE_EC_POINT_COMPRESSED_HEX 66 +#define SIZE_EC_PRIVATE_KEY 32 + +#define SIZE_SHA256_HEX 64 + +/** + * Holds the private and public elliptic-curve keypair generated for the DH-key exchange. + */ +typedef struct TFA_EC_Keypair { + uint8_t private_key[SIZE_EC_PRIVATE_KEY]; + uint8_t public_key[SIZE_EC_POINT_COMPRESSED]; +} TFA_EC_Keypair; + +// Stores the SHA-256 hash of the ecdh public key from the mobile app. +__extension__ static uint8_t TFA_IN_HASH_PUB[] = {[0 ... SHA256_DIGEST_LENGTH - 1] = 0x00}; +__extension__ static uint8_t TFA_ZEROS[] = {[0 ... SHA256_DIGEST_LENGTH - 1] = 0x00}; + +// Stores the ecdh keypair used in creating the shared secret for the mobile +// app communication. +__extension__ static TFA_EC_Keypair tfa_keypair = { + {[0 ... SIZE_EC_PRIVATE_KEY - 1] = 0x00}, + {[0 ... SIZE_EC_POINT_COMPRESSED - 1] = 0x00} +}; + +// The position of the yet-to-be-verified byte. +static uint8_t TFA_VERIFY_BYTEPOS = 0; + +// The position of the current 2 bit inside the yet-to-be-verified byte. +// Valid values are 1 to 4. +static uint8_t TFA_VERIFY_BITPOS = 1; + +/** + * Generates a new EC key pair on the SECP256k1 curve and stores it in the address given as a + * parameter. + */ +static int ecdh_generate_key(TFA_EC_Keypair *keypair) +{ + do { + if (random_bytes(keypair->private_key, sizeof(keypair->private_key), 0) == DBB_ERROR) { + return DBB_ERR_MEM_ATAES; + } + } while (!bitcoin_ecc.ecc_isValid(keypair->private_key, ECC_SECP256k1)); + + ecc_get_public_key33(keypair->private_key, keypair->public_key, ECC_SECP256k1); + return DBB_OK; +} + +/** + * Processes the { "ecdh" : { "hash_pubkey" : "..." } } command. + * The command triggers the (re-)generation of a new ECDH keypair, + * which requires a long-touch. + * If an error happens, the function prepares a response. + */ +static void ecdh_hash_pubkey_command(const char *pair_hash_pubkey) +{ + if (strlens(pair_hash_pubkey) != SIZE_SHA256_HEX) { + commander_fill_report(cmd_str(CMD_ecdh), NULL, DBB_ERR_KEY_HASH_ECDH_LEN); + return; + } + + int status = touch_button_press(DBB_TOUCH_LONG); + if (status != DBB_TOUCHED) { + utils_zero(TFA_IN_HASH_PUB, SHA256_DIGEST_LENGTH); + commander_fill_report(cmd_str(CMD_ecdh), NULL, status); + return; + } + + TFA_VERIFY_BITPOS = 1; + TFA_VERIFY_BYTEPOS = 0; + int ret = ecdh_generate_key(&tfa_keypair); + if (ret != DBB_OK) { + utils_zero(TFA_IN_HASH_PUB, SHA256_DIGEST_LENGTH); + commander_fill_report(cmd_str(CMD_ecdh), NULL, ret); + return; + } + + char msg[256]; + uint8_t hash_pubkey[SHA256_DIGEST_LENGTH]; + sha256_Raw(tfa_keypair.public_key, sizeof(tfa_keypair.public_key), hash_pubkey); + snprintf(msg, sizeof(msg), "{\"%s\":\"%s\"}", + cmd_str(CMD_hash_pubkey), utils_uint8_to_hex(hash_pubkey, sizeof(hash_pubkey))); + + memcpy(TFA_IN_HASH_PUB, utils_hex_to_uint8(pair_hash_pubkey), SHA256_DIGEST_LENGTH); + commander_clear_report(); + commander_fill_report(cmd_str(CMD_ecdh), msg, DBB_JSON_ARRAY); +} + +/** + * Process the { "ecdh" : { "pubkey" : "..." } } command. + * First, we check whether the provided public key is the one to which the pairing partner + * committed to in a previous request (hash_pubkey). + * Then, we create the ECDH shared secret and store it in the eeprom. + * Lastly, the hash of the pairing partner's public key and keypair for generating the shared + * secret is reset. + */ +static void ecdh_pubkey_command(const char *pair_pubkey) +{ + // 66 bytes because it's the hex representation of a compressed EC public key. + if (strlens(pair_pubkey) != SIZE_EC_POINT_COMPRESSED_HEX) { + commander_fill_report(cmd_str(CMD_ecdh), NULL, DBB_ERR_KEY_ECDH_LEN); + goto cleanup; + } + + uint8_t pair_pubkey_bytes[SIZE_EC_POINT_COMPRESSED]; + memcpy(pair_pubkey_bytes, utils_hex_to_uint8(pair_pubkey), SIZE_EC_POINT_COMPRESSED); + + // Point-at-infinity not allowed + if (MEMEQ(TFA_ZEROS, pair_pubkey_bytes + 1, SHA256_DIGEST_LENGTH)) { + commander_fill_report(cmd_str(CMD_ecdh), NULL, DBB_ERR_TFA_HASH_MATCH); + goto cleanup; + } + + // Check if hashed pubkey was provided + if (MEMEQ(TFA_ZEROS, TFA_IN_HASH_PUB, SHA256_DIGEST_LENGTH)) { + commander_fill_report(cmd_str(CMD_ecdh), NULL, DBB_ERR_TFA_HASH_MATCH); + goto cleanup; + } + + uint8_t h[SHA256_DIGEST_LENGTH]; + sha256_Raw(pair_pubkey_bytes, SIZE_EC_POINT_COMPRESSED, h); + + // Check if the pubkey matches the provided hashed pubkey + if (!MEMEQ(h, TFA_IN_HASH_PUB, SHA256_DIGEST_LENGTH)) { + commander_fill_report(cmd_str(CMD_ecdh), NULL, DBB_ERR_TFA_HASH_MATCH); + goto cleanup; + } + + uint8_t ecdh_shared_secret[SIZE_ECDH_SHARED_SECRET]; + if (bitcoin_ecc.ecc_ecdh(pair_pubkey_bytes, tfa_keypair.private_key, ecdh_shared_secret, + ECC_SECP256k1)) { + commander_fill_report(cmd_str(CMD_ecdh), NULL, DBB_ERR_KEY_ECDH); + goto cleanup; + } + + uint8_t ret = memory_write_tfa_shared_secret(ecdh_shared_secret); + if (ret != DBB_OK) { + commander_fill_report(cmd_str(CMD_ecdh), NULL, ret); + goto cleanup; + } + + char msg[256]; + snprintf(msg, sizeof(msg), "{\"%s\":\"%s\"}", + cmd_str(CMD_pubkey), utils_uint8_to_hex(tfa_keypair.public_key, + sizeof(tfa_keypair.public_key))); + + commander_fill_report(cmd_str(CMD_ecdh), msg, DBB_JSON_OBJECT); + +cleanup: + utils_zero(TFA_IN_HASH_PUB, SHA256_DIGEST_LENGTH); + utils_zero(&tfa_keypair, sizeof(TFA_EC_Keypair)); + utils_clear_buffers(); + +} + +/** + * Challenge the pairing by blinking the LED to verify the shared secret. + * When this command is called, the LED blinks 1 - 4 times to communicate the value of the + * 2 bits at the current position of the shared secret. + * The position is advanced afterwards. + */ +static void ecdh_challenge_command(void) +{ + uint8_t *shared_secret = memory_report_aeskey(TFA_SHARED_SECRET); + uint8_t encryption_and_authentication_key[SHA512_DIGEST_LENGTH]; + uint8_t encryption_and_authentication_challenge[SHA256_DIGEST_LENGTH]; + + sha512_Raw(shared_secret, SIZE_ECDH_SHARED_SECRET, encryption_and_authentication_key); + sha256_Raw(encryption_and_authentication_key, SHA512_DIGEST_LENGTH, + encryption_and_authentication_challenge); + + uint8_t two_bit = (encryption_and_authentication_challenge[TFA_VERIFY_BYTEPOS] >> + (SIZE_BYTE - 2 * + TFA_VERIFY_BITPOS)) & 3; + + TFA_VERIFY_BITPOS = (TFA_VERIFY_BITPOS + 1) % 5; + if (TFA_VERIFY_BITPOS == 0) { + TFA_VERIFY_BYTEPOS = (TFA_VERIFY_BYTEPOS + 1) % SIZE_ECDH_SHARED_SECRET; + TFA_VERIFY_BITPOS = 1; + } + led_code(two_bit + 1); + + utils_zero(encryption_and_authentication_key, SHA512_DIGEST_LENGTH); + utils_zero(encryption_and_authentication_challenge, SHA256_DIGEST_LENGTH); + + commander_fill_report(cmd_str(CMD_ecdh), attr_str(ATTR_success), DBB_JSON_STRING); +} + +/** + * Aborts the pairing process and thereby resets the positions for blinking. + */ +static void ecdh_abort_command(void) +{ + utils_zero(TFA_IN_HASH_PUB, SHA256_DIGEST_LENGTH); + utils_zero(&tfa_keypair, sizeof(TFA_EC_Keypair)); + utils_clear_buffers(); + TFA_VERIFY_BITPOS = 1; + TFA_VERIFY_BYTEPOS = 0; + commander_fill_report(cmd_str(CMD_ecdh), cmd_str(ATTR_aborted), DBB_JSON_STRING); +} + +/** + * Dispatches the ecdh command. The following commands are accepted: + * + * { "ecdh" : { "hash_pubkey" : "..." } } + * { "ecdh" : { "pubkey" : "..." } } + * { "ecdh" : { "challenge" : true } } + * { "ecdh" : { "abort" : true } } + */ +void ecdh_dispatch_command(yajl_val json_node) +{ + const char *value_path[] = { cmd_str(CMD_ecdh), NULL }; + + yajl_val data = yajl_tree_get(json_node, value_path, yajl_t_any); + + if (YAJL_IS_OBJECT(data)) { + const char *pair_hash_pubkey_path[] = { cmd_str(CMD_hash_pubkey), NULL }; + const char *pair_hash_pubkey = YAJL_GET_STRING(yajl_tree_get(data, pair_hash_pubkey_path, + yajl_t_string)); + + const char *pair_pubkey_path[] = { cmd_str(CMD_pubkey), NULL }; + const char *pair_pubkey = YAJL_GET_STRING(yajl_tree_get(data, pair_pubkey_path, + yajl_t_string)); + + const char *pair_challenge_path[] = { cmd_str(CMD_challenge), NULL }; + yajl_val pair_challenge = yajl_tree_get(data, pair_challenge_path, yajl_t_true); + + const char *pair_abort_path[] = { cmd_str(CMD_abort), NULL }; + yajl_val pair_abort = yajl_tree_get(data, pair_abort_path, yajl_t_true); + + if (strlens(pair_hash_pubkey)) { + ecdh_hash_pubkey_command(pair_hash_pubkey); + } else if (strlens(pair_pubkey)) { + ecdh_pubkey_command(pair_pubkey); + } else if (YAJL_IS_TRUE(pair_challenge)) { + ecdh_challenge_command(); + } else if (YAJL_IS_TRUE(pair_abort)) { + ecdh_abort_command(); + } else { + commander_fill_report(cmd_str(CMD_ecdh), NULL, DBB_ERR_IO_INVALID_CMD); + } + } else { + commander_fill_report(cmd_str(CMD_ecdh), NULL, DBB_ERR_IO_INVALID_CMD); + } +} diff --git a/src/ecdh.h b/src/ecdh.h new file mode 100644 index 00000000..2d7e6f7f --- /dev/null +++ b/src/ecdh.h @@ -0,0 +1,39 @@ +/* + + The MIT License (MIT) + + Copyright (c) 2015-2018 Douglas J. Bakkum, Stephanie Stroka, Shift Cryptosecurity + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef _ECDH_H_ +#define _ECDH_H_ + +#include + +#include "yajl/src/api/yajl_tree.h" + +#define SIZE_ECDH_SHARED_SECRET SHA256_DIGEST_LENGTH + +void ecdh_dispatch_command(yajl_val json_node); + +#endif + diff --git a/src/flags.h b/src/flags.h index 5a818b2e..3f11a8f2 100644 --- a/src/flags.h +++ b/src/flags.h @@ -36,10 +36,9 @@ #define COMMANDER_ARRAY_ELEMENT_MAX 1024 #define COMMANDER_MAX_ATTEMPTS 15// max PASSWORD or LOCK PIN attempts before device reset #define COMMANDER_TOUCH_ATTEMPTS 10// number of attempts until touch button hold required to login -#define VERIFYPASS_FILENAME "verification.pdf" #define VERIFYPASS_CRYPT_TEST "Digital Bitbox 2FA" -#define VERIFYPASS_LOCK_CODE_LEN 16// bytes -#define DEVICE_DEFAULT_NAME "My Digital Bitbox" +#define TFA_PIN_LEN 16// bytes +#define DEVICE_DEFAULT_NAME "My BitBox" #define SD_FILEBUF_LEN_MAX (COMMANDER_REPORT_SIZE * 4 / 7) #define AES_DATA_LEN_MAX (COMMANDER_REPORT_SIZE * 4 / 7)// base64 increases size by ~4/3; AES encryption by max 32 char #define PASSWORD_LEN_MIN 4 @@ -67,6 +66,9 @@ X(led) \ X(xpub) \ X(name) \ X(ecdh) \ +X(hash_pubkey) \ +X(abort) \ +X(challenge) \ X(device) \ X(random) \ X(backup) \ @@ -180,6 +182,7 @@ X(JSON_STRING, 0, 0)\ X(JSON_BOOL, 0, 0)\ X(JSON_ARRAY, 0, 0)\ X(JSON_NUMBER, 0, 0)\ +X(JSON_OBJECT, 0, 0)\ X(JSON_NONE, 0, 0)\ /* placeholder don't move */ \ X(FLAG_ERROR_START, 0, 0)\ @@ -205,6 +208,9 @@ X(ERR_KEY_MASTER, 250, "Master key not present.")\ X(ERR_KEY_CHILD, 251, "Could not generate key.")\ X(ERR_KEY_ECDH, 252, "Could not generate ECDH secret.")\ X(ERR_KEY_ECDH_LEN, 253, "Incorrect serialized pubkey length. A 33-byte hexadecimal value (66 characters) is expected.")\ +X(ERR_KEY_HASH_ECDH_LEN, 254, "Incorrect serialized pubkey hash length. A 32-byte hexadecimal value (64 characters) is expected.")\ +X(ERR_TFA_HASH_MISSING, 255, "Failed to pair with second factor, because the hash of the public key was not received.")\ +X(ERR_TFA_HASH_MATCH, 256, "Failed to pair with second factor, because the previously received hash of the public key does not match the computed hash of the public key.")\ X(ERR_SIGN_PUBKEY_LEN, 300, "Incorrect pubkey length. A 33-byte hexadecimal value (66 characters) is expected.")\ X(ERR_SIGN_HASH_LEN, 301, "Incorrect hash length. A 32-byte hexadecimal value (64 characters) is expected.")\ X(ERR_SIGN_DESERIAL, 302, "Could not deserialize outputs or wrong change keypath.")\ diff --git a/src/flash.c b/src/flash.c index e2f22fa6..d073ff54 100644 --- a/src/flash.c +++ b/src/flash.c @@ -87,7 +87,7 @@ uint32_t flash_erase_page(uint32_t ul_address, uint8_t uc_page_num) uint32_t flash_write(uint32_t ul_address, const void *p_buffer, - uint32_t ul_size, uint32_t ul_erase_flag) + uint32_t ul_size, uint32_t ul_erase_flag) { uint32_t i; uint8_t buf[FLASH_SIG_LEN]; diff --git a/src/flash.h b/src/flash.h index 79f2e5d8..761cc514 100644 --- a/src/flash.h +++ b/src/flash.h @@ -96,8 +96,14 @@ static inline uint32_t mpu_region_size(uint32_t size) #ifdef TESTING -static void HardFault_Handler(void) { exit(1); } -static void MemManage_Handler(void) { exit(2); } +static void HardFault_Handler(void) +{ + exit(1); +} +static void MemManage_Handler(void) +{ + exit(2); +} uint8_t flash_read_unique_id(uint32_t *serial, uint32_t len); uint32_t flash_erase_user_signature(void); @@ -105,7 +111,7 @@ uint32_t flash_write_user_signature(const void *p_buffer, uint32_t ul_size); uint32_t flash_read_user_signature(uint32_t *p_data, uint32_t ul_size); uint32_t flash_erase_page(uint32_t ul_address, uint8_t uc_page_num); uint32_t flash_write(uint32_t ul_address, const void *p_buffer, - uint32_t ul_size, uint32_t ul_erase_flag); + uint32_t ul_size, uint32_t ul_erase_flag); #endif void flash_read_sig_area(uint8_t *sig, uint32_t ul_address, uint32_t len); diff --git a/src/led.c b/src/led.c index 8690d02e..c4ff000e 100644 --- a/src/led.c +++ b/src/led.c @@ -74,7 +74,10 @@ void led_toggle(void) ioport_set_pin_level(LED_0_PIN, !ioport_get_pin_level(LED_0_PIN)); } - +/** + * Blink the LED. + * Do not expose this function via API (to prevent possible security problems during TFA pairing). + */ void led_blink(void) { led_on(); @@ -87,26 +90,23 @@ void led_abort(void) { led_off(); delay_ms(300); - led_on(); - delay_ms(100); - led_off(); - delay_ms(100); - led_on(); - delay_ms(100); - led_off(); + for (int i = 0; i < 6; i++) { + led_on(); + delay_ms(100); + led_off(); + delay_ms(100); + } } -void led_code(uint8_t *code, uint8_t len) +void led_code(uint8_t code) { - uint8_t i, j; + uint8_t i; delay_ms(500); - for (i = 0; i < len; i++) { - for (j = 0; j < code[i]; j++) { - led_toggle(); - delay_ms(300); - led_toggle(); - delay_ms(300); - } - delay_ms(500); + for (i = 0; i < code; i++) { + led_toggle(); + delay_ms(300); + led_toggle(); + delay_ms(300); } + delay_ms(500); } diff --git a/src/led.h b/src/led.h index b3975dcb..054ddec2 100644 --- a/src/led.h +++ b/src/led.h @@ -41,7 +41,7 @@ void led_off(void); void led_toggle(void); void led_blink(void); void led_abort(void); -void led_code(uint8_t *code, uint8_t len); +void led_code(uint8_t code); #endif diff --git a/src/memory.c b/src/memory.c index 1baa059f..956548d5 100644 --- a/src/memory.c +++ b/src/memory.c @@ -31,6 +31,7 @@ #include "commander.h" #include "ataes132.h" +#include "aescbcb64.h" #include "memory.h" #include "random.h" #include "utils.h" @@ -135,8 +136,8 @@ static uint8_t memory_eeprom_crypt(const uint8_t *write_b, uint8_t *read_b, if (write_b) { char enc_w[MEM_PAGE_LEN * 4 + 1] = {0}; - enc = aes_cbc_b64_encrypt((unsigned char *)utils_uint8_to_hex(write_b, MEM_PAGE_LEN), - MEM_PAGE_LEN * 2, &enc_len, mempass); + enc = aescbcb64_encrypt((unsigned char *)utils_uint8_to_hex(write_b, MEM_PAGE_LEN), + MEM_PAGE_LEN * 2, &enc_len, mempass); if (!enc) { goto err; } @@ -178,8 +179,8 @@ static uint8_t memory_eeprom_crypt(const uint8_t *write_b, uint8_t *read_b, } } - dec = aes_cbc_b64_decrypt((unsigned char *)enc_r, MEM_PAGE_LEN * 4, &dec_len, - mempass); + dec = aescbcb64_decrypt((unsigned char *)enc_r, MEM_PAGE_LEN * 4, &dec_len, + mempass); if (!dec) { goto err; } @@ -288,7 +289,7 @@ void memory_reset_hww(void) memory_scramble_rn(); memory_master_u2f(u2f); memory_random_password(PASSWORD_STAND); - memory_random_password(PASSWORD_VERIFY); + memory_random_password(TFA_SHARED_SECRET); memory_random_password(PASSWORD_HIDDEN); memory_erase_hww_seed(); memory_name(DEVICE_DEFAULT_NAME); @@ -417,6 +418,16 @@ uint8_t *memory_active_key_get(void) return MEM_active_key; } +uint8_t memory_write_tfa_shared_secret(const uint8_t *secret) +{ + int ret = memory_eeprom_crypt(secret, MEM_aeskey_verify, + MEM_AESKEY_SHARED_SECRET_ADDR) - DBB_OK; + if (ret) { + return DBB_ERR_MEM_ATAES; + } else { + return DBB_OK; + } +} uint8_t memory_write_aeskey(const char *password, int len, PASSWORD_ID id) { @@ -438,9 +449,6 @@ uint8_t memory_write_aeskey(const char *password, int len, PASSWORD_ID id) case PASSWORD_HIDDEN: memcpy(MEM_aeskey_hidden, password_b, MEM_PAGE_LEN); break; - case PASSWORD_VERIFY: - memcpy(MEM_aeskey_verify, password_b, MEM_PAGE_LEN); - break; default: { /* never reached */ } @@ -450,8 +458,6 @@ uint8_t memory_write_aeskey(const char *password, int len, PASSWORD_ID id) MEM_AESKEY_STAND_ADDR) - DBB_OK; ret |= memory_eeprom_crypt(MEM_aeskey_hidden, MEM_aeskey_hidden, MEM_AESKEY_HIDDEN_ADDR) - DBB_OK; - ret |= memory_eeprom_crypt(MEM_aeskey_verify, MEM_aeskey_verify, - MEM_AESKEY_VERIFY_ADDR) - DBB_OK; utils_zero(password_b, MEM_PAGE_LEN); @@ -469,7 +475,7 @@ void memory_read_aeskeys(void) if (!read) { memory_eeprom_crypt(NULL, MEM_aeskey_stand, MEM_AESKEY_STAND_ADDR); memory_eeprom_crypt(NULL, MEM_aeskey_hidden, MEM_AESKEY_HIDDEN_ADDR); - memory_eeprom_crypt(NULL, MEM_aeskey_verify, MEM_AESKEY_VERIFY_ADDR); + memory_eeprom_crypt(NULL, MEM_aeskey_verify, MEM_AESKEY_SHARED_SECRET_ADDR); sha256_Raw(MEM_aeskey_stand, MEM_PAGE_LEN, MEM_user_entropy); read++; } @@ -483,7 +489,7 @@ uint8_t *memory_report_aeskey(PASSWORD_ID id) return MEM_aeskey_stand; case PASSWORD_HIDDEN: return MEM_aeskey_hidden; - case PASSWORD_VERIFY: + case TFA_SHARED_SECRET: return MEM_aeskey_verify; default: return NULL; diff --git a/src/memory.h b/src/memory.h index 63c876f0..d658dd58 100644 --- a/src/memory.h +++ b/src/memory.h @@ -46,7 +46,7 @@ #define MEM_MASTER_BIP32_ADDR 0x0200 #define MEM_MASTER_BIP32_CHAIN_ADDR 0x0300 #define MEM_AESKEY_STAND_ADDR 0x0400 -#define MEM_AESKEY_VERIFY_ADDR 0x0500 +#define MEM_AESKEY_SHARED_SECRET_ADDR 0x0500 #define MEM_AESKEY_Z6_ADDR 0x0600// Zone 6 reserved first 32*4 bytes #define MEM_AESKEY_Z7_ADDR 0x0700// Zone 7 reserved #define MEM_AESKEY_HIDDEN_ADDR 0x0800 @@ -57,7 +57,7 @@ // Extension flags -#define MEM_EXT_MASK_U2F 0x00000001// Mask of bit to enable (1) or disable (0) U2F functions +#define MEM_EXT_MASK_U2F 0x00000001// Mask of bit to enable (1) or disable (0) U2F functions // Will override and disable U2F_HIJACK bit when disabled #define MEM_EXT_MASK_U2F_HIJACK 0x00000002// Mask of bit to enable (1) or disable (0) U2F_HIJACK interface @@ -73,7 +73,7 @@ typedef enum PASSWORD_ID { PASSWORD_STAND, PASSWORD_HIDDEN, - PASSWORD_VERIFY, + TFA_SHARED_SECRET, PASSWORD_NONE /* keep last */ } PASSWORD_ID; @@ -87,6 +87,7 @@ void memory_clear(void); void memory_active_key_set(uint8_t *key); uint8_t *memory_active_key_get(void); +uint8_t memory_write_tfa_shared_secret(const uint8_t *secret); uint8_t memory_write_aeskey(const char *password, int len, PASSWORD_ID id); void memory_read_aeskeys(void); uint8_t *memory_report_aeskey(PASSWORD_ID id); diff --git a/src/sharedsecret.c b/src/sharedsecret.c new file mode 100644 index 00000000..ce95fea3 --- /dev/null +++ b/src/sharedsecret.c @@ -0,0 +1,47 @@ +/* + + The MIT License (MIT) + + Copyright (c) 2015-2018 Douglas J. Bakkum + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include + +#include "sha2.h" +#include "utils.h" +#include "sharedsecret.h" + +void sharedsecret_derive_keys(const uint8_t *shared_secret, uint8_t *encryption_key, + uint8_t *authentication_key) +{ + uint8_t encryption_and_authentication_key[SHA512_DIGEST_LENGTH]; + sha512_Raw(shared_secret, SHA256_DIGEST_LENGTH, encryption_and_authentication_key); + + int KEY_SIZE = SHA512_DIGEST_LENGTH / 2; + + memcpy(encryption_key, encryption_and_authentication_key, KEY_SIZE); + memcpy(authentication_key, encryption_and_authentication_key + KEY_SIZE, KEY_SIZE); + + utils_zero(encryption_and_authentication_key, SHA512_DIGEST_LENGTH); +} + + diff --git a/src/sharedsecret.h b/src/sharedsecret.h new file mode 100644 index 00000000..f01479b2 --- /dev/null +++ b/src/sharedsecret.h @@ -0,0 +1,33 @@ +/* + + The MIT License (MIT) + + Copyright (c) 2015-2018 Douglas J. Bakkum + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef _SHAREDSECRET_H_ +#define _SHAREDSECRET_H_ + +void sharedsecret_derive_keys(const uint8_t *shared_secret, uint8_t *encryption_key, + uint8_t *authentication_key); + +#endif diff --git a/src/utils.c b/src/utils.c index 5449cecc..46fda0a2 100644 --- a/src/utils.c +++ b/src/utils.c @@ -37,6 +37,9 @@ static uint8_t utils_buffer[UTILS_BUFFER_LEN]; +/** + * Sets len 0s into the given memory at address dst. + */ volatile void *utils_zero(volatile void *dst, size_t len) { volatile char *buf; diff --git a/src/utils.h b/src/utils.h index 9d595be0..f4f3091f 100644 --- a/src/utils.h +++ b/src/utils.h @@ -29,6 +29,7 @@ #define _UTILS_H_ +#include #include #include #include "memory.h" diff --git a/tests/api.h b/tests/api.h index 5704ef6e..de3ba8f0 100644 --- a/tests/api.h +++ b/tests/api.h @@ -36,6 +36,8 @@ #include "u2f/u2f.h" #include "u2f_device.h" #include "commander.h" +#include "aescbcb64.h" +#include "random.h" #include "utest.h" #include "usb.h" @@ -105,8 +107,8 @@ static void api_decrypt_report(const char *report, uint8_t *key) const char *ciphertext = YAJL_GET_STRING(yajl_tree_get(json_node, ciphertext_path, yajl_t_string)); if (ciphertext) { - dec = aes_cbc_b64_decrypt((const unsigned char *)ciphertext, strlens(ciphertext), - &decrypt_len, key); + dec = aescbcb64_decrypt((const unsigned char *)ciphertext, strlens(ciphertext), + &decrypt_len, key); if (!dec) { strcpy(decrypted_report, "/* error: Failed to decrypt. */"); goto exit; @@ -391,7 +393,7 @@ static void api_hid_send(const char *cmd) static void api_hid_send_encrypt(const char *cmd, uint8_t *key) { int enc_len; - char *enc = aes_cbc_b64_encrypt((const unsigned char *)cmd, strlens(cmd), &enc_len, key); + char *enc = aescbcb64_encrypt((const unsigned char *)cmd, strlens(cmd), &enc_len, key); api_hid_send_len(enc, enc_len); free(enc); } @@ -479,8 +481,8 @@ static char *api_read_value_decrypt(int cmd, uint8_t *key) memset(val_dec, 0, sizeof(val_dec)); int decrypt_len; - char *dec = aes_cbc_b64_decrypt((const unsigned char *)val, strlens(val), - &decrypt_len, key); + char *dec = aescbcb64_decrypt((const unsigned char *)val, strlens(val), + &decrypt_len, key); snprintf(val_dec, HID_REPORT_SIZE, "%.*s", decrypt_len, dec); free(dec); diff --git a/tests/hmac_check.h b/tests/hmac_check.h new file mode 100644 index 00000000..13b6c186 --- /dev/null +++ b/tests/hmac_check.h @@ -0,0 +1,106 @@ +/* + + The MIT License (MIT) + + Copyright (c) 2018 Douglas J. Bakkum, Stephanie Stroka, Shift Cryptosecurity + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + +*/ + + +#ifndef _HMAC_CHECK_H_ +#define _HMAC_CHECK_H_ + + +#include +#include "yajl/src/api/yajl_tree.h" +#include "sharedsecret.h" +#include "aescbcb64.h" +#include "base64.h" +#include "flags.h" +#include "utils.h" +#include "utest.h" +#include "hmac.h" +#include "sha2.h" +#include "aes.h" + + +/** + * Performs an hmac sha256 message integrity check and returns 0 if the integrity check + * failed and 1 if it succeeded. + */ +static int check_hmac_sha256(const uint8_t *key, const uint32_t keylen, + const unsigned char *in, + const unsigned int length, const uint8_t *hmac) +{ + uint8_t verify_hmac[SHA256_DIGEST_LENGTH]; + hmac_sha256(key, keylen, in, length, verify_hmac); + if (MEMEQ(hmac, verify_hmac, SHA256_DIGEST_LENGTH)) { + return 1; + } + return 0; +} + + +static char *decrypt_and_check_hmac(const unsigned char *in, int inlen, int *out_msg_len, + const uint8_t *shared_secret, uint8_t *out_hmac) +{ + if (!in || inlen == 0) { + return NULL; + } + + uint8_t encryption_key[SHA256_DIGEST_LENGTH]; + uint8_t authentication_key[SHA256_DIGEST_LENGTH]; + + sharedsecret_derive_keys(shared_secret, encryption_key, authentication_key); + + // Unbase64 + int ub64len; + unsigned char *ub64 = unbase64((const char *)in, inlen, &ub64len); + if (!ub64) { + return NULL; + } + if ((ub64len % N_BLOCK) || ub64len < N_BLOCK) { + memset(ub64, 0, ub64len); + free(ub64); + return NULL; + } + + memcpy(out_hmac, ub64 + (ub64len - SHA256_DIGEST_LENGTH), SHA256_DIGEST_LENGTH); + int hmac_len = ub64len - SHA256_DIGEST_LENGTH; + + char *decrypted = NULL; + if (check_hmac_sha256(authentication_key, SHA256_DIGEST_LENGTH, ub64, hmac_len, + out_hmac)) { + decrypted = aescbcb64_init_and_decrypt(ub64, + ub64len - SHA256_DIGEST_LENGTH, + out_msg_len, + encryption_key); + } + + memset(ub64, 0, ub64len); + free(ub64); + utils_zero(encryption_key, sizeof(encryption_key)); + utils_zero(authentication_key, sizeof(authentication_key)); + return decrypted; +} + + +#endif diff --git a/tests/tests_api.c b/tests/tests_api.c index 420843b8..e048909a 100644 --- a/tests/tests_api.c +++ b/tests/tests_api.c @@ -30,19 +30,21 @@ #include #include "sd.h" +#include "ecdh.h" #include "ecc.h" #include "sha2.h" #include "utest.h" #include "utils.h" #include "flags.h" #include "random.h" +#include "aescbcb64.h" #include "commander.h" #include "yajl/src/api/yajl_tree.h" #include "secp256k1/include/secp256k1.h" #include "secp256k1/include/secp256k1_recovery.h" #include "api.h" - +#include "hmac_check.h" int U_TESTS_RUN = 0; int U_TESTS_FAIL = 0; @@ -52,7 +54,7 @@ static void tests_seed_xpub_backup(void) { char name0[] = "name0"; char key[] = "password"; - char xpub0[112], xpub1[112], *echo; + char xpub0[112], xpub1[112]; char seed_usb[512], seed_c[512], seed_b[512], back[512], check[512], erase_file[512]; char filename[] = "tests_backup.pdf"; char filename2[] = "tests_backup2.pdf"; @@ -109,8 +111,14 @@ static void tests_seed_xpub_backup(void) u_assert_str_not_eq(xpub0, xpub1); if (!TEST_LIVE_DEVICE) { - echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); + int len; + uint8_t hmac[SHA256_DIGEST_LENGTH]; + const char *val = api_read_value(CMD_echo); + char *echo = decrypt_and_check_hmac((const unsigned char *)val, strlens(val), &len, + memory_report_aeskey(TFA_SHARED_SECRET), hmac); + u_assert(echo); u_assert_str_eq(xpub0, echo); + free(echo); } // check backup list and erase @@ -147,8 +155,14 @@ static void tests_seed_xpub_backup(void) ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); memcpy(xpub1, api_read_value(CMD_xpub), sizeof(xpub1)); if (!TEST_LIVE_DEVICE) { - echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); + int len; + uint8_t hmac[SHA256_DIGEST_LENGTH]; + const char *val = api_read_value(CMD_echo); + char *echo = decrypt_and_check_hmac((const unsigned char *)val, strlens(val), &len, + memory_report_aeskey(TFA_SHARED_SECRET), hmac); + u_assert(echo); u_assert_str_eq(xpub0, echo); + free(echo); } // check xpubs @@ -198,7 +212,8 @@ static void tests_seed_xpub_backup(void) char lbn[SD_FILEBUF_LEN_MAX / 8]; size_t i; - memset(long_backup_name, '-', sizeof(long_backup_name)); + memset(long_backup_name, '-', sizeof(long_backup_name) - 1); + long_backup_name[(SD_FILEBUF_LEN_MAX / 8) - 1] = 0; for (i = 0; i < SD_FILEBUF_LEN_MAX / sizeof(long_backup_name); i++) { snprintf(lbn, sizeof(lbn), "%lu%s", (unsigned long)i, long_backup_name); @@ -1040,7 +1055,6 @@ static void tests_u2f(void) // if (!TEST_LIVE_DEVICE) { - char *echo; const char one_input[] = "{\"data\":[{\"hash\":\"c6fa4c236f59020ec8ffde22f85a78e7f256e94cd975eb5199a4a5cc73e26e4a\", \"keypath\":\"m/44'/0'/0'/1/7\"}]}"; const char hash_1_input[] = @@ -1096,8 +1110,14 @@ static void tests_u2f(void) api_format_send_cmd(cmd_str(CMD_sign), one_input, KEY_STANDARD); ASSERT_REPORT_HAS(cmd_str(CMD_echo)); if (!TEST_LIVE_DEVICE) { - echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); + int len; + uint8_t hmac[SHA256_DIGEST_LENGTH]; + const char *val = api_read_value(CMD_echo); + char *echo = decrypt_and_check_hmac((const unsigned char *)val, strlens(val), &len, + memory_report_aeskey(TFA_SHARED_SECRET), hmac); + u_assert(echo); u_assert_str_has(echo, "m/44'/0'/0'/1/7"); + free(echo); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); ASSERT_REPORT_HAS_NOT(hash_1_input); @@ -1137,8 +1157,14 @@ static void tests_u2f(void) api_format_send_cmd(cmd_str(CMD_sign), one_input, KEY_STANDARD); ASSERT_REPORT_HAS(cmd_str(CMD_echo)); if (!TEST_LIVE_DEVICE) { - echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); + int len; + uint8_t hmac[SHA256_DIGEST_LENGTH]; + const char *val = api_read_value(CMD_echo); + char *echo = decrypt_and_check_hmac((const unsigned char *)val, strlens(val), &len, + memory_report_aeskey(TFA_SHARED_SECRET), hmac); + u_assert(echo); u_assert_str_has(echo, "m/44'/0'/0'/1/7"); + free(echo); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); ASSERT_REPORT_HAS(hash_1_input); @@ -1160,8 +1186,14 @@ static void tests_u2f(void) api_format_send_cmd(cmd_str(CMD_sign), one_input, KEY_STANDARD); ASSERT_REPORT_HAS(cmd_str(CMD_echo)); if (!TEST_LIVE_DEVICE) { - echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); + int len; + uint8_t hmac[SHA256_DIGEST_LENGTH]; + const char *val = api_read_value(CMD_echo); + char *echo = decrypt_and_check_hmac((const unsigned char *)val, strlens(val), &len, + memory_report_aeskey(TFA_SHARED_SECRET), hmac); + u_assert(echo); u_assert_str_has(echo, "m/44'/0'/0'/1/7"); + free(echo); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); ASSERT_REPORT_HAS_NOT(hash_1_input); @@ -1207,8 +1239,14 @@ static void tests_u2f(void) api_format_send_cmd(cmd_str(CMD_sign), one_input, KEY_STANDARD); ASSERT_REPORT_HAS(cmd_str(CMD_echo)); if (!TEST_LIVE_DEVICE) { - echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); + int len; + uint8_t hmac[SHA256_DIGEST_LENGTH]; + const char *val = api_read_value(CMD_echo); + char *echo = decrypt_and_check_hmac((const unsigned char *)val, strlens(val), &len, + memory_report_aeskey(TFA_SHARED_SECRET), hmac); + u_assert(echo); u_assert_str_has(echo, "m/44'/0'/0'/1/7"); + free(echo); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); ASSERT_REPORT_HAS(hash_1_input); @@ -1230,8 +1268,14 @@ static void tests_u2f(void) api_format_send_cmd(cmd_str(CMD_sign), one_input, KEY_STANDARD); ASSERT_REPORT_HAS(cmd_str(CMD_echo)); if (!TEST_LIVE_DEVICE) { - echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); + int len; + uint8_t hmac[SHA256_DIGEST_LENGTH]; + const char *val = api_read_value(CMD_echo); + char *echo = decrypt_and_check_hmac((const unsigned char *)val, strlens(val), &len, + memory_report_aeskey(TFA_SHARED_SECRET), hmac); + u_assert(echo); u_assert_str_has(echo, "m/44'/0'/0'/1/7"); + free(echo); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); ASSERT_REPORT_HAS_NOT(hash_1_input); @@ -1254,8 +1298,14 @@ static void tests_u2f(void) api_format_send_cmd(cmd_str(CMD_sign), one_input, KEY_STANDARD); ASSERT_REPORT_HAS(cmd_str(CMD_echo)); if (!TEST_LIVE_DEVICE) { - echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); + int len; + uint8_t hmac[SHA256_DIGEST_LENGTH]; + const char *val = api_read_value(CMD_echo); + char *echo = decrypt_and_check_hmac((const unsigned char *)val, strlens(val), &len, + memory_report_aeskey(TFA_SHARED_SECRET), hmac); + u_assert(echo); u_assert_str_has(echo, "m/44'/0'/0'/1/7"); + free(echo); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); ASSERT_REPORT_HAS(hash_1_input); @@ -1284,9 +1334,6 @@ static void tests_device(void) api_format_send_cmd(cmd_str(CMD_led), attr_str(ATTR_blink), KEY_STANDARD); ASSERT_SUCCESS; - api_format_send_cmd(cmd_str(CMD_led), attr_str(ATTR_abort), KEY_STANDARD); - ASSERT_SUCCESS; - api_format_send_cmd(cmd_str(CMD_led), "invalid_cmd", KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_INVALID_CMD)); @@ -1333,8 +1380,7 @@ static void tests_device(void) api_format_send_cmd(cmd_str(CMD_device), "", KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_INVALID_CMD)); - api_format_send_cmd(cmd_str(CMD_verifypass), "", KEY_STANDARD); - + api_format_send_cmd(cmd_str(CMD_ecdh), "", KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_INVALID_CMD)); api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); @@ -1350,8 +1396,10 @@ static void tests_device(void) "{\"source\":\"create\", \"filename\":\"c.pdf\", \"key\":\"password\"}", KEY_STANDARD); ASSERT_SUCCESS; - api_format_send_cmd(cmd_str(CMD_verifypass), attr_str(ATTR_create), KEY_STANDARD); - ASSERT_SUCCESS; + api_format_send_cmd(cmd_str(CMD_ecdh), + "{ \"hash_ecdh\" : \"6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b\" }", + KEY_STANDARD); + ASSERT_REPORT_HAS(cmd_str(CMD_ecdh)); api_format_send_cmd(cmd_str(CMD_backup), "{\"filename\":\"b.pdf\", \"key\":\"password\"}", KEY_STANDARD); @@ -1364,7 +1412,9 @@ static void tests_device(void) "{\"source\":\"create\", \"filename\":\"l.pdf\", \"key\":\"password\"}", KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_LOCKED)); - api_format_send_cmd(cmd_str(CMD_verifypass), attr_str(ATTR_create), KEY_STANDARD); + api_format_send_cmd(cmd_str(CMD_ecdh), + "{ \"hash_ecdh\" : \"6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b\" }", + KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_LOCKED)); api_format_send_cmd(cmd_str(CMD_backup), "{\"filename\":\"b.pdf\", \"key\":\"password\"}", @@ -1396,8 +1446,11 @@ static void tests_device(void) yajl_t_string)); u_assert_int_eq(!ciphertext, 0); int decrypt_len; - char *dec = aes_cbc_b64_decrypt((const unsigned char *)ciphertext, strlens(ciphertext), - &decrypt_len, memory_report_aeskey(PASSWORD_VERIFY)); + uint8_t hmac[SHA256_DIGEST_LENGTH]; + char *dec = decrypt_and_check_hmac((const unsigned char *)ciphertext, + strlens(ciphertext), + &decrypt_len, memory_report_aeskey(TFA_SHARED_SECRET), hmac); + u_assert(dec); u_assert_str_eq(dec, VERIFYPASS_CRYPT_TEST); free(dec); yajl_tree_free(json_node); @@ -1567,31 +1620,82 @@ static void tests_password(void) // - // Test ECDH verifypass + // Test ECDH // + const char *ecdh_priv_hex = + "769a3f70c6f4820e717fd50477e218f0c7456013ded246043b40ac49e9accd5f"; + const char *ecdh_pub_hex = + "035f40a5dcd64074194b3f88767dbf7a527ddd227d7734b68dd2e9d25c2d20d080"; + char ecdh_cmd[256]; + snprintf(ecdh_cmd, 256, "{\"pubkey\":\"%s\"}", ecdh_pub_hex); + + uint8_t ecdh_priv[32]; + memcpy(ecdh_priv, utils_hex_to_uint8(ecdh_priv_hex), 32); + uint8_t ecdh_pub[33]; + memcpy(ecdh_pub, utils_hex_to_uint8(ecdh_pub_hex), 33); + + uint8_t hash_ecdh[SHA256_DIGEST_LENGTH]; + sha256_Raw(ecdh_pub, 33, hash_ecdh); + + char hash_ecdh_hex[65]; + memcpy(hash_ecdh_hex, utils_uint8_to_hex(hash_ecdh, SHA256_DIGEST_LENGTH), 65); - char ecdh[] = - "{\"ecdh\":\"028d3bce812ac027fdea0e4ad98b2549a90bb9aa996396eec6bb1a8ed56e6976b8\"}"; - api_format_send_cmd(cmd_str(CMD_verifypass), ecdh, KEY_STANDARD); + char ecdh_hash_cmd[256]; + snprintf(ecdh_hash_cmd, 256, "{\"hash_pubkey\":\"%s\"}", hash_ecdh_hex); + + api_format_send_cmd(cmd_str(CMD_ecdh), ecdh_hash_cmd, KEY_STANDARD); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - ASSERT_REPORT_HAS(cmd_str(CMD_ecdh)); - ASSERT_REPORT_HAS(cmd_str(CMD_ciphertext)); + ASSERT_REPORT_HAS(cmd_str(CMD_hash_pubkey)); - if (!TEST_LIVE_DEVICE) { - yajl_val json_node = yajl_tree_parse(api_read_decrypted_report(), NULL, 0); - const char *ciphertext_path[] = { cmd_str(CMD_verifypass), cmd_str(CMD_ciphertext), (const char *) 0 }; - const char *ciphertext = YAJL_GET_STRING(yajl_tree_get(json_node, ciphertext_path, - yajl_t_string)); - if (ciphertext) { - int decrypt_len; - char *dec = aes_cbc_b64_decrypt((const unsigned char *)ciphertext, strlens(ciphertext), - &decrypt_len, memory_report_aeskey(PASSWORD_VERIFY)); - u_assert_str_eq(dec, VERIFYPASS_CRYPT_TEST); - free(dec); - } - yajl_tree_free(json_node); - } + const char *response_hash_ecdh = api_read_decrypted_report(); + + yajl_val json_node_0 = yajl_tree_parse(response_hash_ecdh, NULL, 0); + u_assert(json_node_0); + u_assert(YAJL_IS_OBJECT(json_node_0)); + + const char *path[] = { cmd_str(CMD_ecdh), NULL }; + yajl_val ecdh_node = yajl_tree_get(json_node_0, path, yajl_t_any); + const char *path_hash_ecdh[] = { cmd_str(CMD_hash_pubkey), NULL }; + const char *out_hash_ecdh_hex = YAJL_GET_STRING(yajl_tree_get(ecdh_node, + path_hash_ecdh, yajl_t_string)); + + // we should have received a hex representation of the hashed ECDH public key of the bitbox. + u_assert(out_hash_ecdh_hex); + + uint8_t out_hash_ecdh[32]; + memcpy(out_hash_ecdh, utils_hex_to_uint8(out_hash_ecdh_hex), 32 * sizeof(uint8_t)); + + api_format_send_cmd(cmd_str(CMD_ecdh), ecdh_cmd, KEY_STANDARD); + ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); + ASSERT_REPORT_HAS(cmd_str(CMD_pubkey)); + // TODO: assert that hash of response is the same as the hash received above. + + const char *response_ecdh = api_read_decrypted_report(); + yajl_val json_node_1 = yajl_tree_parse(response_ecdh, NULL, 0); + u_assert(json_node_1); + u_assert(YAJL_IS_OBJECT(json_node_1)); + + ecdh_node = yajl_tree_get(json_node_1, path, yajl_t_any); + const char *path_ecdh[] = { cmd_str(CMD_pubkey), NULL }; + const char *out_ecdh_pub_hex = YAJL_GET_STRING(yajl_tree_get(ecdh_node, path_ecdh, + yajl_t_string)); + u_assert(out_ecdh_pub_hex); + + uint8_t out_ecdh_pub[33]; + memcpy(out_ecdh_pub, utils_hex_to_uint8(out_ecdh_pub_hex), 33); + + uint8_t calculated_hash[32]; + sha256_Raw(out_ecdh_pub, 33, calculated_hash); + u_assert_mem_eq(out_hash_ecdh, calculated_hash, SHA256_DIGEST_LENGTH); + + api_format_send_cmd(cmd_str(CMD_ecdh), cmd_str(CMD_challenge), KEY_STANDARD); + + yajl_tree_free(json_node_0); + yajl_tree_free(json_node_1); + + // TODO: test blinking by writing the number that has been blinked into a buffer and read from + // this buffer to compare it with the calculated shared secret. // // Test hidden password @@ -1798,10 +1902,8 @@ static void tests_password(void) u_assert_str_not_eq(xpub_hdn, xpub_tst); } - static void tests_echo_tfa(void) { - char *echo; char hash_sign[] = "{\"meta\":\"hash\", \"data\":[{\"keypath\":\"m/44'/0'/0'/1/7\", \"hash\":\"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\"}] }"; char hash_sign2[] = @@ -1828,23 +1930,41 @@ static void tests_echo_tfa(void) "{\"source\":\"create\", \"filename\":\"c.pdf\", \"key\":\"password\"}", KEY_STANDARD); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - // test verifypass - api_format_send_cmd(cmd_str(CMD_verifypass), attr_str(ATTR_create), KEY_STANDARD); - ASSERT_SUCCESS; + // + // Test ECDH + // + const char *ecdh_pub_hex = + "028d3bce812ac027fdea0e4ad98b2549a90bb9aa996396eec6bb1a8ed56e6976b8"; + char ecdh_cmd[256]; + snprintf(ecdh_cmd, 256, "{\"pubkey\":\"%s\"}", ecdh_pub_hex); - api_format_send_cmd(cmd_str(CMD_backup), attr_str(ATTR_erase), KEY_STANDARD); - ASSERT_SUCCESS; + uint8_t ecdh_pub[34]; + memcpy(ecdh_pub, utils_hex_to_uint8(ecdh_pub_hex), 33); + ecdh_pub[33] = 0; - api_format_send_cmd(cmd_str(CMD_backup), attr_str(ATTR_list), KEY_STANDARD); - ASSERT_REPORT_HAS_NOT(VERIFYPASS_FILENAME); + uint8_t hash_ecdh[SHA256_DIGEST_LENGTH]; + sha256_Raw(ecdh_pub, 33, hash_ecdh); - api_format_send_cmd(cmd_str(CMD_verifypass), attr_str(ATTR_export), KEY_STANDARD); - ASSERT_SUCCESS; + char hash_ecdh_hex[65]; + memcpy(hash_ecdh_hex, utils_uint8_to_hex(hash_ecdh, SHA256_DIGEST_LENGTH), 64); + hash_ecdh_hex[64] = 0; - api_format_send_cmd(cmd_str(CMD_backup), attr_str(ATTR_list), KEY_STANDARD); - ASSERT_REPORT_HAS(VERIFYPASS_FILENAME); + char ecdh_hash_cmd[256]; + snprintf(ecdh_hash_cmd, 256, "{\"hash_pubkey\":\"%s\"}", hash_ecdh_hex); + + api_format_send_cmd(cmd_str(CMD_ecdh), ecdh_hash_cmd, KEY_STANDARD); + ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); + ASSERT_REPORT_HAS(cmd_str(CMD_hash_pubkey)); - api_format_send_cmd(cmd_str(CMD_verifypass), "invalid_cmd", KEY_STANDARD); + api_format_send_cmd(cmd_str(CMD_ecdh), ecdh_cmd, KEY_STANDARD); + ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); + ASSERT_REPORT_HAS(cmd_str(CMD_ecdh)); + // end test ECDH + + api_format_send_cmd(cmd_str(CMD_backup), attr_str(ATTR_erase), KEY_STANDARD); + ASSERT_SUCCESS + + api_format_send_cmd(cmd_str(CMD_ecdh), "invalid_cmd", KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_INVALID_CMD)); // test echo @@ -1884,8 +2004,14 @@ static void tests_echo_tfa(void) api_format_send_cmd(cmd_str(CMD_sign), hash_sign, KEY_STANDARD); ASSERT_REPORT_HAS(cmd_str(CMD_echo)); if (!TEST_LIVE_DEVICE) { - echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); + int len; + uint8_t hmac[SHA256_DIGEST_LENGTH]; + const char *val = api_read_value(CMD_echo); + char *echo = decrypt_and_check_hmac((const unsigned char *)val, strlens(val), &len, + memory_report_aeskey(TFA_SHARED_SECRET), hmac); + u_assert(echo); u_assert_str_has(echo, cmd_str(CMD_pin)); + free(echo); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); @@ -1894,8 +2020,14 @@ static void tests_echo_tfa(void) api_format_send_cmd(cmd_str(CMD_sign), hash_sign, KEY_STANDARD); ASSERT_REPORT_HAS(cmd_str(CMD_echo)); if (!TEST_LIVE_DEVICE) { - echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); + int len; + uint8_t hmac[SHA256_DIGEST_LENGTH]; + const char *val = api_read_value(CMD_echo); + char *echo = decrypt_and_check_hmac((const unsigned char *)val, strlens(val), &len, + memory_report_aeskey(TFA_SHARED_SECRET), hmac); + u_assert(echo); u_assert_str_has(echo, cmd_str(CMD_pin)); + free(echo); } // correct pin @@ -1912,9 +2044,6 @@ static void tests_echo_tfa(void) KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_LOCKED)); - api_format_send_cmd(cmd_str(CMD_verifypass), attr_str(ATTR_export), KEY_STANDARD); - ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_LOCKED)); - api_format_send_cmd(cmd_str(CMD_backup), attr_str(ATTR_list), KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_LOCKED)); } @@ -1988,8 +2117,6 @@ const char hash_2_input_2[] = static void tests_sign(void) { int i, res; - char *echo; - char one_input_msg[] = "c6fa4c236f59020ec8ffde22f85a78e7f256e94cd975eb5199a4a5cc73e26e4a"; char one_input[] = "{\"meta\":\"_meta_data_\", \"data\":[{\"hash\":\"c6fa4c236f59020ec8ffde22f85a78e7f256e94cd975eb5199a4a5cc73e26e4a\", \"keypath\":\"m/44'/0'/0'/1/7\"}]}"; @@ -2114,7 +2241,7 @@ static void tests_sign(void) // sign 1 more than max number of hashes per sign command api_format_send_cmd(cmd_str(CMD_sign), hashoverflow, KEY_STANDARD); - ASSERT_REPORT_HAS(cmd_str(CMD_echo)); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_REPORT_BUF)); api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_REPORT_BUF)); @@ -2133,10 +2260,16 @@ static void tests_sign(void) api_format_send_cmd(cmd_str(CMD_sign), one_input, KEY_STANDARD); ASSERT_REPORT_HAS(cmd_str(CMD_echo)); if (!TEST_LIVE_DEVICE) { - echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); + int len; + uint8_t hmac[SHA256_DIGEST_LENGTH]; + const char *val = api_read_value(CMD_echo); + char *echo = decrypt_and_check_hmac((const unsigned char *)val, strlens(val), &len, + memory_report_aeskey(TFA_SHARED_SECRET), hmac); + u_assert(echo); u_assert_str_has_not(echo, cmd_str(CMD_recid)); u_assert_str_has(echo, "_meta_data_"); u_assert_str_has(echo, "m/44'/0'/0'/1/7"); + free(echo); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); @@ -2150,10 +2283,16 @@ static void tests_sign(void) api_format_send_cmd(cmd_str(CMD_sign), two_inputs, KEY_STANDARD); ASSERT_REPORT_HAS(cmd_str(CMD_echo)); if (!TEST_LIVE_DEVICE) { - echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); + int len; + uint8_t hmac[SHA256_DIGEST_LENGTH]; + const char *val = api_read_value(CMD_echo); + char *echo = decrypt_and_check_hmac((const unsigned char *)val, strlens(val), &len, + memory_report_aeskey(TFA_SHARED_SECRET), hmac); + u_assert(echo); u_assert_str_has_not(echo, cmd_str(CMD_recid)); u_assert_str_has(echo, "_meta_data_"); u_assert_str_has(echo, "m/44'/0'/0'/1/8"); + free(echo); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); @@ -2173,11 +2312,17 @@ static void tests_sign(void) api_format_send_cmd(cmd_str(CMD_sign), checkpub, KEY_STANDARD); ASSERT_REPORT_HAS(cmd_str(CMD_echo)); if (!TEST_LIVE_DEVICE) { - echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); + int len; + uint8_t hmac[SHA256_DIGEST_LENGTH]; + const char *val = api_read_value(CMD_echo); + char *echo = decrypt_and_check_hmac((const unsigned char *)val, strlens(val), &len, + memory_report_aeskey(TFA_SHARED_SECRET), hmac); + u_assert(echo); u_assert_str_has_not(echo, cmd_str(CMD_recid)); u_assert_str_has(echo, "\"meta\":"); u_assert_str_has(echo, check_1); u_assert_str_has(echo, check_2); + free(echo); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); @@ -2207,11 +2352,17 @@ static void tests_sign(void) api_format_send_cmd(cmd_str(CMD_sign), one_input, KEY_STANDARD); ASSERT_REPORT_HAS(cmd_str(CMD_echo)); if (!TEST_LIVE_DEVICE) { - echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); + int len; + uint8_t hmac[SHA256_DIGEST_LENGTH]; + const char *val = api_read_value(CMD_echo); + char *echo = decrypt_and_check_hmac((const unsigned char *)val, strlens(val), &len, + memory_report_aeskey(TFA_SHARED_SECRET), hmac); + u_assert(echo); u_assert_str_has_not(echo, cmd_str(CMD_recid)); u_assert_str_has(echo, "_meta_data_"); u_assert_str_has(echo, "m/44'/0'/0'/1/7"); u_assert_str_has(echo, cmd_str(CMD_pin)); + free(echo); } // skip sending pin @@ -2225,11 +2376,17 @@ static void tests_sign(void) api_format_send_cmd(cmd_str(CMD_sign), one_input, KEY_STANDARD); ASSERT_REPORT_HAS(cmd_str(CMD_echo)); if (!TEST_LIVE_DEVICE) { - echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); + int len; + uint8_t hmac[SHA256_DIGEST_LENGTH]; + const char *val = api_read_value(CMD_echo); + char *echo = decrypt_and_check_hmac((const unsigned char *)val, strlens(val), &len, + memory_report_aeskey(TFA_SHARED_SECRET), hmac); + u_assert(echo); u_assert_str_has_not(echo, cmd_str(CMD_recid)); u_assert_str_has(echo, "_meta_data_"); u_assert_str_has(echo, "m/44'/0'/0'/1/7"); u_assert_str_has(echo, cmd_str(CMD_pin)); + free(echo); } api_format_send_cmd(cmd_str(CMD_sign), "{\"pin\":\"000\"}", KEY_STANDARD); @@ -2239,11 +2396,17 @@ static void tests_sign(void) api_format_send_cmd(cmd_str(CMD_sign), one_input, KEY_STANDARD); ASSERT_REPORT_HAS(cmd_str(CMD_echo)); if (!TEST_LIVE_DEVICE) { - echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); + int len; + uint8_t hmac[SHA256_DIGEST_LENGTH]; + const char *val = api_read_value(CMD_echo); + char *echo = decrypt_and_check_hmac((const unsigned char *)val, strlens(val), &len, + memory_report_aeskey(TFA_SHARED_SECRET), hmac); + u_assert(echo); u_assert_str_has_not(echo, cmd_str(CMD_recid)); u_assert_str_has(echo, "_meta_data_"); u_assert_str_has(echo, "m/44'/0'/0'/1/7"); u_assert_str_has(echo, cmd_str(CMD_pin)); + free(echo); } else { pin_err_count++; } @@ -2266,10 +2429,16 @@ static void tests_sign(void) api_format_send_cmd(cmd_str(CMD_sign), two_inputs, KEY_STANDARD); ASSERT_REPORT_HAS(cmd_str(CMD_echo)); if (!TEST_LIVE_DEVICE) { - echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); + int len; + uint8_t hmac[SHA256_DIGEST_LENGTH]; + const char *val = api_read_value(CMD_echo); + char *echo = decrypt_and_check_hmac((const unsigned char *)val, strlens(val), &len, + memory_report_aeskey(TFA_SHARED_SECRET), hmac); + u_assert(echo); u_assert_str_has_not(echo, cmd_str(CMD_recid)); u_assert_str_has(echo, "_meta_data_"); u_assert_str_has(echo, "m/44'/0'/0'/1/8"); + free(echo); } // send correct pin diff --git a/tests/tests_unit.c b/tests/tests_unit.c index 8a901202..4b5723c2 100644 --- a/tests/tests_unit.c +++ b/tests/tests_unit.c @@ -30,8 +30,10 @@ #include #include #include +#include #include "commander.h" +#include "aescbcb64.h" #include "wallet.h" #include "random.h" #include "base64.h" @@ -46,6 +48,7 @@ #include "uECC.h" #include "ecc.h" #include "aes.h" +#include "hmac_check.h" int U_TESTS_RUN = 0; @@ -687,6 +690,46 @@ static void test_aes_cbc(void) } } +static void test_aes_encrypt_decrypt_hmac(void) +{ + const char *msg = "A test msg.\n"; + int b64_length = 0; + const uint8_t *shared_secret = + utils_hex_to_uint8("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"); + char *encrypted_msg = aescbcb64_hmac_encrypt((const unsigned char *)msg, strlen(msg), + &b64_length, shared_secret); + u_assert_int_eq(b64_length, strlen(encrypted_msg)); + + int out_length = 0; + uint8_t hmac[SHA256_DIGEST_LENGTH]; + char *decrypted_msg = decrypt_and_check_hmac((const unsigned char *)encrypted_msg, + b64_length, &out_length, + shared_secret, hmac); + u_assert(decrypted_msg); + u_assert_str_eq(decrypted_msg, msg); + u_assert_int_eq(out_length, strlen(msg) + 1); + + int ub64_length = 0; + unsigned char *ub64 = unbase64((const char *)encrypted_msg, b64_length, &ub64_length); + u_assert(ub64); + + unsigned char corrupted_encrypted_ub64_msg[ub64_length]; + memcpy(corrupted_encrypted_ub64_msg, ub64, ub64_length); + // corrupt it... + corrupted_encrypted_ub64_msg[ub64_length - 1]++; + char *corrupted_encrypted_b64_msg = base64(corrupted_encrypted_ub64_msg, ub64_length, + &b64_length); + + char *corrupted_decrypted_msg = decrypt_and_check_hmac((const unsigned char *) + corrupted_encrypted_b64_msg, b64_length, &out_length, + shared_secret, hmac); + u_assert(corrupted_decrypted_msg == NULL); + free(encrypted_msg); + free(decrypted_msg); + free(corrupted_encrypted_b64_msg); + free(ub64); +} + static void test_address(void) { @@ -1092,6 +1135,7 @@ int main(void) u_run_test(test_aes_cbc); u_run_test(test_buffer_overflow); u_run_test(test_utils); + u_run_test(test_aes_encrypt_decrypt_hmac); // unit tests for secp256k1 rfc6979 are in tests_secp256k1.c u_run_test(test_rfc6979);