22
33from ..hwwclient import HardwareWalletClient
44from ..errors import ActionCanceledError , BadArgumentError , DeviceAlreadyInitError , DeviceAlreadyUnlockedError , DeviceConnectionError , DEVICE_NOT_INITIALIZED , DeviceNotReadyError , UnavailableActionError , common_err_msgs , handle_errors
5+ from .trezorlib import firmware
56from .trezorlib .client import TrezorClient as Trezor
67from .trezorlib .debuglink import TrezorClientDebugLink
78from .trezorlib .exceptions import Cancelled
@@ -86,6 +87,40 @@ def interactive_get_pin(self, code=None):
8687 else :
8788 return pin
8889
90+ ALLOWED_FIRMWARE_FORMATS = {
91+ 1 : (firmware .FirmwareFormat .TREZOR_ONE , firmware .FirmwareFormat .TREZOR_ONE_V2 ),
92+ 2 : (firmware .FirmwareFormat .TREZOR_T ,),
93+ }
94+
95+ def _print_version (version ):
96+ vstr = "Firmware version {major}.{minor}.{patch} build {build}" .format (** version )
97+ print (vstr , file = sys .stderr )
98+
99+ def validate_firmware (version , fw , expected_fingerprint = None ):
100+ if version == firmware .FirmwareFormat .TREZOR_ONE :
101+ if fw .embedded_onev2 :
102+ print ("Trezor One firmware with embedded v2 image (1.8.0 or later)" , file = sys .stderr )
103+ _print_version (fw .embedded_onev2 .firmware_header .version )
104+ else :
105+ print ("Trezor One firmware image." , file = sys .stderr )
106+ elif version == firmware .FirmwareFormat .TREZOR_ONE_V2 :
107+ print ("Trezor One v2 firmware (1.8.0 or later)" , file = sys .stderr )
108+ _print_version (fw .firmware_header .version )
109+ elif version == firmware .FirmwareFormat .TREZOR_T :
110+ print ("Trezor T firmware image." , file = sys .stderr )
111+ vendor = fw .vendor_header .vendor_string
112+ vendor_version = "{major}.{minor}" .format (** fw .vendor_header .version )
113+ print ("Vendor header from {}, version {}" .format (vendor , vendor_version ), file = sys .stderr )
114+ _print_version (fw .firmware_header .version )
115+
116+ firmware .validate (version , fw , allow_unsigned = False )
117+ print ("Signatures are valid." , file = sys .stderr )
118+
119+ fingerprint = firmware .digest (version , fw ).hex ()
120+ print ("Firmware fingerprint: {}" .format (fingerprint ), file = sys .stderr )
121+ if expected_fingerprint and fingerprint != expected_fingerprint :
122+ raise BadArgumentError ('Expected firmware fingerprint {} does not match computed fingerprint {}.' .format (expected_fingerprint , fingerprint ))
123+
89124# This class extends the HardwareWalletClient for Trezor specific things
90125class TrezorClient (HardwareWalletClient ):
91126
@@ -431,7 +466,37 @@ def send_pin(self, pin):
431466
432467 # Verify firmware file then load it onto device
433468 def update_firmware (self , file ):
434- raise NotImplementedError ('The {} does not implement this method yet' .format (self .type ))
469+ self .client .init_device (True )
470+ if not self .client .features .bootloader_mode :
471+ raise DeviceConnectionError ('Device needs to be in bootloader mode' )
472+
473+ bootloader_onev2 = self .client .features .major_version == 1 and self .client .features .minor_version >= 8
474+
475+ data = open (file , "rb" ).read ()
476+ version , fw = firmware .parse (data )
477+ validate_firmware (version , fw )
478+
479+ if bootloader_onev2 and version == firmware .FirmwareFormat .TREZOR_ONE and not fw .embedded_onev2 :
480+ raise BadArgumentError ('Firmware is too old for your device' )
481+ elif not bootloader_onev2 and version == firmware .FirmwareFormat .TREZOR_ONE_V2 :
482+ raise BadArgumentError ('You need to upgrade to bootloader 1.8.0 first.' )
483+
484+ if self .client .features .major_version not in ALLOWED_FIRMWARE_FORMATS :
485+ raise BadArgumentError ('Device has unknown version, unable to upgrade firmware' )
486+ elif version not in ALLOWED_FIRMWARE_FORMATS [self .client .features .major_version ]:
487+ raise BadArgumentError ('Firmware does not match your device' )
488+
489+ # special handling for embedded-OneV2 format:
490+ # for bootloader < 1.8, keep the embedding
491+ # for bootloader 1.8.0 and up, strip the old OneV1 header
492+ if bootloader_onev2 and data [:4 ] == b"TRZR" and data [256 :256 + 4 ] == b"TRZF" :
493+ data = data [256 :]
494+
495+ if self .client .features .major_version == 1 and self .client .features .firmware_present is not False :
496+ # Trezor One does not send ButtonRequest
497+ print ("Please confirm the action on your device" , file = sys .stderr )
498+ firmware .update (self .client , data )
499+ return {'success' : True }
435500
436501def enumerate (password = '' ):
437502 results = []
@@ -448,7 +513,7 @@ def enumerate(password=''):
448513 client = None
449514 with handle_errors (common_err_msgs ["enumerate" ], d_data ):
450515 client = TrezorClient (d_data ['path' ], password )
451- client .client .init_device ()
516+ client .client .init_device (True )
452517 if 'trezor' not in client .client .features .vendor :
453518 continue
454519
0 commit comments