From 958cbe230063ce31da483ea5c46089a130b588bc Mon Sep 17 00:00:00 2001 From: matejcik Date: Thu, 19 Jan 2023 12:24:24 +0100 Subject: [PATCH] feat(legacy): allow Casa eth & btc signing for m/45' --- legacy/firmware/crypto.c | 28 ++++++++++++++------- legacy/firmware/ethereum.c | 49 ++++++++++++++++++++++++++++++++++-- tests/ui_tests/fixtures.json | 3 +++ 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/legacy/firmware/crypto.c b/legacy/firmware/crypto.c index 1a1296d12..8e111a112 100644 --- a/legacy/firmware/crypto.c +++ b/legacy/firmware/crypto.c @@ -527,15 +527,25 @@ bool coin_path_check(const CoinInfo *coin, InputScriptType script_type, valid = valid && (address_n[2] <= PATH_MAX_CHANGE); valid = valid && (address_n[3] <= PATH_MAX_ADDRESS_INDEX); } else if (address_n_count == 5) { - // Unchained Capital compatibility pattern. Will be removed in the - // future. - // m / 45' / coin_type' / account' / [0-1000000] / address_index - valid = valid && check_cointype(coin, address_n[1], full_check); - valid = valid && (address_n[2] & PATH_HARDENED); - valid = - valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT); - valid = valid && (address_n[3] <= 1000000); - valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX); + if (address_n[1] & PATH_HARDENED) { + // Unchained Capital compatibility pattern. Will be removed in the + // future. + // m / 45' / coin_type' / account' / [0-1000000] / address_index + valid = valid && check_cointype(coin, address_n[1], full_check); + valid = valid && (address_n[2] & PATH_HARDENED); + valid = + valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT); + valid = valid && (address_n[3] <= 1000000); + valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX); + } else { + // Casa proposed "universal multisig" pattern with unhardened parts. + // m/45'/coin_type/account/change/address_index + valid = valid && + check_cointype(coin, address_n[1] | PATH_HARDENED, full_check); + valid = valid && (address_n[2] <= PATH_MAX_ACCOUNT); + valid = valid && (address_n[3] <= PATH_MAX_CHANGE); + valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX); + } } else if (address_n_count == 6) { // Unchained Capital compatibility pattern. Will be removed in the // future. diff --git a/legacy/firmware/ethereum.c b/legacy/firmware/ethereum.c index a6a01ccf1..a35188e28 100644 --- a/legacy/firmware/ethereum.c +++ b/legacy/firmware/ethereum.c @@ -1041,8 +1041,9 @@ bool ethereum_parse(const char *address, uint8_t pubkeyhash[20]) { return true; } -bool ethereum_path_check(uint32_t address_n_count, const uint32_t *address_n, - bool pubkey_export, uint64_t chain) { +static bool ethereum_path_check_bip44(uint32_t address_n_count, + const uint32_t *address_n, + bool pubkey_export, uint64_t chain) { bool valid = (address_n_count >= 3); valid = valid && (address_n[0] == (PATH_HARDENED | 44)); valid = valid && (address_n[1] & PATH_HARDENED); @@ -1098,3 +1099,47 @@ bool ethereum_path_check(uint32_t address_n_count, const uint32_t *address_n, return valid; } + +static bool ethereum_path_check_casa45(uint32_t address_n_count, + const uint32_t *address_n, + uint64_t chain) { + bool valid = (address_n_count == 5); + valid = valid && (address_n[0] == (PATH_HARDENED | 45)); + valid = valid && (address_n[1] < PATH_HARDENED); + valid = valid && (address_n[2] <= PATH_MAX_ACCOUNT); + valid = valid && (address_n[3] <= PATH_MAX_CHANGE); + valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX); + + uint32_t path_slip44 = address_n[1]; + if (chain == CHAIN_ID_UNKNOWN) { + valid = valid && (is_ethereum_slip44(path_slip44)); + } else { + uint32_t chain_slip44 = ethereum_slip44_by_chain_id(chain); + if (chain_slip44 == SLIP44_UNKNOWN) { + // Allow Ethereum or testnet paths for unknown networks. + valid = valid && (path_slip44 == 60 || path_slip44 == 1); + } else if (chain_slip44 != 60 && chain_slip44 != 1) { + // Allow cross-signing with Ethereum unless it's testnet. + valid = valid && (path_slip44 == chain_slip44 || path_slip44 == 60); + } else { + valid = valid && (path_slip44 == chain_slip44); + } + } + + return valid; +} + +bool ethereum_path_check(uint32_t address_n_count, const uint32_t *address_n, + bool pubkey_export, uint64_t chain) { + if (address_n_count == 0) { + return false; + } + if (address_n[0] == (PATH_HARDENED | 44)) { + return ethereum_path_check_bip44(address_n_count, address_n, pubkey_export, + chain); + } + if (address_n[0] == (PATH_HARDENED | 45)) { + return ethereum_path_check_casa45(address_n_count, address_n, chain); + } + return false; +} diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index 29c80e2cf..a1507f4ba 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -161,6 +161,7 @@ "T1_bitcoin-test_nonstandard_paths.py::test_getaddress_multisig[paths4-address_index4]": "c245c47efa462d2e6d487d81f131d3ae561c0f30ad90e55b53b48ba8b46ebea1", "T1_bitcoin-test_nonstandard_paths.py::test_getaddress_multisig[paths5-address_index5]": "81e4b1b39a135814413c96e9fa4e2639a8ab6b2cb9e945445ee12a99e994b523", "T1_bitcoin-test_nonstandard_paths.py::test_getaddress_multisig[paths6-address_index6]": "5d57a31f47490c5738af8136bf9ec8ee9d0db980d1653b9eaedace9d60e74119", +"T1_bitcoin-test_nonstandard_paths.py::test_getaddress_multisig[paths7-address_index7]": "a28c13fc59443f3745bf794c002718c22ff39815e8961d7af4fbbd90777783b7", "T1_bitcoin-test_nonstandard_paths.py::test_getpublicnode[m-1195487518-6-255-script_types3]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_bitcoin-test_nonstandard_paths.py::test_getpublicnode[m-1195487518-script_types2]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_bitcoin-test_nonstandard_paths.py::test_getpublicnode[m-3h-100h-4-255-script_types1]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", @@ -183,6 +184,7 @@ "T1_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths4-address_index4]": "9f11f570c87a5fdf33dcd40fc7042b6beda409561b97d9b6cbe62df4b7979fd7", "T1_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths5-address_index5]": "9f11f570c87a5fdf33dcd40fc7042b6beda409561b97d9b6cbe62df4b7979fd7", "T1_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths6-address_index6]": "9f11f570c87a5fdf33dcd40fc7042b6beda409561b97d9b6cbe62df4b7979fd7", +"T1_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths7-address_index7]": "9f11f570c87a5fdf33dcd40fc7042b6beda409561b97d9b6cbe62df4b7979fd7", "T1_bitcoin-test_op_return.py::test_nonzero_opreturn": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_bitcoin-test_op_return.py::test_opreturn": "d38174445b68e2119f9d141c071ae7a993f76597f4ca3c5fd8f6a2e1e89aebcf", "T1_bitcoin-test_op_return.py::test_opreturn_address": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", @@ -362,6 +364,7 @@ "T1_ethereum-test_sign_verify_message.py::test_signmessage[parameters5-result5]": "a6b54a1244c1c457de732c2d27371475c02e451a4820f283912a5e3c62fe2842", "T1_ethereum-test_sign_verify_message.py::test_signmessage[parameters6-result6]": "858c5f14ecf61cbc85acb7d11f85b65a88c3d14f2591f185e1925092a977a7d6", "T1_ethereum-test_sign_verify_message.py::test_signmessage[parameters7-result7]": "72795258943a821df0fca67a6e1d915883c414e5066cff8f93d4a5735c0ba45b", +"T1_ethereum-test_sign_verify_message.py::test_signmessage[parameters8-result8]": "76c51c49c1ae5c4f77e15b9f053d8f27665d25a9866487b69d6eebb6386bf855", "T1_ethereum-test_sign_verify_message.py::test_verify[parameters0-result0]": "cdec0f79f2abbd90f4346494037f7bb4dd4dccc7c6739b497873d0c5603f2a26", "T1_ethereum-test_sign_verify_message.py::test_verify[parameters1-result1]": "9e51345ff0d3736f81a5c169768f9a80c555187eef2db3046f5ad12262990466", "T1_ethereum-test_sign_verify_message.py::test_verify[parameters2-result2]": "e2aff8a36dffd76b0e48256a5012ff191d0ce877a42cf31e1f25f7c3cd786d64",