From 686246e1732f9f8905616c109f05151ef24ea379 Mon Sep 17 00:00:00 2001 From: djb Date: Sun, 18 Feb 2018 19:15:24 +0100 Subject: [PATCH] revert session key; update hidden wallet --- src/bootloader.c | 1 + src/bootloader.h | 11 +- src/commander.c | 267 +++++++++++++++++++--------------- src/firmware.c | 24 +++- src/flags.h | 24 ---- src/flash.h | 93 ++++++++++++ src/memory.c | 182 ++++++++++++++--------- src/memory.h | 24 ++-- src/random.c | 2 + src/sd.h | 2 +- src/startup.c | 21 +-- src/wallet.c | 87 +++++------ src/wallet.h | 3 +- tests/tests_api.c | 360 +++++++++++++++++++++++----------------------- 14 files changed, 620 insertions(+), 481 deletions(-) create mode 100644 src/flash.h diff --git a/src/bootloader.c b/src/bootloader.c index 030430d8..17958a97 100644 --- a/src/bootloader.c +++ b/src/bootloader.c @@ -34,6 +34,7 @@ #include "uECC.h" #include "sha2.h" #include "flags.h" +#include "flash.h" #include "utils.h" #include "touch.h" #include "version.h" diff --git a/src/bootloader.h b/src/bootloader.h index a33d033c..1126509a 100644 --- a/src/bootloader.h +++ b/src/bootloader.h @@ -32,18 +32,9 @@ #include -#define MPU_REGION_VALID (0x10) -#define MPU_REGION_ENABLE (0x01) -#define MPU_REGION_NORMAL (8 << 16)// TEX:0b001 S:0b0 C:0b0 B:0b0 -#define MPU_REGION_STATE_NA (0x00 << 24)// No access -#define MPU_REGION_STATE_PRIV_RW (0x01 << 24) -#define MPU_REGION_STATE_RW (0x03 << 24) -#define MPU_REGION_STATE_PRIV_RO (0x05 << 24) -#define MPU_REGION_STATE_RO (0x06 << 24) -#define MPU_REGION_STATE_XN (0x01 << 28) - #define BOOT_SIG_M 4 + typedef enum BOOT_OP_CODES { OP_WRITE = 'w',/* 0x77 */ OP_ERASE = 'e',/* 0x65 */ diff --git a/src/commander.c b/src/commander.c index 6304570c..d5f57c07 100644 --- a/src/commander.c +++ b/src/commander.c @@ -36,6 +36,7 @@ #include "base64.h" #include "wallet.h" #include "utils.h" +#include "flash.h" #include "flags.h" #include "sha2.h" #include "aes.h" @@ -50,7 +51,11 @@ #endif +#define BRACED(x) (strlens(x) ? (((x[0]) == '{') && ((x[strlens(x) - 1]) == '}')) : 0) + + extern const uint8_t MEM_PAGE_ERASE[MEM_PAGE_LEN]; +extern const uint8_t MEM_PAGE_ERASE_FE[MEM_PAGE_LEN]; static int REPORT_BUF_OVERFLOW = 0; __extension__ static char json_array[] = {[0 ... COMMANDER_ARRAY_MAX] = 0}; @@ -639,7 +644,7 @@ static void commander_process_seed(yajl_val json_node) snprintf(entropy_c, sizeof(entropy_c), "%s", utils_uint8_to_hex(entropy_b, sizeof(entropy_b))); - ret = wallet_generate_master(key, entropy_c); + ret = wallet_create(key, entropy_c); if (ret == DBB_OK) { if (commander_process_backup_create(key, filename, attr_str(ATTR_all)) != DBB_OK) { memory_erase_hww_seed(); @@ -693,7 +698,7 @@ static void commander_process_seed(yajl_val json_node) memory_name(name + 1); } snprintf(entropy_c, sizeof(entropy_c), "%s", backup_hex); - ret = wallet_generate_master(key, entropy_c); + ret = wallet_create(key, entropy_c); } } utils_zero(backup_hex, strlens(backup_hex)); @@ -794,7 +799,7 @@ static void commander_process_random(yajl_val json_node) encoded_report = aes_cbc_b64_encrypt((unsigned char *)echo_number, strlens(echo_number), &encrypt_len, - memory_report_verification_key()); + memory_report_aeskey(PASSWORD_VERIFY)); if (encoded_report) { commander_fill_report(cmd_str(CMD_echo), encoded_report, DBB_OK); free(encoded_report); @@ -810,10 +815,12 @@ static int commander_process_ecdh(int cmd, const uint8_t *pair_pubkey, { uint8_t rand_privkey[32], ecdh_secret[32], rand_led, ret, i = 0; - 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; - } + 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); @@ -878,7 +885,7 @@ static void commander_process_verifypass(yajl_val json_node) if (strlens(value)) { if (STREQ(value, attr_str(ATTR_export))) { - memcpy(text, utils_uint8_to_hex(memory_report_verification_key(), 32), 64 + 1); + 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); @@ -936,7 +943,7 @@ static void commander_process_verifypass(yajl_val json_node) char *enc = aes_cbc_b64_encrypt((const unsigned char *)VERIFYPASS_CRYPT_TEST, strlens(VERIFYPASS_CRYPT_TEST), &encrypt_len, - memory_report_verification_key()); + 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)), @@ -976,7 +983,7 @@ static void commander_process_xpub(yajl_val json_node) encoded_report = aes_cbc_b64_encrypt((unsigned char *)xpub, strlens(xpub), &encrypt_len, - memory_report_verification_key()); + memory_report_aeskey(PASSWORD_VERIFY)); if (encoded_report) { commander_fill_report(cmd_str(CMD_echo), encoded_report, DBB_OK); free(encoded_report); @@ -1002,33 +1009,6 @@ static uint8_t commander_bootloader_unlocked(void) } -static void commander_process_session(yajl_val json_node) -{ - const char *path[] = { cmd_str(CMD_session), NULL }; - const char *value = YAJL_GET_STRING(yajl_tree_get(json_node, path, yajl_t_string)); - - if (!strlens(value)) { - commander_fill_report(cmd_str(CMD_session), NULL, DBB_ERR_IO_INVALID_CMD); - return; - } - - if (STREQ(value, attr_str(ATTR_set))) { - commander_fill_report(cmd_str(CMD_session), - utils_uint8_to_hex(memory_session_key_update(), - MEM_PAGE_LEN), DBB_OK); - return; - } - - if (STREQ(value, attr_str(ATTR_off))) { - memory_session_key_off(); - commander_fill_report(cmd_str(CMD_session), attr_str(ATTR_success), DBB_OK); - return; - } - - commander_fill_report(cmd_str(CMD_session), NULL, DBB_ERR_IO_INVALID_CMD); -} - - static void commander_process_device(yajl_val json_node) { const char *path[] = { cmd_str(CMD_device), NULL }; @@ -1112,7 +1092,7 @@ static void commander_process_device(yajl_val json_node) char *tfa = aes_cbc_b64_encrypt((const unsigned char *)VERIFYPASS_CRYPT_TEST, strlens(VERIFYPASS_CRYPT_TEST), &tfa_len, - memory_report_verification_key()); + memory_report_aeskey(PASSWORD_VERIFY)); if (!tfa) { commander_clear_report(); commander_fill_report(cmd_str(CMD_device), NULL, DBB_ERR_MEM_ENCRYPT); @@ -1257,42 +1237,96 @@ static void commander_process_bootloader(yajl_val json_node) } -static void commander_process_password(yajl_val json_node, int cmd, PASSWORD_ID id) +static uint8_t commander_check_password_collision(void) { - int ret; - const char *path[] = { cmd_str(cmd), NULL }; - const char *value = YAJL_GET_STRING(yajl_tree_get(json_node, path, yajl_t_string)); + if (!memcmp(memory_report_aeskey(PASSWORD_STAND), memory_report_aeskey(PASSWORD_HIDDEN), + MEM_PAGE_LEN)) { + memory_active_key_set(memory_report_aeskey(PASSWORD_STAND)); + memory_random_password(PASSWORD_HIDDEN); + memory_hidden_hww_chaincode(MEM_PAGE_ERASE_FE); + memory_hidden_hww(MEM_PAGE_ERASE_FE); + commander_fill_report(cmd_str(CMD_password), NULL, DBB_ERR_IO_PW_COLLIDE); + return DBB_ERROR; + } + return DBB_OK; +} + + +static void commander_process_hidden_password(yajl_val json_node) +{ + const char *pw_path[] = {cmd_str(CMD_hidden_password), cmd_str(CMD_password), NULL }; + const char *key_path[] = {cmd_str(CMD_hidden_password), cmd_str(CMD_key), NULL }; + const char *pw = YAJL_GET_STRING(yajl_tree_get(json_node, pw_path, yajl_t_string)); + const char *key = YAJL_GET_STRING(yajl_tree_get(json_node, key_path, yajl_t_string)); + + HDNode node; + uint8_t ret; + char seed[MEM_PAGE_LEN * 2 + 1]; - if (wallet_is_locked() && id == PASSWORD_HIDDEN) { - commander_fill_report(cmd_str(CMD_password), NULL, DBB_ERR_IO_LOCKED); + if (wallet_is_locked()) { + commander_fill_report(cmd_str(CMD_hidden_password), NULL, DBB_ERR_IO_LOCKED); return; } - if (wallet_is_hidden() && id == PASSWORD_STAND) { - id = PASSWORD_HIDDEN; + if (wallet_seeded() != DBB_OK) { + commander_fill_report(cmd_str(CMD_hidden_password), NULL, DBB_ERR_KEY_MASTER); + return; } - ret = commander_process_aes_key(value, strlens(value), id); + if (wallet_is_hidden()) { + /* should never enter */ + commander_fill_report(cmd_str(CMD_hidden_password), NULL, DBB_ERR_IO_LOCKED); + return; + } - if (!memcmp(memory_read_aeskey(PASSWORD_STAND), memory_read_aeskey(PASSWORD_HIDDEN), - MEM_PAGE_LEN)) { + if (!strlens(key) || !strlens(pw)) { + commander_fill_report(cmd_str(CMD_hidden_password), NULL, DBB_ERR_IO_INVALID_CMD); + return; + } - if (memory_session_key_get_state() == MEM_SESSION_KEY_STATE_STATIC) { - memory_session_key_set(memory_read_aeskey(PASSWORD_STAND)); - memory_session_key_off(); - } + ret = commander_process_aes_key(pw, strlens(pw), PASSWORD_HIDDEN); - memory_random_password(PASSWORD_HIDDEN); - memory_clear_passwords(); - commander_fill_report(cmd_str(CMD_password), NULL, DBB_ERR_IO_PW_COLLIDE); + if (ret != DBB_OK) { + commander_fill_report(cmd_str(CMD_hidden_password), NULL, ret); return; } + if (commander_check_password_collision() != DBB_OK) { + return; + } + + snprintf(seed, sizeof(seed), "%s", utils_uint8_to_hex(memory_master_hww_entropy(NULL), + MEM_PAGE_LEN)); + if (wallet_generate_node(key, seed, &node) == DBB_ERROR) { + commander_fill_report(cmd_str(CMD_hidden_password), NULL, DBB_ERR_MEM_ATAES); + return; + } + memory_hidden_hww(node.private_key); + memory_hidden_hww_chaincode(node.chain_code); + + commander_fill_report(cmd_str(CMD_hidden_password), attr_str(ATTR_success), DBB_OK); +} + + +static void commander_process_password(yajl_val json_node) +{ + int ret; + const char *pw_path[] = { cmd_str(CMD_password), NULL }; + const char *pw = YAJL_GET_STRING(yajl_tree_get(json_node, pw_path, yajl_t_string)); + + ret = wallet_is_hidden() ? + commander_process_aes_key(pw, strlens(pw), PASSWORD_HIDDEN) : + commander_process_aes_key(pw, strlens(pw), PASSWORD_STAND); + if (ret != DBB_OK) { commander_fill_report(cmd_str(CMD_password), NULL, ret); return; } + if (commander_check_password_collision() != DBB_OK) { + return; + } + commander_fill_report(cmd_str(CMD_password), attr_str(ATTR_success), DBB_OK); } @@ -1305,15 +1339,11 @@ static int commander_process(int cmd, yajl_val json_node) return DBB_RESET; case CMD_hidden_password: - commander_process_password(json_node, cmd, PASSWORD_HIDDEN); + commander_process_hidden_password(json_node); break; case CMD_password: - commander_process_password(json_node, cmd, PASSWORD_STAND); - break; - - case CMD_session: - commander_process_session(json_node); + commander_process_password(json_node); break; case CMD_verifypass: @@ -1502,7 +1532,7 @@ static int commander_echo_command(yajl_val json_node) encoded_report = aes_cbc_b64_encrypt((unsigned char *)json_report, strlens(json_report), &encrypt_len, - memory_report_verification_key()); + memory_report_aeskey(PASSWORD_VERIFY)); commander_clear_report(); if (encoded_report) { commander_fill_report(cmd_str(CMD_echo), encoded_report, DBB_OK); @@ -1632,11 +1662,8 @@ static void commander_parse(char *command) encoded_report = aes_cbc_b64_encrypt((unsigned char *)json_report, strlens(json_report), &encrypt_len, - memory_session_key_report()); + memory_active_key_get()); - if (memory_session_key_get_state() == MEM_SESSION_KEY_STATE_UPDATING) { - memory_session_key_set(NULL); - } commander_clear_report(); if (encoded_report) { commander_fill_report(cmd_str(CMD_ciphertext), encoded_report, DBB_OK); @@ -1649,74 +1676,72 @@ static void commander_parse(char *command) } -static void commander_set_session_key(const char *encrypted_command) +static uint8_t commander_find_active_key(const char *encrypted_command) { - char *command; - int command_len = 0; - size_t json_object_len = 0; - uint8_t *key; + char *cmd_std, *cmd_hdn; + int len_std = 0, len_hdn = 0; + uint8_t *key_std, *key_hdn, ret = DBB_ERROR; - // Try standard wallet password - key = memory_read_aeskey(PASSWORD_STAND); - command = aes_cbc_b64_decrypt((const unsigned char *)encrypted_command, - strlens(encrypted_command), - &command_len, key); + memory_read_aeskeys(); + key_std = memory_report_aeskey(PASSWORD_STAND); + key_hdn = memory_report_aeskey(PASSWORD_HIDDEN); - if (strlens(command)) { - yajl_val json_node = yajl_tree_parse(command, NULL, 0); - if (json_node && YAJL_IS_OBJECT(json_node)) { - json_object_len = json_node->u.object.len; - } - yajl_tree_free(json_node); - free(command); - - if (json_object_len) { - wallet_set_hidden(0); - memory_session_key_set(key); - return; - } - } - - // Try hidden wallet password - key = memory_read_aeskey(PASSWORD_HIDDEN); - command = aes_cbc_b64_decrypt((const unsigned char *)encrypted_command, + cmd_std = aes_cbc_b64_decrypt((const unsigned char *)encrypted_command, strlens(encrypted_command), - &command_len, key); + &len_std, key_std); - if (strlens(command)) { - yajl_val json_node = yajl_tree_parse(command, NULL, 0); - if (json_node && YAJL_IS_OBJECT(json_node)) { - json_object_len = json_node->u.object.len; + cmd_hdn = aes_cbc_b64_decrypt((const unsigned char *)encrypted_command, + strlens(encrypted_command), + &len_hdn, key_hdn); + + if (strlens(cmd_std)) { + if (BRACED(cmd_std)) { + yajl_val json_node = yajl_tree_parse(cmd_std, NULL, 0); + if (json_node && YAJL_IS_OBJECT(json_node)) { + if (json_node->u.object.len) { + wallet_set_hidden(0); + memory_active_key_set(key_std); + ret = DBB_OK; + } + } + yajl_tree_free(json_node); } - yajl_tree_free(json_node); - free(command); + } + free(cmd_std); - if (json_object_len) { - wallet_set_hidden(1); - memory_session_key_set(key); - return; + if (strlens(cmd_hdn)) { + if (BRACED(cmd_hdn)) { + yajl_val json_node = yajl_tree_parse(cmd_hdn, NULL, 0); + if (json_node && YAJL_IS_OBJECT(json_node)) { + if (json_node->u.object.len) { + wallet_set_hidden(1); + memory_active_key_set(key_hdn); + ret = DBB_OK; + } + } + yajl_tree_free(json_node); } } + free(cmd_hdn); + + return ret; } static char *commander_decrypt(const char *encrypted_command) { - char *command; + char *command = NULL; int command_len = 0, err = 0; uint16_t err_count = 0, err_iter = 0; size_t json_object_len = 0; - if (memory_session_key_get_state() == MEM_SESSION_KEY_STATE_OFF) { - commander_set_session_key(encrypted_command); - memory_clear_passwords(); + 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 = aes_cbc_b64_decrypt((const unsigned char *)encrypted_command, - strlens(encrypted_command), - &command_len, - memory_session_key_report()); - err_count = memory_report_access_err_count(); err_iter = memory_report_access_err_count() + 1; @@ -1785,7 +1810,7 @@ static int commander_check_init(const char *encrypted_command) } // Pong whether or not password is set - if (encrypted_command[0] == '{') { + if (BRACED(encrypted_command)) { yajl_val json_node = yajl_tree_parse(encrypted_command, NULL, 0); if (json_node && YAJL_IS_OBJECT(json_node)) { const char *path[] = { cmd_str(CMD_ping), NULL }; @@ -1808,7 +1833,14 @@ static int commander_check_init(const char *encrypted_command) return DBB_OK; } - if (encrypted_command[0] == '{') { + // An erased wallet should not have seed material present. + if (wallet_erased() == DBB_ERROR) { + /* should never enter */ + commander_force_reset(); + return DBB_ERROR; + } + + if (BRACED(encrypted_command)) { yajl_val json_node = yajl_tree_parse(encrypted_command, NULL, 0); if (json_node && YAJL_IS_OBJECT(json_node)) { const char *path[] = { cmd_str(CMD_password), NULL }; @@ -1849,4 +1881,3 @@ char *commander(const char *command) memory_clear(); return json_report; } - diff --git a/src/firmware.c b/src/firmware.c index 274d3cc0..25dbe86e 100644 --- a/src/firmware.c +++ b/src/firmware.c @@ -33,6 +33,7 @@ #include "usb.h" #include "ecc.h" #include "led.h" +#include "flash.h" #include "touch.h" #include "memory.h" #include "random.h" @@ -46,6 +47,7 @@ uint32_t __stack_chk_guard = 0; extern void __attribute__((noreturn)) __stack_chk_fail(void); void __attribute__((noreturn)) __stack_chk_fail(void) { + udc_stop(); while (1) { led_toggle(); delay_ms(300); @@ -61,6 +63,7 @@ void SysTick_Handler(void) void HardFault_Handler(void) { + udc_stop(); while (1) { led_toggle(); delay_ms(500); @@ -70,6 +73,7 @@ void HardFault_Handler(void) void MemManage_Handler(void) { + udc_stop(); while (1) { led_toggle(); delay_ms(1000); @@ -77,11 +81,27 @@ void MemManage_Handler(void) } +static void enable_usersig_area(void) +{ + __DSB(); + __ISB(); + MPU->CTRL = 0; + MPU->RBAR = FLASH_USERSIG_START | MPU_REGION_VALID | 1; + MPU->RASR = MPU_REGION_ENABLE | MPU_REGION_NORMAL | mpu_region_size( + FLASH_USERSIG_SIZE) | MPU_REGION_STATE_RW; + MPU->CTRL = 0x1 | MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_HFNMIENA_Msk; + __DSB(); + __ISB(); +} + + char usb_serial_number[USB_DEVICE_GET_SERIAL_NAME_LENGTH]; + int main (void) { wdt_disable(WDT); + enable_usersig_area(); irq_initialize_vectors(); cpu_irq_enable(); sleepmgr_init(); @@ -108,6 +128,8 @@ int main (void) usb_serial_number[USB_DEVICE_GET_SERIAL_NAME_LENGTH - 1] = '-'; } + memory_setup(); + usb_suspend_action(); udc_start(); @@ -115,8 +137,6 @@ int main (void) delay_ms(300); led_off(); - memory_setup(); - while (1) { sleepmgr_enter_sleep(); } diff --git a/src/flags.h b/src/flags.h index cf59261c..5a818b2e 100644 --- a/src/flags.h +++ b/src/flags.h @@ -29,27 +29,6 @@ #define _FLAGS_H_ -// Flash: 256kB = 512 pages * 512B per page -#ifndef IFLASH0_ADDR -#define IFLASH0_ADDR (0x00400000u) -#endif -#define FLASH_BOOT_START (IFLASH0_ADDR) -#define FLASH_BOOT_LEN (0x00008000u) -#define FLASH_SIG_START (IFLASH0_ADDR + FLASH_BOOT_LEN) -#define FLASH_SIG_LEN (0x00001000u)// note: min flash erase size is 0x1000 (8 512-Byte pages) -#define FLASH_APP_START (IFLASH0_ADDR + FLASH_BOOT_LEN + FLASH_SIG_LEN) -#define FLASH_APP_LEN (IFLASH0_SIZE - FLASH_BOOT_LEN - FLASH_SIG_LEN) -#define FLASH_APP_PAGE_NUM (FLASH_APP_LEN / IFLASH0_PAGE_SIZE) -#define FLASH_APP_VERSION_LEN (4)// 4 byte big endian unsigned int -#define FLASH_APP_VERSION_START (FLASH_APP_START + FLASH_APP_LEN - FLASH_APP_VERSION_LEN) -#define FLASH_BOOT_OP_LEN (2)// 1 byte op code and 1 byte parameter -#define FLASH_BOOT_PAGES_PER_CHUNK (8) -#define FLASH_BOOT_CHUNK_LEN (IFLASH0_PAGE_SIZE * FLASH_BOOT_PAGES_PER_CHUNK) -#define FLASH_BOOT_CHUNK_NUM (FLASH_APP_LEN / FLASH_BOOT_CHUNK_LEN)// app len should be a multiple of chunk len -#define FLASH_BOOT_LOCK_BYTE (FLASH_SIG_LEN - 1) -#define FLASH_BOOT_LATEST_APP_VERSION_BYTES (FLASH_BOOT_LOCK_BYTE - FLASH_APP_VERSION_LEN) - - #define COMMANDER_REPORT_SIZE 3584 #define COMMANDER_NUM_SIG_MIN 14// Must be >= desktop app's `MAX_INPUTS_PER_SIGN` !! #define COMMANDER_SIG_LEN 154// sig + recid + json formatting @@ -84,7 +63,6 @@ X(REQUIRE_TOUCH) /* placeholder - do not move */\ /* parent keys */\ /* w/o touch */\ X(verifypass) \ -X(session) \ X(led) \ X(xpub) \ X(name) \ @@ -147,8 +125,6 @@ X(unlock) \ X(decrypt) \ X(encrypt) \ X(verify) \ -X(set) \ -X(off) \ X(true) \ X(false) \ X(erase) \ diff --git a/src/flash.h b/src/flash.h new file mode 100644 index 00000000..d166c405 --- /dev/null +++ b/src/flash.h @@ -0,0 +1,93 @@ +/* + + The MIT License (MIT) + + Copyright (c) 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 _FLASH_H_ +#define _FLASH_H_ + + +#include + + +// Flash: 256kB = 512 pages * 512B per page +// Memory map: +// bootloader area [ 32kB; last 2kB reserved for factory installed entropy ] +// firmware memory [ 4kB; contains firmaware signatures (7*64B), version (4B) and flags (1B); remaining RFU ] +// firmware code [ 220kB ] +#ifndef IFLASH0_ADDR +#define IFLASH0_ADDR (0x00400000u) +#endif +#define FLASH_PAGE_SIZE (IFLASH0_PAGE_SIZE) +#define FLASH_ERASE_SIZE (FLASH_PAGE_SIZE * 8)// note: min flash erase size is 0x1000 (8 512-Byte pages) +#define FLASH_BOOT_START (IFLASH0_ADDR) +#define FLASH_BOOT_LEN (0x00008000u) +#define FLASH_BOOT_OP_LEN (2)// 1 byte op code and 1 byte parameter +#define FLASH_BOOT_PAGES_PER_CHUNK (8) +#define FLASH_BOOT_CHUNK_LEN (IFLASH0_PAGE_SIZE * FLASH_BOOT_PAGES_PER_CHUNK) +#define FLASH_BOOT_CHUNK_NUM (FLASH_APP_LEN / FLASH_BOOT_CHUNK_LEN)// app len should be a multiple of chunk len +#define FLASH_BOOT_LOCK_BYTE (FLASH_SIG_LEN - 1) +#define FLASH_USERSIG_START (0x00400000u) +#define FLASH_USERSIG_SIZE (0x200u) +#define FLASH_USERSIG_RN_LEN (0x20u) +#define FLASH_SIG_START (IFLASH0_ADDR + FLASH_BOOT_LEN) +#define FLASH_SIG_LEN (FLASH_ERASE_SIZE) +#define FLASH_APP_START (IFLASH0_ADDR + FLASH_BOOT_LEN + FLASH_SIG_LEN) +#define FLASH_APP_LEN (IFLASH0_SIZE - FLASH_BOOT_LEN - FLASH_SIG_LEN) +#define FLASH_APP_PAGE_NUM (FLASH_APP_LEN / FLASH_PAGE_SIZE) +#define FLASH_APP_VERSION_LEN (4)// 4 byte big endian unsigned int +#define FLASH_APP_VERSION_START (FLASH_APP_START + FLASH_APP_LEN - FLASH_APP_VERSION_LEN) +#define FLASH_BOOT_LATEST_APP_VERSION_BYTES (FLASH_BOOT_LOCK_BYTE - FLASH_APP_VERSION_LEN) + + +#define MPU_REGION_VALID (0x10) +#define MPU_REGION_ENABLE (0x01) +#define MPU_REGION_NORMAL (8 << 16)// TEX:0b001 S:0b0 C:0b0 B:0b0 +#define MPU_REGION_STATE_NA (0x00 << 24)// No access +#define MPU_REGION_STATE_PRIV_RW (0x01 << 24) +#define MPU_REGION_STATE_RW (0x03 << 24) +#define MPU_REGION_STATE_PRIV_RO (0x05 << 24) +#define MPU_REGION_STATE_RO (0x06 << 24) +#define MPU_REGION_STATE_XN (0x01 << 28) + + +static inline uint32_t mpu_region_size(uint32_t size) +{ + uint32_t regionSize = 32; + uint32_t ret = 4; + + while (ret < 31) { + if (size <= regionSize) { + break; + } else { + ret++; + } + regionSize <<= 1; + } + return (ret << 1); +} + + +#endif diff --git a/src/memory.c b/src/memory.c index 95c8c9f0..915a95f7 100644 --- a/src/memory.c +++ b/src/memory.c @@ -34,13 +34,15 @@ #include "random.h" #include "utils.h" #include "flags.h" +#include "flash.h" #include "hmac.h" #include "sha2.h" #ifndef TESTING -#include "ataes132.h" #include #include #include +#include "ataes132.h" +#include "mcu.h" #endif @@ -51,22 +53,23 @@ static uint32_t MEM_ext_flags = DEFAULT_ext_flags; static uint32_t MEM_u2f_count = DEFAULT_u2f_count; static uint16_t MEM_pin_err = DBB_ACCESS_INITIALIZE; static uint16_t MEM_access_err = DBB_ACCESS_INITIALIZE; -static uint8_t MEM_session_key_state = MEM_SESSION_KEY_STATE_OFF; +__extension__ static uint8_t MEM_active_key[] = {[0 ... MEM_PAGE_LEN - 1] = 0xFF}; __extension__ static uint8_t MEM_user_entropy[] = {[0 ... MEM_PAGE_LEN - 1] = 0xFF}; -__extension__ static uint8_t MEM_session_key[] = {[0 ... MEM_PAGE_LEN - 1] = 0xFF}; -__extension__ static uint8_t MEM_session_key_new[] = {[0 ... MEM_PAGE_LEN - 1] = 0xFF}; __extension__ static uint8_t MEM_aeskey_stand[] = {[0 ... MEM_PAGE_LEN - 1] = 0xFF}; __extension__ static uint8_t MEM_aeskey_hidden[] = {[0 ... MEM_PAGE_LEN - 1] = 0xFF}; __extension__ static uint8_t MEM_aeskey_verify[] = {[0 ... MEM_PAGE_LEN - 1] = 0xFF}; __extension__ static uint8_t MEM_master_hww_entropy[] = {[0 ... MEM_PAGE_LEN - 1] = 0xFF}; __extension__ static uint8_t MEM_master_hww_chain[] = {[0 ... MEM_PAGE_LEN - 1] = 0xFF}; __extension__ static uint8_t MEM_master_hww[] = {[0 ... MEM_PAGE_LEN - 1] = 0xFF}; +__extension__ static uint8_t MEM_hidden_hww_chain[] = {[0 ... MEM_PAGE_LEN - 1] = 0xFF}; +__extension__ static uint8_t MEM_hidden_hww[] = {[0 ... MEM_PAGE_LEN - 1] = 0xFF}; __extension__ static uint8_t MEM_master_u2f[] = {[0 ... MEM_PAGE_LEN - 1] = 0xFF}; __extension__ static uint8_t MEM_name[] = {[0 ... MEM_PAGE_LEN - 1] = '0'}; __extension__ const uint8_t MEM_PAGE_ERASE[] = {[0 ... MEM_PAGE_LEN - 1] = 0xFF}; __extension__ const uint16_t MEM_PAGE_ERASE_2X[] = {[0 ... MEM_PAGE_LEN - 1] = 0xFFFF}; +__extension__ const uint8_t MEM_PAGE_ERASE_FE[] = {[0 ... MEM_PAGE_LEN - 1] = 0xFE}; static uint8_t memory_eeprom(uint8_t *write_b, uint8_t *read_b, const int32_t addr, @@ -124,9 +127,17 @@ static uint8_t memory_eeprom_crypt(const uint8_t *write_b, uint8_t *read_b, // bootloader bytes. memset(mempass, 0, sizeof(mempass)); #ifndef TESTING + uint8_t rn[FLASH_USERSIG_RN_LEN] = {0}; sha256_Raw((uint8_t *)(FLASH_BOOT_START), FLASH_BOOT_LEN, mempass); + flash_read_user_signature((uint32_t *)rn, FLASH_USERSIG_RN_LEN / sizeof(uint32_t)); + if (memcmp(rn, MEM_PAGE_ERASE, FLASH_USERSIG_RN_LEN)) { + hmac_sha256(mempass, MEM_PAGE_LEN, rn, FLASH_USERSIG_RN_LEN, mempass); + } #endif sha256_Raw(mempass, MEM_PAGE_LEN, mempass); + sha256_Raw((const uint8_t *)(utils_uint8_to_hex(mempass, MEM_PAGE_LEN)), MEM_PAGE_LEN * 2, + mempass); + sha256_Raw(mempass, MEM_PAGE_LEN, mempass); if (read_b) { enc = aes_cbc_b64_encrypt((unsigned char *)utils_uint8_to_hex(read_b, MEM_PAGE_LEN), @@ -215,6 +226,34 @@ static uint8_t memory_read_setup(void) } +static void memory_scramble_default_aeskeys(void) +{ + uint8_t number[32] = {0}; + random_bytes(number, sizeof(number), 0); + memcpy(MEM_aeskey_stand, number, MEM_PAGE_LEN); + memcpy(MEM_aeskey_hidden, number, MEM_PAGE_LEN); + memcpy(MEM_aeskey_verify, number, MEM_PAGE_LEN); + memcpy(MEM_active_key, number, MEM_PAGE_LEN); +} + + +static void memory_scramble_rn(void) +{ +#ifndef TESTING + uint32_t i = 0; + uint8_t usersig[FLASH_USERSIG_SIZE]; + uint8_t number[FLASH_USERSIG_RN_LEN] = {0}; + random_bytes(number, FLASH_USERSIG_RN_LEN, 0); + flash_read_user_signature((uint32_t *)usersig, FLASH_USERSIG_SIZE / sizeof(uint32_t)); + for (i = 0; i < FLASH_USERSIG_RN_LEN; i++) { + usersig[i] ^= number[i]; + } + flash_erase_user_signature(); + flash_write_user_signature((uint32_t *)usersig, FLASH_USERSIG_SIZE / sizeof(uint32_t)); +#endif +} + + uint8_t memory_setup(void) { if (memory_read_setup()) { @@ -235,15 +274,12 @@ uint8_t memory_setup(void) memory_write_setup(0x00); } else { memory_read_ext_flags(); - memory_read_aeskey(PASSWORD_VERIFY); memory_eeprom(NULL, &MEM_erased, MEM_ERASED_ADDR, 1); memory_master_u2f(NULL);// Load cache so that U2F speed is fast enough memory_read_access_err_count();// Load cache memory_u2f_count_read(); } - memory_session_key_update(); - memory_session_key_set(NULL); - memory_session_key_off(); + memory_scramble_default_aeskeys(); return DBB_OK; } @@ -253,11 +289,18 @@ void memory_erase_hww_seed(void) memory_master_hww_entropy(MEM_PAGE_ERASE); memory_master_hww_chaincode(MEM_PAGE_ERASE); memory_master_hww(MEM_PAGE_ERASE); + memory_hidden_hww_chaincode(MEM_PAGE_ERASE_FE); + memory_hidden_hww(MEM_PAGE_ERASE_FE); + memory_random_password(PASSWORD_HIDDEN); } void memory_reset_hww(void) { + uint8_t u2f[MEM_PAGE_LEN]; + memcpy(u2f, MEM_master_u2f, MEM_PAGE_LEN); + memory_scramble_rn(); + memory_master_u2f(u2f); memory_random_password(PASSWORD_STAND); memory_random_password(PASSWORD_VERIFY); memory_random_password(PASSWORD_HIDDEN); @@ -268,7 +311,7 @@ void memory_reset_hww(void) memory_write_ext_flags(DEFAULT_ext_flags); memory_access_err_count(DBB_ACCESS_INITIALIZE); memory_pin_err_count(DBB_ACCESS_INITIALIZE); - memory_session_key_off(); + utils_zero(u2f, sizeof(u2f)); } @@ -298,6 +341,8 @@ void memory_clear(void) #ifndef TESTING // Zero important variables in RAM on embedded MCU. // Do not clear for testing routines (i.e. not embedded). + memcpy(MEM_hidden_hww_chain, MEM_PAGE_ERASE, MEM_PAGE_LEN); + memcpy(MEM_hidden_hww, MEM_PAGE_ERASE, MEM_PAGE_LEN); memcpy(MEM_master_hww_chain, MEM_PAGE_ERASE, MEM_PAGE_LEN); memcpy(MEM_master_hww, MEM_PAGE_ERASE, MEM_PAGE_LEN); memcpy(MEM_master_hww_entropy, MEM_PAGE_ERASE, MEM_PAGE_LEN); @@ -318,6 +363,30 @@ uint8_t *memory_name(const char *name) } +uint8_t *memory_hidden_hww(const uint8_t *master) +{ + memory_eeprom_crypt(NULL, MEM_hidden_hww, MEM_HIDDEN_BIP32_ADDR); + if ((master == NULL) && !memcmp(MEM_hidden_hww, MEM_PAGE_ERASE, 32)) { + // Backward compatible with firmware <=2.2.3 + return memory_master_hww_chaincode(NULL); + } + memory_eeprom_crypt(master, MEM_hidden_hww, MEM_HIDDEN_BIP32_ADDR); + return MEM_hidden_hww; +} + + +uint8_t *memory_hidden_hww_chaincode(const uint8_t *chain) +{ + memory_eeprom_crypt(NULL, MEM_hidden_hww_chain, MEM_HIDDEN_BIP32_CHAIN_ADDR); + if ((chain == NULL) && !memcmp(MEM_hidden_hww_chain, MEM_PAGE_ERASE, 32)) { + // Backward compatible with firmware <=2.2.3 + return memory_master_hww(NULL); + } + memory_eeprom_crypt(chain, MEM_hidden_hww_chain, MEM_HIDDEN_BIP32_CHAIN_ADDR); + return MEM_hidden_hww_chain; +} + + uint8_t *memory_master_hww(const uint8_t *master) { memory_eeprom_crypt(master, MEM_master_hww, MEM_MASTER_BIP32_ADDR); @@ -352,62 +421,23 @@ uint8_t *memory_report_master_u2f(void) } -void memory_clear_passwords(void) -{ - uint8_t number[32] = {0}; - random_bytes(number, sizeof(number), 0); -#ifndef TESTING - memcpy(MEM_aeskey_stand, number, MEM_PAGE_LEN); - memcpy(MEM_aeskey_hidden, number, MEM_PAGE_LEN); -#endif -} - - -uint8_t *memory_session_key_update(void) -{ - MEM_session_key_state = MEM_SESSION_KEY_STATE_UPDATING; - random_bytes(MEM_session_key_new, MEM_PAGE_LEN, 0); - return MEM_session_key_new; -} - - -void memory_session_key_off(void) -{ - MEM_session_key_state = MEM_SESSION_KEY_STATE_OFF; -} - - -void memory_session_key_set(uint8_t *key) +void memory_active_key_set(uint8_t *key) { if (key) { - memcpy(MEM_session_key, key, MEM_PAGE_LEN); - MEM_session_key_state = MEM_SESSION_KEY_STATE_STATIC; - } else { - if (MEM_session_key_state == MEM_SESSION_KEY_STATE_UPDATING) { - memcpy(MEM_session_key, MEM_session_key_new, MEM_PAGE_LEN); - } else { - random_bytes(MEM_session_key, MEM_PAGE_LEN, 0); - } - MEM_session_key_state = MEM_SESSION_KEY_STATE_EPHEMERAL; + memcpy(MEM_active_key, key, MEM_PAGE_LEN); } } -uint8_t *memory_session_key_report(void) -{ - return MEM_session_key; -} - - -uint8_t memory_session_key_get_state(void) +uint8_t *memory_active_key_get(void) { - return MEM_session_key_state; + return MEM_active_key; } uint8_t memory_write_aeskey(const char *password, int len, PASSWORD_ID id) { - uint8_t ret = DBB_ERROR; + int ret = 0; uint8_t password_b[MEM_PAGE_LEN]; memset(password_b, 0, MEM_PAGE_LEN); @@ -420,41 +450,57 @@ uint8_t memory_write_aeskey(const char *password, int len, PASSWORD_ID id) switch ((int)id) { case PASSWORD_STAND: - ret = memory_eeprom_crypt(password_b, MEM_aeskey_stand, MEM_AESKEY_STAND_ADDR); - sha256_Raw(password_b, MEM_PAGE_LEN, password_b); - memcpy(MEM_user_entropy, password_b, MEM_PAGE_LEN); + memcpy(MEM_aeskey_stand, password_b, MEM_PAGE_LEN); break; case PASSWORD_HIDDEN: - ret = memory_eeprom_crypt(password_b, MEM_aeskey_hidden, MEM_AESKEY_HIDDEN_ADDR); + memcpy(MEM_aeskey_hidden, password_b, MEM_PAGE_LEN); break; case PASSWORD_VERIFY: - ret = memory_eeprom_crypt(password_b, MEM_aeskey_verify, MEM_AESKEY_VERIFY_ADDR); + memcpy(MEM_aeskey_verify, password_b, MEM_PAGE_LEN); break; default: { /* never reached */ } } + ret |= memory_eeprom_crypt(MEM_aeskey_stand, MEM_aeskey_stand, + 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); - if (ret == DBB_OK) { - return DBB_OK; - } else { + + if (ret) { return DBB_ERR_MEM_ATAES; + } else { + return DBB_OK; } } -uint8_t *memory_read_aeskey(PASSWORD_ID id) +void memory_read_aeskeys(void) +{ + static uint8_t read = 0; + 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); + sha256_Raw(MEM_aeskey_stand, MEM_PAGE_LEN, MEM_user_entropy); + read++; + } +} + + +uint8_t *memory_report_aeskey(PASSWORD_ID id) { switch ((int)id) { case PASSWORD_STAND: - memory_eeprom_crypt(NULL, MEM_aeskey_stand, MEM_AESKEY_STAND_ADDR); return MEM_aeskey_stand; case PASSWORD_HIDDEN: - memory_eeprom_crypt(NULL, MEM_aeskey_hidden, MEM_AESKEY_HIDDEN_ADDR); return MEM_aeskey_hidden; case PASSWORD_VERIFY: - memory_eeprom_crypt(NULL, MEM_aeskey_verify, MEM_AESKEY_VERIFY_ADDR); return MEM_aeskey_verify; default: return NULL; @@ -462,12 +508,6 @@ uint8_t *memory_read_aeskey(PASSWORD_ID id) } -uint8_t *memory_report_verification_key(void) -{ - return MEM_aeskey_verify; -} - - uint8_t *memory_report_user_entropy(void) { return MEM_user_entropy; diff --git a/src/memory.h b/src/memory.h index ca3a7063..6fe34af7 100644 --- a/src/memory.h +++ b/src/memory.h @@ -52,6 +52,8 @@ #define MEM_AESKEY_HIDDEN_ADDR 0x0800 #define MEM_MASTER_ENTROPY_ADDR 0x0900 #define MEM_MASTER_U2F_ADDR 0x0A00 +#define MEM_HIDDEN_BIP32_ADDR 0x0B00 +#define MEM_HIDDEN_BIP32_CHAIN_ADDR 0x0B80 // Extension flags @@ -76,32 +78,22 @@ typedef enum PASSWORD_ID { } PASSWORD_ID; -typedef enum MEM_SESSION_KEY_STATE { - MEM_SESSION_KEY_STATE_OFF, - MEM_SESSION_KEY_STATE_STATIC, - MEM_SESSION_KEY_STATE_EPHEMERAL, - MEM_SESSION_KEY_STATE_UPDATING -} MEM_SESSION_KEY_STATE; - - uint8_t memory_setup(void); void memory_reset_u2f(void); void memory_reset_hww(void); void memory_erase_hww_seed(void); void memory_random_password(PASSWORD_ID id); -void memory_clear_passwords(void); void memory_clear(void); -void memory_session_key_off(void); -void memory_session_key_set(uint8_t *key); -uint8_t *memory_session_key_update(void); -uint8_t *memory_session_key_report(void); -uint8_t memory_session_key_get_state(void); +void memory_active_key_set(uint8_t *key); +uint8_t *memory_active_key_get(void); uint8_t memory_write_aeskey(const char *password, int len, PASSWORD_ID id); -uint8_t *memory_read_aeskey(PASSWORD_ID id); -uint8_t *memory_report_verification_key(void); +void memory_read_aeskeys(void); +uint8_t *memory_report_aeskey(PASSWORD_ID id); uint8_t *memory_report_user_entropy(void); uint8_t *memory_name(const char *name); +uint8_t *memory_hidden_hww(const uint8_t *master_priv_key); +uint8_t *memory_hidden_hww_chaincode(const uint8_t *chain_code); uint8_t *memory_master_hww(const uint8_t *master_priv_key); uint8_t *memory_master_hww_chaincode(const uint8_t *chain_code); uint8_t *memory_master_hww_entropy(const uint8_t *master_entropy); diff --git a/src/random.c b/src/random.c index 4254fcd5..34b8380d 100644 --- a/src/random.c +++ b/src/random.c @@ -30,6 +30,7 @@ #include "random.h" #include "memory.h" #include "flags.h" +#include "flash.h" #ifndef TESTING #include "ataes132.h" #include "sha2.h" @@ -80,6 +81,7 @@ int random_bytes(uint8_t *buf, uint32_t len, uint8_t update_seed) if (ret == DBB_OK && ataes_ret[0]) { memcpy(buf + i, ataes_ret + 2, (len - i) < 16 ? (len - i) : 16); } else { + HardFault_Handler(); return DBB_ERROR; } i += 16; diff --git a/src/sd.h b/src/sd.h index edc480c2..6f7d37d9 100644 --- a/src/sd.h +++ b/src/sd.h @@ -58,7 +58,7 @@ #define SD_PDF_TEXT_END "\nET\n" #define SD_PDF_4_0_END "endstream\nendobj\n" #define SD_PDF_END "xref\n0 5\n0000000000 65535 f \n%010i 00000 n \n%010i 00000 n \n%010i 00000 n \n%010i 00000 n \ntrailer\n<<\n/Size 5\n/Root 1 0 R\n>>\nstartxref\n%i\n" -#define SD_PDF_EOF "%%%%EOF"// FIXME - deleted 4 '%' signs - check if ok LIVE testing +#define SD_PDF_EOF "%%%%EOF" uint8_t sd_list(int cmd); diff --git a/src/startup.c b/src/startup.c index c88716dc..22cd1975 100644 --- a/src/startup.c +++ b/src/startup.c @@ -32,6 +32,7 @@ #include "usb.h" #include "sha2.h" #include "flags.h" +#include "flash.h" #include "touch.h" #include "systick.h" #include "bootloader.h" @@ -45,6 +46,7 @@ uint32_t __stack_chk_guard = 0; extern void __attribute__((noreturn)) __stack_chk_fail(void); void __attribute__((noreturn)) __stack_chk_fail(void) { + udc_stop(); while (1) { led_toggle(); delay_ms(300); @@ -60,6 +62,7 @@ void SysTick_Handler(void) void HardFault_Handler(void) { + udc_stop(); while (1) { led_toggle(); delay_ms(500); @@ -69,6 +72,7 @@ void HardFault_Handler(void) void MemManage_Handler(void) { + udc_stop(); while (1) { led_toggle(); delay_ms(1000); @@ -76,23 +80,6 @@ void MemManage_Handler(void) } -static uint32_t mpu_region_size(uint32_t size) -{ - uint32_t regionSize = 32; - uint32_t ret = 4; - - while (ret < 31) { - if (size <= regionSize) { - break; - } else { - ret++; - } - regionSize <<= 1; - } - return (ret << 1); -} - - static void mpu_init(void) { int i = 0; diff --git a/src/wallet.c b/src/wallet.c index db7a5cf7..20fe551d 100644 --- a/src/wallet.c +++ b/src/wallet.c @@ -42,6 +42,7 @@ extern const uint8_t MEM_PAGE_ERASE[MEM_PAGE_LEN]; +extern const uint8_t MEM_PAGE_ERASE_FE[MEM_PAGE_LEN]; extern const uint16_t MEM_PAGE_ERASE_2X[MEM_PAGE_LEN]; @@ -68,65 +69,56 @@ int wallet_is_locked(void) uint8_t *wallet_get_master(void) { - if (HIDDEN) { - return memory_master_hww_chaincode(NULL); - } else { - return memory_master_hww(NULL); - } + uint8_t *std = memory_master_hww(NULL); + uint8_t *hdn = memory_hidden_hww(NULL); + return (HIDDEN ? hdn : std); } uint8_t *wallet_get_chaincode(void) { - if (HIDDEN) { - return memory_master_hww(NULL); - } else { - return memory_master_hww_chaincode(NULL); - } + uint8_t *std = memory_master_hww_chaincode(NULL); + uint8_t *hdn = memory_hidden_hww_chaincode(NULL); + return (HIDDEN ? hdn : std); } - int wallet_seeded(void) { if (!memcmp(memory_master_hww(NULL), MEM_PAGE_ERASE, 32) || - !memcmp(memory_master_hww_chaincode(NULL), MEM_PAGE_ERASE, 32)) { + !memcmp(memory_master_hww_chaincode(NULL), MEM_PAGE_ERASE, 32) || + !memcmp(memory_master_hww_entropy(NULL), MEM_PAGE_ERASE, 32)) { return DBB_ERROR; } else { return DBB_OK; } } - -static void wallet_report_xpriv(const char *keypath, char *xpriv) +int wallet_erased(void) { - HDNode node; - if (wallet_seeded() == DBB_OK) { - if (wallet_generate_key(&node, keypath, wallet_get_master(), - wallet_get_chaincode()) == DBB_OK) { - hdnode_serialize_private(&node, xpriv, 112); - } + if (memcmp(memory_master_hww(NULL), MEM_PAGE_ERASE, 32) || + memcmp(memory_master_hww_chaincode(NULL), MEM_PAGE_ERASE, 32) || + memcmp(memory_hidden_hww(NULL), MEM_PAGE_ERASE_FE, 32) || + memcmp(memory_hidden_hww_chaincode(NULL), MEM_PAGE_ERASE_FE, 32) || + memcmp(memory_master_hww_entropy(NULL), MEM_PAGE_ERASE, 32)) { + return DBB_ERROR; + } else { + return DBB_OK; } - utils_zero(&node, sizeof(HDNode)); } - -int wallet_generate_master(const char *passphrase, const char *entropy_in) +int wallet_create(const char *passphrase, const char *entropy_in) { int ret = DBB_OK; uint8_t entropy[MEM_PAGE_LEN]; HDNode node; - if (strlens(entropy_in) != MEM_PAGE_LEN * 2) { - return DBB_ERROR; - } - ret = wallet_generate_node(passphrase, entropy_in, &node); if (ret != DBB_OK) { goto exit; } + memory_erase_hww_seed(); memcpy(entropy, utils_hex_to_uint8(entropy_in), sizeof(entropy)); - memory_master_hww(node.private_key); memory_master_hww_chaincode(node.chain_code); memory_master_hww_entropy(entropy); @@ -150,8 +142,8 @@ int wallet_generate_key(HDNode *node, const char *keypath, const uint8_t *privke static char prime[] = "phH\'"; static char digits[] = "0123456789"; uint64_t idx = 0; - char *pch, *kp = malloc(strlens(keypath) + 1); + char *kp = strdup(keypath); if (!kp) { return DBB_ERROR_MEM; } @@ -160,9 +152,6 @@ int wallet_generate_key(HDNode *node, const char *keypath, const uint8_t *privke goto err; } - memset(kp, 0, strlens(keypath) + 1); - memcpy(kp, keypath, strlens(keypath)); - if (kp[0] != 'm' || kp[1] != '/') { goto err; } @@ -174,21 +163,29 @@ int wallet_generate_key(HDNode *node, const char *keypath, const uint8_t *privke memcpy(node->private_key, privkeymaster, 32); hdnode_fill_public_key(node); - pch = strtok(kp + 2, delim); + char *pch = strtok(kp + 2, delim); + if (pch == NULL) { + goto err; + } + int has_prm = 0; while (pch != NULL) { size_t i = 0; int prm = 0; - for ( ; i < strlens(pch); i++) { + size_t pch_len = strlens(pch); + for ( ; i < pch_len; i++) { if (strchr(prime, pch[i])) { - if (i != strlens(pch) - 1) { + if (i != pch_len - 1) { goto err; } prm = 1; + has_prm = 1; } else if (!strchr(digits, pch[i])) { goto err; } } - + if (prm && pch_len == 1) { + goto err; + } idx = strtoull(pch, NULL, 10); if (idx > UINT32_MAX) { goto err; @@ -205,6 +202,9 @@ int wallet_generate_key(HDNode *node, const char *keypath, const uint8_t *privke } pch = strtok(NULL, delim); } + if (!has_prm) { + goto err; + } free(kp); return DBB_OK; @@ -219,6 +219,11 @@ int wallet_generate_node(const char *passphrase, const char *entropy, HDNode *no int ret; uint8_t seed[PBKDF2_HMACLEN]; char salt[8 + strlens(passphrase) + 1]; + + if (strlens(entropy) != MEM_PAGE_LEN * 2) { + return DBB_ERROR; + } + snprintf(salt, sizeof(salt), "%s%s", "mnemonic", passphrase); pbkdf2_hmac_sha512((const uint8_t *)entropy, strlens(entropy), salt, seed, sizeof(seed)); @@ -252,9 +257,12 @@ void wallet_report_id(char *id) { uint8_t h[32]; char xpub[112] = {0}; - wallet_report_xpub("m/", xpub); - sha256_Raw((uint8_t *)xpub, 112, h); - memcpy(id, utils_uint8_to_hex(h, 32), 64); + wallet_report_xpub("m/151'/144'", xpub);// ascii 'i' / 'd' + if (xpub[0]) { + sha256_Raw((uint8_t *)xpub, 112, h); + sha256_Raw(h, 32, h); + memcpy(id, utils_uint8_to_hex(h, 32), 64); + } } @@ -377,4 +385,3 @@ void wallet_get_wif(const uint8_t *priv_key, uint8_t version, char *wif, int wif data[33] = 0x01; base58_encode_check(data, 34, wif, wifsize); } - diff --git a/src/wallet.h b/src/wallet.h index d83a0dd1..54fac091 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -41,7 +41,8 @@ uint8_t *wallet_get_master(void); uint8_t *wallet_get_chaincode(void); int wallet_split_seed(char **seed_words, const char *message); int wallet_seeded(void); -int wallet_generate_master(const char *passphrase, const char *entropy_in); +int wallet_erased(void); +int wallet_create(const char *passphrase, const char *entropy_in); int wallet_check_pubkey(const char *pubkey, const char *keypath); int wallet_sign(const char *message, const char *keypath); void wallet_report_xpub(const char *keypath, char *xpub); diff --git a/tests/tests_api.c b/tests/tests_api.c index 49cd5250..e98f8cf6 100644 --- a/tests/tests_api.c +++ b/tests/tests_api.c @@ -109,7 +109,7 @@ 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_verification_key()); + echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); u_assert_str_eq(xpub0, echo); } @@ -147,7 +147,7 @@ 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_verification_key()); + echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); u_assert_str_eq(xpub0, echo); } @@ -222,8 +222,13 @@ static void tests_seed_xpub_backup(void) ASSERT_SUCCESS // test keypath - api_format_send_cmd(cmd_str(CMD_xpub), "m/111", KEY_STANDARD); + api_format_send_cmd(cmd_str(CMD_xpub), "m/111'", KEY_STANDARD); ASSERT_REPORT_HAS("\"xpub\":"); + api_format_send_cmd(cmd_str(CMD_xpub), "m/1/2'/3/4", KEY_STANDARD); + ASSERT_REPORT_HAS("\"xpub\":"); + + api_format_send_cmd(cmd_str(CMD_xpub), "m/1/2/3", KEY_STANDARD); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_CHILD)); api_format_send_cmd(cmd_str(CMD_xpub), "111", KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_CHILD)); @@ -246,11 +251,28 @@ static void tests_seed_xpub_backup(void) api_format_send_cmd(cmd_str(CMD_xpub), "m/-111", KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_CHILD)); + api_format_send_cmd(cmd_str(CMD_xpub), "m/'0", KEY_STANDARD); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_CHILD)); + + api_format_send_cmd(cmd_str(CMD_xpub), "m/'", KEY_STANDARD); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_CHILD)); + + api_format_send_cmd(cmd_str(CMD_xpub), "m/", KEY_STANDARD); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_CHILD)); + + api_format_send_cmd(cmd_str(CMD_xpub), "m//", KEY_STANDARD); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_CHILD)); + + api_format_send_cmd(cmd_str(CMD_xpub), "m/ ", KEY_STANDARD); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_CHILD)); + + + // test create seeds differ memset(xpub0, 0, sizeof(xpub0)); memset(xpub1, 0, sizeof(xpub1)); - api_format_send_cmd(cmd_str(CMD_xpub), "m/0", KEY_STANDARD); + api_format_send_cmd(cmd_str(CMD_xpub), "m/0'", KEY_STANDARD); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); memcpy(xpub0, api_read_value(CMD_xpub), sizeof(xpub0)); u_assert_str_not_eq(xpub0, xpub1); @@ -277,7 +299,7 @@ static void tests_seed_xpub_backup(void) api_format_send_cmd(cmd_str(CMD_seed), seed_create_2, KEY_STANDARD); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - api_format_send_cmd(cmd_str(CMD_xpub), "m/0", KEY_STANDARD); + api_format_send_cmd(cmd_str(CMD_xpub), "m/0'", KEY_STANDARD); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); memcpy(xpub1, api_read_value(CMD_xpub), sizeof(xpub0)); u_assert_str_not_eq(xpub0, xpub1); @@ -330,7 +352,7 @@ static void tests_seed_xpub_backup(void) api_format_send_cmd(cmd_str(CMD_seed), seed_usb, KEY_STANDARD); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - api_format_send_cmd(cmd_str(CMD_xpub), "m/0", KEY_STANDARD); + api_format_send_cmd(cmd_str(CMD_xpub), "m/0'", KEY_STANDARD); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); memcpy(xpub0, api_read_value(CMD_xpub), sizeof(xpub0)); u_assert_str_not_eq(xpub0, xpub1); @@ -343,7 +365,7 @@ static void tests_seed_xpub_backup(void) ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); // verify xpubs not same - api_format_send_cmd(cmd_str(CMD_xpub), "m/0", KEY_STANDARD); + api_format_send_cmd(cmd_str(CMD_xpub), "m/0'", KEY_STANDARD); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); memcpy(xpub1, api_read_value(CMD_xpub), sizeof(xpub1)); u_assert_str_not_eq(xpub0, xpub1); @@ -353,7 +375,7 @@ static void tests_seed_xpub_backup(void) ASSERT_SUCCESS // verify xpub matches - api_format_send_cmd(cmd_str(CMD_xpub), "m/0", KEY_STANDARD); + api_format_send_cmd(cmd_str(CMD_xpub), "m/0'", KEY_STANDARD); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); memcpy(xpub1, api_read_value(CMD_xpub), sizeof(xpub1)); u_assert_str_eq(xpub0, xpub1); @@ -1074,7 +1096,7 @@ 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_verification_key()); + echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); u_assert_str_has(echo, "m/44'/0'/0'/1/7"); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); @@ -1115,7 +1137,7 @@ 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_verification_key()); + echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); u_assert_str_has(echo, "m/44'/0'/0'/1/7"); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); @@ -1138,7 +1160,7 @@ 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_verification_key()); + echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); u_assert_str_has(echo, "m/44'/0'/0'/1/7"); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); @@ -1185,7 +1207,7 @@ 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_verification_key()); + echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); u_assert_str_has(echo, "m/44'/0'/0'/1/7"); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); @@ -1208,7 +1230,7 @@ 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_verification_key()); + echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); u_assert_str_has(echo, "m/44'/0'/0'/1/7"); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); @@ -1232,7 +1254,7 @@ 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_verification_key()); + echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); u_assert_str_has(echo, "m/44'/0'/0'/1/7"); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); @@ -1375,7 +1397,7 @@ static void tests_device(void) 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_verification_key()); + &decrypt_len, memory_report_aeskey(PASSWORD_VERIFY)); u_assert_str_eq(dec, VERIFYPASS_CRYPT_TEST); free(dec); yajl_tree_free(json_node); @@ -1525,6 +1547,9 @@ static void tests_input(void) static void tests_password(void) { + char cmd[512], xpub_std[112], xpub_hdn[112], xpub_tst[112]; + char keypath[] = "m/44'/0'/0'/0/0"; + api_reset_device(); api_format_send_cmd(cmd_str(CMD_name), "", NULL); @@ -1568,7 +1593,7 @@ static void tests_password(void) if (ciphertext) { int decrypt_len; char *dec = aes_cbc_b64_decrypt((const unsigned char *)ciphertext, strlens(ciphertext), - &decrypt_len, memory_report_verification_key()); + &decrypt_len, memory_report_aeskey(PASSWORD_VERIFY)); u_assert_str_eq(dec, VERIFYPASS_CRYPT_TEST); free(dec); } @@ -1583,22 +1608,47 @@ static void tests_password(void) // Login to hidden wallet -> error (hidden key not set) api_format_send_cmd(cmd_str(CMD_name), "", KEY_HIDDEN); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_JSON_PARSE)); - // Set hidden key - api_format_send_cmd(cmd_str(CMD_hidden_password), hidden_pwd, KEY_STANDARD); + // Set hidden key - error no master + snprintf(cmd, sizeof(cmd), "{\"%s\":\"%s\",\"%s\":\"%s\"}", cmd_str(CMD_password), + hidden_pwd, cmd_str(CMD_key), hidden_pwd); + api_format_send_cmd(cmd_str(CMD_hidden_password), cmd, KEY_STANDARD); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_MASTER)); + // Erase backups + api_format_send_cmd(cmd_str(CMD_backup), attr_str(ATTR_erase), KEY_STANDARD); + ASSERT_SUCCESS + // Seed wallets + snprintf(cmd, sizeof(cmd), + "{\"source\":\"create\",\"filename\":\"h.pdf\",\"key\":\"%s\"}", tests_pwd); + api_format_send_cmd(cmd_str(CMD_seed), cmd, KEY_STANDARD); ASSERT_SUCCESS + // Send deprecated command to set hidden key + api_format_send_cmd(cmd_str(CMD_hidden_password), "1234", KEY_STANDARD); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_INVALID_CMD)); + // Send incommplete command to set hidden key + snprintf(cmd, sizeof(cmd), "{\"%s\":\"%s\"}", cmd_str(CMD_password), hidden_pwd); + api_format_send_cmd(cmd_str(CMD_hidden_password), cmd, KEY_STANDARD); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_INVALID_CMD)); + // Send incommplete command to set hidden key + snprintf(cmd, sizeof(cmd), "{\"%s\":\"%s\"}", cmd_str(CMD_key), hidden_pwd); + api_format_send_cmd(cmd_str(CMD_hidden_password), cmd, KEY_STANDARD); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_INVALID_CMD)); // Set hidden key with wrong length -> hidden key not reset - api_format_send_cmd(cmd_str(CMD_hidden_password), "123", KEY_STANDARD); + snprintf(cmd, sizeof(cmd), "{\"%s\":\"123\",\"%s\":\"%s\"}", cmd_str(CMD_password), + cmd_str(CMD_key), hidden_pwd); + api_format_send_cmd(cmd_str(CMD_hidden_password), cmd, KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_PASSWORD_LEN)); - // Turn off session key to allow switching wallets - api_format_send_cmd(cmd_str(CMD_session), "off", KEY_STANDARD); + // Access hidden wallet - should fail if no key set + api_format_send_cmd(cmd_str(CMD_xpub), keypath, KEY_HIDDEN); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_JSON_PARSE)); + // Set hidden key + snprintf(cmd, sizeof(cmd), "{\"%s\":\"%s\",\"%s\":\"%s\"}", cmd_str(CMD_password), + hidden_pwd, cmd_str(CMD_key), hidden_pwd); + api_format_send_cmd(cmd_str(CMD_hidden_password), cmd, KEY_STANDARD); ASSERT_SUCCESS // Login to hidden wallet api_format_send_cmd(cmd_str(CMD_name), "", KEY_HIDDEN); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); ASSERT_REPORT_HAS(DEVICE_DEFAULT_NAME); - // Turn off session key to allow switching wallets - api_format_send_cmd(cmd_str(CMD_session), "off", KEY_HIDDEN); - ASSERT_SUCCESS // Change standard key to hidden key from standard wallet -> error collision // -> delete hidden key and set standard key to hidden key api_format_send_cmd(cmd_str(CMD_password), hidden_pwd, KEY_STANDARD); @@ -1615,25 +1665,18 @@ static void tests_password(void) if (!TEST_U2FAUTH_HIJACK) { ASSERT_SUCCESS } - // Turn off session key to allow switching wallets - api_format_send_cmd(cmd_str(CMD_session), "off", KEY_HIDDEN); - ASSERT_SUCCESS // Login to standard wallet (hidden key) api_format_send_cmd(cmd_str(CMD_name), "", KEY_STANDARD); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); ASSERT_REPORT_HAS(DEVICE_DEFAULT_NAME); - // Turn off session key to allow switching wallets - api_format_send_cmd(cmd_str(CMD_session), "off", KEY_STANDARD); - ASSERT_SUCCESS // Login to hidden wallet -> error (hidden key not set) api_format_send_cmd(cmd_str(CMD_name), "", KEY_HIDDEN); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_JSON_PARSE)); // Reset hidden key - api_format_send_cmd(cmd_str(CMD_hidden_password), hidden_pwd, KEY_STANDARD); - ASSERT_SUCCESS - // Turn off session key to allow switching wallets - api_format_send_cmd(cmd_str(CMD_session), "off", KEY_STANDARD); + snprintf(cmd, sizeof(cmd), "{\"%s\":\"%s\",\"%s\":\"%s\"}", cmd_str(CMD_password), + hidden_pwd, cmd_str(CMD_key), hidden_pwd); + api_format_send_cmd(cmd_str(CMD_hidden_password), cmd, KEY_STANDARD); ASSERT_SUCCESS // Login to hidden wallet api_format_send_cmd(cmd_str(CMD_name), "", KEY_HIDDEN); @@ -1653,175 +1696,114 @@ static void tests_password(void) api_format_send_cmd(cmd_str(CMD_name), "", KEY_STANDARD); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); ASSERT_REPORT_HAS(DEVICE_DEFAULT_NAME); + + // Reset hidden key + snprintf(cmd, sizeof(cmd), "{\"%s\":\"%s\",\"%s\":\"%s\"}", cmd_str(CMD_password), + hidden_pwd, cmd_str(CMD_key), hidden_pwd); + api_format_send_cmd(cmd_str(CMD_hidden_password), cmd, KEY_STANDARD); + ASSERT_SUCCESS + // Login to hidden wallet + api_format_send_cmd(cmd_str(CMD_name), "", KEY_HIDDEN); + ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); + ASSERT_REPORT_HAS(DEVICE_DEFAULT_NAME); // Change hidden key to standard key from standard wallet -> error collision - // -> delete hidden key - api_format_send_cmd(cmd_str(CMD_hidden_password), tests_pwd, KEY_STANDARD); + // -> deletes hidden key + snprintf(cmd, sizeof(cmd), "{\"%s\":\"%s\",\"%s\":\"%s\"}", cmd_str(CMD_password), + tests_pwd, cmd_str(CMD_key), tests_pwd); + api_format_send_cmd(cmd_str(CMD_hidden_password), cmd, KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_PW_COLLIDE)); // Login to standard wallet api_format_send_cmd(cmd_str(CMD_name), "", KEY_STANDARD); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); ASSERT_REPORT_HAS(DEVICE_DEFAULT_NAME); - // Turn off session key to allow switching wallets - api_format_send_cmd(cmd_str(CMD_session), "off", KEY_STANDARD); - ASSERT_SUCCESS // Login to hidden wallet -> error (hidden key not set) api_format_send_cmd(cmd_str(CMD_name), "", KEY_HIDDEN); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_JSON_PARSE)); - // Reset hidden key - api_format_send_cmd(cmd_str(CMD_hidden_password), hidden_pwd, KEY_STANDARD); - ASSERT_SUCCESS - // Login to standard wallet - api_format_send_cmd(cmd_str(CMD_name), "", KEY_STANDARD); - ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - ASSERT_REPORT_HAS(DEVICE_DEFAULT_NAME); - // Turn off session key to allow switching wallets - api_format_send_cmd(cmd_str(CMD_session), "off", KEY_STANDARD); - ASSERT_SUCCESS - // Login to hidden wallet - api_format_send_cmd(cmd_str(CMD_name), "", KEY_HIDDEN); - ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - ASSERT_REPORT_HAS(DEVICE_DEFAULT_NAME); - // Verify hidden wallet uses different keys - char keypath[] = "m/44'/0'/0'/0/0"; - char xpub0[112], xpub1[112]; - memset(xpub0, 0, sizeof(xpub0)); - memset(xpub1, 0, sizeof(xpub1)); - // Turn off session key to allow switching wallets - api_format_send_cmd(cmd_str(CMD_session), "off", KEY_HIDDEN); - ASSERT_SUCCESS - // Erase backups - api_format_send_cmd(cmd_str(CMD_backup), attr_str(ATTR_erase), KEY_STANDARD); - ASSERT_SUCCESS - // Seed wallets - api_format_send_cmd(cmd_str(CMD_seed), - "{\"source\":\"create\", \"filename\":\"h.pdf\", \"key\":\"password\"}", KEY_STANDARD); + memset(xpub_std, 0, sizeof(xpub_std)); + memset(xpub_hdn, 0, sizeof(xpub_hdn)); + memset(xpub_tst, 0, sizeof(xpub_tst)); + // Reset hidden key + snprintf(cmd, sizeof(cmd), "{\"%s\":\"%s\",\"%s\":\"%s\"}", cmd_str(CMD_password), + hidden_pwd, cmd_str(CMD_key), hidden_pwd); + api_format_send_cmd(cmd_str(CMD_hidden_password), cmd, KEY_STANDARD); ASSERT_SUCCESS // Get standard wallet xpub api_format_send_cmd(cmd_str(CMD_xpub), keypath, KEY_STANDARD); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - memcpy(xpub0, api_read_value(CMD_xpub), sizeof(xpub0)); - u_assert_str_not_eq(xpub0, xpub1); - // Turn off session key to allow switching wallets - api_format_send_cmd(cmd_str(CMD_session), "off", KEY_STANDARD); - ASSERT_SUCCESS + memcpy(xpub_std, api_read_value(CMD_xpub), sizeof(xpub_std)); + u_assert_str_not_eq(xpub_std, xpub_hdn); + u_assert_str_not_eq(xpub_std, xpub_tst); // Get hidden wallet xpub and check that it is different api_format_send_cmd(cmd_str(CMD_xpub), keypath, KEY_HIDDEN); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - memcpy(xpub1, api_read_value(CMD_xpub), sizeof(xpub1)); - u_assert_str_not_eq(xpub0, xpub1); - + memcpy(xpub_hdn, api_read_value(CMD_xpub), sizeof(xpub_hdn)); + u_assert_str_not_eq(xpub_std, xpub_hdn); + u_assert_str_not_eq(xpub_hdn, xpub_tst); // Change key in hidden wallet - api_format_send_cmd(cmd_str(CMD_password), hidden_pwd, KEY_HIDDEN); + char tmp_pw[] = "tmp_pw"; + uint8_t tmp_key[32]; + sha256_Raw((const uint8_t *)tmp_pw, strlens(tmp_pw), tmp_key); + sha256_Raw(tmp_key, MEM_PAGE_LEN, tmp_key); + api_format_send_cmd(cmd_str(CMD_password), tmp_pw, KEY_HIDDEN); ASSERT_SUCCESS // Login to hidden wallet - api_format_send_cmd(cmd_str(CMD_name), "", KEY_HIDDEN); + api_format_send_cmd(cmd_str(CMD_name), "", tmp_key); + ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); + // Get hidden wallet xpub and check that it is same (hidden wallet not reset on change pw) + api_format_send_cmd(cmd_str(CMD_xpub), keypath, tmp_key); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); + memcpy(xpub_tst, api_read_value(CMD_xpub), sizeof(xpub_tst)); + u_assert_str_not_eq(xpub_std, xpub_tst); + u_assert_str_eq(xpub_hdn, xpub_tst); + // Change key in hidden wallet back to original + api_format_send_cmd(cmd_str(CMD_password), hidden_pwd, tmp_key); + ASSERT_SUCCESS // Change hidden key in hidden wallet -> error disabled - api_format_send_cmd(cmd_str(CMD_hidden_password), tests_pwd, KEY_HIDDEN); + snprintf(cmd, sizeof(cmd), "{\"%s\":\"%s\",\"%s\":\"%s\"}", cmd_str(CMD_password), + "junk_pwd", cmd_str(CMD_key), "junk_pwd"); + api_format_send_cmd(cmd_str(CMD_hidden_password), cmd, KEY_HIDDEN); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_LOCKED)); - // Login to hidden wallet - api_format_send_cmd(cmd_str(CMD_name), "", KEY_HIDDEN); + // Get hidden wallet xpub and check that it is same (hidden wallet not reset) + api_format_send_cmd(cmd_str(CMD_xpub), keypath, KEY_HIDDEN); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - // Turn off session key to allow switching wallets - api_format_send_cmd(cmd_str(CMD_session), "off", KEY_HIDDEN); - ASSERT_SUCCESS + memcpy(xpub_tst, api_read_value(CMD_xpub), sizeof(xpub_tst)); + u_assert_str_not_eq(xpub_std, xpub_tst); + u_assert_str_eq(xpub_hdn, xpub_tst); // Login to standard wallet api_format_send_cmd(cmd_str(CMD_name), "", KEY_STANDARD); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - - // - // Test session password - // - - uint8_t sessionkey[MEM_PAGE_LEN]; - uint8_t sessionkey2[MEM_PAGE_LEN]; - char xpub0s[112], xpub1s[112]; - memset(xpub0s, 0, sizeof(xpub0s)); - memset(xpub1s, 0, sizeof(xpub1s)); - // Turn off session key to allow switching wallets - api_format_send_cmd(cmd_str(CMD_session), "off", KEY_STANDARD); - ASSERT_SUCCESS - // Login standard wallet - api_format_send_cmd(cmd_str(CMD_name), "", KEY_STANDARD); - ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - // Send bad session commands - api_format_send_cmd(cmd_str(CMD_session), "", KEY_STANDARD); - ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_INVALID_CMD)); - api_format_send_cmd(cmd_str(CMD_session), "wrong_cmd", KEY_STANDARD); - ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_INVALID_CMD)); - // Set session key - api_format_send_cmd(cmd_str(CMD_session), attr_str(ATTR_set), KEY_STANDARD); - ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - memcpy(sessionkey, utils_hex_to_uint8(api_read_value(CMD_session)), MEM_PAGE_LEN); - // Login standard wallet -> error (session key set) - api_format_send_cmd(cmd_str(CMD_name), "", KEY_STANDARD); - ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_JSON_PARSE)); - // Login standard wallet (with session key) - api_format_send_cmd(cmd_str(CMD_name), "", sessionkey); - ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - // Set new session key - api_format_send_cmd(cmd_str(CMD_session), attr_str(ATTR_set), sessionkey); - ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - memcpy(sessionkey2, utils_hex_to_uint8(api_read_value(CMD_session)), MEM_PAGE_LEN); - u_assert_mem_not_eq(sessionkey, sessionkey2, MEM_PAGE_LEN); - // Login standard wallet (with old session key) -> error - api_format_send_cmd(cmd_str(CMD_name), "", sessionkey); - ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_JSON_PARSE)); - // Update sessionkey - memcpy(sessionkey, sessionkey2, MEM_PAGE_LEN); - // Login standard wallet (with session key) - api_format_send_cmd(cmd_str(CMD_name), "", sessionkey); - ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - // Set hidden key - api_format_send_cmd(cmd_str(CMD_hidden_password), hidden_pwd, sessionkey); - ASSERT_SUCCESS - // Check standard wallet xpub is correct - api_format_send_cmd(cmd_str(CMD_xpub), keypath, sessionkey); - ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - memcpy(xpub0s, api_read_value(CMD_xpub), sizeof(xpub0s)); - u_assert_str_eq(xpub0, xpub0s); - // Turn off session key to allow switching wallets - api_format_send_cmd(cmd_str(CMD_session), "off", sessionkey); + // Use hidden key to seed from backup + // -> puts the hidden wallet into a standard wallet + // -> erases hidden wallet + snprintf(cmd, sizeof(cmd), + "{\"source\":\"backup\",\"filename\":\"h.pdf\",\"key\":\"%s\"}", hidden_pwd); + api_format_send_cmd(cmd_str(CMD_seed), cmd, KEY_STANDARD); ASSERT_SUCCESS - // Login hidden wallet - api_format_send_cmd(cmd_str(CMD_name), "", KEY_HIDDEN); - ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - // Set session key - api_format_send_cmd(cmd_str(CMD_session), attr_str(ATTR_set), KEY_HIDDEN); + // Get standard wallet xpub + // xpubs equal + api_format_send_cmd(cmd_str(CMD_xpub), keypath, KEY_STANDARD); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - memcpy(sessionkey, utils_hex_to_uint8(api_read_value(CMD_session)), MEM_PAGE_LEN); - // Login hidden wallet -> error (session key set) + memcpy(xpub_tst, api_read_value(CMD_xpub), sizeof(xpub_tst)); + u_assert_str_eq(xpub_hdn, xpub_tst); + u_assert_str_not_eq(xpub_std, xpub_tst); + // Error logging in hidden wallet api_format_send_cmd(cmd_str(CMD_name), "", KEY_HIDDEN); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_JSON_PARSE)); - // Login hidden wallet (with session key) - api_format_send_cmd(cmd_str(CMD_name), "", sessionkey); - ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - // Check hidden wallet xpub is correct - api_format_send_cmd(cmd_str(CMD_xpub), keypath, sessionkey); - ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - memcpy(xpub1s, api_read_value(CMD_xpub), sizeof(xpub1s)); - u_assert_str_eq(xpub1, xpub1s); - // Change hidden key in hidden wallet -> error disabled - api_format_send_cmd(cmd_str(CMD_hidden_password), tests_pwd, sessionkey); - ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_LOCKED)); - // Change (hidden) key to standard key from hidden wallet -> error collision - // -> delete hidden key - api_format_send_cmd(cmd_str(CMD_password), tests_pwd, sessionkey); - ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_PW_COLLIDE)); - // Turn off session key to allow switching wallets - api_format_send_cmd(cmd_str(CMD_session), "off", sessionkey); + // Use standard key to seed from backup + // -> get the original standard wallet + snprintf(cmd, sizeof(cmd), + "{\"source\":\"backup\",\"filename\":\"h.pdf\",\"key\":\"%s\"}", tests_pwd); + api_format_send_cmd(cmd_str(CMD_seed), cmd, KEY_STANDARD); ASSERT_SUCCESS - // Login to hidden wallet -> error (hidden key not set) - api_format_send_cmd(cmd_str(CMD_name), "", KEY_HIDDEN); - ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_JSON_PARSE)); - // Login to standard wallet - api_format_send_cmd(cmd_str(CMD_name), "", KEY_STANDARD); + // Get standard wallet xpub + // xpubs equal + api_format_send_cmd(cmd_str(CMD_xpub), keypath, KEY_STANDARD); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - // Turn off session key to avoid conflicts in next unit test - api_format_send_cmd(cmd_str(CMD_session), "off", KEY_STANDARD); - ASSERT_SUCCESS + memcpy(xpub_tst, api_read_value(CMD_xpub), sizeof(xpub_tst)); + u_assert_str_eq(xpub_std, xpub_tst); + u_assert_str_not_eq(xpub_hdn, xpub_tst); } @@ -1829,11 +1811,11 @@ static void tests_echo_tfa(void) { char *echo; char hash_sign[] = - "{\"meta\":\"hash\", \"data\":[{\"keypath\":\"m/\", \"hash\":\"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\"}] }"; + "{\"meta\":\"hash\", \"data\":[{\"keypath\":\"m/44'/0'/0'/1/7\", \"hash\":\"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\"}] }"; char hash_sign2[] = - "{\"meta\":\"hash\", \"data\":[{\"keypath\":\"m/\", \"hash\":\"ffff456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\"}] }"; + "{\"meta\":\"hash\", \"data\":[{\"keypath\":\"m/44'/0'/0'/1/7\", \"hash\":\"ffff456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\"}] }"; char hash_sign3[] = - "{\"meta\":\"hash\", \"data\":[{\"keypath\":\"m/\", \"hash\":\"456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\"}] }"; + "{\"meta\":\"hash\", \"data\":[{\"keypath\":\"m/44'/0'/0'/1/7\", \"hash\":\"456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\"}] }"; api_reset_device(); @@ -1910,7 +1892,7 @@ 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_verification_key()); + echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); u_assert_str_has(echo, cmd_str(CMD_pin)); } @@ -1920,7 +1902,7 @@ 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_verification_key()); + echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); u_assert_str_has(echo, cmd_str(CMD_pin)); } @@ -2149,7 +2131,7 @@ 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_verification_key()); + echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); 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"); @@ -2166,7 +2148,7 @@ 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_verification_key()); + echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); 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"); @@ -2189,7 +2171,7 @@ 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_verification_key()); + echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); u_assert_str_has_not(echo, cmd_str(CMD_recid)); u_assert_str_has(echo, "\"meta\":"); u_assert_str_has(echo, check_1); @@ -2223,7 +2205,7 @@ 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_verification_key()); + echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); 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"); @@ -2241,7 +2223,7 @@ 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_verification_key()); + echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); 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"); @@ -2255,7 +2237,7 @@ 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_verification_key()); + echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); 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"); @@ -2282,7 +2264,7 @@ 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_verification_key()); + echo = api_read_value_decrypt(CMD_echo, memory_report_aeskey(PASSWORD_VERIFY)); 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"); @@ -2325,6 +2307,13 @@ static void tests_sign(void) static void tests_memory_setup(void) { + uint8_t key_00[MEM_PAGE_LEN]; + uint8_t key_FE[MEM_PAGE_LEN]; + uint8_t key_FF[MEM_PAGE_LEN]; + memset(key_00, 0x00, MEM_PAGE_LEN); + memset(key_FE, 0xFE, MEM_PAGE_LEN); + memset(key_FF, 0xFF, MEM_PAGE_LEN); + api_reset_device(); if (!TEST_LIVE_DEVICE && !TEST_U2FAUTH_HIJACK) { @@ -2337,6 +2326,15 @@ static void tests_memory_setup(void) api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); ASSERT_SUCCESS + + api_format_send_cmd(cmd_str(CMD_led), "abort", key_00); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_JSON_PARSE)); + + api_format_send_cmd(cmd_str(CMD_led), "abort", key_FE); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_JSON_PARSE)); + + api_format_send_cmd(cmd_str(CMD_led), "abort", key_FF); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_JSON_PARSE)); }