From addf5cdcfc4789986e23846b0f66430240322206 Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Thu, 29 Nov 2018 16:41:41 +0100 Subject: [PATCH] add feature to give access to legacy hidden wallets --- src/commander.c | 34 +++++++++++- src/flags.h | 1 + src/memory.c | 9 ++- src/memory.h | 5 +- tests/tests_api.c | 138 +++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 179 insertions(+), 8 deletions(-) diff --git a/src/commander.c b/src/commander.c index e4bf1eb3..c6c5525f 100644 --- a/src/commander.c +++ b/src/commander.c @@ -822,6 +822,7 @@ static void commander_process_device(yajl_val json_node) char bootlock[6] = {0}; char u2f_enabled[6] = {0}; char u2f_hijack_enabled[6] = {0}; + char new_hidden_wallet_enabled[6] = {0}; uint32_t serial[4] = {0}; flash_wrapper_read_unique_id(serial, 4); @@ -859,6 +860,14 @@ static void commander_process_device(yajl_val json_node) snprintf(u2f_hijack_enabled, sizeof(u2f_hijack_enabled), "%s", attr_str(ATTR_false)); } + if (ext_flags & MEM_EXT_MASK_NEW_HIDDEN_WALLET) { + // Bit is set == enabled + snprintf(new_hidden_wallet_enabled, sizeof(new_hidden_wallet_enabled), "%s", attr_str(ATTR_true)); + } else { + snprintf(new_hidden_wallet_enabled, sizeof(new_hidden_wallet_enabled), "%s", attr_str(ATTR_false)); + } + + if (sd_card_inserted() == DBB_OK) { snprintf(sdcard, sizeof(sdcard), "%s", attr_str(ATTR_true)); } else { @@ -877,7 +886,7 @@ static void commander_process_device(yajl_val json_node) } snprintf(msg, sizeof(msg), - "{\"%s\":\"%s\",\"%s\":\"%s\",\"%s\":\"%s\",\"%s\":\"%s\",\"%s\":%s,\"%s\":%s,\"%s\":%s,\"%s\":%s,\"%s\":\"%s\",\"%s\":%s,\"%s\":%s}", + "{\"%s\":\"%s\",\"%s\":\"%s\",\"%s\":\"%s\",\"%s\":\"%s\",\"%s\":%s,\"%s\":%s,\"%s\":%s,\"%s\":%s,\"%s\":\"%s\",\"%s\":%s,\"%s\":%s,\"%s\":%s}", attr_str(ATTR_serial), utils_uint8_to_hex((uint8_t *)serial, sizeof(serial)), attr_str(ATTR_version), DIGITAL_BITBOX_VERSION, attr_str(ATTR_name), (char *)memory_name(""), @@ -888,7 +897,8 @@ static void commander_process_device(yajl_val json_node) attr_str(ATTR_sdcard), sdcard, attr_str(ATTR_TFA), tfa, attr_str(ATTR_U2F), u2f_enabled, - attr_str(ATTR_U2F_hijack), u2f_hijack_enabled); + attr_str(ATTR_U2F_hijack), u2f_hijack_enabled, + attr_str(ATTR_new_hidden_wallet), new_hidden_wallet_enabled); free(tfa); commander_fill_report(cmd_str(CMD_device), msg, DBB_JSON_ARRAY); @@ -930,10 +940,12 @@ static void commander_process_feature_set(yajl_val json_node) uint32_t flags = memory_report_ext_flags(); const char *u2f_path[] = { cmd_str(CMD_U2F), NULL }; const char *u2f_hijack_path[] = { cmd_str(CMD_U2F_hijack), NULL }; + const char *new_hidden_wallet_path[] = { attr_str(ATTR_new_hidden_wallet), NULL }; yajl_val u2f = yajl_tree_get(data, u2f_path, yajl_t_any); yajl_val u2f_hijack = yajl_tree_get(data, u2f_hijack_path, yajl_t_any); + yajl_val new_hidden_wallet = yajl_tree_get(data, new_hidden_wallet_path, yajl_t_any); - if (!u2f && !u2f_hijack) { + if (!u2f && !u2f_hijack && !new_hidden_wallet) { goto err; } @@ -959,6 +971,22 @@ static void commander_process_feature_set(yajl_val json_node) } } + // Set the bit == enabled + if (new_hidden_wallet) { + if (wallet_is_locked()) { + commander_fill_report(cmd_str(CMD_feature_set), NULL, DBB_ERR_IO_LOCKED); + return; + } + + if (YAJL_IS_TRUE(new_hidden_wallet)) { + flags |= MEM_EXT_MASK_NEW_HIDDEN_WALLET; + } else if (YAJL_IS_FALSE(new_hidden_wallet)) { + flags &= ~(MEM_EXT_MASK_NEW_HIDDEN_WALLET); + } else { + goto err; + } + } + memory_write_ext_flags(flags); commander_fill_report(cmd_str(CMD_feature_set), attr_str(ATTR_success), DBB_OK); return; diff --git a/src/flags.h b/src/flags.h index b3c5cd26..1736483d 100644 --- a/src/flags.h +++ b/src/flags.h @@ -150,6 +150,7 @@ X(U2F) \ X(U2F_load) \ X(U2F_create) \ X(U2F_hijack) \ +X(new_hidden_wallet) \ X(__ERASE__) \ X(__FORCE__) \ X(NUM) /* keep last */ diff --git a/src/memory.c b/src/memory.c index 5ef7ab63..8d655a55 100644 --- a/src/memory.c +++ b/src/memory.c @@ -566,7 +566,10 @@ uint8_t *memory_hidden_hww(const uint8_t *master) { memory_eeprom_crypt(NULL, MEM_hidden_hww, MEM_HIDDEN_BIP32_ADDR_IDX, MEM_memory_map_version); - if ((master == NULL) && MEMEQ(MEM_hidden_hww, MEM_PAGE_ERASE, 32)) { + + uint32_t ext_flags = memory_report_ext_flags(); + uint8_t legacy = !(ext_flags & MEM_EXT_MASK_NEW_HIDDEN_WALLET); + if ((master == NULL) && (legacy || MEMEQ(MEM_hidden_hww, MEM_PAGE_ERASE, 32))) { // Backward compatible with firmware <=2.2.3 return memory_master_hww_chaincode(NULL); } @@ -580,7 +583,9 @@ uint8_t *memory_hidden_hww_chaincode(const uint8_t *chain) { memory_eeprom_crypt(NULL, MEM_hidden_hww_chain, MEM_HIDDEN_BIP32_CHAIN_ADDR_IDX, MEM_memory_map_version); - if ((chain == NULL) && MEMEQ(MEM_hidden_hww_chain, MEM_PAGE_ERASE, 32)) { + uint32_t ext_flags = memory_report_ext_flags(); + uint8_t legacy = !(ext_flags & MEM_EXT_MASK_NEW_HIDDEN_WALLET); + if ((chain == NULL) && (legacy || MEMEQ(MEM_hidden_hww_chain, MEM_PAGE_ERASE, 32))) { // Backward compatible with firmware <=2.2.3 return memory_master_hww(NULL); } diff --git a/src/memory.h b/src/memory.h index c54dd402..db7515df 100644 --- a/src/memory.h +++ b/src/memory.h @@ -31,8 +31,9 @@ #include #define MEM_PAGE_LEN 32 -#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 +#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 +#define MEM_EXT_MASK_NEW_HIDDEN_WALLET 0x00000004 // Mask of bit to enable (1) or disable (0) the new hidden wallet derivation // Default settings #define MEM_DEFAULT_unlocked 0xFF #define MEM_DEFAULT_erased 0xFF diff --git a/tests/tests_api.c b/tests/tests_api.c index d654bde8..b0232b81 100644 --- a/tests/tests_api.c +++ b/tests/tests_api.c @@ -496,6 +496,141 @@ static void tests_name(void) u_assert_str_eq(name1, api_read_value(CMD_name)); } +static void tests_legacy_hidden_wallet(void) +{ + api_reset_device(); + api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); + ASSERT_SUCCESS; + + api_format_send_cmd(cmd_str(CMD_device), attr_str(ATTR_info), KEY_STANDARD); + ASSERT_REPORT_HAS("\"new_hidden_wallet\":true"); + + // Disable new hidden wallet + api_format_send_cmd(cmd_str(CMD_feature_set), "{\"new_hidden_wallet\":false}", KEY_STANDARD); + ASSERT_SUCCESS; + api_format_send_cmd(cmd_str(CMD_device), attr_str(ATTR_info), KEY_STANDARD); + ASSERT_REPORT_HAS("\"U2F\":true"); + ASSERT_REPORT_HAS("\"U2F_hijack\":true"); + ASSERT_REPORT_HAS("\"new_hidden_wallet\":false"); + + // Enable new hidden wallet + api_format_send_cmd(cmd_str(CMD_feature_set), "{\"new_hidden_wallet\":true}", KEY_STANDARD); + ASSERT_SUCCESS; + api_format_send_cmd(cmd_str(CMD_device), attr_str(ATTR_info), KEY_STANDARD); + ASSERT_REPORT_HAS("\"new_hidden_wallet\":true"); + + char set_hidden_wallet_cmd_1[512]; + snprintf(set_hidden_wallet_cmd_1, sizeof(set_hidden_wallet_cmd_1), "{\"%s\":\"%s\",\"%s\":\"%s\"}", cmd_str(CMD_password), + hidden_pwd, cmd_str(CMD_key), "key1"); + char set_hidden_wallet_cmd_2[512]; + snprintf(set_hidden_wallet_cmd_2, sizeof(set_hidden_wallet_cmd_2), "{\"%s\":\"%s\",\"%s\":\"%s\"}", cmd_str(CMD_password), + hidden_pwd, cmd_str(CMD_key), "key2"); + char keypath[] = "m/44'/0'/0'/0/0"; + + if (!TEST_LIVE_DEVICE) { + // copy test sd_files to sd card directory + int ret = system("cp ../tests/sd_files/*.pdf tests/digitalbitbox/"); + u_assert(ret == 0); + + // seed from backup file + char seed[512]; + snprintf(seed, sizeof(seed), "{\"source\":\"%s\", \"filename\":\"%s\", \"key\":\"key\"}", + attr_str(ATTR_backup), "test_backup_hww.pdf"); + api_format_send_cmd(cmd_str(CMD_seed), seed, KEY_STANDARD); + ASSERT_SUCCESS; + } else { + api_format_send_cmd(cmd_str(CMD_seed), + "{\"source\":\"create\", \"filename\":\"legacy_hidden_wallet_test.pdf\", \"key\":\"key\"}", + KEY_STANDARD); + ASSERT_SUCCESS; + } + + // clean up sd card + api_format_send_cmd(cmd_str(CMD_backup), attr_str(ATTR_erase), KEY_STANDARD); + ASSERT_SUCCESS; + + + api_format_send_cmd(cmd_str(CMD_hidden_password), set_hidden_wallet_cmd_1, KEY_STANDARD); + ASSERT_SUCCESS; + + // can't modify new_hidden_wallet when the device is locked + api_format_send_cmd(cmd_str(CMD_feature_set), "{\"new_hidden_wallet\":true}", KEY_HIDDEN); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_LOCKED)); + api_format_send_cmd(cmd_str(CMD_feature_set), "{\"new_hidden_wallet\":false}", KEY_HIDDEN); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_LOCKED)); + api_format_send_cmd(cmd_str(CMD_device), attr_str(ATTR_info), KEY_STANDARD); + ASSERT_REPORT_HAS("\"new_hidden_wallet\":true"); + + api_format_send_cmd(cmd_str(CMD_xpub), keypath, KEY_STANDARD); + ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); + char xpub_main[112]; + memcpy(xpub_main, api_read_value(CMD_xpub), sizeof(xpub_main)); + + api_format_send_cmd(cmd_str(CMD_xpub), keypath, KEY_HIDDEN); + ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); + char xpub_hidden[112]; + memcpy(xpub_hidden, api_read_value(CMD_xpub), sizeof(xpub_hidden)); + + // Disable new hidden wallet (activate legacy) + api_format_send_cmd(cmd_str(CMD_feature_set), "{\"new_hidden_wallet\":false}", KEY_STANDARD); + ASSERT_SUCCESS; + + api_format_send_cmd(cmd_str(CMD_xpub), keypath, KEY_HIDDEN); + ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); + char xpub_hidden_legacy[112]; + memcpy(xpub_hidden_legacy, api_read_value(CMD_xpub), sizeof(xpub_hidden_legacy)); + + // Re-enable new hidden wallet, check that it produces the same as before. + api_format_send_cmd(cmd_str(CMD_feature_set), "{\"new_hidden_wallet\":true}", KEY_STANDARD); + ASSERT_SUCCESS; + + api_format_send_cmd(cmd_str(CMD_xpub), keypath, KEY_HIDDEN); + ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); + char xpub_hidden_2[112]; + memcpy(xpub_hidden_2, api_read_value(CMD_xpub), sizeof(xpub_hidden_2)); + u_assert_str_eq(xpub_hidden_2, xpub_hidden); + + if (!TEST_LIVE_DEVICE) { + u_assert_str_eq(xpub_main, "xpub6GddvKfUWU9VV4mbUCsz6C97rzvL7kdEfKsR7akEhM964mceXkXnv9FkCxnELYkc3rKybg4fyrqzE5GpUw1j45a3tejwCsFCs3c4oFiRgn9"); + u_assert_str_eq(xpub_hidden, "xpub6G8MNx3mFXKh6i6rf1JpW7Aqu5aoexLq6yw3sUCZwukDi2Ghzg7PE1n4tQPTeZS2j9cm28HFHYBu4D1iqCwBN1Jt5t4cCP5GWgbzhBAMYXB"); + u_assert_str_eq(xpub_hidden_legacy, "xpub6FhGYqMLDAHdheptxhErzFd2F13h16AE6fWTMd2voqijUU2TWjjKnKdiCucgDnh18R46Jkrq5i6rHrjXGds87CU6y69NzKFBDqN3cUPDXzg"); + } else { + u_assert_str_not_eq(xpub_main, xpub_hidden); + u_assert_str_not_eq(xpub_main, xpub_hidden_legacy); + u_assert_str_not_eq(xpub_hidden, xpub_hidden_legacy); + } + + { + // when legacy mode is enabled, hww reset and setting a hidden wallet still needs to work. + // do this by setting a new hidden wallet while in legacy mode and checking. + api_format_send_cmd(cmd_str(CMD_hidden_password), set_hidden_wallet_cmd_1, KEY_STANDARD); + ASSERT_SUCCESS; + + api_format_send_cmd(cmd_str(CMD_xpub), keypath, KEY_HIDDEN); + ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); + char xpub[112]; + memcpy(xpub, api_read_value(CMD_xpub), sizeof(xpub)); + api_format_send_cmd(cmd_str(CMD_feature_set), "{\"new_hidden_wallet\":false}", KEY_STANDARD); + ASSERT_SUCCESS; + + api_format_send_cmd(cmd_str(CMD_hidden_password), set_hidden_wallet_cmd_2, KEY_STANDARD); + ASSERT_SUCCESS; + api_format_send_cmd(cmd_str(CMD_feature_set), "{\"new_hidden_wallet\":true}", KEY_STANDARD); + ASSERT_SUCCESS; + api_format_send_cmd(cmd_str(CMD_xpub), keypath, KEY_HIDDEN); + ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); + u_assert_str_not_eq(api_read_value(CMD_xpub), xpub); + + // Check that the legacy hidden wallet has not changed despite the new + // hidden wallet having been changed. + api_format_send_cmd(cmd_str(CMD_feature_set), "{\"new_hidden_wallet\":false}", KEY_STANDARD); + ASSERT_SUCCESS; + api_format_send_cmd(cmd_str(CMD_xpub), keypath, KEY_HIDDEN); + ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); + u_assert_str_eq(api_read_value(CMD_xpub), xpub_hidden_legacy); + + } +} static void tests_u2f(void) { @@ -527,7 +662,7 @@ static void tests_u2f(void) api_format_send_cmd(cmd_str(CMD_seed), "{\"source\":\"create\", \"filename\":\"u2f_test_0.pdf\", \"key\":\"password\"}", KEY_STANDARD); - ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); + ASSERT_SUCCESS; // U2F command runs api_hid_send_frame(&f); @@ -2648,6 +2783,7 @@ static void run_utests(void) { u_run_test(tests_memory_setup);// Keep first u_run_test(tests_name); + u_run_test(tests_legacy_hidden_wallet); u_run_test(tests_u2f); u_run_test(tests_echo_tfa); u_run_test(tests_password);