From b798ca479469a29e4eb66b4bfa011cd708a865ad Mon Sep 17 00:00:00 2001 From: matejcik Date: Mon, 1 Jul 2024 15:26:40 +0200 Subject: [PATCH 1/4] trezor: add Trezor Safe 5 support --- hwilib/devices/trezorlib/models.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/hwilib/devices/trezorlib/models.py b/hwilib/devices/trezorlib/models.py index af83214a2..3e1f7261a 100644 --- a/hwilib/devices/trezorlib/models.py +++ b/hwilib/devices/trezorlib/models.py @@ -63,6 +63,15 @@ class TrezorModel: default_mapping=mapping.DEFAULT_MAPPING, ) +T3T1 = TrezorModel( + name="Safe 5", + internal_name="T3T1", + minimum_version=(2, 1, 0), + vendors=VENDORS, + usb_ids=((0x1209, 0x53C1), (0x1209, 0x53C0)), + default_mapping=mapping.DEFAULT_MAPPING, +) + DISC1 = TrezorModel( name="DISC1", internal_name="D001", @@ -72,15 +81,26 @@ class TrezorModel: default_mapping=mapping.DEFAULT_MAPPING, ) +DISC2 = TrezorModel( + name="DISC2", + internal_name="D002", + minimum_version=(2, 1, 0), + vendors=VENDORS, + usb_ids=((0x1209, 0x53C1), (0x1209, 0x53C0)), + default_mapping=mapping.DEFAULT_MAPPING, +) + # ==== model based names ==== TREZOR_ONE = T1B1 TREZOR_T = T2T1 TREZOR_R = T2B1 TREZOR_SAFE3 = T2B1 +TREZOR_SAFE5 = T3T1 TREZOR_DISC1 = DISC1 +TREZOR_DISC2 = DISC2 -TREZORS = {T1B1, T2T1, T2B1, DISC1} +TREZORS = {T1B1, T2T1, T2B1, T3T1, DISC1, DISC2} def by_name(name: Optional[str]) -> Optional[TrezorModel]: From e114031f48b639119ff79391890553f654c0573a Mon Sep 17 00:00:00 2001 From: matejcik Date: Mon, 1 Jul 2024 16:20:25 +0200 Subject: [PATCH 2/4] build: make sure Trezors are built with gcc 12 this is already installed --- test/setup_environment.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/setup_environment.sh b/test/setup_environment.sh index 25e6bde3f..6a3f948d3 100755 --- a/test/setup_environment.sh +++ b/test/setup_environment.sh @@ -90,6 +90,7 @@ if [[ -n ${build_trezor_1} || -n ${build_trezor_t} ]]; then poetry install cd legacy export EMULATOR=1 TREZOR_TRANSPORT_V1=1 DEBUG_LINK=1 HEADLESS=1 + export CC=gcc-12 poetry run pip install protobuf==3.20.0 poetry run script/setup poetry run script/cibuild @@ -108,6 +109,7 @@ if [[ -n ${build_trezor_1} || -n ${build_trezor_t} ]]; then # But there should be some caching that makes this faster poetry install cd core + export CC=gcc-12 poetry run make build_unix # Delete any emulator.img file find . -name "trezor.flash" -exec rm {} \; From 232aa54e6646118778146413123322163839769a Mon Sep 17 00:00:00 2001 From: matejcik Date: Thu, 17 Mar 2022 12:37:25 +0100 Subject: [PATCH 3/4] trezor: restore support for external inputs --- docs/devices/index.rst | 100 +++++++++++++++++++-------------------- hwilib/devices/trezor.py | 37 ++++++--------- test/test_device.py | 2 +- test/test_trezor.py | 25 +++++++--- 4 files changed, 84 insertions(+), 80 deletions(-) diff --git a/docs/devices/index.rst b/docs/devices/index.rst index 62bd179ed..d04718b8b 100644 --- a/docs/devices/index.rst +++ b/docs/devices/index.rst @@ -10,56 +10,56 @@ The table below lists what devices and features are supported for each device. * ``✗`` - supported by the firmware and not implemented in HWI * ``―`` - not supported by the firmware -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| Feature \\ Device | Ledger Nano X | Ledger Nano S | Trezor One | Trezor Model T | BitBox01 | BitBox02 | KeepKey | Coldcard | Blockstream Jade | -+====================================+===============+===============+==============+================+==========+==========+=========+==========+==================+ -| Support Planned | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| Implemented | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| xpub retrieval | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| Message Signing | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| Device Setup | ― | ― | ✓ | ✓ | ✓ | ✓ | ✓ | ― | ― | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| Device Wipe | ― | ― | ✓ | ✓ | ✓ | ✓ | ✓ | ― | ― | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| Device Recovery | ― | ― | ✓ | ✓ | ― | ✓ | ✓ | ― | ― | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| Device Backup | ― | ― | ― | ― | ✓ | ✓ | ― | ✓ | ― | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| P2PKH Inputs | ✓ | ✓ | ✓ | ✓ | ✓ | ― | ✓ | ✓ | ✓ | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| P2SH-P2WPKH Inputs | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| P2WPKH Inputs | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| P2SH Multisig Inputs | ✓ | ✓ | ✓ | ✓ | ✓ | ― | ✓ | ✓ | ✓ | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| P2SH-P2WSH Multisig Inputs | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| P2WSH Multisig Inputs | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| Bare Multisig Inputs | ✓ | ✓ | ― | ― | ✓ | ― | ― | ― | ✓ | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| Taproot Inputs | ✓* | ✓* | ✓* | ✓* | ― | ― | ― | ― | ― | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| Arbitrary scriptPubKey Inputs | ✓ | ✓ | ― | ― | ✓ | ― | ― | ― | ✓ | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| Arbitrary redeemScript Inputs | ✓ | ✓ | ― | ― | ✓ | ― | ― | ― | ✓ | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| Arbitrary witnessScript Inputs | ✓ | ✓ | ― | ― | ✓ | ― | ― | ― | ✓ | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| Non-wallet inputs | ✓ | ✓ | ✗\ :sup:`1` | ✗\ :sup:`2` | ✓ | ― | ✓ | ✓ | ✓ | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| Mixed Segwit and Non-Segwit Inputs | ― | ― | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ -| Display on device screen | ✓ | ✓ | ✓ | ✓ | ― | ✓ | ✓ | ✓ | ✓ | -+------------------------------------+---------------+---------------+--------------+----------------+----------+----------+---------+----------+------------------+ - -* 1 - Support removed for devices with firmware 1.10.6 and greater. -* 2 - Support removed for devices with firmware 2.4.4 and greater. ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| Feature \\ Device | Ledger Nano X | Ledger Nano S | Trezor One | later Trezor models | BitBox01 | BitBox02 | KeepKey | Coldcard | Blockstream Jade | ++====================================+===============+===============+==============+=====================+==========+==========+=========+==========+==================+ +| Support Planned | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| Implemented | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| xpub retrieval | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| Message Signing | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| Device Setup | ― | ― | ✓ | ✓ | ✓ | ✓ | ✓ | ― | ― | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| Device Wipe | ― | ― | ✓ | ✓ | ✓ | ✓ | ✓ | ― | ― | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| Device Recovery | ― | ― | ✓ | ✓ | ― | ✓ | ✓ | ― | ― | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| Device Backup | ― | ― | ― | ― | ✓ | ✓ | ― | ✓ | ― | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| P2PKH Inputs | ✓ | ✓ | ✓ | ✓ | ✓ | ― | ✓ | ✓ | ✓ | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| P2SH-P2WPKH Inputs | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| P2WPKH Inputs | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| P2SH Multisig Inputs | ✓ | ✓ | ✓ | ✓ | ✓ | ― | ✓ | ✓ | ✓ | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| P2SH-P2WSH Multisig Inputs | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| P2WSH Multisig Inputs | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| Bare Multisig Inputs | ✓ | ✓ | ― | ― | ✓ | ― | ― | ― | ✓ | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| Taproot Inputs | ✓* | ✓* | ✓* | ✓* | ― | ― | ― | ― | ― | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| Arbitrary scriptPubKey Inputs | ✓ | ✓ | ― | ― | ✓ | ― | ― | ― | ✓ | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| Arbitrary redeemScript Inputs | ✓ | ✓ | ― | ― | ✓ | ― | ― | ― | ✓ | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| Arbitrary witnessScript Inputs | ✓ | ✓ | ― | ― | ✓ | ― | ― | ― | ✓ | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| Non-wallet inputs | ✓ | ✓ | ✓\ :sup:`1` | ✓\ :sup:`2` | ✓ | ― | ✓ | ✓ | ✓ | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| Mixed Segwit and Non-Segwit Inputs | ― | ― | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ +| Display on device screen | ✓ | ✓ | ✓ | ✓ | ― | ✓ | ✓ | ✓ | ✓ | ++------------------------------------+---------------+---------------+--------------+---------------------+----------+----------+---------+----------+------------------+ + +* 1 - Since firmware 1.11.1, safety checks must be disabled. +* 2 - Since firmware 2.5.1, safety checks must be disabled. \* There are some caveats. See the `sign_tx` for these devices. diff --git a/hwilib/devices/trezor.py b/hwilib/devices/trezor.py index bd5a79733..68e455f91 100644 --- a/hwilib/devices/trezor.py +++ b/hwilib/devices/trezor.py @@ -276,6 +276,14 @@ def get_path_transport( raise BadArgumentError(f"Could not find device by path: {path}") +def _use_external_script_type(client) -> bool: + if client.features.model == "1" and client.version >= (1, 11, 1): + return True + if client.features.vendor == "trezor.io" and client.version >= (2, 5, 1): + return True + return False + + # This class extends the HardwareWalletClient for Trezor specific things class TrezorClient(HardwareWalletClient): @@ -332,15 +340,6 @@ def _check_unlocked(self) -> None: if self.client.features.passphrase_protection and self.password is None: raise NoPasswordError("Passphrase protection is enabled, passphrase must be provided") - def _supports_external(self) -> bool: - if self.client.features.model == "1" and self.client.version <= (1, 10, 5): - return True - if self.client.features.model == "T" and self.client.version <= (2, 4, 3): - return True - if self.client.features.model == "K1-14AM": - return True - return False - @trezor_exception def get_pubkey_at_path(self, path: str) -> ExtendedKey: self._check_unlocked() @@ -436,9 +435,13 @@ def sign_tx(self, tx: PSBT) -> PSBT: p2wsh = True def ignore_input() -> None: - txinputtype.address_n = [0x80000000 | 84, 0x80000000 | (0 if self.chain == Chain.MAIN else 1), 0x80000000, 0, 0] txinputtype.multisig = None - txinputtype.script_type = messages.InputScriptType.SPENDWITNESS + if _use_external_script_type(self.client): + txinputtype.script_type = messages.InputScriptType.EXTERNAL + txinputtype.script_pubkey = utxo.scriptPubKey + else: + txinputtype.address_n = [0x80000000 | 84, 0x80000000 | (0 if self.chain == Chain.MAIN else 1), 0x80000000, 0, 0] + txinputtype.script_type = messages.InputScriptType.SPENDWITNESS inputs.append(txinputtype) to_ignore.append(input_num) @@ -451,21 +454,12 @@ def ignore_input() -> None: if utxo.is_p2sh(): txinputtype.script_type = messages.InputScriptType.SPENDMULTISIG else: - # Cannot sign bare multisig, ignore it - if not self._supports_external(): - raise BadArgumentError("Cannot sign bare multisig") ignore_input() continue elif not is_ms and not is_wit and not is_p2pkh(scriptcode): - # Cannot sign unknown spk, ignore it - if not self._supports_external(): - raise BadArgumentError("Cannot sign unknown scripts") ignore_input() continue elif not is_ms and is_wit and p2wsh: - # Cannot sign unknown witness script, ignore it - if not self._supports_external(): - raise BadArgumentError("Cannot sign unknown witness versions") ignore_input() continue @@ -502,9 +496,6 @@ def ignore_input() -> None: passes = our_keys if not found and not found_in_sigs: # None of our keys were in hd_keypaths or in partial_sigs - # This input is not one of ours - if not self._supports_external(): - raise BadArgumentError("Cannot sign external inputs") ignore_input() continue elif not found and found_in_sigs: diff --git a/test/test_device.py b/test/test_device.py index eb7fe501c..2edbfbcc5 100644 --- a/test/test_device.py +++ b/test/test_device.py @@ -584,7 +584,7 @@ def test_signtx(self): # Make a huge transaction which might cause some problems with different interfaces def test_big_tx(self): # make a huge transaction - keypool_desc = self.do_command(self.dev_args + ["getkeypool", "--account", "10", "--addr-type", "legacy", "0", "100"]) + keypool_desc = self.do_command(self.dev_args + ["getkeypool", "--account", "10", "--addr-type", "legacy", "0", "200"]) import_result = self.wrpc.importdescriptors(keypool_desc) self.assertTrue(import_result[0]['success']) outputs = [] diff --git a/test/test_trezor.py b/test/test_trezor.py index 0b70e0ee4..cdde6eabc 100755 --- a/test/test_trezor.py +++ b/test/test_trezor.py @@ -28,7 +28,7 @@ ) from hwilib._cli import process_commands -from hwilib.devices.trezor import TrezorClient +from hwilib.devices.trezor import TrezorClient, _use_external_script_type from types import MethodType @@ -63,6 +63,7 @@ def __init__(self, path, model): self.strict_bip48 = True self.include_xpubs = False self.supports_device_multiple_multisig = True + self.client = None def start(self): super().start() @@ -85,12 +86,12 @@ def start(self): # Setup the emulator wirelink = UdpTransport.enumerate()[0] - client = TrezorClientDebugLink(wirelink) - client.init_device() - device.wipe(client) - load_device_by_mnemonic(client=client, mnemonic='alcohol woman abuse must during monitor noble actual mixed trade anger aisle', pin='', passphrase_protection=False, label='test') # From Trezor device tests + self.client = TrezorClientDebugLink(wirelink) + self.client.init_device() + device.wipe(self.client) + load_device_by_mnemonic(client=self.client, mnemonic='alcohol woman abuse must during monitor noble actual mixed trade anger aisle', pin='', passphrase_protection=False, label='test') # From Trezor device tests atexit.register(self.stop) - return client + return self.client def stop(self): super().stop() @@ -415,6 +416,16 @@ def test_passphrase(self): else: self.fail("Did not enumerate device") +class TestSignTxTrezorExternal(TestSignTx): + def setUp(self): + super().setUp() + if _use_external_script_type(self.emulator.client): + device.apply_settings(self.emulator.client, safety_checks=messages.SafetyCheckLevel.PromptTemporarily) + + def tearDown(self): + device.apply_settings(self.emulator.client, safety_checks=messages.SafetyCheckLevel.Strict) + return super().tearDown() + def trezor_test_suite(emulator, bitcoind, interface, model): assert model in TREZOR_MODELS # Redirect stderr to /dev/null as it's super spammy @@ -429,6 +440,7 @@ def trezor_test_suite(emulator, bitcoind, interface, model): (["legacy", "segwit"], ["legacy", "segwit"], False, True), (["legacy", "segwit", "tap"], ["legacy", "segwit"], False, True), ] + signtx_cases_external = [(_addr, _multi, True, _opret) for _addr, _multi, _ext, _opret in signtx_cases] # Generic Device tests suite = unittest.TestSuite() @@ -436,6 +448,7 @@ def trezor_test_suite(emulator, bitcoind, interface, model): suite.addTest(DeviceTestCase.parameterize(TestGetDescriptors, bitcoind, emulator=dev_emulator, interface=interface)) suite.addTest(DeviceTestCase.parameterize(TestGetKeypool, bitcoind, emulator=dev_emulator, interface=interface)) suite.addTest(DeviceTestCase.parameterize(TestSignTx, bitcoind, emulator=dev_emulator, interface=interface, signtx_cases=signtx_cases)) + suite.addTest(DeviceTestCase.parameterize(TestSignTxTrezorExternal, bitcoind, emulator=dev_emulator, interface=interface, signtx_cases=signtx_cases_external)) suite.addTest(DeviceTestCase.parameterize(TestDisplayAddress, bitcoind, emulator=dev_emulator, interface=interface)) suite.addTest(DeviceTestCase.parameterize(TestSignMessage, bitcoind, emulator=dev_emulator, interface=interface)) if model != 't': From 015936525cb8085d8fa000743397a89f94b052c4 Mon Sep 17 00:00:00 2001 From: matejcik Date: Wed, 23 Mar 2022 11:51:44 +0100 Subject: [PATCH 4/4] trezor: add support for pre-signed external inputs --- hwilib/devices/trezor.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hwilib/devices/trezor.py b/hwilib/devices/trezor.py index 68e455f91..34840cee5 100644 --- a/hwilib/devices/trezor.py +++ b/hwilib/devices/trezor.py @@ -439,6 +439,11 @@ def ignore_input() -> None: if _use_external_script_type(self.client): txinputtype.script_type = messages.InputScriptType.EXTERNAL txinputtype.script_pubkey = utxo.scriptPubKey + if psbt_in.final_script_sig: + txinputtype.script_sig = psbt_in.final_script_sig + witness = psbt_in.final_script_witness.serialize() + if witness: + txinputtype.witness = witness else: txinputtype.address_n = [0x80000000 | 84, 0x80000000 | (0 if self.chain == Chain.MAIN else 1), 0x80000000, 0, 0] txinputtype.script_type = messages.InputScriptType.SPENDWITNESS