Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions src/commander.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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 {
Expand All @@ -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(""),
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/flags.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
9 changes: 7 additions & 2 deletions src/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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);
}
Expand Down
5 changes: 3 additions & 2 deletions src/memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@
#include <stdint.h>

#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
Expand Down
138 changes: 137 additions & 1 deletion tests/tests_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down