From d9e5fd2682f68cab09bd7dcf1486cf23368785b5 Mon Sep 17 00:00:00 2001 From: matejcik Date: Tue, 4 Dec 2018 15:42:28 +0100 Subject: [PATCH] client: refuse to work with outdated firmware required firmware versions are hardcoded and should be updated by a build script --- trezorlib/__init__.py | 7 +++++++ trezorlib/client.py | 35 ++++++++++++++++++++++++++++++++++- trezorlib/exceptions.py | 4 ++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/trezorlib/__init__.py b/trezorlib/__init__.py index ae6db5f176..3cf120afab 100644 --- a/trezorlib/__init__.py +++ b/trezorlib/__init__.py @@ -1 +1,8 @@ __version__ = "0.11.0" + +# fmt: off +MINIMUM_FIRMWARE_VERSION = { + "1": (1, 6, 3), + "T": (2, 0, 10), +} +# fmt: on diff --git a/trezorlib/client.py b/trezorlib/client.py index 79e1fda71a..ac153f648b 100644 --- a/trezorlib/client.py +++ b/trezorlib/client.py @@ -20,7 +20,7 @@ import warnings from mnemonic import Mnemonic -from . import exceptions, messages, tools +from . import MINIMUM_FIRMWARE_VERSION, exceptions, messages, tools if sys.version_info.major < 3: raise Exception("Trezorlib does not support Python 2 anymore.") @@ -36,6 +36,12 @@ Incompatible Trezor library detected. (Original error: {}) """.strip() +OUTDATED_FIRMWARE_ERROR = """ +Your Trezor firmware is out of date. Update it with the following command: + trezorctl firmware-update +Or visit https://wallet.trezor.io/ +""".strip() + def get_buttonrequest_value(code): # Converts integer code to its string representation of ButtonRequestType @@ -156,6 +162,7 @@ class TrezorClient: @tools.session def call(self, msg): + self.check_firmware_version() resp = self.call_raw(msg) while True: if isinstance(resp, messages.PinMatrixRequest): @@ -183,6 +190,25 @@ class TrezorClient: # A side-effect of this is a sanity check for broken protobuf definitions. # If the `vendor` field doesn't exist, you probably have a mismatched # checkout of trezor-common. + self.version = ( + self.features.major_version, + self.features.minor_version, + self.features.patch_version, + ) + self.check_firmware_version(warn_only=True) + + def is_outdated(self): + if self.features.bootloader_mode: + return False + required_version = MINIMUM_FIRMWARE_VERSION[self.features.model] + return self.version < required_version + + def check_firmware_version(self, warn_only=False): + if self.is_outdated(): + if warn_only: + warnings.warn(OUTDATED_FIRMWARE_ERROR, stacklevel=2) + else: + raise exceptions.OutdatedFirmwareError(OUTDATED_FIRMWARE_ERROR) @tools.expect(messages.Success, field="message") def ping( @@ -192,6 +218,13 @@ class TrezorClient: pin_protection=False, passphrase_protection=False, ): + # We would like ping to work on any valid TrezorClient instance, but + # due to the protection modes, we need to go through self.call, and that will + # raise an exception if the firmware is too old. + # So we short-circuit the simplest variant of ping with call_raw. + if not button_protection and not pin_protection and not passphrase_protection: + return self.call_raw(messages.Ping(message=msg)) + msg = messages.Ping( message=msg, button_protection=button_protection, diff --git a/trezorlib/exceptions.py b/trezorlib/exceptions.py index 90970a6b4a..f95271eef4 100644 --- a/trezorlib/exceptions.py +++ b/trezorlib/exceptions.py @@ -45,3 +45,7 @@ class PinException(TrezorException): class Cancelled(TrezorException): pass + + +class OutdatedFirmwareError(TrezorException): + pass