From fddac0434b584fb83b3172e43c3bd4e602d1866d Mon Sep 17 00:00:00 2001 From: Martin Milata Date: Wed, 16 Jun 2021 22:04:08 +0200 Subject: [PATCH] test: add device tests for non-standard paths --- tests/device_tests/test_nonstandard_paths.py | 240 +++++++++++++++++++ tests/ui_tests/fixtures.json | 34 +++ 2 files changed, 274 insertions(+) create mode 100644 tests/device_tests/test_nonstandard_paths.py diff --git a/tests/device_tests/test_nonstandard_paths.py b/tests/device_tests/test_nonstandard_paths.py new file mode 100644 index 000000000..7a8d84967 --- /dev/null +++ b/tests/device_tests/test_nonstandard_paths.py @@ -0,0 +1,240 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2021 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +import pytest + +from trezorlib import btc, messages +from trezorlib.tools import parse_path + +from ..tx_cache import TxCache + +TX_CACHE_MAINNET = TxCache("Bitcoin") + +TXHASH_6189e3 = bytes.fromhex( + "6189e3febb5a21cee8b725aa1ef04ffce7e609448446d3a8d6f483c634ef5315" +) +TXHASH_d5f65e = bytes.fromhex( + "d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882" +) + + +VECTORS = ( + # GreenAddress A m/[1,4]/address_index + ( + "m/4/255", + ( + messages.InputScriptType.SPENDADDRESS, + messages.InputScriptType.SPENDWITNESS, + messages.InputScriptType.SPENDP2SHWITNESS, + ), + ), + # GreenAddress B m/3'/[1-100]'/[1,4]/address_index + ( + "m/3'/100'/4/255", + ( + messages.InputScriptType.SPENDADDRESS, + messages.InputScriptType.SPENDWITNESS, + messages.InputScriptType.SPENDP2SHWITNESS, + ), + ), + # GreenAdress Sign A m/1195487518 + ( + "m/1195487518", + ( + messages.InputScriptType.SPENDADDRESS, + messages.InputScriptType.SPENDWITNESS, + messages.InputScriptType.SPENDP2SHWITNESS, + ), + ), + # GreenAdress Sign B m/1195487518/6/address_index + ( + "m/1195487518/6/255", + ( + messages.InputScriptType.SPENDADDRESS, + messages.InputScriptType.SPENDWITNESS, + messages.InputScriptType.SPENDP2SHWITNESS, + ), + ), + # Casa m/49/coin_type/account/change/address_index + ( + "m/49/0/63/0/255", + (messages.InputScriptType.SPENDP2SHWITNESS,), + ), +) + +# 2-of-3 multisig, first path is ours +VECTORS_MULTISIG = ( + # GreenAddress A m/[1,4]/address_index + (("m/1", "m/1", "m/4"), [255]), + # GreenAddress B m/3'/[1-100]'/[1,4]/address_index + (("m/3'/100'/1", "m/3'/99'/1", "m/3'/98'/1"), [255]), + # GreenAdress Sign A m/1195487518 + (("m/1195487518", "m/1195487518", "m/1195487518"), []), + # GreenAdress Sign B m/1195487518/6/address_index + (("m/1195487518/6", "m/1195487518/6", "m/1195487518/6"), [255]), + # Unchained hardened m/45'/coin_type'/account'/[0-1000000]/change/address_index + ( + ("m/45'/0'/63'/1000000", "m/45'/0'/62'/1000000", "m/45'/0'/61'/1000000"), + [0, 255], + ), + # Unchained unhardened m/45'/coin_type/account/[0-1000000]/change/address_index + (("m/45'/0/63/1000000", "m/45'/0/62/1000000", "m/45'/0/61/1000000"), [0, 255]), + # Unchained deprecated m/45'/coin_type'/account'/[0-1000000]/address_index + (("m/45'/0'/63'/1000000", "m/45'/0'/62'/1000000", "m/45'/0/61/1000000"), [255]), +) + + +# Has AlwaysMatchingSchema but let's make sure the nonstandard paths are +# accepted in case we make this more restrictive in the future. +@pytest.mark.parametrize("path, script_types", VECTORS) +def test_getpublicnode(client, path, script_types): + for script_type in script_types: + res = btc.get_public_node( + client, parse_path(path), coin_name="Bitcoin", script_type=script_type + ) + + assert res.xpub + + +# See https://github.com/trezor/trezor-firmware/issues/1660 +# T1 fails on: +# test_getaddress[m/45'/0'/63'/1000000/0/255-script_types5] +# test_getaddress[m/45'/0'/63'/1000000/255-script_types7] +# test_getaddress[m/45'/0/63/1000000/0/255-script_types6] +@pytest.mark.skip_t1 +@pytest.mark.parametrize("path, script_types", VECTORS) +def test_getaddress(client, path, script_types): + for script_type in script_types: + res = btc.get_address( + client, + "Bitcoin", + parse_path(path), + show_display=True, + script_type=script_type, + ) + + assert res + + +@pytest.mark.parametrize("path, script_types", VECTORS) +def test_signmessage(client, path, script_types): + for script_type in script_types: + sig = btc.sign_message( + client, + coin_name="Bitcoin", + n=parse_path(path), + script_type=script_type, + message="This is an example of a signed message.", + ) + + assert sig.signature + + +@pytest.mark.parametrize("path, script_types", VECTORS) +def test_signtx(client, path, script_types): + # tx: d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882 + # input 0: 0.0039 BTC + + for script_type in script_types: + inp1 = messages.TxInputType( + address_n=parse_path(path), + amount=390000, + prev_hash=TXHASH_d5f65e, + prev_index=0, + script_type=script_type, + ) + + out1 = messages.TxOutputType( + address="1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1", + amount=390000 - 10000, + script_type=messages.OutputScriptType.PAYTOADDRESS, + ) + + _, serialized_tx = btc.sign_tx( + client, "Bitcoin", [inp1], [out1], prev_txes=TX_CACHE_MAINNET + ) + + assert serialized_tx.hex() + + +# See https://github.com/trezor/trezor-firmware/issues/1660 +# T1 fails on the Unchained paths +@pytest.mark.skip_t1 +@pytest.mark.multisig +@pytest.mark.parametrize("paths, address_index", VECTORS_MULTISIG) +def test_getaddress_multisig(client, paths, address_index): + pubs = [ + messages.HDNodePathType( + node=btc.get_public_node( + client, parse_path(path), coin_name="Bitcoin" + ).node, + address_n=address_index, + ) + for path in paths + ] + multisig = messages.MultisigRedeemScriptType(pubkeys=pubs, m=2) + + address = btc.get_address( + client, + "Bitcoin", + parse_path(paths[0]) + address_index, + show_display=True, + multisig=multisig, + script_type=messages.InputScriptType.SPENDMULTISIG, + ) + + assert address + + +# NOTE: we're signing input using the wrong key (and possibly script type) so +# the test is going to fail if we make firmware stricter about this +@pytest.mark.multisig +@pytest.mark.parametrize("paths, address_index", VECTORS_MULTISIG) +def test_signtx_multisig(client, paths, address_index): + pubs = [ + messages.HDNodePathType( + node=btc.get_public_node( + client, parse_path(path), coin_name="Bitcoin" + ).node, + address_n=address_index, + ) + for path in paths + ] + signatures = [b""] * 3 + multisig = messages.MultisigRedeemScriptType( + pubkeys=pubs, signatures=signatures, m=2 + ) + + out1 = messages.TxOutputType( + address="17kTB7qSk3MupQxWdiv5ZU3zcrZc2Azes1", + amount=10000, + script_type=messages.OutputScriptType.PAYTOADDRESS, + ) + + inp1 = messages.TxInputType( + address_n=parse_path(paths[0]) + address_index, + amount=20000, + prev_hash=TXHASH_6189e3, + prev_index=1, + script_type=messages.InputScriptType.SPENDMULTISIG, + multisig=multisig, + ) + + sig, serialized_tx = btc.sign_tx( + client, "Bitcoin", [inp1], [out1], prev_txes=TX_CACHE_MAINNET + ) + + assert sig[0] diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index 67ab6910a..e304c06bc 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -608,6 +608,40 @@ "test_multisig_change.py-test_multisig_external_external": "ddecdadd659b0d1360a6a255c6f9dbf2c5b813039877b76d4062ddab765e1912", "test_multisig_change.py-test_multisig_mismatch_change": "7cb243b20be31a587dced4aaaf782a2d8487595369dde66aacb1b9a76e89c4fe", "test_multisig_change.py-test_multisig_mismatch_inputs": "64741bd84c5394e719125c1fbe8c34ef866ac63ca24ee1299e4268c59a199466", +"test_nonstandard_paths.py::test_getaddress[m-1195487518-6-255-script_types3]": "4e1ff4e743e91769318fa032d10343c08ee016820df26c1b2a7c6e41b342b24c", +"test_nonstandard_paths.py::test_getaddress[m-1195487518-script_types2]": "fc89fc6ed34c87e94e45a4a0aeab1904009da2f52c5a54ca427209ae7ec0c968", +"test_nonstandard_paths.py::test_getaddress[m-3'-100'-4-255-script_types1]": "d16eecce052da865fe42416a7ab742f567cf7b508d1bb137de46116a513db52c", +"test_nonstandard_paths.py::test_getaddress[m-4-255-script_types0]": "a52db70d53d60c64f1a71f218e555e22e9ae94d7ffd52732047f44538f3830b2", +"test_nonstandard_paths.py::test_getaddress[m-49-0-63-0-255-script_types4]": "b085b06f6f9df4b30ea804a60ebec236ef49b0b3eaee160aeda761bb66233f41", +"test_nonstandard_paths.py::test_getaddress_multisig[paths0-address_index0]": "6167834e580e5b71924efb2d49c2cd6cb15b9b867ed370bf7ace8de93268d8b7", +"test_nonstandard_paths.py::test_getaddress_multisig[paths1-address_index1]": "daf72a1871bb00065121b40b2a3f21ff4dca95a5a8ff070837bb279c220077d2", +"test_nonstandard_paths.py::test_getaddress_multisig[paths2-address_index2]": "1db974899c9d3dd2226b5536a8322bd290ecdc0c2f4c0afbd6d2e95e15f84a93", +"test_nonstandard_paths.py::test_getaddress_multisig[paths3-address_index3]": "5f963cffddbc3d67449426d35527bc5e55f8d18722be82359a9fd33d7ebc5995", +"test_nonstandard_paths.py::test_getaddress_multisig[paths4-address_index4]": "120b10dee52854ac1b8c81f287c6ec3431837c8496a744b736d9e355032a6346", +"test_nonstandard_paths.py::test_getaddress_multisig[paths5-address_index5]": "5b7eff6e24643cd4d210adbfe07749fe85cd6972e5630d31b190117c76dfaab5", +"test_nonstandard_paths.py::test_getaddress_multisig[paths6-address_index6]": "8bf6b910c0825cdfadad54e05424c504282f9f7d85697e8d46eaeba5c9d6e614", +"test_nonstandard_paths.py::test_getpublicnode[m-1195487518-6-255-script_types3]": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586", +"test_nonstandard_paths.py::test_getpublicnode[m-1195487518-script_types2]": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586", +"test_nonstandard_paths.py::test_getpublicnode[m-3'-100'-4-255-script_types1]": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586", +"test_nonstandard_paths.py::test_getpublicnode[m-4-255-script_types0]": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586", +"test_nonstandard_paths.py::test_getpublicnode[m-49-0-63-0-255-script_types4]": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586", +"test_nonstandard_paths.py::test_signmessage[m-1195487518-6-255-script_types3]": "4e9b7043ca9370f0d5096ed23e765167338b77b9fb757bd1d97bdfde0cac22c2", +"test_nonstandard_paths.py::test_signmessage[m-1195487518-script_types2]": "4e9b7043ca9370f0d5096ed23e765167338b77b9fb757bd1d97bdfde0cac22c2", +"test_nonstandard_paths.py::test_signmessage[m-3'-100'-4-255-script_types1]": "4e9b7043ca9370f0d5096ed23e765167338b77b9fb757bd1d97bdfde0cac22c2", +"test_nonstandard_paths.py::test_signmessage[m-4-255-script_types0]": "4e9b7043ca9370f0d5096ed23e765167338b77b9fb757bd1d97bdfde0cac22c2", +"test_nonstandard_paths.py::test_signmessage[m-49-0-63-0-255-script_types4]": "7a87ec7a17c6acb3522c496318a116b4adde87373d4408201a8e3de4dacfaa3d", +"test_nonstandard_paths.py::test_signtx[m-1195487518-6-255-script_types3]": "fd335f664cc3833358f00155a066db16f5da4cc70a869346f3da0ab4b44408e8", +"test_nonstandard_paths.py::test_signtx[m-1195487518-script_types2]": "87882c107c09b5ba9fcfd13d57c37d2a7bb6a8bf76b108a3dbfbad3d0b81c613", +"test_nonstandard_paths.py::test_signtx[m-3'-100'-4-255-script_types1]": "b5508e9edf659b9924af8ebfc1d7d9e9970413d9db3fa5205ce711d73d2b8775", +"test_nonstandard_paths.py::test_signtx[m-4-255-script_types0]": "b5508e9edf659b9924af8ebfc1d7d9e9970413d9db3fa5205ce711d73d2b8775", +"test_nonstandard_paths.py::test_signtx[m-49-0-63-0-255-script_types4]": "f6b6662fa1384f20640522a169575f8ca26185fca8ca3bc2a3a5ccd1fa9d2f68", +"test_nonstandard_paths.py::test_signtx_multisig[paths0-address_index0]": "20062ac3d40bfb9636f020983c104e2d2636e91e616748e22ed425d77bbcaeda", +"test_nonstandard_paths.py::test_signtx_multisig[paths1-address_index1]": "20062ac3d40bfb9636f020983c104e2d2636e91e616748e22ed425d77bbcaeda", +"test_nonstandard_paths.py::test_signtx_multisig[paths2-address_index2]": "428937206cad23cde98c14c3f8b1b9a21d9a70bb891aff4570b01adb0c544366", +"test_nonstandard_paths.py::test_signtx_multisig[paths3-address_index3]": "2b978eae303cfdfd0168c65dafcd8c2272a5e9f463a5e0be7b79f1f0fa905c58", +"test_nonstandard_paths.py::test_signtx_multisig[paths4-address_index4]": "20062ac3d40bfb9636f020983c104e2d2636e91e616748e22ed425d77bbcaeda", +"test_nonstandard_paths.py::test_signtx_multisig[paths5-address_index5]": "20062ac3d40bfb9636f020983c104e2d2636e91e616748e22ed425d77bbcaeda", +"test_nonstandard_paths.py::test_signtx_multisig[paths6-address_index6]": "20062ac3d40bfb9636f020983c104e2d2636e91e616748e22ed425d77bbcaeda", "test_op_return.py-test_nonzero_opreturn": "ff8306b910f6886638e30736acd025ff7f45dde3c6648de1f6c6922bc6f590c5", "test_op_return.py-test_opreturn": "87907ef9c2f4ce30ac95ad7d0cb3eac66762756e4ace52147bc589d64277f3b1", "test_op_return.py-test_opreturn_address": "ff8306b910f6886638e30736acd025ff7f45dde3c6648de1f6c6922bc6f590c5",