From 907b12547ca309bd8a644399091acf69aeb0dac4 Mon Sep 17 00:00:00 2001 From: djb Date: Mon, 11 Feb 2019 00:20:35 +0100 Subject: [PATCH] xpub API: give warning and do not echo if non-whitelisted keypath; require reset before hww seed creation or recovery from backup --- src/commander.c | 59 +++--- src/flags.h | 2 + src/flash.c | 2 +- src/sd.c | 2 +- src/sd.h | 2 +- src/simulator.c | 12 +- src/wallet.c | 90 +++++++-- src/wallet.h | 19 +- tests/tests_api.c | 368 +++++++++++++++++++++++++++++++++---- tests/tests_u2f_standard.c | 6 +- 10 files changed, 480 insertions(+), 82 deletions(-) diff --git a/src/commander.c b/src/commander.c index 5d533739..70a21513 100644 --- a/src/commander.c +++ b/src/commander.c @@ -317,8 +317,10 @@ static int commander_process_backup_check(const char *key, const char *filename, if (wallet_generate_node(key, seed, &node) == DBB_ERROR) { ret = DBB_ERROR; } else { - uint8_t main_ok = MEMEQ(node.private_key, memory_master_hww(NULL), MEM_PAGE_LEN) && MEMEQ(node.chain_code, memory_master_hww_chaincode(NULL), MEM_PAGE_LEN); - uint8_t hidden_ok = MEMEQ(node.private_key, memory_hidden_hww(NULL), MEM_PAGE_LEN) && MEMEQ(node.chain_code, memory_hidden_hww_chaincode(NULL), MEM_PAGE_LEN); + uint8_t main_ok = MEMEQ(node.private_key, memory_master_hww(NULL), MEM_PAGE_LEN) && + MEMEQ(node.chain_code, memory_master_hww_chaincode(NULL), MEM_PAGE_LEN); + uint8_t hidden_ok = MEMEQ(node.private_key, memory_hidden_hww(NULL), MEM_PAGE_LEN) && + MEMEQ(node.chain_code, memory_hidden_hww_chaincode(NULL), MEM_PAGE_LEN); ret = (main_ok | hidden_ok) ? DBB_OK : DBB_ERROR; // bitwise for constant time } utils_zero(seed, sizeof(seed)); @@ -512,6 +514,13 @@ static void commander_process_seed(yajl_val json_node) return; } + if (wallet_seeded() == DBB_OK && + !STREQ(source, attr_str(ATTR_U2F_load)) && + !STREQ(source, attr_str(ATTR_U2F_create))) { + commander_fill_report(cmd_str(CMD_seed), NULL, DBB_ERR_SEED_SEEDED); + return; + } + if (STREQ(source, attr_str(ATTR_create))) { // Generate a new wallet, optionally with entropy entered via USB uint8_t i, add_entropy = 1, entropy_b[MEM_PAGE_LEN]; @@ -757,21 +766,24 @@ static void commander_process_xpub(yajl_val json_node) return; } - wallet_report_xpub(value, xpub); + int ret = wallet_report_xpub(value, xpub); if (xpub[0]) { commander_fill_report(cmd_str(CMD_xpub), xpub, DBB_OK); - int encrypt_len; - char *encoded_report = cipher_aes_b64_hmac_encrypt((unsigned char *) xpub, strlens(xpub), - &encrypt_len, memory_report_aeskey(TFA_SHARED_SECRET)); - - if (encoded_report) { - commander_fill_report(cmd_str(CMD_echo), encoded_report, DBB_OK); - free(encoded_report); - } else { - commander_clear_report(); - commander_fill_report(cmd_str(CMD_xpub), NULL, DBB_ERR_MEM_ENCRYPT); + if (ret == DBB_WARN_KEYPATH) { + commander_fill_report(cmd_str(CMD_warning), flag_msg(DBB_WARN_KEYPATH), DBB_OK); + } else if (ret == DBB_OK) { + int encrypt_len; + char *encoded_report = cipher_aes_b64_hmac_encrypt((unsigned char *) xpub, strlens(xpub), + &encrypt_len, memory_report_aeskey(TFA_SHARED_SECRET)); + if (encoded_report) { + commander_fill_report(cmd_str(CMD_echo), encoded_report, DBB_OK); + free(encoded_report); + } else { + commander_clear_report(); + commander_fill_report(cmd_str(CMD_xpub), NULL, DBB_ERR_MEM_ENCRYPT); + } } } else { commander_fill_report(cmd_str(CMD_xpub), NULL, DBB_ERR_KEY_CHILD); @@ -864,9 +876,11 @@ static void commander_process_device(yajl_val json_node) 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)); + 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)); + snprintf(new_hidden_wallet_enabled, sizeof(new_hidden_wallet_enabled), "%s", + attr_str(ATTR_false)); } if (wallet_is_paired()) { @@ -1258,16 +1272,16 @@ static int commander_tfa_check_pin(yajl_val json_node) return DBB_OK; } -static uint8_t check_disable_pairing(const char* keypath) +static uint8_t check_disable_pairing(const char *keypath) { if (keypath == NULL) { return 0; } - const char* eth_keypath = "m/44'/60'/"; - const char* etc_keypath = "m/44'/61'/"; + const char *eth_keypath = "m/44'/60'/"; + const char *etc_keypath = "m/44'/61'/"; // check if keypath starts with eth_keypath or etc_keypath. return !strncmp(keypath, eth_keypath, strlen(eth_keypath)) || - !strncmp(keypath, etc_keypath, strlen(etc_keypath)); + !strncmp(keypath, etc_keypath, strlen(etc_keypath)); } static int commander_echo_command(yajl_val json_node) @@ -1353,7 +1367,8 @@ static int commander_echo_command(yajl_val json_node) uint8_t disable_pairing = 1; for (size_t i = 0; i < data->u.array.len; i++) { - const char *keypath = YAJL_GET_STRING(yajl_tree_get(data->u.array.values[i], keypath_path, yajl_t_string)); + const char *keypath = YAJL_GET_STRING(yajl_tree_get(data->u.array.values[i], keypath_path, + yajl_t_string)); if (!check_disable_pairing(keypath)) { disable_pairing = 0; break; @@ -1366,7 +1381,7 @@ static int commander_echo_command(yajl_val json_node) } int length; char *encoded_report = cipher_aes_b64_hmac_encrypt((unsigned char *) json_report, - strlens(json_report), &length, memory_report_aeskey(TFA_SHARED_SECRET)); + strlens(json_report), &length, memory_report_aeskey(TFA_SHARED_SECRET)); commander_clear_report(); if (encoded_report) { commander_fill_report(cmd_str(CMD_echo), encoded_report, DBB_OK); @@ -1651,7 +1666,7 @@ static int commander_check_init(const char *encrypted_command) } char device_info[100]; snprintf(device_info, sizeof(device_info), - "{\"%s\":\"%s\"}", attr_str(ATTR_version), DIGITAL_BITBOX_VERSION); + "{\"%s\":\"%s\"}", attr_str(ATTR_version), DIGITAL_BITBOX_VERSION); commander_fill_report(cmd_str(CMD_device), device_info, DBB_JSON_OBJECT); yajl_tree_free(json_node); return DBB_ERROR; diff --git a/src/flags.h b/src/flags.h index a76a32fa..29072025 100644 --- a/src/flags.h +++ b/src/flags.h @@ -205,6 +205,7 @@ X(ERR_SEED_SD, 200, "Seed creation requires an SD card for automatic enc X(ERR_SEED_SD_NUM, 201, "Too many backup files. Please remove one from the SD card.")\ X(ERR_SEED_MEM, 202, "Could not allocate memory for seed.")\ X(ERR_SEED_INVALID, 204, "Invalid seed.")\ +X(ERR_SEED_SEEDED, 205, "Wallet is seeded. Reset to continue.")\ X(ERR_KEY_MASTER, 250, "Master key not present.")\ X(ERR_KEY_CHILD, 251, "Could not generate key.")\ X(ERR_KEY_ECDH, 252, "Could not generate ECDH secret.")\ @@ -240,6 +241,7 @@ X(WARN_RESET, 900, "attempts remain before the device is reset.")\ X(WARN_NO_MCU, 901, "Ignored for non-embedded testing.")\ X(WARN_SD_NUM_FILES, 902, "Too many backup files to read. The list is truncated.")\ X(WARN_RESET_TOUCH, 903, "attempts remain before the device is reset. The next login requires holding the touch button.")\ +X(WARN_KEYPATH, 904, "Non-standard keypath.")\ X(FLAG_NUM, 0, 0)/* keep last */ diff --git a/src/flash.c b/src/flash.c index 1e20bb65..7dbea926 100644 --- a/src/flash.c +++ b/src/flash.c @@ -131,7 +131,7 @@ uint32_t flash_wrapper_erase_page(uint32_t ul_address, uint8_t uc_page_num) uint32_t flash_wrapper_write(uint32_t ul_address, void *p_buffer, - uint32_t ul_size, uint32_t ul_erase_flag) + uint32_t ul_size, uint32_t ul_erase_flag) { #ifdef TESTING uint32_t i; diff --git a/src/sd.c b/src/sd.c index 779fcf47..a196c3fc 100644 --- a/src/sd.c +++ b/src/sd.c @@ -54,7 +54,7 @@ static char ROOTDIR[] = "tests/digitalbitbox";// If change, update tests/CMakeLi #else static char ROOTDIR[256]; // 255 char path -void set_root_dir(const char* path) +void set_root_dir(const char *path) { memset(ROOTDIR, 0, sizeof(ROOTDIR)); snprintf(ROOTDIR, sizeof(ROOTDIR), "%s", path); diff --git a/src/sd.h b/src/sd.h index bd90d0df..58e8040a 100644 --- a/src/sd.h +++ b/src/sd.h @@ -69,7 +69,7 @@ char *sd_load(const char *fn, int cmd); uint8_t sd_write(const char *fn, const char *wallet_backup, const char *wallet_name, const char *u2f_backup, uint8_t replace, int cmd); #ifdef SIMULATOR -void set_root_dir(const char* path); +void set_root_dir(const char *path); #endif diff --git a/src/simulator.c b/src/simulator.c index 0e8cde97..d34963f7 100644 --- a/src/simulator.c +++ b/src/simulator.c @@ -12,7 +12,7 @@ #define PORT 35345 #define BUF_SIZE COMMANDER_REPORT_SIZE -int main(int argc, char* argv[]) +int main(int argc, char *argv[]) { // Get and set the root directory if (argc < 2) { @@ -47,7 +47,7 @@ int main(int argc, char* argv[]) sock.sin_port = htons(PORT); // Bind socket - if (bind(s, (struct sockaddr*)&sock, sizeof(sock)) == -1) { + if (bind(s, (struct sockaddr *)&sock, sizeof(sock)) == -1) { fprintf(stderr, "Could not bind socket\n"); exit(1); } @@ -55,7 +55,7 @@ int main(int argc, char* argv[]) // Wait for connections and handle char buf[BUF_SIZE]; - char* result; + char *result; struct sockaddr_in in_sock; int recv_len; socklen_t in_sock_len = sizeof(in_sock); @@ -63,7 +63,8 @@ int main(int argc, char* argv[]) int rc; // Receive memset(buf, 0, BUF_SIZE); - if ((recv_len = recvfrom(s, buf, BUF_SIZE, 0, (struct sockaddr*)&in_sock, &in_sock_len)) < 0) { + if ((recv_len = recvfrom(s, buf, BUF_SIZE, 0, (struct sockaddr *)&in_sock, + &in_sock_len)) < 0) { fprintf(stderr, "Failed to receive udp data, error %d\n", recv_len); exit(1); } @@ -72,7 +73,8 @@ int main(int argc, char* argv[]) result = commander(buf); // Send result to socket - if ((rc = sendto(s, result, strlen(result), 0, (struct sockaddr*)&in_sock, in_sock_len)) < 0) { + if ((rc = sendto(s, result, strlen(result), 0, (struct sockaddr *)&in_sock, + in_sock_len)) < 0) { fprintf(stderr, "Failed to send udp data, error %d\n", rc); exit(1); } diff --git a/src/wallet.c b/src/wallet.c index cb7a2123..ca3959d0 100644 --- a/src/wallet.c +++ b/src/wallet.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "commander.h" @@ -41,6 +42,15 @@ #include "ecc.h" +typedef enum BIP44_LEVELS { + BIP44_LEVEL_PURPOSE, + BIP44_LEVEL_COIN_TYPE, + BIP44_LEVEL_ACCOUNT, + BIP44_LEVEL_CHANGE, + BIP44_LEVEL_ADDRESS, +} BIP44_LEVELS; + + 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]; @@ -139,6 +149,11 @@ int wallet_create(const char *passphrase, const char *entropy_in) } +/* + Returns DBB_OK if successful and keypath is whitelisted + Returns DBB_WARN_KEYPATH if successful but keypath is not whitelisted + Returns DBB_ERROR if could not generate a key +*/ int wallet_generate_key(HDNode *node, const char *keypath, const uint8_t *privkeymaster, const uint8_t *chaincode) { @@ -171,23 +186,24 @@ int wallet_generate_key(HDNode *node, const char *keypath, const uint8_t *privke if (pch == NULL) { goto err; } - int has_prm = 0; + uint8_t path_level = 0; + bool has_prime = false, whitelisted_keypath = true; while (pch != NULL) { size_t i = 0; - int prm = 0; + bool is_prime = false; size_t pch_len = strlens(pch); for ( ; i < pch_len; i++) { if (strchr(prime, pch[i])) { if (i != pch_len - 1) { goto err; } - prm = 1; - has_prm = 1; + is_prime = true; + has_prime = true; } else if (!strchr(digits, pch[i])) { goto err; } } - if (prm && pch_len == 1) { + if (is_prime && pch_len == 1) { goto err; } idx = strtoull(pch, NULL, 10); @@ -195,21 +211,69 @@ int wallet_generate_key(HDNode *node, const char *keypath, const uint8_t *privke goto err; } - if (prm) { + if (is_prime) { if (hdnode_private_ckd_prime(node, idx) != DBB_OK) { goto err; } } else { + // Note: if `idx` >= 0x80000000, prime derivation still occurs + // even if the `keypath` does not have a `prime` marker, following + // the BIP32 standard. if (hdnode_private_ckd(node, idx) != DBB_OK) { goto err; } } + + // Check if the keypath is whitelisted + if (whitelisted_keypath) { + switch (path_level) { + case (BIP44_LEVEL_PURPOSE): + if (is_prime != BIP44_PURPOSE_HARDENED || ( + idx != BIP44_PURPOSE_P2PKH && + idx != BIP44_PURPOSE_P2WPKH_P2SH && + idx != BIP44_PURPOSE_P2WPKH + )) { + whitelisted_keypath = false; + } + break; + case (BIP44_LEVEL_COIN_TYPE): + if (is_prime != BIP44_COIN_TYPE_HARDENED || ( + idx != BIP44_COIN_TYPE_BTC && + idx != BIP44_COIN_TYPE_LTC && + idx != BIP44_COIN_TYPE_TESTNET + )) { + whitelisted_keypath = false; + } + break; + case (BIP44_LEVEL_ACCOUNT): + if (is_prime != BIP44_ACCOUNT_HARDENED || idx > BIP44_ACCOUNT_MAX) { + whitelisted_keypath = false; + } + break; + case (BIP44_LEVEL_CHANGE): + if (is_prime != BIP44_CHANGE_HARDENED || idx > BIP44_CHANGE_MAX) { + whitelisted_keypath = false; + } + break; + case (BIP44_LEVEL_ADDRESS): + if (is_prime != BIP44_ADDRESS_HARDENED || idx > BIP44_ADDRESS_MAX) { + whitelisted_keypath = false; + } + break; + default: + whitelisted_keypath = false; + } + } pch = strtok(NULL, delim); + path_level++; } - if (!has_prm) { + if (!has_prime) { goto err; } free(kp); + if (!whitelisted_keypath) { + return DBB_WARN_KEYPATH; + } return DBB_OK; err: @@ -244,16 +308,18 @@ int wallet_generate_node(const char *passphrase, const char *entropy, HDNode *no } -void wallet_report_xpub(const char *keypath, char *xpub) +int wallet_report_xpub(const char *keypath, char *xpub) { HDNode node; + int ret = DBB_ERROR; if (wallet_seeded() == DBB_OK) { - if (wallet_generate_key(&node, keypath, wallet_get_master(), - wallet_get_chaincode()) == DBB_OK) { + ret = wallet_generate_key(&node, keypath, wallet_get_master(), wallet_get_chaincode()); + if (ret != DBB_ERROR) { hdnode_serialize_public(&node, xpub, 112); } } utils_zero(&node, sizeof(HDNode)); + return ret; } @@ -288,7 +354,7 @@ int wallet_check_pubkey(const char *pubkey, const char *keypath) } if (wallet_generate_key(&node, keypath, wallet_get_master(), - wallet_get_chaincode()) != DBB_OK) { + wallet_get_chaincode()) == DBB_ERROR) { commander_clear_report(); commander_fill_report(cmd_str(CMD_checkpub), NULL, DBB_ERR_KEY_CHILD); goto err; @@ -329,7 +395,7 @@ int wallet_sign(const char *message, const char *keypath) } if (wallet_generate_key(&node, keypath, wallet_get_master(), - wallet_get_chaincode()) != DBB_OK) { + wallet_get_chaincode()) == DBB_ERROR) { commander_clear_report(); commander_fill_report(cmd_str(CMD_sign), NULL, DBB_ERR_KEY_CHILD); goto err; diff --git a/src/wallet.h b/src/wallet.h index bde4a455..5bf74f6a 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -33,6 +33,23 @@ #include "bip32.h" +// BIP44 keypath whitelist for the xpub command +// m / purpose' / coin_type' / account' / change / address_index +#define BIP44_PURPOSE_HARDENED true +#define BIP44_PURPOSE_P2PKH 44 // BIP44 legacy +#define BIP44_PURPOSE_P2WPKH_P2SH 49 // BIP49 segwit nested in pay to script hash +#define BIP44_PURPOSE_P2WPKH 84 // BIP84 native segwit +#define BIP44_COIN_TYPE_HARDENED true +#define BIP44_COIN_TYPE_BTC 0 +#define BIP44_COIN_TYPE_TESTNET 1 +#define BIP44_COIN_TYPE_LTC 2 +#define BIP44_ACCOUNT_HARDENED true +#define BIP44_ACCOUNT_MAX 9// 10 accounts (0:9) +#define BIP44_CHANGE_HARDENED false +#define BIP44_CHANGE_MAX 0// A client is not expected to request an xpub on a change path +#define BIP44_ADDRESS_HARDENED false +#define BIP44_ADDRESS_MAX 999999// 1M accounts (0:999999) + /* BIP32 */ void wallet_set_hidden(int hide); int wallet_is_hidden(void); @@ -46,7 +63,7 @@ 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); +int wallet_report_xpub(const char *keypath, char *xpub); void wallet_report_id(char *id); int wallet_generate_key(HDNode *node, const char *keypath, const uint8_t *privkeymaster, const uint8_t *chaincode); diff --git a/tests/tests_api.c b/tests/tests_api.c index 2aa66908..f1b98758 100644 --- a/tests/tests_api.c +++ b/tests/tests_api.c @@ -39,6 +39,7 @@ #include "flags.h" #include "random.h" #include "cipher.h" +#include "wallet.h" #include "commander.h" #include "yajl/src/api/yajl_tree.h" #include "secp256k1/include/secp256k1.h" @@ -214,24 +215,39 @@ static void tests_seed_xpub_backup(void) api_reset_device(); - api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); ASSERT_SUCCESS; snprintf(erase_cmd, sizeof(erase_cmd), "{\"erase\":\"%s\"}", "seed_create.pdf"); api_format_send_cmd(cmd_str(CMD_backup), erase_cmd, KEY_STANDARD); + api_format_send_cmd(cmd_str(CMD_seed), seed_create_bad, KEY_STANDARD); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_SD_BAD_CHAR)); + + api_format_send_cmd(cmd_str(CMD_backup), attr_str(ATTR_list), KEY_STANDARD); + ASSERT_REPORT_HAS_NOT("../seed_create_bad.pdf"); + api_format_send_cmd(cmd_str(CMD_seed), seed_create, KEY_STANDARD); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); api_format_send_cmd(cmd_str(CMD_backup), attr_str(ATTR_list), KEY_STANDARD); ASSERT_REPORT_HAS("seed_create.pdf"); - api_format_send_cmd(cmd_str(CMD_seed), seed_create_bad, KEY_STANDARD); - ASSERT_REPORT_HAS(flag_msg(DBB_ERR_SD_BAD_CHAR)); - - api_format_send_cmd(cmd_str(CMD_backup), attr_str(ATTR_list), KEY_STANDARD); - ASSERT_REPORT_HAS_NOT("../seed_create_bad.pdf"); + // cleanup + snprintf(erase_cmd, sizeof(erase_cmd), "{\"erase\":\"%s\"}", "seed_create.pdf"); + api_format_send_cmd(cmd_str(CMD_backup), erase_cmd, KEY_STANDARD); + snprintf(erase_cmd, sizeof(erase_cmd), "{\"erase\":\"%s\"}", "seed_create_2.pdf"); + api_format_send_cmd(cmd_str(CMD_backup), erase_cmd, KEY_STANDARD); + snprintf(erase_cmd, sizeof(erase_cmd), "{\"erase\":\"%s\"}", "tests_backup.pdf"); + api_format_send_cmd(cmd_str(CMD_backup), erase_cmd, KEY_STANDARD); + snprintf(erase_cmd, sizeof(erase_cmd), "{\"erase\":\"%s\"}", "tests_backup2.pdf"); + api_format_send_cmd(cmd_str(CMD_backup), erase_cmd, KEY_STANDARD); + snprintf(erase_cmd, sizeof(erase_cmd), "{\"erase\":\"%s\"}", "tests_backup_hww.pdf"); + api_format_send_cmd(cmd_str(CMD_backup), erase_cmd, KEY_STANDARD); + snprintf(erase_cmd, sizeof(erase_cmd), "{\"erase\":\"%s\"}", "tests_backup_u2f.pdf"); + api_format_send_cmd(cmd_str(CMD_backup), erase_cmd, KEY_STANDARD); + snprintf(erase_cmd, sizeof(erase_cmd), "{\"erase\":\"%s\"}", "tests_backup_v2.2.3.pdf"); + api_format_send_cmd(cmd_str(CMD_backup), erase_cmd, KEY_STANDARD); // test sd list overflow char long_backup_name[SD_FILEBUF_LEN_MAX / 8]; @@ -276,48 +292,248 @@ static void tests_seed_xpub_backup(void) // test keypath api_format_send_cmd(cmd_str(CMD_xpub), "m/111'", KEY_STANDARD); ASSERT_REPORT_HAS("\"xpub\":"); + ASSERT_REPORT_HAS(flag_msg(DBB_WARN_KEYPATH)); + api_format_send_cmd(cmd_str(CMD_xpub), "m/1/2'/3/4", KEY_STANDARD); ASSERT_REPORT_HAS("\"xpub\":"); + ASSERT_REPORT_HAS(flag_msg(DBB_WARN_KEYPATH)); api_format_send_cmd(cmd_str(CMD_xpub), "m/1/2/3", KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_CHILD)); + ASSERT_REPORT_HAS_NOT(flag_msg(DBB_WARN_KEYPATH)); api_format_send_cmd(cmd_str(CMD_xpub), "111", KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_CHILD)); + ASSERT_REPORT_HAS_NOT(flag_msg(DBB_WARN_KEYPATH)); api_format_send_cmd(cmd_str(CMD_xpub), "/111", KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_CHILD)); + ASSERT_REPORT_HAS_NOT(flag_msg(DBB_WARN_KEYPATH)); api_format_send_cmd(cmd_str(CMD_xpub), "m", KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_CHILD)); + ASSERT_REPORT_HAS_NOT(flag_msg(DBB_WARN_KEYPATH)); api_format_send_cmd(cmd_str(CMD_xpub), "m111", KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_CHILD)); + ASSERT_REPORT_HAS_NOT(flag_msg(DBB_WARN_KEYPATH)); api_format_send_cmd(cmd_str(CMD_xpub), "m/a", KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_CHILD)); + ASSERT_REPORT_HAS_NOT(flag_msg(DBB_WARN_KEYPATH)); api_format_send_cmd(cmd_str(CMD_xpub), "m/!", KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_CHILD)); + ASSERT_REPORT_HAS_NOT(flag_msg(DBB_WARN_KEYPATH)); api_format_send_cmd(cmd_str(CMD_xpub), "m/-111", KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_CHILD)); + ASSERT_REPORT_HAS_NOT(flag_msg(DBB_WARN_KEYPATH)); api_format_send_cmd(cmd_str(CMD_xpub), "m/'0", KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_CHILD)); + ASSERT_REPORT_HAS_NOT(flag_msg(DBB_WARN_KEYPATH)); api_format_send_cmd(cmd_str(CMD_xpub), "m/'", KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_CHILD)); + ASSERT_REPORT_HAS_NOT(flag_msg(DBB_WARN_KEYPATH)); api_format_send_cmd(cmd_str(CMD_xpub), "m/", KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_CHILD)); + ASSERT_REPORT_HAS_NOT(flag_msg(DBB_WARN_KEYPATH)); api_format_send_cmd(cmd_str(CMD_xpub), "m//", KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_CHILD)); + ASSERT_REPORT_HAS_NOT(flag_msg(DBB_WARN_KEYPATH)); api_format_send_cmd(cmd_str(CMD_xpub), "m/ ", KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_KEY_CHILD)); + ASSERT_REPORT_HAS_NOT(flag_msg(DBB_WARN_KEYPATH)); + + + // Test xpub whitelist + char kp[128]; + char xpub[112]; + + // Legacy BTC + snprintf(kp, sizeof(kp), "m/%i%s/%i%s/%i%s/%i%s/%i%s", + BIP44_PURPOSE_P2PKH, BIP44_PURPOSE_HARDENED ? "p" : "", + BIP44_COIN_TYPE_BTC, BIP44_COIN_TYPE_HARDENED ? "p" : "", + BIP44_ACCOUNT_MAX, BIP44_ACCOUNT_HARDENED ? "p" : "", + BIP44_CHANGE_MAX, BIP44_CHANGE_HARDENED ? "p" : "", + BIP44_ADDRESS_MAX, BIP44_ADDRESS_HARDENED ? "p" : ""); + api_format_send_cmd(cmd_str(CMD_xpub), kp, KEY_STANDARD); + ASSERT_REPORT_HAS("\"xpub\":"); + ASSERT_REPORT_HAS_NOT(flag_msg(DBB_WARN_KEYPATH)); + ASSERT_REPORT_HAS(cmd_str(CMD_echo)); + memcpy(xpub, api_read_value(CMD_xpub), sizeof(xpub)); + if (!TEST_LIVE_DEVICE) { + int len; + const char *val = api_read_value(CMD_echo); + char *echo = cipher_aes_b64_hmac_decrypt((const unsigned char *)val, strlens(val), &len, + memory_report_aeskey(TFA_SHARED_SECRET)); + u_assert(echo); + u_assert_str_has(echo, "xpub"); + u_assert_str_has_not(echo, flag_msg(DBB_WARN_KEYPATH)); + u_assert_str_eq(xpub, echo); + free(echo); + } + + // P2WPKH + snprintf(kp, sizeof(kp), "m/%i%s/%i%s/%i%s/%i%s/%i%s", + BIP44_PURPOSE_P2WPKH, BIP44_PURPOSE_HARDENED ? "p" : "", + BIP44_COIN_TYPE_BTC, BIP44_COIN_TYPE_HARDENED ? "p" : "", + BIP44_ACCOUNT_MAX, BIP44_ACCOUNT_HARDENED ? "p" : "", + BIP44_CHANGE_MAX, BIP44_CHANGE_HARDENED ? "p" : "", + BIP44_ADDRESS_MAX, BIP44_ADDRESS_HARDENED ? "p" : ""); + api_format_send_cmd(cmd_str(CMD_xpub), kp, KEY_STANDARD); + ASSERT_REPORT_HAS("\"xpub\":"); + ASSERT_REPORT_HAS_NOT(flag_msg(DBB_WARN_KEYPATH)); + ASSERT_REPORT_HAS(cmd_str(CMD_echo)); + + // P2WPKH_P2SH + snprintf(kp, sizeof(kp), "m/%i%s/%i%s/%i%s/%i%s/%i%s", + BIP44_PURPOSE_P2WPKH_P2SH, BIP44_PURPOSE_HARDENED ? "p" : "", + BIP44_COIN_TYPE_BTC, BIP44_COIN_TYPE_HARDENED ? "p" : "", + BIP44_ACCOUNT_MAX, BIP44_ACCOUNT_HARDENED ? "p" : "", + BIP44_CHANGE_MAX, BIP44_CHANGE_HARDENED ? "p" : "", + BIP44_ADDRESS_MAX, BIP44_ADDRESS_HARDENED ? "p" : ""); + api_format_send_cmd(cmd_str(CMD_xpub), kp, KEY_STANDARD); + ASSERT_REPORT_HAS("\"xpub\":"); + ASSERT_REPORT_HAS_NOT(flag_msg(DBB_WARN_KEYPATH)); + ASSERT_REPORT_HAS(cmd_str(CMD_echo)); + + // Exceeds account max + snprintf(kp, sizeof(kp), "m/%i%s/%i%s/%i%s/%i%s/%i%s", + BIP44_PURPOSE_P2PKH, BIP44_PURPOSE_HARDENED ? "p" : "", + BIP44_COIN_TYPE_BTC, BIP44_COIN_TYPE_HARDENED ? "p" : "", + BIP44_ACCOUNT_MAX + 1, BIP44_ACCOUNT_HARDENED ? "p" : "", + BIP44_CHANGE_MAX, BIP44_CHANGE_HARDENED ? "p" : "", + BIP44_ADDRESS_MAX, BIP44_ADDRESS_HARDENED ? "p" : ""); + api_format_send_cmd(cmd_str(CMD_xpub), kp, KEY_STANDARD); + ASSERT_REPORT_HAS("\"xpub\":"); + ASSERT_REPORT_HAS(flag_msg(DBB_WARN_KEYPATH)); + ASSERT_REPORT_HAS_NOT(cmd_str(CMD_echo)); + + // Exceeds change max + snprintf(kp, sizeof(kp), "m/%i%s/%i%s/%i%s/%i%s/%i%s", + BIP44_PURPOSE_P2PKH, BIP44_PURPOSE_HARDENED ? "p" : "", + BIP44_COIN_TYPE_BTC, BIP44_COIN_TYPE_HARDENED ? "p" : "", + BIP44_ACCOUNT_MAX, BIP44_ACCOUNT_HARDENED ? "p" : "", + BIP44_CHANGE_MAX + 1, BIP44_CHANGE_HARDENED ? "p" : "", + BIP44_ADDRESS_MAX, BIP44_ADDRESS_HARDENED ? "p" : ""); + api_format_send_cmd(cmd_str(CMD_xpub), kp, KEY_STANDARD); + ASSERT_REPORT_HAS("\"xpub\":"); + ASSERT_REPORT_HAS(flag_msg(DBB_WARN_KEYPATH)); + ASSERT_REPORT_HAS_NOT(cmd_str(CMD_echo)); + + // Exceeds address max + snprintf(kp, sizeof(kp), "m/%i%s/%i%s/%i%s/%i%s/%i%s", + BIP44_PURPOSE_P2PKH, BIP44_PURPOSE_HARDENED ? "p" : "", + BIP44_COIN_TYPE_BTC, BIP44_COIN_TYPE_HARDENED ? "p" : "", + BIP44_ACCOUNT_MAX, BIP44_ACCOUNT_HARDENED ? "p" : "", + BIP44_CHANGE_MAX, BIP44_CHANGE_HARDENED ? "p" : "", + BIP44_ADDRESS_MAX + 1, BIP44_ADDRESS_HARDENED ? "p" : ""); + api_format_send_cmd(cmd_str(CMD_xpub), kp, KEY_STANDARD); + ASSERT_REPORT_HAS("\"xpub\":"); + ASSERT_REPORT_HAS(flag_msg(DBB_WARN_KEYPATH)); + ASSERT_REPORT_HAS_NOT(cmd_str(CMD_echo)); + + // Purpose not hardened + snprintf(kp, sizeof(kp), "m/%i%s/%i%s/%i%s/%i%s/%i%s", + BIP44_PURPOSE_P2PKH, !BIP44_PURPOSE_HARDENED ? "p" : "", + BIP44_COIN_TYPE_BTC, BIP44_COIN_TYPE_HARDENED ? "p" : "", + BIP44_ACCOUNT_MAX, BIP44_ACCOUNT_HARDENED ? "p" : "", + BIP44_CHANGE_MAX, BIP44_CHANGE_HARDENED ? "p" : "", + BIP44_ADDRESS_MAX, BIP44_ADDRESS_HARDENED ? "p" : ""); + api_format_send_cmd(cmd_str(CMD_xpub), kp, KEY_STANDARD); + ASSERT_REPORT_HAS("\"xpub\":"); + ASSERT_REPORT_HAS(flag_msg(DBB_WARN_KEYPATH)); + ASSERT_REPORT_HAS_NOT(cmd_str(CMD_echo)); + + // Coin type not hardened + snprintf(kp, sizeof(kp), "m/%i%s/%i%s/%i%s/%i%s/%i%s", + BIP44_PURPOSE_P2PKH, BIP44_PURPOSE_HARDENED ? "p" : "", + BIP44_COIN_TYPE_BTC, !BIP44_COIN_TYPE_HARDENED ? "p" : "", + BIP44_ACCOUNT_MAX, BIP44_ACCOUNT_HARDENED ? "p" : "", + BIP44_CHANGE_MAX, BIP44_CHANGE_HARDENED ? "p" : "", + BIP44_ADDRESS_MAX, BIP44_ADDRESS_HARDENED ? "p" : ""); + api_format_send_cmd(cmd_str(CMD_xpub), kp, KEY_STANDARD); + ASSERT_REPORT_HAS("\"xpub\":"); + ASSERT_REPORT_HAS(flag_msg(DBB_WARN_KEYPATH)); + ASSERT_REPORT_HAS_NOT(cmd_str(CMD_echo)); + + // Account not hardened + snprintf(kp, sizeof(kp), "m/%i%s/%i%s/%i%s/%i%s/%i%s", + BIP44_PURPOSE_P2PKH, BIP44_PURPOSE_HARDENED ? "p" : "", + BIP44_COIN_TYPE_BTC, BIP44_COIN_TYPE_HARDENED ? "p" : "", + BIP44_ACCOUNT_MAX, !BIP44_ACCOUNT_HARDENED ? "p" : "", + BIP44_CHANGE_MAX, BIP44_CHANGE_HARDENED ? "p" : "", + BIP44_ADDRESS_MAX, BIP44_ADDRESS_HARDENED ? "p" : ""); + api_format_send_cmd(cmd_str(CMD_xpub), kp, KEY_STANDARD); + ASSERT_REPORT_HAS("\"xpub\":"); + ASSERT_REPORT_HAS(flag_msg(DBB_WARN_KEYPATH)); + ASSERT_REPORT_HAS_NOT(cmd_str(CMD_echo)); + + // Change hardened + snprintf(kp, sizeof(kp), "m/%i%s/%i%s/%i%s/%i%s/%i%s", + BIP44_PURPOSE_P2PKH, BIP44_PURPOSE_HARDENED ? "p" : "", + BIP44_COIN_TYPE_BTC, BIP44_COIN_TYPE_HARDENED ? "p" : "", + BIP44_ACCOUNT_MAX, BIP44_ACCOUNT_HARDENED ? "p" : "", + BIP44_CHANGE_MAX, !BIP44_CHANGE_HARDENED ? "p" : "", + BIP44_ADDRESS_MAX, BIP44_ADDRESS_HARDENED ? "p" : ""); + api_format_send_cmd(cmd_str(CMD_xpub), kp, KEY_STANDARD); + ASSERT_REPORT_HAS("\"xpub\":"); + ASSERT_REPORT_HAS(flag_msg(DBB_WARN_KEYPATH)); + ASSERT_REPORT_HAS_NOT(cmd_str(CMD_echo)); + // Address hardened + snprintf(kp, sizeof(kp), "m/%i%s/%i%s/%i%s/%i%s/%i%s", + BIP44_PURPOSE_P2PKH, BIP44_PURPOSE_HARDENED ? "p" : "", + BIP44_COIN_TYPE_BTC, BIP44_COIN_TYPE_HARDENED ? "p" : "", + BIP44_ACCOUNT_MAX, BIP44_ACCOUNT_HARDENED ? "p" : "", + BIP44_CHANGE_MAX, BIP44_CHANGE_HARDENED ? "p" : "", + BIP44_ADDRESS_MAX, !BIP44_ADDRESS_HARDENED ? "p" : ""); + api_format_send_cmd(cmd_str(CMD_xpub), kp, KEY_STANDARD); + ASSERT_REPORT_HAS("\"xpub\":"); + ASSERT_REPORT_HAS(flag_msg(DBB_WARN_KEYPATH)); + ASSERT_REPORT_HAS_NOT(cmd_str(CMD_echo)); + + // LTC + snprintf(kp, sizeof(kp), "m/%i%s/%i%s/%i%s/%i%s/%i%s", + BIP44_PURPOSE_P2PKH, BIP44_PURPOSE_HARDENED ? "p" : "", + BIP44_COIN_TYPE_LTC, BIP44_COIN_TYPE_HARDENED ? "p" : "", + BIP44_ACCOUNT_MAX, BIP44_ACCOUNT_HARDENED ? "p" : "", + BIP44_CHANGE_MAX, BIP44_CHANGE_HARDENED ? "p" : "", + BIP44_ADDRESS_MAX, BIP44_ADDRESS_HARDENED ? "p" : ""); + api_format_send_cmd(cmd_str(CMD_xpub), kp, KEY_STANDARD); + ASSERT_REPORT_HAS("\"xpub\":"); + ASSERT_REPORT_HAS_NOT(flag_msg(DBB_WARN_KEYPATH)); + ASSERT_REPORT_HAS(cmd_str(CMD_echo)); + + // TESTNET + snprintf(kp, sizeof(kp), "m/%i%s/%i%s/%i%s/%i%s/%i%s", + BIP44_PURPOSE_P2PKH, BIP44_PURPOSE_HARDENED ? "p" : "", + BIP44_COIN_TYPE_TESTNET, BIP44_COIN_TYPE_HARDENED ? "p" : "", + BIP44_ACCOUNT_MAX, BIP44_ACCOUNT_HARDENED ? "p" : "", + BIP44_CHANGE_MAX, BIP44_CHANGE_HARDENED ? "p" : "", + BIP44_ADDRESS_MAX, BIP44_ADDRESS_HARDENED ? "p" : ""); + api_format_send_cmd(cmd_str(CMD_xpub), kp, KEY_STANDARD); + ASSERT_REPORT_HAS("\"xpub\":"); + ASSERT_REPORT_HAS_NOT(flag_msg(DBB_WARN_KEYPATH)); + ASSERT_REPORT_HAS(cmd_str(CMD_echo)); + + // ETH (not supported, so should not give an echo) + snprintf(kp, sizeof(kp), "m/%i%s/%i%s/%i%s/%i%s/%i%s", + BIP44_PURPOSE_P2PKH, BIP44_PURPOSE_HARDENED ? "p" : "", + 60, BIP44_COIN_TYPE_HARDENED ? "p" : "", + BIP44_ACCOUNT_MAX, BIP44_ACCOUNT_HARDENED ? "p" : "", + BIP44_CHANGE_MAX, BIP44_CHANGE_HARDENED ? "p" : "", + BIP44_ADDRESS_MAX, BIP44_ADDRESS_HARDENED ? "p" : ""); + api_format_send_cmd(cmd_str(CMD_xpub), kp, KEY_STANDARD); + ASSERT_REPORT_HAS("\"xpub\":"); + ASSERT_REPORT_HAS(flag_msg(DBB_WARN_KEYPATH)); + ASSERT_REPORT_HAS_NOT(cmd_str(CMD_echo)); // test create seeds differ @@ -352,17 +568,25 @@ static void tests_seed_xpub_backup(void) { // Check backup should also work with the hidden password. char set_hidden_wallet_cmd[512]; - snprintf(set_hidden_wallet_cmd, sizeof(set_hidden_wallet_cmd), "{\"%s\":\"%s\",\"%s\":\"%s\"}", cmd_str(CMD_password), + snprintf(set_hidden_wallet_cmd, sizeof(set_hidden_wallet_cmd), + "{\"%s\":\"%s\",\"%s\":\"%s\"}", cmd_str(CMD_password), hidden_pwd, cmd_str(CMD_key), "hiddenpassword"); api_format_send_cmd(cmd_str(CMD_hidden_password), set_hidden_wallet_cmd, KEY_STANDARD); ASSERT_SUCCESS; - snprintf(check, sizeof(check), "{\"check\":\"%s\", \"key\":\"hiddenpassword\"}", filename); + snprintf(check, sizeof(check), "{\"check\":\"%s\", \"key\":\"hiddenpassword\"}", + filename); api_format_send_cmd(cmd_str(CMD_backup), check, KEY_STANDARD); ASSERT_SUCCESS; } + api_reset_device(); + api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); + ASSERT_SUCCESS; + + snprintf(erase_cmd, sizeof(erase_cmd), "{\"erase\":\"%s\"}", "seed_create_2.pdf"); + api_format_send_cmd(cmd_str(CMD_backup), erase_cmd, KEY_STANDARD); api_format_send_cmd(cmd_str(CMD_seed), seed_create_2, KEY_STANDARD); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); @@ -376,16 +600,17 @@ static void tests_seed_xpub_backup(void) ASSERT_REPORT_HAS(flag_msg(DBB_ERR_SD_NO_MATCH)); // test cannot overwrite existing backup file + api_format_send_cmd(cmd_str(CMD_seed), seed_create_2, KEY_STANDARD); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_SEED_SEEDED)); + + api_reset_device(); + api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); + ASSERT_SUCCESS; + api_format_send_cmd(cmd_str(CMD_seed), seed_create_2, KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_SD_OPEN_FILE)); // test erase single backup file - if (!TEST_LIVE_DEVICE) { - // testing buffer gets overwritten by seed command, so reset it - api_format_send_cmd(cmd_str(CMD_backup), back, KEY_STANDARD); - ASSERT_SUCCESS; - } - api_format_send_cmd(cmd_str(CMD_backup), attr_str(ATTR_list), KEY_STANDARD); ASSERT_REPORT_HAS(filename); @@ -409,7 +634,6 @@ static void tests_seed_xpub_backup(void) memset(xpub1, 0, sizeof(xpub1)); api_reset_device(); - api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); ASSERT_SUCCESS; @@ -423,6 +647,10 @@ static void tests_seed_xpub_backup(void) u_assert_str_not_eq(xpub0, xpub1); // seed with extra entropy from device (entropy too short) + api_reset_device(); + api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); + ASSERT_SUCCESS; + snprintf(seed_usb, sizeof(seed_usb), "{\"source\":\"create\",\"entropy\":\"%s\",\"filename\":\"%s\",\"key\":\"%s\"}", seed_entropy_short, filename2, key); @@ -430,6 +658,10 @@ static void tests_seed_xpub_backup(void) ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_INVALID_CMD)); // seed with extra entropy from device (entropy too long) + api_reset_device(); + api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); + ASSERT_SUCCESS; + snprintf(seed_usb, sizeof(seed_usb), "{\"source\":\"create\",\"entropy\":\"%s\",\"filename\":\"%s\",\"key\":\"%s\"}", seed_entropy_long, filename2, key); @@ -437,6 +669,10 @@ static void tests_seed_xpub_backup(void) ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_INVALID_CMD)); // seed with extra entropy from device + api_reset_device(); + api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); + ASSERT_SUCCESS; + snprintf(seed_usb, sizeof(seed_usb), "{\"source\":\"create\",\"entropy\":\"%s\",\"filename\":\"%s\",\"key\":\"%s\"}", seed_entropy, filename2, key); @@ -450,6 +686,10 @@ static void tests_seed_xpub_backup(void) u_assert_str_not_eq(xpub0, xpub1); // load backup + api_reset_device(); + api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); + ASSERT_SUCCESS; + api_format_send_cmd(cmd_str(CMD_seed), seed_b, KEY_STANDARD); ASSERT_SUCCESS; @@ -600,7 +840,8 @@ static void tests_legacy_hidden_wallet(void) 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); + 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"); @@ -608,16 +849,19 @@ static void tests_legacy_hidden_wallet(void) 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); + 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), + 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), + 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"; @@ -653,7 +897,8 @@ static void tests_legacy_hidden_wallet(void) // 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); + 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"); @@ -669,7 +914,8 @@ static void tests_legacy_hidden_wallet(void) 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); + 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); @@ -678,7 +924,8 @@ static void tests_legacy_hidden_wallet(void) 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); + 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); @@ -688,9 +935,12 @@ static void tests_legacy_hidden_wallet(void) 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"); + 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); @@ -707,12 +957,14 @@ static void tests_legacy_hidden_wallet(void) 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); + 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); + 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)); @@ -720,7 +972,8 @@ static void tests_legacy_hidden_wallet(void) // 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); + 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)); @@ -872,7 +1125,6 @@ static void tests_u2f(void) // erase sd // reset u2f fail (would create backup `all` by default but cannot because not seeded) api_reset_device(); - api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); ASSERT_SUCCESS; @@ -936,6 +1188,10 @@ static void tests_u2f(void) // verify backup1 `u2f` success // verify backup1 `hww` success // verify backup1 `` success + api_reset_device(); + api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); + ASSERT_SUCCESS; + snprintf(cmd, sizeof(cmd), "{\"erase\":\"%s\"}", fn1); api_format_send_cmd(cmd_str(CMD_backup), cmd, KEY_STANDARD); snprintf(cmd, sizeof(cmd), @@ -1187,6 +1443,10 @@ static void tests_u2f(void) // recover backup0 u2f // recover backup3u hww fail // recover backup3u all fail (invalid cmd) + api_reset_device(); + api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); + ASSERT_SUCCESS; + snprintf(cmd, sizeof(cmd), "{\"source\":\"%s\", \"filename\":\"%s\"}", attr_str(ATTR_U2F_load), fn0); api_format_send_cmd(cmd_str(CMD_seed), cmd, KEY_STANDARD); @@ -1246,10 +1506,8 @@ static void tests_u2f(void) // recover backup0 hww // recover backup3h u2f fail // recover backup3h all fail (invalid cmd) - snprintf(cmd, sizeof(cmd), - "{\"source\":\"%s\", \"filename\":\"%s\", \"key\":\"password\"}", attr_str(ATTR_backup), - fn0); - api_format_send_cmd(cmd_str(CMD_seed), cmd, KEY_STANDARD); + api_reset_device(); + api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); ASSERT_SUCCESS; snprintf(cmd, sizeof(cmd), @@ -1264,6 +1522,12 @@ static void tests_u2f(void) api_format_send_cmd(cmd_str(CMD_seed), cmd, KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_INVALID_CMD)); + snprintf(cmd, sizeof(cmd), + "{\"source\":\"%s\", \"filename\":\"%s\", \"key\":\"password\"}", attr_str(ATTR_backup), + fn0); + api_format_send_cmd(cmd_str(CMD_seed), cmd, KEY_STANDARD); + ASSERT_SUCCESS; + // verify backup3h `hww` fail // verify backup0 `u2f` fail @@ -1375,6 +1639,10 @@ static void tests_u2f(void) // verify v23u u2f success // verify v23u hww fail // sign fail + api_reset_device(); + api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); + ASSERT_SUCCESS; + snprintf(cmd, sizeof(cmd), "{\"source\":\"%s\", \"filename\":\"%s\"}", attr_str(ATTR_U2F_load), tb_v2_3u); api_format_send_cmd(cmd_str(CMD_seed), cmd, KEY_STANDARD); @@ -1447,6 +1715,10 @@ static void tests_u2f(void) // recover fn4 hww success // verify fn4 hww success // sign fail + api_reset_device(); + api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); + ASSERT_SUCCESS; + snprintf(cmd, sizeof(cmd), "{\"source\":\"%s\", \"filename\":\"%s\", \"key\":\"key\"}", attr_str(ATTR_backup), fn4); api_format_send_cmd(cmd_str(CMD_seed), cmd, KEY_STANDARD); @@ -1473,6 +1745,10 @@ static void tests_u2f(void) // verify v23a hww success // verify v23h hww success // sign success + api_reset_device(); + api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); + ASSERT_SUCCESS; + snprintf(cmd, sizeof(cmd), "{\"source\":\"%s\", \"filename\":\"%s\"}", attr_str(ATTR_U2F_load), tb_v2_3a); api_format_send_cmd(cmd_str(CMD_seed), cmd, KEY_STANDARD); @@ -1515,6 +1791,10 @@ static void tests_u2f(void) // recover fn4 hww success // verify fn4 hww success // sign fail + api_reset_device(); + api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); + ASSERT_SUCCESS; + snprintf(cmd, sizeof(cmd), "{\"source\":\"%s\", \"filename\":\"%s\", \"key\":\"key\"}", attr_str(ATTR_backup), fn4); api_format_send_cmd(cmd_str(CMD_seed), cmd, KEY_STANDARD); @@ -1537,6 +1817,10 @@ static void tests_u2f(void) // recover v22 hww success // recover v22 u2f fail // sign success + api_reset_device(); + api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); + ASSERT_SUCCESS; + snprintf(cmd, sizeof(cmd), "{\"source\":\"%s\", \"filename\":\"%s\", \"key\":\"key\"}", attr_str(ATTR_backup), tb_v2_2); api_format_send_cmd(cmd_str(CMD_seed), cmd, KEY_STANDARD); @@ -2134,6 +2418,10 @@ static void tests_password(void) // Use hidden key to seed from backup // -> puts the hidden wallet into a standard wallet // -> erases hidden wallet + api_reset_device(); + api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); + ASSERT_SUCCESS; + snprintf(cmd, sizeof(cmd), "{\"source\":\"backup\",\"filename\":\"h.pdf\",\"key\":\"%s\"}", hidden_pwd); api_format_send_cmd(cmd_str(CMD_seed), cmd, KEY_STANDARD); @@ -2150,6 +2438,10 @@ static void tests_password(void) ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_JSON_PARSE)); // Use standard key to seed from backup // -> get the original standard wallet + api_reset_device(); + api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); + ASSERT_SUCCESS; + snprintf(cmd, sizeof(cmd), "{\"source\":\"backup\",\"filename\":\"h.pdf\",\"key\":\"%s\"}", tests_pwd); api_format_send_cmd(cmd_str(CMD_seed), cmd, KEY_STANDARD); @@ -2720,9 +3012,9 @@ static void tests_sign(void) sizeof(recid_device_1)); u_assert_int_eq(0, recover_public_key_verify_sig(sig_device_1, HASH_INPUT_ONE, - recid_device_1, pubkey_device_1)); + recid_device_1, pubkey_device_1)); u_assert_int_eq(0, recover_public_key_verify_sig(sig_device_2, HASH_DEFAULT, - recid_device_2, pubkey_device_2)); + recid_device_2, pubkey_device_2)); // If TESTING, a deterministic seed is loaded when 'raw' is specified ASSERT_REPORT_HAS(check_sig_1); @@ -2806,7 +3098,8 @@ static void tests_sign(void) pin_err_count++; } - { // Check exception for ETH/ETC. + { + // Check exception for ETH/ETC. api_format_send_cmd(cmd_str(CMD_sign), "{\"meta\":\"_meta_data_\", \"data\":[{\"hash\":\"" HASH_INPUT_TWO_1 "\", \"keypath\":\"" "m/44'/60'/" "\"},{\"hash\":\"" HASH_INPUT_TWO_2 "\", \"keypath\":\"" @@ -2853,7 +3146,8 @@ static void tests_sign(void) api_format_send_cmd(cmd_str(CMD_device), attr_str(ATTR_lock), KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_NO_PASSWORD)); - { // if pairing=true, locked=false, check that a wrong PIN does not work. + { + // if pairing=true, locked=false, check that a wrong PIN does not work. api_reset_device(); api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); diff --git a/tests/tests_u2f_standard.c b/tests/tests_u2f_standard.c index d7758cce..7ae50fb9 100644 --- a/tests/tests_u2f_standard.c +++ b/tests/tests_u2f_standard.c @@ -304,7 +304,8 @@ static void check_CounterUpdate(void) "{\"source\":\"create\", \"filename\":\"hwwcountertest.pdf\", \"key\":\"key\"}"); api_format_send_cmd(cmd_str(CMD_seed), cmd, KEY_STANDARD); ASSERT_SUCCESS; - api_format_send_cmd(cmd_str(CMD_backup), "{\"erase\":\"hwwcountertest.pdf\"}", KEY_STANDARD); + api_format_send_cmd(cmd_str(CMD_backup), "{\"erase\":\"hwwcountertest.pdf\"}", + KEY_STANDARD); if (U2Fob_liveDeviceTesting()) { PRINT_MESSAGE("Creating new U2F key. LONG touch to continue...\n"); @@ -333,7 +334,8 @@ static void check_CounterUpdate(void) c); api_format_send_cmd(cmd_str(CMD_seed), cmd, KEY_STANDARD); ASSERT_SUCCESS; - api_format_send_cmd(cmd_str(CMD_backup), "{\"erase\":\"u2fcountertest.pdf\"}", KEY_STANDARD); + api_format_send_cmd(cmd_str(CMD_backup), "{\"erase\":\"u2fcountertest.pdf\"}", + KEY_STANDARD); // Ctr should be c + 1. WaitForUserPresence(device, arg_hasButton);