From 08852f256074ac38da5cc10dbcb15b9b97373e04 Mon Sep 17 00:00:00 2001 From: djb Date: Sat, 15 Sep 2018 08:39:05 +0200 Subject: [PATCH] remove deprecated USB API backup recovery require user supplied entropy to be 64-characters hash entropy --- src/commander.c | 21 ++- src/random.c | 13 +- src/sha2.c | 7 +- src/utils.c | 2 +- tests/api.h | 24 ++++ tests/tests_api.c | 331 ++++++++++++++++++++++++++++++++-------------- 6 files changed, 288 insertions(+), 110 deletions(-) diff --git a/src/commander.c b/src/commander.c index 19aeedef..8af88348 100644 --- a/src/commander.c +++ b/src/commander.c @@ -50,6 +50,11 @@ #include "ecdh.h" +#if defined(TESTING) && defined(__SAM4S4A__) +#error TESTING should not be defined for embedded code +#endif + + #define BRACED(x) (strlens(x) ? (((x[0]) == '{') && ((x[strlens(x) - 1]) == '}')) : 0) @@ -467,13 +472,11 @@ static void commander_process_seed(yajl_val json_node) int ret; const char *key_path[] = { cmd_str(CMD_seed), cmd_str(CMD_key), NULL }; - const char *raw_path[] = { cmd_str(CMD_seed), cmd_str(CMD_raw), NULL }; const char *source_path[] = { cmd_str(CMD_seed), cmd_str(CMD_source), NULL }; const char *entropy_path[] = { cmd_str(CMD_seed), cmd_str(CMD_entropy), NULL }; const char *filename_path[] = { cmd_str(CMD_seed), cmd_str(CMD_filename), NULL }; const char *u2f_counter_path[] = { cmd_str(CMD_seed), cmd_str(CMD_U2F_counter), NULL }; const char *key = YAJL_GET_STRING(yajl_tree_get(json_node, key_path, yajl_t_string)); - const char *raw = YAJL_GET_STRING(yajl_tree_get(json_node, raw_path, yajl_t_string)); const char *source = YAJL_GET_STRING(yajl_tree_get(json_node, source_path, yajl_t_string)); const char *entropy = YAJL_GET_STRING(yajl_tree_get(json_node, entropy_path, @@ -510,7 +513,7 @@ static void commander_process_seed(yajl_val json_node) if (STREQ(source, attr_str(ATTR_create))) { // Generate a new wallet, optionally with entropy entered via USB - uint8_t i, add_entropy, entropy_b[MEM_PAGE_LEN]; + uint8_t i, add_entropy = 1, entropy_b[MEM_PAGE_LEN]; char entropy_c[MEM_PAGE_LEN * 2 + 1]; memset(entropy_b, 0, sizeof(entropy_b)); @@ -521,20 +524,23 @@ static void commander_process_seed(yajl_val json_node) } if (strlens(entropy)) { - if (strlens(entropy) == MEM_PAGE_LEN * 2 && utils_is_hex(entropy)) { - // Allows recover from a Digital Bitbox backup text entered via USB - memcpy(entropy_b, utils_hex_to_uint8(entropy), sizeof(entropy_b)); + if (strlens(entropy) != SHA256_DIGEST_LENGTH * 2) { + commander_fill_report(cmd_str(CMD_seed), NULL, DBB_ERR_IO_INVALID_CMD); + return; } sha256_Raw((const uint8_t *)entropy, strlens(entropy), entropy_b); } +#ifdef TESTING // Add extra entropy from device unless raw is set - add_entropy = 1; + const char *raw_path[] = { cmd_str(CMD_seed), cmd_str(CMD_raw), NULL }; + const char *raw = YAJL_GET_STRING(yajl_tree_get(json_node, raw_path, yajl_t_string)); if (strlens(entropy) && strlens(raw)) { if (STREQ(raw, attr_str(ATTR_true))) { add_entropy = 0; } } +#endif if (add_entropy) { uint8_t number[MEM_PAGE_LEN]; @@ -545,6 +551,7 @@ static void commander_process_seed(yajl_val json_node) for (i = 0; i < MEM_PAGE_LEN; i++) { entropy_b[i] ^= number[i]; } + sha256_Raw(entropy_b, sizeof(entropy_b), entropy_b); } snprintf(entropy_c, sizeof(entropy_c), "%s", utils_uint8_to_hex(entropy_b, diff --git a/src/random.c b/src/random.c index 566bbe0a..55172202 100644 --- a/src/random.c +++ b/src/random.c @@ -89,7 +89,7 @@ int random_bytes(uint8_t *buf, uint32_t len, uint8_t update_seed) for (i = 0; i < MIN(len, MEM_PAGE_LEN); i++) { buf[i] ^= entropy[i]; } - // Add entropy from usersig + // Add entropy from the random number stored in the MCU's 'user signature' memory area flash_read_user_signature((uint32_t *)usersig, FLASH_USERSIG_SIZE / sizeof(uint32_t)); sha256_Raw(usersig, FLASH_USERSIG_SIZE, entropy); for (i = 0; i < MIN(len, MEM_PAGE_LEN); i++) { @@ -105,13 +105,13 @@ int random_bytes(uint8_t *buf, uint32_t len, uint8_t update_seed) while (len > n) { if (update_seed) { ret = ataes_process(ataes_cmd_up, sizeof(ataes_cmd_up), ataes_ret, sizeof(ataes_ret)); - update_seed = 0; } else { ret = ataes_process(ataes_cmd, sizeof(ataes_cmd), ataes_ret, sizeof(ataes_ret)); } if (ret == DBB_OK && ataes_ret[0] && !ataes_ret[1]) { - for (i = 0; i < MIN(len - n, ATAES_RAND_LEN); i++) { - buf[(n + i) % len] ^= ataes_ret[(2 + i) % sizeof(ataes_ret)]; + sha256_Raw(ataes_ret + 2, ATAES_RAND_LEN, entropy); + for (i = 0; i < MIN(len - n, MEM_PAGE_LEN); i++) { + buf[(n + i)] ^= entropy[i]; } } else { flash_erase_user_signature(); @@ -121,6 +121,11 @@ int random_bytes(uint8_t *buf, uint32_t len, uint8_t update_seed) } n += ATAES_RAND_LEN; } + // Replaces up to the first 32B of the random number with a SHA256 hash + // Helps protect against a backdoored HRNG + sha256_Raw(buf, len, entropy); + memcpy(buf, entropy, MIN(len, MEM_PAGE_LEN)); + utils_zero(entropy, sizeof(entropy)); #endif return DBB_OK; } diff --git a/src/sha2.c b/src/sha2.c index ba3853be..c38cee79 100644 --- a/src/sha2.c +++ b/src/sha2.c @@ -134,8 +134,13 @@ typedef uint64_t sha2_word64; /* Exactly 8 bytes */ } \ } -#define MEMSET_BZERO(p,l) memset((p), 0, (l)) #define MEMCPY_BCOPY(d,s,l) memcpy((d), (s), (l)) +static volatile void *MEMSET_BZERO(volatile void *dst, size_t len) +{ + volatile char *buf; + for (buf = (volatile char *)dst; len; buf[--len] = 0); + return dst; +} /*** THE SIX LOGICAL FUNCTIONS ****************************************/ /* diff --git a/src/utils.c b/src/utils.c index 46fda0a2..70dd72a6 100644 --- a/src/utils.c +++ b/src/utils.c @@ -43,7 +43,7 @@ static uint8_t utils_buffer[UTILS_BUFFER_LEN]; volatile void *utils_zero(volatile void *dst, size_t len) { volatile char *buf; - for (buf = (volatile char *)dst; len; buf[--len] = 0); + for (buf = (volatile char *)dst; len; buf[--len] = 0); return dst; } diff --git a/tests/api.h b/tests/api.h index de3ba8f0..b2bf8cf0 100644 --- a/tests/api.h +++ b/tests/api.h @@ -474,6 +474,30 @@ static const char *api_read_value_depth_2(int cmd, int cmd_2) } +static const char *api_read_array_value(int cmd, int cmd_2, int index) +{ + const char *path[] = { cmd_str(cmd), NULL }; + const char *a_path[] = { cmd_str(cmd_2), NULL }; + static char value[HID_REPORT_SIZE]; + memset(value, 0, sizeof(value)); + + yajl_val json_node = yajl_tree_parse(api_read_decrypted_report(), NULL, 0); + if (json_node && YAJL_IS_OBJECT(json_node)) { + yajl_val data = yajl_tree_get(json_node, path, yajl_t_array); + if (YAJL_IS_ARRAY(data) && data->u.array.len != 0) { + yajl_val obj = data->u.array.values[index]; + const char *a = YAJL_GET_STRING(yajl_tree_get(obj, a_path, yajl_t_string)); + if (a) { + snprintf(value, sizeof(value), "%s", a); + } + } + } + + yajl_tree_free(json_node); + return value; +} + + static char *api_read_value_decrypt(int cmd, uint8_t *key) { const char *val = api_read_value(cmd); diff --git a/tests/tests_api.c b/tests/tests_api.c index e048909a..d2433cde 100644 --- a/tests/tests_api.c +++ b/tests/tests_api.c @@ -33,6 +33,7 @@ #include "ecdh.h" #include "ecc.h" #include "sha2.h" +#include "bip32.h" #include "utest.h" #include "utils.h" #include "flags.h" @@ -46,6 +47,23 @@ #include "api.h" #include "hmac_check.h" + +#define HASH_DEFAULT "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" +#define HASH_INPUT_ONE "c6fa4c236f59020ec8ffde22f85a78e7f256e94cd975eb5199a4a5cc73e26e4a" +#define HASH_INPUT_TWO_1 "c12d791451bb41fd4b5145bcef25f794ca33c0cf4fe9d24f956086c5aa858a9d" +#define HASH_INPUT_TWO_2 "3dfc3b1ed349e9b361b31c706fbf055ebf46ae725740f6739e2dfa87d2a98790" +#define KEYPATH_BASE "m/44p" +#define KEYPATH_ONE "m/44'/0'/0'/1/7" +#define KEYPATH_TWO "m/44'/0'/0'/1/8" +#define KEYPATH_THREE "m/44'/0'/0'/0/5" +#define PUBKEY_INPUT_ONE "025acc8c55e1a786f7b8ca742f725909019c849abe2051b7bc8bc580af3dc17154" +#define PUBKEY_INPUT_TWO_1 "035e8c69793fd853795759b8ca12229d7b2e7ec2223221dc224885fc9a1e7e1704" +#define PUBKEY_INPUT_TWO_2 "03cc673784d8dfe97ded72c91ebb1b87a52761e4be20f6229e56fd61fdf28ae3f2" +#define PUBKEY_ZERO "000000000000000000000000000000000000000000000000000000000000000000" +#define RECID_01 "01" +#define RECID_EE "ee" + + int U_TESTS_RUN = 0; int U_TESTS_FAIL = 0; @@ -67,7 +85,10 @@ static void tests_seed_xpub_backup(void) "{\"source\":\"create\", \"filename\":\"seed_create_2.pdf\", \"key\":\"password\"}"; char seed_create_bad[] = "{\"source\":\"create\", \"filename\":\"../seed_create_bad.pdf\", \"key\":\"password\"}"; - char seed_entropy[] = "entropy9s21ZrQkmL8hdy"; + char seed_entropy[] = "entropy_>= COMMANDER_NUM_SIG_MIN, 1); - char checkpub_msg_1[] = - "c6fa4c236f59020ec8ffde22f85a78e7f256e94cd975eb5199a4a5cc73e26e4a"; - char checkpub_msg_2[] = - "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; char checkpub[] = - "{\"meta\":\"<>\", \"data\": [{\"hash\":\"c6fa4c236f59020ec8ffde22f85a78e7f256e94cd975eb5199a4a5cc73e26e4a\", \"keypath\":\"m/44p\"},{\"hash\":\"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\", \"keypath\":\"m/44p\"}], \"checkpub\":[{\"pubkey\":\"000000000000000000000000000000000000000000000000000000000000000000\", \"keypath\":\"m/44p/0p/0p/1/8\"},{\"pubkey\":\"035e8c69793fd853795759b8ca12229d7b2e7ec2223221dc224885fc9a1e7e1704\", \"keypath\":\"m/44p/0p/0p/1/8\"}]}"; + "{\"meta\":\"<>\", \"data\": [{\"hash\":\"" HASH_INPUT_ONE + "\", \"keypath\":\"" KEYPATH_BASE "\"},{\"hash\":\"" HASH_DEFAULT "\", \"keypath\":\"" + KEYPATH_BASE "\"}], \"checkpub\":[{\"pubkey\":\"" PUBKEY_ZERO "\", \"keypath\":\"" + KEYPATH_TWO "\"},{\"pubkey\":\"" PUBKEY_INPUT_TWO_1 "\", \"keypath\":\"" + KEYPATH_TWO"\"}]}"; char check_1[] = - "\"pubkey\":\"000000000000000000000000000000000000000000000000000000000000000000\", \"present\":false"; + "\"pubkey\":\"" PUBKEY_ZERO "\", \"present\":false"; char check_2[] = - "\"pubkey\":\"035e8c69793fd853795759b8ca12229d7b2e7ec2223221dc224885fc9a1e7e1704\", \"present\":true"; + "\"pubkey\":\"" PUBKEY_INPUT_TWO_1 "\", \"present\":true"; // check_sig_1 is normalized (low-S). The non-normalized value is "2a756acd456b732e779cd7e7f05ae2855ee3128bca75cb8fc805bba8c6fbabba924e51ccac655024165bb302d00174d842e5960cde8c448a6a900d26fe342fe9" char check_sig_1[] = "2a756acd456b732e779cd7e7f05ae2855ee3128bca75cb8fc805bba8c6fbabba6db1ae33539aafdbe9a44cfd2ffe8b2677c946d9d0bc5bb155425165d2021158"; @@ -2181,14 +2203,26 @@ static void tests_sign(void) "3323b353e9974ab1eb452eca19e1dc4dad0556dba668089c8c1214ca09b58ad12c82da6671a65f9b3803c1fe7a14f35d885caebce701f972641748738275782b"; char check_pubkey[] = "02df86355b162c942b89e297c1e919f40943834d448b0b6b4538556e805ea4e18c"; - char check_recid_1[] = "01"; - char check_recid_2[] = "01"; char checkpub_wrong_addr_len[] = - "{\"meta\":\"<>\", \"data\": [{\"hash\":\"c6fa4c236f59020ec8ffde22f85a78e7f256e94cd975eb5199a4a5cc73e26e4a\", \"keypath\":\"m/44p\"},{\"hash\":\"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\", \"keypath\":\"m/44p\"}], \"checkpub\":[{\"pubkey\":\"00\", \"keypath\":\"m/44p/0p/0p/1/8\"},{\"pubkey\":\"035e8c69793fd853795759b8ca12229d7b2e7ec2223221dc224885fc9a1e7e1704\", \"keypath\":\"m/44p/0p/0p/1/8\"}]}"; + "{\"meta\":\"<>\", \"data\": [{\"hash\":\"" HASH_INPUT_ONE + "\", \"keypath\":\"" KEYPATH_BASE "\"},{\"hash\":\"" HASH_DEFAULT "\", \"keypath\":\"" + KEYPATH_BASE "\"}], \"checkpub\":[{\"pubkey\":\"00\", \"keypath\":\"" KEYPATH_TWO + "\"},{\"pubkey\":\"" PUBKEY_INPUT_TWO_1 "\", \"keypath\":\"" KEYPATH_TWO "\"}]}"; char checkpub_missing_parameter[] = - "{\"meta\":\"<>\", \"data\": [{\"hash\":\"c6fa4c236f59020ec8ffde22f85a78e7f256e94cd975eb5199a4a5cc73e26e4a\", \"keypath\":\"m/44p\"},{\"hash\":\"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\", \"keypath\":\"m/44p\"}], \"checkpub\":[{\"pubkey\":\"035e8c69793fd853795759b8ca12229d7b2e7ec2223221dc224885fc9a1e7e1704\"}]}"; + "{\"meta\":\"<>\", \"data\": [{\"hash\":\"" HASH_INPUT_ONE + "\", \"keypath\":\"" KEYPATH_BASE "\"},{\"hash\":\"" HASH_DEFAULT "\", \"keypath\":\"" + KEYPATH_BASE "\"}], \"checkpub\":[{\"pubkey\":\"" PUBKEY_INPUT_TWO_1 "\"}]}"; + + char sig_device_1[64 * 2 + 1]; + char sig_device_2[64 * 2 + 1]; + char recid_device_1[2 + 1]; + char recid_device_2[2 + 1]; + char pubkey_device_1[33 * 2 + 1]; + char pubkey_device_2[33 * 2 + 1]; + HDNode node; + memset(&node, 0, sizeof(HDNode)); api_reset_device(); @@ -2200,9 +2234,14 @@ static void tests_sign(void) api_format_send_cmd(cmd_str(CMD_backup), attr_str(ATTR_erase), KEY_STANDARD); ASSERT_SUCCESS; - // seed - char seed[] = - "{\"key\":\"key\", \"source\":\"create\", \"entropy\":\"entropy_rawH13ucR3\", \"raw\":\"true\", \"filename\":\"s.pdf\"}"; + // 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.pdf"); api_format_send_cmd(cmd_str(CMD_seed), seed, KEY_STANDARD); ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); @@ -2257,6 +2296,11 @@ static void tests_sign(void) ASSERT_REPORT_HAS(flag_msg(DBB_ERR_IO_INVALID_CMD)); // sign using one input + api_format_send_cmd(cmd_str(CMD_xpub), KEYPATH_ONE, 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)); + api_format_send_cmd(cmd_str(CMD_sign), one_input, KEY_STANDARD); ASSERT_REPORT_HAS(cmd_str(CMD_echo)); if (!TEST_LIVE_DEVICE) { @@ -2268,18 +2312,48 @@ static void tests_sign(void) 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, "m/44'/0'/0'/1/7"); + u_assert_str_has(echo, KEYPATH_ONE); free(echo); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); ASSERT_REPORT_HAS(cmd_str(CMD_recid)); - ASSERT_REPORT_HAS(hash_1_input); - res = recover_public_key_verify_sig(hash_1_input, one_input_msg, recid_1_input, - pubkey_1_input); - u_assert_int_eq(res, 0); + + memcpy(sig_device_1, api_read_array_value(CMD_sign, CMD_sig, 0), sizeof(sig_device_1)); + memcpy(recid_device_1, api_read_array_value(CMD_sign, CMD_recid, 0), + 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)); + + if (!TEST_LIVE_DEVICE) { + // If TESTING, a deterministic seed is loaded when 'raw' is specified + ASSERT_REPORT_HAS(sig_1_input); + u_assert_str_eq(sig_device_1, sig_1_input); + u_assert_str_eq(pubkey_device_1, PUBKEY_INPUT_ONE); +#ifdef ECC_USE_SECP256K1_LIB + u_assert_str_eq(recid_device_1, RECID_01); +#else + u_assert_str_eq(recid_device_1, RECID_EE); +#endif + } else { + // Random seed generated on device + ASSERT_REPORT_HAS_NOT(sig_1_input); + u_assert_str_not_eq(sig_device_1, sig_1_input); + u_assert_str_not_eq(pubkey_device_1, PUBKEY_INPUT_ONE); + } // sign using two inputs + api_format_send_cmd(cmd_str(CMD_xpub), KEYPATH_TWO, 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)); + + api_format_send_cmd(cmd_str(CMD_xpub), KEYPATH_THREE, KEY_STANDARD); + hdnode_deserialize(api_read_value(CMD_xpub), &node); + 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), two_inputs, KEY_STANDARD); ASSERT_REPORT_HAS(cmd_str(CMD_echo)); if (!TEST_LIVE_DEVICE) { @@ -2291,24 +2365,60 @@ static void tests_sign(void) 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, "m/44'/0'/0'/1/8"); + u_assert_str_has(echo, KEYPATH_TWO); free(echo); } api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); ASSERT_REPORT_HAS(cmd_str(CMD_sign)); - ASSERT_REPORT_HAS(hash_2_input_1); - ASSERT_REPORT_HAS(hash_2_input_2); - ASSERT_REPORT_HAS(cmd_str(CMD_recid)); - res = recover_public_key_verify_sig(hash_2_input_1, two_input_msg_1, recid_2_input_1, - pubkey_2_input_1); - u_assert_int_eq(res, 0); - res = recover_public_key_verify_sig(hash_2_input_2, two_input_msg_2, recid_2_input_2, - pubkey_2_input_2); - u_assert_int_eq(res, 0); + 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_2)); + + u_assert_int_eq(0, recover_public_key_verify_sig(sig_device_1, HASH_INPUT_TWO_1, + recid_device_1, pubkey_device_1)); + u_assert_int_eq(0, recover_public_key_verify_sig(sig_device_2, HASH_INPUT_TWO_2, + recid_device_2, pubkey_device_2)); + + if (!TEST_LIVE_DEVICE) { + // If TESTING, a deterministic seed is loaded when 'raw' is specified + ASSERT_REPORT_HAS(sig_2_input_1); + ASSERT_REPORT_HAS(sig_2_input_2); + ASSERT_REPORT_HAS(cmd_str(CMD_recid)); + u_assert_str_eq(sig_device_1, sig_2_input_1); + u_assert_str_eq(sig_device_2, sig_2_input_2); + u_assert_str_eq(pubkey_device_1, PUBKEY_INPUT_TWO_1); + u_assert_str_eq(pubkey_device_2, PUBKEY_INPUT_TWO_2); +#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(sig_2_input_1); + ASSERT_REPORT_HAS_NOT(sig_2_input_2); + ASSERT_REPORT_HAS(cmd_str(CMD_recid)); + u_assert_str_not_eq(sig_device_1, sig_2_input_1); + u_assert_str_not_eq(sig_device_2, sig_2_input_2); + u_assert_str_not_eq(pubkey_device_1, PUBKEY_INPUT_TWO_1); + 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) { @@ -2327,14 +2437,41 @@ static void tests_sign(void) api_format_send_cmd(cmd_str(CMD_sign), "", KEY_STANDARD); ASSERT_REPORT_HAS(cmd_str(CMD_sign)); - ASSERT_REPORT_HAS(check_sig_1); - ASSERT_REPORT_HAS(check_sig_2); - res = recover_public_key_verify_sig(check_sig_1, checkpub_msg_1, check_recid_1, - check_pubkey); - u_assert_int_eq(res, 0); - res = recover_public_key_verify_sig(check_sig_2, checkpub_msg_2, check_recid_2, - check_pubkey); - u_assert_int_eq(res, 0); + + 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)); @@ -2360,7 +2497,7 @@ static void tests_sign(void) 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, "m/44'/0'/0'/1/7"); + u_assert_str_has(echo, KEYPATH_ONE); u_assert_str_has(echo, cmd_str(CMD_pin)); free(echo); } @@ -2384,7 +2521,7 @@ static void tests_sign(void) 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, "m/44'/0'/0'/1/7"); + u_assert_str_has(echo, KEYPATH_ONE); u_assert_str_has(echo, cmd_str(CMD_pin)); free(echo); } @@ -2404,7 +2541,7 @@ static void tests_sign(void) 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, "m/44'/0'/0'/1/7"); + u_assert_str_has(echo, KEYPATH_ONE); u_assert_str_has(echo, cmd_str(CMD_pin)); free(echo); } else { @@ -2415,10 +2552,10 @@ static void tests_sign(void) if (!TEST_LIVE_DEVICE) { ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); ASSERT_REPORT_HAS(cmd_str(CMD_sign)); - ASSERT_REPORT_HAS(hash_1_input); + ASSERT_REPORT_HAS(sig_1_input); ASSERT_REPORT_HAS(cmd_str(CMD_recid)); - res = recover_public_key_verify_sig(hash_1_input, one_input_msg, recid_1_input, - pubkey_1_input); + res = recover_public_key_verify_sig(sig_1_input, HASH_INPUT_ONE, RECID_01, + PUBKEY_INPUT_ONE); u_assert_int_eq(res, 0); } else { pin_err_count++; @@ -2437,7 +2574,7 @@ static void tests_sign(void) 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, "m/44'/0'/0'/1/8"); + u_assert_str_has(echo, KEYPATH_TWO); free(echo); } @@ -2446,14 +2583,14 @@ static void tests_sign(void) if (!TEST_LIVE_DEVICE) { ASSERT_REPORT_HAS_NOT(attr_str(ATTR_error)); ASSERT_REPORT_HAS(cmd_str(CMD_sign)); - ASSERT_REPORT_HAS(hash_2_input_1); - ASSERT_REPORT_HAS(hash_2_input_2); + ASSERT_REPORT_HAS(sig_2_input_1); + ASSERT_REPORT_HAS(sig_2_input_2); ASSERT_REPORT_HAS(cmd_str(CMD_recid)); - res = recover_public_key_verify_sig(hash_2_input_1, two_input_msg_1, recid_2_input_1, - pubkey_2_input_1); + res = recover_public_key_verify_sig(sig_2_input_1, HASH_INPUT_TWO_1, RECID_01, + PUBKEY_INPUT_TWO_1); u_assert_int_eq(res, 0); - res = recover_public_key_verify_sig(hash_2_input_2, two_input_msg_2, recid_2_input_2, - pubkey_2_input_2); + res = recover_public_key_verify_sig(sig_2_input_2, HASH_INPUT_TWO_2, RECID_01, + PUBKEY_INPUT_TWO_2); u_assert_int_eq(res, 0); } else { pin_err_count++;