diff --git a/src/commander.c b/src/commander.c index 054808ec..13a4218e 100644 --- a/src/commander.c +++ b/src/commander.c @@ -822,6 +822,7 @@ static void commander_process_device(yajl_val json_node) char u2f_enabled[6] = {0}; char u2f_hijack_enabled[6] = {0}; char new_hidden_wallet_enabled[6] = {0}; + char pairing_enabled[6] = {0}; uint32_t serial[4] = {0}; flash_wrapper_read_unique_id(serial, 4); @@ -866,6 +867,12 @@ static void commander_process_device(yajl_val json_node) snprintf(new_hidden_wallet_enabled, sizeof(new_hidden_wallet_enabled), "%s", attr_str(ATTR_false)); } + if (wallet_is_paired()) { + snprintf(pairing_enabled, sizeof(pairing_enabled), "%s", attr_str(ATTR_true)); + } else { + snprintf(pairing_enabled, sizeof(pairing_enabled), "%s", attr_str(ATTR_false)); + } + if (sd_card_inserted() == DBB_OK) { snprintf(sdcard, sizeof(sdcard), "%s", attr_str(ATTR_true)); @@ -885,7 +892,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,\"%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(""), @@ -897,7 +904,8 @@ static void commander_process_device(yajl_val json_node) attr_str(ATTR_TFA), tfa, attr_str(ATTR_U2F), u2f_enabled, attr_str(ATTR_U2F_hijack), u2f_hijack_enabled, - attr_str(ATTR_new_hidden_wallet), new_hidden_wallet_enabled); + attr_str(ATTR_new_hidden_wallet), new_hidden_wallet_enabled, + attr_str(ATTR_pairing), pairing_enabled); free(tfa); commander_fill_report(cmd_str(CMD_device), msg, DBB_JSON_ARRAY); @@ -940,11 +948,13 @@ static void commander_process_feature_set(yajl_val json_node) 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 }; + const char *pairing_path[] = { attr_str(ATTR_pairing), 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); + yajl_val pairing = yajl_tree_get(data, pairing_path, yajl_t_any); - if (!u2f && !u2f_hijack && !new_hidden_wallet) { + if (!u2f && !u2f_hijack && !new_hidden_wallet && !pairing) { goto err; } @@ -986,6 +996,15 @@ static void commander_process_feature_set(yajl_val json_node) } } + // Set the bit == enabled + if (pairing) { + if (YAJL_IS_TRUE(pairing)) { + flags &= ~(MEM_EXT_MASK_NOTPAIRED); + } else { + goto err; + } + } + memory_write_ext_flags(flags); commander_fill_report(cmd_str(CMD_feature_set), attr_str(ATTR_success), DBB_OK); return; @@ -1197,26 +1216,26 @@ static int commander_process(int cmd, yajl_val json_node) static int commander_tfa_append_pin(void) { - if (wallet_is_locked()) { - // Create one-time PIN - uint8_t pin_b[TFA_PIN_LEN]; - memset(TFA_PIN, 0, sizeof(TFA_PIN)); - if (random_bytes(pin_b, TFA_PIN_LEN, 0) == DBB_ERROR) { - commander_fill_report(cmd_str(CMD_random), NULL, DBB_ERR_MEM_ATAES); - return DBB_ERROR; - } + // Create one-time PIN + uint8_t pin_b[TFA_PIN_LEN]; + memset(TFA_PIN, 0, sizeof(TFA_PIN)); + if (random_bytes(pin_b, TFA_PIN_LEN, 0) == DBB_ERROR) { + commander_fill_report(cmd_str(CMD_random), NULL, DBB_ERR_MEM_ATAES); + return DBB_ERROR; + } + // Note: if you change this to binary, be sure to update the TFA_PIN check + // in commander_parse(). #ifdef TESTING - snprintf(TFA_PIN, sizeof(TFA_PIN), "0001"); + snprintf(TFA_PIN, sizeof(TFA_PIN), "0001"); #else - snprintf(TFA_PIN, sizeof(TFA_PIN), "%s", - utils_uint8_to_hex(pin_b, TFA_PIN_LEN)); -#endif - // Append PIN to echo - commander_fill_report(cmd_str(CMD_pin), TFA_PIN, DBB_OK); + snprintf(TFA_PIN, sizeof(TFA_PIN), "%s", + utils_uint8_to_hex(pin_b, TFA_PIN_LEN)); +#endif - } + // Append PIN to echo + commander_fill_report(cmd_str(CMD_pin), TFA_PIN, DBB_OK); return DBB_OK; } @@ -1237,6 +1256,18 @@ static int commander_tfa_check_pin(yajl_val json_node) return DBB_OK; } +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'/"; + // 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)); +} + static int commander_echo_command(yajl_val json_node) { const char *meta_path[] = { cmd_str(CMD_sign), cmd_str(CMD_meta), NULL }; @@ -1246,6 +1277,7 @@ static int commander_echo_command(yajl_val json_node) const char *meta = YAJL_GET_STRING(yajl_tree_get(json_node, meta_path, yajl_t_string)); yajl_val check = yajl_tree_get(json_node, check_path, yajl_t_array); yajl_val data = yajl_tree_get(json_node, data_path, yajl_t_any); + const char *keypath_path[] = { cmd_str(CMD_keypath), NULL }; if (meta) { commander_fill_report(cmd_str(CMD_meta), meta, DBB_OK); @@ -1258,7 +1290,6 @@ static int commander_echo_command(yajl_val json_node) } else { memset(json_array, 0, COMMANDER_ARRAY_MAX); for (size_t i = 0; i < data->u.array.len; i++) { - const char *keypath_path[] = { cmd_str(CMD_keypath), NULL }; const char *hash_path[] = { cmd_str(CMD_hash), NULL }; yajl_val obj = data->u.array.values[i]; @@ -1284,7 +1315,6 @@ static int commander_echo_command(yajl_val json_node) int ret; memset(json_array, 0, COMMANDER_ARRAY_MAX); for (size_t i = 0; i < check->u.array.len; i++) { - const char *keypath_path[] = { cmd_str(CMD_keypath), NULL }; const char *pubkey_path[] = { cmd_str(CMD_pubkey), NULL }; yajl_val obj = check->u.array.values[i]; @@ -1319,20 +1349,33 @@ static int commander_echo_command(yajl_val json_node) memset(json_report, 0, COMMANDER_REPORT_SIZE); commander_fill_report(cmd_str(CMD_sign), json_array, DBB_JSON_ARRAY); - if (commander_tfa_append_pin() != DBB_OK) { - return DBB_ERROR; + 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)); + if (!check_disable_pairing(keypath)) { + disable_pairing = 0; + break; + } } - int length; - char *encoded_report = cipher_aes_b64_hmac_encrypt((unsigned char *) json_report, - strlens(json_report), &length, memory_report_aeskey(TFA_SHARED_SECRET)); - commander_clear_report(); - if (encoded_report) { - commander_fill_report(cmd_str(CMD_echo), encoded_report, DBB_OK); + if (!disable_pairing && wallet_is_paired()) { + if (commander_tfa_append_pin() != DBB_OK) { + return DBB_ERROR; + } + int length; + char *encoded_report = cipher_aes_b64_hmac_encrypt((unsigned char *) json_report, + strlens(json_report), &length, memory_report_aeskey(TFA_SHARED_SECRET)); + commander_clear_report(); + if (encoded_report) { + commander_fill_report(cmd_str(CMD_echo), encoded_report, DBB_OK); + } else { + commander_fill_report(cmd_str(CMD_echo), NULL, DBB_ERR_MEM_ENCRYPT); + } + free(encoded_report); } else { - commander_fill_report(cmd_str(CMD_echo), NULL, DBB_ERR_MEM_ENCRYPT); + commander_clear_report(); + commander_fill_report(cmd_str(CMD_echo), "", DBB_OK); } - free(encoded_report); return DBB_OK; } @@ -1404,12 +1447,12 @@ static void commander_parse(char *command) goto other; } - if (wallet_is_locked()) { + if (TFA_PIN[0]) { if (commander_tfa_check_pin(json_node) != DBB_OK) { - memset(TFA_PIN, 0, sizeof(TFA_PIN)); commander_access_err(DBB_ERR_SIGN_TFA_PIN, memory_read_pin_err_count() + 1); memory_pin_err_count(DBB_ACCESS_ITERATE); memset(sign_command, 0, COMMANDER_REPORT_SIZE); + memset(TFA_PIN, 0, sizeof(TFA_PIN)); goto exit; } else { memory_pin_err_count(DBB_ACCESS_INITIALIZE); @@ -1431,9 +1474,9 @@ static void commander_parse(char *command) // Verification 'echo' for signing if (found_cmd == CMD_sign) { if (commander_echo_command(json_node) == DBB_OK) { - TFA_VERIFY = 1; memset(sign_command, 0, COMMANDER_REPORT_SIZE); snprintf(sign_command, COMMANDER_REPORT_SIZE, "%s", command); + TFA_VERIFY = 1; } goto exit; } diff --git a/src/flags.h b/src/flags.h index 1736483d..a76a32fa 100644 --- a/src/flags.h +++ b/src/flags.h @@ -151,6 +151,7 @@ X(U2F_load) \ X(U2F_create) \ X(U2F_hijack) \ X(new_hidden_wallet) \ +X(pairing) \ X(__ERASE__) \ X(__FORCE__) \ X(NUM) /* keep last */ diff --git a/src/memory.h b/src/memory.h index db7515df..ad7c56fb 100644 --- a/src/memory.h +++ b/src/memory.h @@ -34,6 +34,7 @@ #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 +#define MEM_EXT_MASK_NOTPAIRED 0x00000008 // Mask of bit to disable (1) or enable (0) pairing // Default settings #define MEM_DEFAULT_unlocked 0xFF #define MEM_DEFAULT_erased 0xFF diff --git a/src/wallet.c b/src/wallet.c index eeda502e..cb7a2123 100644 --- a/src/wallet.c +++ b/src/wallet.c @@ -66,6 +66,10 @@ int wallet_is_locked(void) return HIDDEN || !memory_read_unlocked(); } +int wallet_is_paired(void) +{ + return wallet_is_locked() || !(memory_report_ext_flags() & MEM_EXT_MASK_NOTPAIRED); +} uint8_t *wallet_get_master(void) { diff --git a/src/wallet.h b/src/wallet.h index 54fac091..bde4a455 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -37,6 +37,7 @@ void wallet_set_hidden(int hide); int wallet_is_hidden(void); int wallet_is_locked(void); +int wallet_is_paired(void); uint8_t *wallet_get_master(void); uint8_t *wallet_get_chaincode(void); int wallet_split_seed(char **seed_words, const char *message); diff --git a/tests/tests_api.c b/tests/tests_api.c index 39a61167..b21e0f41 100644 --- a/tests/tests_api.c +++ b/tests/tests_api.c @@ -512,6 +512,56 @@ static void tests_name(void) u_assert_str_eq(name1, api_read_value(CMD_name)); } +static void tests_pairing(void) +{ + api_reset_device(); + api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); + ASSERT_SUCCESS; + + // Pairing off by default. + api_format_send_cmd(cmd_str(CMD_device), attr_str(ATTR_info), KEY_STANDARD); + ASSERT_REPORT_HAS("\"pairing\":false"); + + // Can turn on, but not turn off. + api_format_send_cmd(cmd_str(CMD_feature_set), "{\"pairing\":true}", KEY_STANDARD); + ASSERT_SUCCESS; + api_format_send_cmd(cmd_str(CMD_device), attr_str(ATTR_info), KEY_STANDARD); + ASSERT_REPORT_HAS("\"pairing\":true"); + api_format_send_cmd(cmd_str(CMD_feature_set), "{\"pairing\":false}", KEY_STANDARD); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_INVALID_CMD)); + api_format_send_cmd(cmd_str(CMD_device), attr_str(ATTR_info), KEY_STANDARD); + ASSERT_REPORT_HAS("\"pairing\":true"); + + // Reset turns it off again. + api_reset_device(); + api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); + ASSERT_SUCCESS; + // Pairing off by default. + api_format_send_cmd(cmd_str(CMD_device), attr_str(ATTR_info), KEY_STANDARD); + ASSERT_REPORT_HAS("\"pairing\":false"); + + // Forcabily turned on if device is locked. + char seed_c[512]; + snprintf(seed_c, sizeof(seed_c), + "{\"source\":\"%s\", \"filename\":\"%s\", \"key\":\"%s\"}", attr_str(ATTR_create), + "test_pairing.pdf", "key"); + api_format_send_cmd(cmd_str(CMD_seed), seed_c, KEY_STANDARD); + + // lock device + api_format_send_cmd(cmd_str(CMD_device), attr_str(ATTR_lock), KEY_STANDARD); + ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); + + api_format_send_cmd(cmd_str(CMD_device), attr_str(ATTR_info), KEY_STANDARD); + ASSERT_REPORT_HAS("\"pairing\":true"); + + api_format_send_cmd(cmd_str(CMD_feature_set), "{\"pairing\":true}", KEY_STANDARD); + ASSERT_SUCCESS; + api_format_send_cmd(cmd_str(CMD_device), attr_str(ATTR_info), KEY_STANDARD); + ASSERT_REPORT_HAS("\"pairing\":true"); + + +} + static void tests_legacy_hidden_wallet(void) { api_reset_device(); @@ -555,6 +605,10 @@ static void tests_legacy_hidden_wallet(void) api_format_send_cmd(cmd_str(CMD_seed), seed, KEY_STANDARD); ASSERT_SUCCESS; } else { + api_format_send_cmd(cmd_str(CMD_backup), + "{\"erase\":\"legacy_hidden_wallet_test.pdf\"}", + KEY_STANDARD); + api_format_send_cmd(cmd_str(CMD_seed), "{\"source\":\"create\", \"filename\":\"legacy_hidden_wallet_test.pdf\", \"key\":\"key\"}", KEY_STANDARD); @@ -1294,13 +1348,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) { - 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, KEYPATH_ONE); - free(echo); + u_assert_str_eq(api_read_value(CMD_echo), ""); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); ASSERT_REPORT_HAS_NOT(sig_1_input); @@ -1340,13 +1388,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) { - 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, KEYPATH_ONE); - free(echo); + u_assert_str_eq(api_read_value(CMD_echo), ""); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); ASSERT_REPORT_HAS(sig_1_input); @@ -1368,13 +1410,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) { - 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, KEYPATH_ONE); - free(echo); + u_assert_str_eq(api_read_value(CMD_echo), ""); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); ASSERT_REPORT_HAS_NOT(sig_1_input); @@ -1420,13 +1456,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) { - 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, KEYPATH_ONE); - free(echo); + u_assert_str_eq(api_read_value(CMD_echo), ""); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); ASSERT_REPORT_HAS(sig_1_input); @@ -1448,13 +1478,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) { - 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, KEYPATH_ONE); - free(echo); + u_assert_str_eq(api_read_value(CMD_echo), ""); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); ASSERT_REPORT_HAS_NOT(sig_1_input); @@ -1477,13 +1501,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) { - 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, KEYPATH_ONE); - free(echo); + u_assert_str_eq(api_read_value(CMD_echo), ""); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); ASSERT_REPORT_HAS(sig_1_input); @@ -2424,21 +2442,6 @@ static void tests_sign(void) KEY_STANDARD); ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_INVALID_CMD)); - - // sign max number of hashes per sign command - api_format_send_cmd(cmd_str(CMD_sign), maxhashes, KEY_STANDARD); - ASSERT_REPORT_HAS(cmd_str(CMD_echo)); - - api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); - ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); - - // sign 1 more than max number of hashes per sign command - api_format_send_cmd(cmd_str(CMD_sign), hashoverflow, KEY_STANDARD); - ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_REPORT_BUF)); - - api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); - ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_REPORT_BUF)); - // sig using no inputs api_format_send_cmd(cmd_str(CMD_sign), "{\"meta\":\"_meta_data_\", \"data\":[]}", KEY_STANDARD); @@ -2458,15 +2461,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) { - 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_not(echo, cmd_str(CMD_recid)); - u_assert_str_has(echo, "_meta_data_"); - u_assert_str_has(echo, KEYPATH_ONE); - free(echo); + u_assert_str_eq(api_read_value(CMD_echo), ""); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); @@ -2510,15 +2505,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) { - 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_not(echo, cmd_str(CMD_recid)); - u_assert_str_has(echo, "_meta_data_"); - u_assert_str_has(echo, KEYPATH_TWO); - free(echo); + u_assert_str_eq(api_read_value(CMD_echo), ""); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); @@ -2563,74 +2550,6 @@ static void tests_sign(void) u_assert_str_not_eq(pubkey_device_2, PUBKEY_INPUT_TWO_2); } - // test checkpub - api_format_send_cmd(cmd_str(CMD_xpub), KEYPATH_BASE, KEY_STANDARD); - hdnode_deserialize(api_read_value(CMD_xpub), &node); - snprintf(pubkey_device_1, sizeof(pubkey_device_1), "%s", - utils_uint8_to_hex(node.public_key, 33)); - snprintf(pubkey_device_2, sizeof(pubkey_device_2), "%s", - utils_uint8_to_hex(node.public_key, 33)); - - api_format_send_cmd(cmd_str(CMD_sign), checkpub, KEY_STANDARD); - ASSERT_REPORT_HAS(cmd_str(CMD_echo)); - 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_not(echo, cmd_str(CMD_recid)); - u_assert_str_has(echo, "\"meta\":"); - u_assert_str_has(echo, check_1); - u_assert_str_has(echo, check_2); - free(echo); - } - - api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); - ASSERT_REPORT_HAS(cmd_str(CMD_sign)); - - memcpy(sig_device_1, api_read_array_value(CMD_sign, CMD_sig, 0), sizeof(sig_device_1)); - memcpy(sig_device_2, api_read_array_value(CMD_sign, CMD_sig, 1), sizeof(sig_device_2)); - memcpy(recid_device_1, api_read_array_value(CMD_sign, CMD_recid, 0), - sizeof(recid_device_1)); - memcpy(recid_device_2, api_read_array_value(CMD_sign, CMD_recid, 1), - 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)); - u_assert_int_eq(0, recover_public_key_verify_sig(sig_device_2, HASH_DEFAULT, - recid_device_2, pubkey_device_2)); - - if (!TEST_LIVE_DEVICE) { - // If TESTING, a deterministic seed is loaded when 'raw' is specified - ASSERT_REPORT_HAS(check_sig_1); - ASSERT_REPORT_HAS(check_sig_2); - u_assert_str_eq(sig_device_1, check_sig_1); - u_assert_str_eq(sig_device_2, check_sig_2); - u_assert_str_eq(pubkey_device_1, check_pubkey); -#ifdef ECC_USE_SECP256K1_LIB - u_assert_str_eq(recid_device_1, RECID_01); - u_assert_str_eq(recid_device_2, RECID_01); -#else - u_assert_str_eq(recid_device_1, RECID_EE); - u_assert_str_eq(recid_device_2, RECID_EE); -#endif - } else { - // Random seed generated on device - ASSERT_REPORT_HAS_NOT(check_sig_1); - ASSERT_REPORT_HAS_NOT(check_sig_2); - u_assert_str_not_eq(sig_device_1, check_sig_1); - u_assert_str_not_eq(sig_device_2, check_sig_2); - u_assert_str_not_eq(pubkey_device_1, check_pubkey); - } - - api_format_send_cmd(cmd_str(CMD_sign), checkpub_wrong_addr_len, KEY_STANDARD); - ASSERT_REPORT_HAS(flag_msg(DBB_ERR_SIGN_PUBKEY_LEN)); - - api_format_send_cmd(cmd_str(CMD_sign), checkpub_wrong_addr_len, KEY_STANDARD); - ASSERT_REPORT_HAS(flag_msg(DBB_ERR_SIGN_PUBKEY_LEN)); - - // lock to get TFA PINs int pin_err_count = 0; api_format_send_cmd(cmd_str(CMD_device), attr_str(ATTR_lock), KEY_STANDARD); @@ -2709,6 +2628,76 @@ static void tests_sign(void) pin_err_count++; } + // test checkpub + api_format_send_cmd(cmd_str(CMD_xpub), KEYPATH_BASE, KEY_STANDARD); + hdnode_deserialize(api_read_value(CMD_xpub), &node); + snprintf(pubkey_device_1, sizeof(pubkey_device_1), "%s", + utils_uint8_to_hex(node.public_key, 33)); + snprintf(pubkey_device_2, sizeof(pubkey_device_2), "%s", + utils_uint8_to_hex(node.public_key, 33)); + + api_format_send_cmd(cmd_str(CMD_sign), checkpub, KEY_STANDARD); + ASSERT_REPORT_HAS(cmd_str(CMD_echo)); + 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_not(echo, cmd_str(CMD_recid)); + u_assert_str_has(echo, "\"meta\":"); + u_assert_str_has(echo, check_1); + u_assert_str_has(echo, check_2); + free(echo); + } + + api_format_send_cmd(cmd_str(CMD_sign), "{\"pin\":\"0001\"}", KEY_STANDARD); + ASSERT_REPORT_HAS(cmd_str(CMD_sign)); + + + memcpy(sig_device_1, api_read_array_value(CMD_sign, CMD_sig, 0), sizeof(sig_device_1)); + memcpy(sig_device_2, api_read_array_value(CMD_sign, CMD_sig, 1), sizeof(sig_device_2)); + memcpy(recid_device_1, api_read_array_value(CMD_sign, CMD_recid, 0), + sizeof(recid_device_1)); + memcpy(recid_device_2, api_read_array_value(CMD_sign, CMD_recid, 1), + 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)); + u_assert_int_eq(0, recover_public_key_verify_sig(sig_device_2, HASH_DEFAULT, + recid_device_2, pubkey_device_2)); + + if (!TEST_LIVE_DEVICE) { + // If TESTING, a deterministic seed is loaded when 'raw' is specified + ASSERT_REPORT_HAS(check_sig_1); + ASSERT_REPORT_HAS(check_sig_2); + u_assert_str_eq(sig_device_1, check_sig_1); + u_assert_str_eq(sig_device_2, check_sig_2); + u_assert_str_eq(pubkey_device_1, check_pubkey); +#ifdef ECC_USE_SECP256K1_LIB + u_assert_str_eq(recid_device_1, RECID_01); + u_assert_str_eq(recid_device_2, RECID_01); +#else + u_assert_str_eq(recid_device_1, RECID_EE); + u_assert_str_eq(recid_device_2, RECID_EE); +#endif + } else { + // Random seed generated on device + ASSERT_REPORT_HAS_NOT(check_sig_1); + ASSERT_REPORT_HAS_NOT(check_sig_2); + u_assert_str_not_eq(sig_device_1, check_sig_1); + u_assert_str_not_eq(sig_device_2, check_sig_2); + u_assert_str_not_eq(pubkey_device_1, check_pubkey); + } + + api_format_send_cmd(cmd_str(CMD_sign), checkpub_wrong_addr_len, KEY_STANDARD); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_SIGN_PUBKEY_LEN)); + + api_format_send_cmd(cmd_str(CMD_sign), checkpub_wrong_addr_len, KEY_STANDARD); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_SIGN_PUBKEY_LEN)); + + + // sign using two inputs api_format_send_cmd(cmd_str(CMD_sign), two_inputs, KEY_STANDARD); @@ -2743,6 +2732,49 @@ static void tests_sign(void) pin_err_count++; } + + // sign max number of hashes per sign command + api_format_send_cmd(cmd_str(CMD_sign), maxhashes, KEY_STANDARD); + ASSERT_REPORT_HAS(cmd_str(CMD_echo)); + + api_format_send_cmd(cmd_str(CMD_sign), "{\"pin\":\"0001\"}", KEY_STANDARD); + ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); + + // sign 1 more than max number of hashes per sign command + api_format_send_cmd(cmd_str(CMD_sign), hashoverflow, KEY_STANDARD); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_REPORT_BUF)); + + api_format_send_cmd(cmd_str(CMD_sign), "{\"pin\":\"0001\"}", KEY_STANDARD); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_REPORT_BUF)); + + { // 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\":\"" + "m/44'/61'/" "\"}]}", + KEY_STANDARD); + ASSERT_REPORT_HAS(cmd_str(CMD_echo)); + if (!TEST_LIVE_DEVICE) { + u_assert_str_eq(api_read_value(CMD_echo), ""); + } + api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); + ASSERT_REPORT_HAS(cmd_str(CMD_sig)); + + // exception only applies if all keypaths are eth/etc, not if others are + // present too: + api_format_send_cmd(cmd_str(CMD_sign), + "{\"meta\":\"_meta_data_\", \"data\":[{\"hash\":\"" HASH_INPUT_TWO_1 + "\", \"keypath\":\"" "m/44'/1'/" "\"},{\"hash\":\"" HASH_INPUT_TWO_2 "\", \"keypath\":\"" + "m/44'/61'/" "\"}]}", + KEY_STANDARD); + ASSERT_REPORT_HAS(cmd_str(CMD_echo)); + if (!TEST_LIVE_DEVICE) { + u_assert_str_not_eq(api_read_value(CMD_echo), ""); + } + api_format_send_cmd(cmd_str(CMD_sign), "{\"pin\":\"0001\"}", KEY_STANDARD); + ASSERT_REPORT_HAS(cmd_str(CMD_sig)); + } + for (; pin_err_count < COMMANDER_MAX_ATTEMPTS - 1; pin_err_count++) { api_format_send_cmd(cmd_str(CMD_sign), one_input, KEY_STANDARD); ASSERT_REPORT_HAS(cmd_str(CMD_echo)); @@ -2757,6 +2789,26 @@ static void tests_sign(void) ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_RESET)); 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. + api_reset_device(); + + api_format_send_cmd(cmd_str(CMD_password), tests_pwd, NULL); + ASSERT_SUCCESS; + + api_format_send_cmd(cmd_str(CMD_feature_set), "{\"pairing\":true}", KEY_STANDARD); + ASSERT_SUCCESS; + + api_format_send_cmd(cmd_str(CMD_device), attr_str(ATTR_info), KEY_STANDARD); + ASSERT_REPORT_HAS("\"pairing\":true"); + ASSERT_REPORT_HAS("\"lock\":false"); + + api_format_send_cmd(cmd_str(CMD_sign), one_input, KEY_STANDARD); + ASSERT_REPORT_HAS(cmd_str(CMD_echo)); + api_format_send_cmd(cmd_str(CMD_sign), "{\"pin\":\"000\"}", KEY_STANDARD); + ASSERT_REPORT_HAS(flag_msg(DBB_ERR_SIGN_TFA_PIN)); + ASSERT_REPORT_HAS(flag_msg(DBB_WARN_RESET)); + } } @@ -2799,6 +2851,7 @@ static void run_utests(void) { u_run_test(tests_memory_setup);// Keep first u_run_test(tests_name); + u_run_test(tests_pairing); u_run_test(tests_legacy_hidden_wallet); u_run_test(tests_u2f); u_run_test(tests_echo_tfa);