From 473ea195703fe7c2af5725e635fbfb79688906ce Mon Sep 17 00:00:00 2001 From: matejcik Date: Fri, 2 Mar 2018 15:35:56 +0100 Subject: [PATCH 01/12] trezorlib/transport: rename files as separate step (to make diffs nicer) --- trezorlib/{transport.py => transport/__init__.py} | 0 trezorlib/{transport_bridge.py => transport/bridge.py} | 0 trezorlib/{transport_hid.py => transport/hid.py} | 0 trezorlib/{transport_udp.py => transport/udp.py} | 0 trezorlib/{transport_webusb.py => transport/webusb.py} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename trezorlib/{transport.py => transport/__init__.py} (100%) rename trezorlib/{transport_bridge.py => transport/bridge.py} (100%) rename trezorlib/{transport_hid.py => transport/hid.py} (100%) rename trezorlib/{transport_udp.py => transport/udp.py} (100%) rename trezorlib/{transport_webusb.py => transport/webusb.py} (100%) diff --git a/trezorlib/transport.py b/trezorlib/transport/__init__.py similarity index 100% rename from trezorlib/transport.py rename to trezorlib/transport/__init__.py diff --git a/trezorlib/transport_bridge.py b/trezorlib/transport/bridge.py similarity index 100% rename from trezorlib/transport_bridge.py rename to trezorlib/transport/bridge.py diff --git a/trezorlib/transport_hid.py b/trezorlib/transport/hid.py similarity index 100% rename from trezorlib/transport_hid.py rename to trezorlib/transport/hid.py diff --git a/trezorlib/transport_udp.py b/trezorlib/transport/udp.py similarity index 100% rename from trezorlib/transport_udp.py rename to trezorlib/transport/udp.py diff --git a/trezorlib/transport_webusb.py b/trezorlib/transport/webusb.py similarity index 100% rename from trezorlib/transport_webusb.py rename to trezorlib/transport/webusb.py From bc8120230aa6bcb0bbe89aef7e57822bfcd73886 Mon Sep 17 00:00:00 2001 From: matejcik Date: Fri, 2 Mar 2018 15:44:24 +0100 Subject: [PATCH 02/12] trezorlib/transport: make changes to support being a separate submodule, move common functions to superclass --- trezorlib/transport/__init__.py | 22 +++++++++++++++++++--- trezorlib/transport/bridge.py | 26 +++++--------------------- trezorlib/transport/hid.py | 24 ++++-------------------- trezorlib/transport/udp.py | 20 +++++++------------- trezorlib/transport/webusb.py | 21 ++++----------------- 5 files changed, 39 insertions(+), 74 deletions(-) diff --git a/trezorlib/transport/__init__.py b/trezorlib/transport/__init__.py index 9d747c570..a9d15ef02 100644 --- a/trezorlib/transport/__init__.py +++ b/trezorlib/transport/__init__.py @@ -17,9 +17,6 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from __future__ import absolute_import - - class TransportException(Exception): pass @@ -29,6 +26,12 @@ class Transport(object): def __init__(self): self.session_counter = 0 + def __str__(self): + return self.get_path() + + def get_path(self): + return '{}:{}'.format(self.PATH_PREFIX, self.device) + def session_begin(self): if self.session_counter == 0: self.open() @@ -44,3 +47,16 @@ class Transport(object): def close(self): raise NotImplementedError + + @classmethod + def enumerate(cls): + raise NotImplementedError + + @classmethod + def find_by_path(cls, path, prefix_search = True): + for device in cls.enumerate(): + if path is None or device.get_path() == path \ + or (prefix_search and device.get_path().startswith(path)): + return device + + raise TransportException('{} device not found: {}'.format(cls.PATH_PREFIX, path)) diff --git a/trezorlib/transport/bridge.py b/trezorlib/transport/bridge.py index 599f3556e..cb38ec64f 100644 --- a/trezorlib/transport/bridge.py +++ b/trezorlib/transport/bridge.py @@ -17,17 +17,15 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from __future__ import absolute_import - import requests import binascii from io import BytesIO import struct -from . import mapping -from . import messages -from . import protobuf -from .transport import Transport, TransportException +from .. import mapping +from .. import messages +from .. import protobuf +from . import Transport, TransportException TREZORD_HOST = 'http://127.0.0.1:21325' @@ -45,16 +43,13 @@ class BridgeTransport(Transport): HEADERS = {'Origin': 'https://python.trezor.io'} def __init__(self, device): - super(BridgeTransport, self).__init__() + super().__init__() self.device = device self.conn = requests.Session() self.session = None self.response = None - def __str__(self): - return self.get_path() - def get_path(self): return '%s:%s' % (self.PATH_PREFIX, self.device['path']) @@ -68,17 +63,6 @@ class BridgeTransport(Transport): except: return [] - @classmethod - def find_by_path(cls, path): - if isinstance(path, bytes): - path = path.decode() - path = path.replace('%s:' % cls.PATH_PREFIX, '') - - for transport in BridgeTransport.enumerate(): - if path is None or transport.device['path'] == path: - return transport - raise TransportException('Bridge device not found') - def open(self): r = self.conn.post(TREZORD_HOST + '/acquire/%s/null' % self.device['path'], headers=self.HEADERS) if r.status_code != 200: diff --git a/trezorlib/transport/hid.py b/trezorlib/transport/hid.py index 91f19b2f1..3a6884ca2 100644 --- a/trezorlib/transport/hid.py +++ b/trezorlib/transport/hid.py @@ -16,22 +16,20 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from __future__ import absolute_import - import time import hid import os -from .protocol_v1 import ProtocolV1 -from .protocol_v2 import ProtocolV2 -from .transport import Transport, TransportException +from ..protocol_v1 import ProtocolV1 +from ..protocol_v2 import ProtocolV2 +from . import Transport, TransportException DEV_TREZOR1 = (0x534c, 0x0001) DEV_TREZOR2 = (0x1209, 0x53c1) DEV_TREZOR2_BL = (0x1209, 0x53c0) -class HidHandle(object): +class HidHandle: def __init__(self, path): self.path = path @@ -79,9 +77,6 @@ class HidTransport(Transport): self.hid = hid_handle self.hid_version = None - def __str__(self): - return self.get_path() - def get_path(self): return "%s:%s" % (self.PATH_PREFIX, self.device['path'].decode()) @@ -100,17 +95,6 @@ class HidTransport(Transport): devices.append(HidTransport(dev)) return devices - @classmethod - def find_by_path(cls, path): - if isinstance(path, str): - path = path.encode() - path = path.replace(b'%s:' % cls.PATH_PREFIX.encode(), b'') - - for transport in HidTransport.enumerate(): - if path is None or transport.device['path'] == path: - return transport - raise TransportException('HID device not found') - def find_debug(self): if isinstance(self.protocol, ProtocolV2): # For v2 protocol, lets use the same HID interface, but with a different session diff --git a/trezorlib/transport/udp.py b/trezorlib/transport/udp.py index ad520f8d5..dc688c9ec 100644 --- a/trezorlib/transport/udp.py +++ b/trezorlib/transport/udp.py @@ -16,14 +16,12 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from __future__ import absolute_import - import os import socket -from .protocol_v1 import ProtocolV1 -from .protocol_v2 import ProtocolV2 -from .transport import Transport, TransportException +from ..protocol_v1 import ProtocolV1 +from ..protocol_v2 import ProtocolV2 +from . import Transport, TransportException class UdpTransport(Transport): @@ -48,12 +46,13 @@ class UdpTransport(Transport): self.protocol = protocol self.socket = None - def __str__(self): - return self.get_path() - def get_path(self): return "%s:%s:%s" % ((self.PATH_PREFIX,) + self.device) + def find_debug(self): + host, port = self.device + return UdpTransport('{}:{}'.format(host, port+1), self.protocol) + @staticmethod def enumerate(): devices = [] @@ -64,11 +63,6 @@ class UdpTransport(Transport): d.close() return devices - @classmethod - def find_by_path(cls, path): - path = path.replace('%s:' % cls.PATH_PREFIX, '') - return UdpTransport(path) - def open(self): self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.socket.connect(self.device) diff --git a/trezorlib/transport/webusb.py b/trezorlib/transport/webusb.py index 1e8d0b433..4b27aac40 100644 --- a/trezorlib/transport/webusb.py +++ b/trezorlib/transport/webusb.py @@ -16,16 +16,14 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from __future__ import absolute_import - import time import os import atexit import usb1 -from .protocol_v1 import ProtocolV1 -from .protocol_v2 import ProtocolV2 -from .transport import Transport, TransportException +from ..protocol_v1 import ProtocolV1 +from ..protocol_v2 import ProtocolV2 +from . import Transport, TransportException DEV_TREZOR1 = (0x534c, 0x0001) DEV_TREZOR2 = (0x1209, 0x53c1) @@ -37,7 +35,7 @@ DEBUG_INTERFACE = 1 DEBUG_ENDPOINT = 2 -class WebUsbHandle(object): +class WebUsbHandle: def __init__(self, device): self.device = device @@ -88,9 +86,6 @@ class WebUsbTransport(Transport): self.handle = handle self.debug = debug - def __str__(self): - return self.get_path() - def get_path(self): return "%s:%s" % (self.PATH_PREFIX, dev_to_str(self.device)) @@ -109,14 +104,6 @@ class WebUsbTransport(Transport): devices.append(WebUsbTransport(dev)) return devices - @classmethod - def find_by_path(cls, path): - path = path.replace('%s:' % cls.PATH_PREFIX, '') # Remove prefix from __str__() - for transport in WebUsbTransport.enumerate(): - if path is None or dev_to_str(transport.device) == path: - return transport - raise TransportException('WebUSB device not found') - def find_debug(self): if isinstance(self.protocol, ProtocolV2): # TODO test this From 49790d7bfe4176ec04f4055fb2d84b30e7efc8fc Mon Sep 17 00:00:00 2001 From: matejcik Date: Fri, 2 Mar 2018 16:43:41 +0100 Subject: [PATCH 03/12] install the new trasport subpackage --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 8da3a4b48..eef0c1c73 100755 --- a/setup.py +++ b/setup.py @@ -33,6 +33,7 @@ setup( url='https://github.com/trezor/python-trezor', packages=[ 'trezorlib', + 'trezorlib.transport', 'trezorlib.messages', 'trezorlib.qt', 'trezorlib.tests.device_tests', From d2913c20bdf6fde015f31eede8fce362c5ab64e0 Mon Sep 17 00:00:00 2001 From: matejcik Date: Fri, 2 Mar 2018 16:46:10 +0100 Subject: [PATCH 04/12] trezorlib/transport: move TrezorDevice functionality to transport and make it better ^_^ --- trezorlib/device.py | 68 --------------------------------- trezorlib/transport/__init__.py | 34 +++++++++++++++++ 2 files changed, 34 insertions(+), 68 deletions(-) delete mode 100644 trezorlib/device.py diff --git a/trezorlib/device.py b/trezorlib/device.py deleted file mode 100644 index 2e3b42b90..000000000 --- a/trezorlib/device.py +++ /dev/null @@ -1,68 +0,0 @@ -# This file is part of the TREZOR project. -# -# Copyright (C) 2012-2017 Marek Palatinus -# Copyright (C) 2012-2017 Pavol Rusnak -# -# This library is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# 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 GNU Lesser General Public License -# along with this library. If not, see . - - -from .transport_bridge import BridgeTransport -from .transport_hid import HidTransport -from .transport_udp import UdpTransport -from .transport_webusb import WebUsbTransport - - -class TrezorDevice(object): - - @classmethod - def enumerate(cls): - devices = [] - - for d in BridgeTransport.enumerate(): - devices.append(d) - - for d in UdpTransport.enumerate(): - devices.append(d) - - for d in HidTransport.enumerate(): - devices.append(d) - - for d in WebUsbTransport.enumerate(): - devices.append(d) - - return devices - - @classmethod - def find_by_path(cls, path): - if path is None: - try: - return cls.enumerate()[0] - except IndexError: - raise Exception("No TREZOR device found") - - prefix = path.split(':')[0] - - if prefix == BridgeTransport.PATH_PREFIX: - return BridgeTransport.find_by_path(path) - - if prefix == UdpTransport.PATH_PREFIX: - return UdpTransport.find_by_path(path) - - if prefix == WebUsbTransport.PATH_PREFIX: - return WebUsbTransport.find_by_path(path) - - if prefix == HidTransport.PATH_PREFIX: - return HidTransport.find_by_path(path) - - raise Exception("Unknown path prefix '%s'" % prefix) diff --git a/trezorlib/transport/__init__.py b/trezorlib/transport/__init__.py index a9d15ef02..9f60c68d6 100644 --- a/trezorlib/transport/__init__.py +++ b/trezorlib/transport/__init__.py @@ -60,3 +60,37 @@ class Transport(object): return device raise TransportException('{} device not found: {}'.format(cls.PATH_PREFIX, path)) + + +def all_transports(): + from .bridge import BridgeTransport + from .hid import HidTransport + from .udp import UdpTransport + from .webusb import WebUsbTransport + return (BridgeTransport, HidTransport, UdpTransport, WebUsbTransport) + + +def enumerate_devices(): + return [device + for transport in all_transports() + for device in transport.enumerate()] + + +def get_transport(path=None, prefix_search=False): + if path is None: + try: + return enumerate_devices()[0] + except IndexError: + raise Exception("No TREZOR device found") + + # Find whether B is prefix of A (transport name is part of the path) + # or A is prefix of B (path is a prefix, or a name, of transport). + # This naively expects that no two transports have a common prefix. + def match_prefix(a,b): + return a.startswith(b) or b.startswith(a) + + transports = [t for t in all_transports() if match_prefix(path, t.PATH_PREFIX)] + if transports: + return transports[0].find_by_path(path, prefix_search=prefix_search) + + raise Exception("Unknown path prefix '%s'" % prefix) From 6519657808b79a5eb6015258c0117bca2503b88b Mon Sep 17 00:00:00 2001 From: matejcik Date: Fri, 2 Mar 2018 18:22:33 +0100 Subject: [PATCH 05/12] trezorlib/transport: smarter handling of prefix search For UDP transport, it's useful to be able to specify a path that should be tried directly, without enumerating first. --- trezorlib/transport/__init__.py | 2 +- trezorlib/transport/udp.py | 35 +++++++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/trezorlib/transport/__init__.py b/trezorlib/transport/__init__.py index 9f60c68d6..c586e3fce 100644 --- a/trezorlib/transport/__init__.py +++ b/trezorlib/transport/__init__.py @@ -53,7 +53,7 @@ class Transport(object): raise NotImplementedError @classmethod - def find_by_path(cls, path, prefix_search = True): + def find_by_path(cls, path, prefix_search=False): for device in cls.enumerate(): if path is None or device.get_path() == path \ or (prefix_search and device.get_path().startswith(path)): diff --git a/trezorlib/transport/udp.py b/trezorlib/transport/udp.py index dc688c9ec..059222377 100644 --- a/trezorlib/transport/udp.py +++ b/trezorlib/transport/udp.py @@ -53,15 +53,34 @@ class UdpTransport(Transport): host, port = self.device return UdpTransport('{}:{}'.format(host, port+1), self.protocol) - @staticmethod - def enumerate(): + @classmethod + def _try_path(cls, path): + d = cls(path) + try: + d.open() + if d._ping(): + return d + else: + raise TransportException('No TREZOR device found at address {}'.format(path)) + finally: + d.close() + + @classmethod + def enumerate(cls): devices = [] - d = UdpTransport("%s:%d" % (UdpTransport.DEFAULT_HOST, UdpTransport.DEFAULT_PORT)) - d.open() - if d._ping(): - devices.append(d) - d.close() - return devices + default_path = '{}:{}'.format(cls.DEFAULT_HOST, cls.DEFAULT_PORT) + try: + return [cls._try_path(default_path)] + except TransportException: + return [] + + @classmethod + def find_by_path(cls, path, prefix_search=False): + if prefix_search: + return super().find_by_path(path, prefix_search) + else: + path = path.replace('{}:'.format(cls.PATH_PREFIX), '') + return cls._try_path(path) def open(self): self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) From 2a706a751a5211867fa3ac6614ffe3fc55c8862f Mon Sep 17 00:00:00 2001 From: matejcik Date: Fri, 2 Mar 2018 18:24:05 +0100 Subject: [PATCH 06/12] update trezorctl to use prefix search correctly first, try exact path second, try prefix search last, fail :) with reporting used path (for people like me who forget to unset TREZOR_PATH --- trezorctl | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/trezorctl b/trezorctl index 7646d4439..ab3d03a2b 100755 --- a/trezorctl +++ b/trezorctl @@ -29,7 +29,7 @@ import os import sys from trezorlib.client import TrezorClient, TrezorClientVerbose, CallException -from trezorlib.device import TrezorDevice +from trezorlib.transport import get_transport, enumerate_devices, TransportException from trezorlib import messages as proto from trezorlib import protobuf from trezorlib.coins import coins_txapi @@ -72,9 +72,24 @@ CHOICE_OUTPUT_SCRIPT_TYPE = ChoiceType({ def cli(ctx, path, verbose, is_json): if ctx.invoked_subcommand != 'list': if verbose: - ctx.obj = lambda: TrezorClientVerbose(TrezorDevice.find_by_path(path)) + cls = TrezorClientVerbose else: - ctx.obj = lambda: TrezorClient(TrezorDevice.find_by_path(path)) + cls = TrezorClient + + def get_device(): + try: + device = get_transport(path, prefix_search=False) + except: + try: + device = get_transport(path, prefix_search=True) + except: + click.echo("Failed to find a TREZOR device.") + if path is not None: + click.echo("Using path: {}".format(path)) + sys.exit(1) + return cls(device) + + ctx.obj = get_device @cli.resultcallback() @@ -106,7 +121,7 @@ def print_result(res, path, verbose, is_json): @cli.command(name='list', help='List connected TREZOR devices.') def ls(): - return TrezorDevice.enumerate() + return enumerate_devices() @cli.command(help='Show version of trezorctl/trezorlib.') From 513e6aae086b9ea62828ee4abdd0988b60c38129 Mon Sep 17 00:00:00 2001 From: matejcik Date: Fri, 2 Mar 2018 18:25:39 +0100 Subject: [PATCH 07/12] better way for test suite to search for the right device, that also respects TREZOR_PATH --- trezorlib/tests/device_tests/common.py | 77 +++----------------------- 1 file changed, 7 insertions(+), 70 deletions(-) diff --git a/trezorlib/tests/device_tests/common.py b/trezorlib/tests/device_tests/common.py index e702fdd99..0fffb7ae9 100644 --- a/trezorlib/tests/device_tests/common.py +++ b/trezorlib/tests/device_tests/common.py @@ -23,86 +23,23 @@ import pytest import os from trezorlib.client import TrezorClient, TrezorClientDebugLink +from trezorlib.transport import get_transport from trezorlib import tx_api tests_dir = os.path.dirname(os.path.abspath(__file__)) tx_api.cache_dir = os.path.join(tests_dir, '../txcache') -try: - from trezorlib.transport_hid import HidTransport - HID_ENABLED = True -except ImportError as e: - print('HID transport disabled:', e) - HID_ENABLED = False - -try: - from trezorlib.transport_webusb import WebUsbTransport - WEBUSB_ENABLED = True -except ImportError as e: - print('WebUSB transport disabled:', e) - WEBUSB_ENABLED = False - -try: - from trezorlib.transport_pipe import PipeTransport - PIPE_ENABLED = True -except ImportError as e: - print('PIPE transport disabled:', e) - PIPE_ENABLED = False - -try: - from trezorlib.transport_udp import UdpTransport - UDP_ENABLED = True -except ImportError as e: - print('UDP transport disabled:', e) - UDP_ENABLED = False +def get_device(): + path = os.environ.get('TREZOR_PATH') + return get_transport(path) -def pipe_exists(path): - import os - import stat - try: - return stat.S_ISFIFO(os.stat(path).st_mode) - except: - return False - - -def get_transport(): - if HID_ENABLED and HidTransport.enumerate(): - devices = HidTransport.enumerate() - wirelink = devices[0] - debuglink = devices[0].find_debug() - - elif WEBUSB_ENABLED and WebUsbTransport.enumerate(): - devices = WebUsbTransport.enumerate() - wirelink = devices[0] - debuglink = devices[0].find_debug() - - elif PIPE_ENABLED and pipe_exists('/tmp/pipe.trezor.to'): - wirelink = PipeTransport('/tmp/pipe.trezor', False) - debuglink = PipeTransport('/tmp/pipe.trezor_debug', False) - - elif UDP_ENABLED: - wirelink = UdpTransport('127.0.0.1:21324') - debuglink = UdpTransport('127.0.0.1:21325') - - return wirelink, debuglink - - -if HID_ENABLED and HidTransport.enumerate(): - print('Using TREZOR') -elif WEBUSB_ENABLED and WebUsbTransport.enumerate(): - print('Using TREZOR via WebUSB') -elif PIPE_ENABLED and pipe_exists('/tmp/pipe.trezor.to'): - print('Using Emulator (v1=pipe)') -elif UDP_ENABLED: - print('Using Emulator (v2=udp)') - - -class TrezorTest(object): +class TrezorTest: def setup_method(self, method): - wirelink, debuglink = get_transport() + wirelink = get_device() + debuglink = wirelink.find_debug() self.client = TrezorClientDebugLink(wirelink) self.client.set_debuglink(debuglink) self.client.set_tx_api(tx_api.TxApiBitcoin) From 2f1c15b588010c401d1e49c89e7cd421092ed71e Mon Sep 17 00:00:00 2001 From: matejcik Date: Mon, 5 Mar 2018 14:25:37 +0100 Subject: [PATCH 08/12] trezorlib/transport: make flake8 happy --- trezorlib/transport/__init__.py | 3 ++- trezorlib/transport/udp.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/trezorlib/transport/__init__.py b/trezorlib/transport/__init__.py index c586e3fce..83f35b905 100644 --- a/trezorlib/transport/__init__.py +++ b/trezorlib/transport/__init__.py @@ -17,6 +17,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . + class TransportException(Exception): pass @@ -86,7 +87,7 @@ def get_transport(path=None, prefix_search=False): # Find whether B is prefix of A (transport name is part of the path) # or A is prefix of B (path is a prefix, or a name, of transport). # This naively expects that no two transports have a common prefix. - def match_prefix(a,b): + def match_prefix(a, b): return a.startswith(b) or b.startswith(a) transports = [t for t in all_transports() if match_prefix(path, t.PATH_PREFIX)] diff --git a/trezorlib/transport/udp.py b/trezorlib/transport/udp.py index 059222377..7bbad169c 100644 --- a/trezorlib/transport/udp.py +++ b/trezorlib/transport/udp.py @@ -51,7 +51,7 @@ class UdpTransport(Transport): def find_debug(self): host, port = self.device - return UdpTransport('{}:{}'.format(host, port+1), self.protocol) + return UdpTransport('{}:{}'.format(host, port + 1), self.protocol) @classmethod def _try_path(cls, path): From ff80ca1b82d1c2ea9e73b0d67c1d7d6ad5ff699a Mon Sep 17 00:00:00 2001 From: matejcik Date: Mon, 5 Mar 2018 16:18:00 +0100 Subject: [PATCH 09/12] restore device.py as a deprecated compatibility wrapper --- trezorlib/device.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 trezorlib/device.py diff --git a/trezorlib/device.py b/trezorlib/device.py new file mode 100644 index 000000000..7d62b142c --- /dev/null +++ b/trezorlib/device.py @@ -0,0 +1,39 @@ +# This file is part of the TREZOR project. +# +# Copyright (C) 2012-2017 Marek Palatinus +# Copyright (C) 2012-2017 Pavol Rusnak +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# 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 GNU Lesser General Public License +# along with this library. If not, see . + +import warnings + +from .transport import enumerate_devices, get_transport + + +class TrezorDevice: + ''' + This class is deprecated. (There is no reason for it to exist in the first + place, it is nothing but a collection of two functions.) + Instead, please use functions from the ``trezorlib.transport`` module. + ''' + + @classmethod + def enumerate(cls): + warnings.warn('TrezorDevice is deprecated.', DeprecationWarning) + return enumerate_devices() + + @classmethod + def find_by_path(cls, path): + warnings.warn('TrezorDevice is deprecated.', DeprecationWarning) + return get_transport(path, prefix_search=False) From 967d479a193ede0b29af90c16132c0008cc760b1 Mon Sep 17 00:00:00 2001 From: matejcik Date: Mon, 5 Mar 2018 17:30:44 +0100 Subject: [PATCH 10/12] update tools to use the new transport API also drop some python2 compatibility things --- tools/encfs_aes_getpass.py | 12 +++--------- tools/helloworld.py | 14 ++------------ tools/mem_flashblock.py | 8 +++----- tools/mem_read.py | 8 +++----- tools/mem_write.py | 8 +++----- tools/mnemonic_check.py | 7 ------- tools/pwd_reader.py | 20 +++++++------------- tools/rng_entropy_collector.py | 17 +++++------------ tools/signtest.py | 17 ++++++----------- 9 files changed, 32 insertions(+), 79 deletions(-) diff --git a/tools/encfs_aes_getpass.py b/tools/encfs_aes_getpass.py index ef67b5529..64ba603f8 100755 --- a/tools/encfs_aes_getpass.py +++ b/tools/encfs_aes_getpass.py @@ -16,21 +16,15 @@ import hashlib import binascii from trezorlib.client import TrezorClient -from trezorlib.device import TrezorDevice - -# Python2 vs Python3 -try: - input = raw_input -except NameError: - pass +from trezorlib.transport import enumerate_devices def wait_for_devices(): - devices = TrezorDevice.enumerate() + devices = enumerate_devices() while not len(devices): sys.stderr.write("Please connect TREZOR to computer and press Enter...") input() - devices = TrezorDevice.enumerate() + devices = enumerate_devices() return devices diff --git a/tools/helloworld.py b/tools/helloworld.py index 9712a2b26..c91e5d392 100755 --- a/tools/helloworld.py +++ b/tools/helloworld.py @@ -1,21 +1,11 @@ #!/usr/bin/env python3 -from __future__ import print_function - from trezorlib.client import TrezorClient -from trezorlib.device import TrezorDevice +from trezorlib.transport import get_transport def main(): - # List all connected TREZORs on USB/UDP - devices = TrezorDevice.enumerate() - - # Check whether we found any - if len(devices) == 0: - print('No TREZOR found') - return - # Use first connected device - transport = devices[0] + transport = get_transport() # Creates object for manipulating TREZOR client = TrezorClient(transport) diff --git a/tools/mem_flashblock.py b/tools/mem_flashblock.py index af8b15c50..6de4f7a8a 100755 --- a/tools/mem_flashblock.py +++ b/tools/mem_flashblock.py @@ -1,9 +1,7 @@ #!/usr/bin/env python3 -from __future__ import print_function - from trezorlib.debuglink import DebugLink from trezorlib.client import TrezorClient -from trezorlib.transport_hid import HidTransport +from trezorlib.transport import enumerate_devices import binascii import sys @@ -16,8 +14,8 @@ sectorlens = [0x4000, 0x4000, 0x4000, 0x4000, def main(): - # List all connected TREZORs on USB - devices = HidTransport.enumerate() + # List all debuggable TREZORs + devices = [device for device in enumerate_devices() if hasattr(device, 'find_debug')] # Check whether we found any if len(devices) == 0: diff --git a/tools/mem_read.py b/tools/mem_read.py index eafa20571..c6a4a3a16 100755 --- a/tools/mem_read.py +++ b/tools/mem_read.py @@ -1,9 +1,7 @@ #!/usr/bin/env python3 -from __future__ import print_function - from trezorlib.debuglink import DebugLink from trezorlib.client import TrezorClient -from trezorlib.transport_hid import HidTransport +from trezorlib.transport import enumerate_devices import sys # usage examples @@ -16,8 +14,8 @@ import sys def main(): - # List all connected TREZORs on USB - devices = HidTransport.enumerate() + # List all debuggable TREZORs + devices = [device for device in enumerate_devices() if hasattr(device, 'find_debug')] # Check whether we found any if len(devices) == 0: diff --git a/tools/mem_write.py b/tools/mem_write.py index ae94a8bf5..f3f11cece 100755 --- a/tools/mem_write.py +++ b/tools/mem_write.py @@ -1,16 +1,14 @@ #!/usr/bin/env python3 -from __future__ import print_function - from trezorlib.debuglink import DebugLink from trezorlib.client import TrezorClient -from trezorlib.transport_hid import HidTransport +from trezorlib.transport import enumerate_devices import binascii import sys def main(): - # List all connected TREZORs on USB - devices = HidTransport.enumerate() + # List all debuggable TREZORs + devices = [device for device in enumerate_devices() if hasattr(device, 'find_debug')] # Check whether we found any if len(devices) == 0: diff --git a/tools/mnemonic_check.py b/tools/mnemonic_check.py index 6c5c1fc55..1dce3feb6 100755 --- a/tools/mnemonic_check.py +++ b/tools/mnemonic_check.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -from __future__ import print_function import binascii import hashlib import mnemonic @@ -16,12 +15,6 @@ __doc__ = ''' without an internet connection). ''' -# Python2 vs Python3 -try: - input = raw_input -except NameError: - pass - def generate_entropy(strength, internal_entropy, external_entropy): ''' diff --git a/tools/pwd_reader.py b/tools/pwd_reader.py index 2c6c4bd25..0aeb8e3ac 100755 --- a/tools/pwd_reader.py +++ b/tools/pwd_reader.py @@ -1,7 +1,4 @@ #!/usr/bin/env python3 - -from __future__ import print_function - from binascii import hexlify, unhexlify from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend @@ -9,14 +6,10 @@ import hmac import hashlib import json import os -try: - from urllib.parse import urlparse -except: - from urlparse import urlparse - input = raw_input +from urllib.parse import urlparse from trezorlib.client import TrezorClient -from trezorlib.transport_hid import HidTransport +from trezorlib.transport import get_transport # Return path by BIP-32 def getPath(client): @@ -124,12 +117,13 @@ def printEntries(entries): def main(): - devices = HidTransport.enumerate() - if not devices: - print('TREZOR is not plugged in. Please, connect TREZOR and retry.') + try: + transport = get_transport() + except Exception as e: + print(e) return - client = TrezorClient(devices[0]) + client = TrezorClient(transport) print() print('Confirm operation on TREZOR') diff --git a/tools/rng_entropy_collector.py b/tools/rng_entropy_collector.py index 2ae738a32..1f4dcbcde 100755 --- a/tools/rng_entropy_collector.py +++ b/tools/rng_entropy_collector.py @@ -8,21 +8,14 @@ from __future__ import print_function import io import sys from trezorlib.client import TrezorClient -from trezorlib.device import TrezorDevice - - -def get_client(): - devices = TrezorDevice.enumerate() # list all connected TREZORs on USB - if len(devices) == 0: # check whether we found any - return None - transport = devices[0] # use first connected device - return TrezorClient(transport) # creates object for communicating with TREZOR +from trezorlib.transport import get_transport def main(): - client = get_client() - if not client: - print('No TREZOR connected') + try: + client = TrezorClient(get_transport()) + except Exception as e: + print(e) return arg1 = sys.argv[1] # output file diff --git a/tools/signtest.py b/tools/signtest.py index 4cc6bdefa..7ec2f4f5c 100755 --- a/tools/signtest.py +++ b/tools/signtest.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -from __future__ import print_function import binascii import os import random @@ -12,8 +11,7 @@ import hashlib from trezorlib.client import TrezorClient from trezorlib.tx_api import TxApiTestnet from trezorlib.tx_api import TxApiBitcoin -from trezorlib.transport_hid import HidTransport -from trezorlib.transport_bridge import BridgeTransport +from trezorlib.transport import get_transport def hash160(x): @@ -152,16 +150,13 @@ def main(): numinputs = 100 sizeinputtx = 10 - # List all connected TREZORs on USB - devices = HidTransport.enumerate() - - # Check whether we found any - if len(devices) == 0: - print('No TREZOR found') + # Use first connected device + try: + transport = get_transport() + except Exception as e: + print(e) return - # Use first connected device - transport = devices[0] print(transport) txstore = MyTxApiBitcoin() From dc8eec1656cda764465ef220efb3ee26637e3d81 Mon Sep 17 00:00:00 2001 From: matejcik Date: Mon, 5 Mar 2018 17:31:11 +0100 Subject: [PATCH 11/12] trezorlib/transport: for get_transport(None), raise exception from None if no trezor is found, because the IndexError should not be part of the traceback --- trezorlib/transport/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trezorlib/transport/__init__.py b/trezorlib/transport/__init__.py index 83f35b905..fb6d4aa32 100644 --- a/trezorlib/transport/__init__.py +++ b/trezorlib/transport/__init__.py @@ -82,7 +82,7 @@ def get_transport(path=None, prefix_search=False): try: return enumerate_devices()[0] except IndexError: - raise Exception("No TREZOR device found") + raise Exception("No TREZOR device found") from None # Find whether B is prefix of A (transport name is part of the path) # or A is prefix of B (path is a prefix, or a name, of transport). From 9f2583f893ebf206d72025f4041d55b212992c1b Mon Sep 17 00:00:00 2001 From: matejcik Date: Tue, 6 Mar 2018 13:28:26 +0100 Subject: [PATCH 12/12] webusb: check if a device is functional when enumerating this fixes issue #223 on Windows, where a device would be returned in two copies, only one of which works --- trezorlib/transport/webusb.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/trezorlib/transport/webusb.py b/trezorlib/transport/webusb.py index 4b27aac40..44b5dafb5 100644 --- a/trezorlib/transport/webusb.py +++ b/trezorlib/transport/webusb.py @@ -101,7 +101,16 @@ class WebUsbTransport(Transport): continue if not is_vendor_class(dev): continue - devices.append(WebUsbTransport(dev)) + try: + # workaround for issue #223: + # on certain combinations of Windows USB drivers and libusb versions, + # Trezor is returned twice (possibly because Windows know it as both + # a HID and a WebUSB device), and one of the returned devices is + # non-functional. + dev.getProduct() + devices.append(WebUsbTransport(dev)) + except usb1.USBErrorNotSupported: + pass return devices def find_debug(self):