def on_sd_card_protection_refresh(self, _): if self.cur_hw_device and self.cur_hw_device.hw_client: if WndUtils.query_dlg( 'Do you really want to refresh SD card protection?', buttons=QMessageBox.Yes | QMessageBox.Cancel, default_button=QMessageBox.Cancel, icon=QMessageBox.Warning) == QMessageBox.Yes: self.hw_devices.set_sd_protect(self.cur_hw_device, 'refresh') self.update_ui()
def validate_firmware_internal(self, version, fw, expected_fingerprint=None): """Adapted version of the 'validate_firmware' function from trezorlib""" if version == trezorlib.firmware.FirmwareFormat.TREZOR_ONE: if fw.embedded_onev2: log.debug( "Trezor One firmware with embedded v2 image (1.8.0 or later)" ) else: log.debug("Trezor One firmware image.") elif version == trezorlib.firmware.FirmwareFormat.TREZOR_ONE_V2: log.debug("Trezor One v2 firmware (1.8.0 or later)") elif version == trezorlib.firmware.FirmwareFormat.TREZOR_T: log.debug("Trezor T firmware image.") vendor = fw.vendor_header.text vendor_version = "{major}.{minor}".format( **fw.vendor_header.version) log.debug("Vendor header from {}, version {}".format( vendor, vendor_version)) try: trezorlib.firmware.validate(version, fw, allow_unsigned=False) log.debug("Signatures are valid.") except trezorlib.firmware.Unsigned: if WndUtils.query_dlg( 'No signatures found. Continue?', buttons=QMessageBox.Yes | QMessageBox.No, default_button=QMessageBox.Yes, icon=QMessageBox.Information) == QMessageBox.No: raise CancelException() try: trezorlib.firmware.validate(version, fw, allow_unsigned=True) log.debug("Unsigned firmware looking OK.") except trezorlib.firmware.FirmwareIntegrityError as e: log.exception(e) raise Exception("Firmware validation failed, aborting.") except trezorlib.firmware.FirmwareIntegrityError as e: log.exception(e) raise Exception("Firmware validation failed, aborting.") fingerprint = trezorlib.firmware.digest(version, fw).hex() log.debug("Firmware fingerprint: {}".format(fingerprint)) if version == trezorlib.firmware.FirmwareFormat.TREZOR_ONE and fw.embedded_onev2: fingerprint_onev2 = trezorlib.firmware.digest( trezorlib.firmware.FirmwareFormat.TREZOR_ONE_V2, fw.embedded_onev2).hex() log.debug( "Embedded v2 image fingerprint: {}".format(fingerprint_onev2)) if expected_fingerprint and fingerprint != expected_fingerprint: log.error("Expected fingerprint: {}".format(expected_fingerprint)) raise Exception("Fingerprints do not match, aborting.")
def on_sd_card_protection_enable_disable(self, _): if self.cur_hw_device and self.cur_hw_device.hw_client: if self.hw_opt_sd_protection is True: # disable passphrase if WndUtils.query_dlg( 'Do you really want to disable SD card protection?', buttons=QMessageBox.Yes | QMessageBox.Cancel, default_button=QMessageBox.Cancel, icon=QMessageBox.Warning) == QMessageBox.Yes: self.hw_devices.set_sd_protect(self.cur_hw_device, 'disable') self.update_ui() elif self.hw_opt_sd_protection is False: # enable passphrase if WndUtils.query_dlg( 'Do you really want to enable SD card protection?', buttons=QMessageBox.Yes | QMessageBox.Cancel, default_button=QMessageBox.Cancel, icon=QMessageBox.Warning) == QMessageBox.Yes: self.hw_devices.set_sd_protect(self.cur_hw_device, 'enable') self.update_ui()
def on_wipe_code_enable_disable(self, _): if self.cur_hw_device and self.cur_hw_device.hw_client: if self.hw_opt_wipe_code_protection is True: # disable passphrase if WndUtils.query_dlg( 'Do you really want to disable wipe code?', buttons=QMessageBox.Yes | QMessageBox.Cancel, default_button=QMessageBox.Cancel, icon=QMessageBox.Warning) == QMessageBox.Yes: self.hw_devices.set_wipe_code(self.cur_hw_device, remove=True) self.update_ui() elif self.hw_opt_wipe_code_protection is False: # enable passphrase if WndUtils.query_dlg( 'Do you really want to enable wipe code?', buttons=QMessageBox.Yes | QMessageBox.Cancel, default_button=QMessageBox.Cancel, icon=QMessageBox.Warning) == QMessageBox.Yes: self.hw_devices.set_wipe_code(self.cur_hw_device, remove=False) self.update_ui()
def on_passphrase_alwaysondevice_enable_disable(self, _): if self.cur_hw_device and self.cur_hw_device.hw_client: if self.hw_opt_passphrase_always_on_device is True: # disable passphrase if WndUtils.query_dlg( 'Do you really want to disable passphrase always on device option?', buttons=QMessageBox.Yes | QMessageBox.Cancel, default_button=QMessageBox.Cancel, icon=QMessageBox.Warning) == QMessageBox.Yes: self.hw_devices.set_passphrase_always_on_device( self.cur_hw_device, enabled=False) self.update_ui() elif self.hw_opt_passphrase_always_on_device is False: # enable passphrase if WndUtils.query_dlg( 'Do you really want to enable passphrase always on device option?', buttons=QMessageBox.Yes | QMessageBox.Cancel, default_button=QMessageBox.Cancel, icon=QMessageBox.Warning) == QMessageBox.Yes: self.hw_devices.set_passphrase_always_on_device( self.cur_hw_device, enabled=True) self.update_ui()
def on_passphrase_enable_disable(self, _): if self.cur_hw_device and self.cur_hw_device.hw_client: if self.hw_opt_passphrase_protection is True: # disable passphrase if WndUtils.query_dlg( 'Do you really want to disable passphrase protection on %s?' % self.cur_hw_device.get_description(), buttons=QMessageBox.Yes | QMessageBox.Cancel, default_button=QMessageBox.Cancel, icon=QMessageBox.Warning) == QMessageBox.Yes: self.hw_devices.set_passphrase_option(self.cur_hw_device, enabled=False) self.update_ui() elif self.hw_opt_passphrase_protection is False: # enable passphrase if WndUtils.query_dlg( 'Do you really want to enable passphrase protection on %s?' % self.cur_hw_device.get_description(), buttons=QMessageBox.Yes | QMessageBox.Cancel, default_button=QMessageBox.Cancel, icon=QMessageBox.Warning) == QMessageBox.Yes: self.hw_devices.set_passphrase_option(self.cur_hw_device, enabled=True) self.update_ui()
def sign_message(hw_client, bip32_path: str, message: str, hw_session: Optional[HWSessionBase]): # Ledger doesn't accept characters other that ascii printable: # https://ledgerhq.github.io/btchip-doc/bitcoin-technical.html#_sign_message message = message.encode('ascii', 'ignore') bip32_path = clean_bip32_path(bip32_path) ok = False for i in range(1, 4): info = hw_client.signMessagePrepare(bip32_path, message) if info['confirmationNeeded'] and info['confirmationType'] == 34: if i == 1 or \ WndUtils.query_dlg('Another application (such as Ledger Wallet Bitcoin app) has probably taken over ' 'the communication with the Ledger device.' '\n\nTo continue, close that application and click the <b>Retry</b> button.' '\nTo cancel, click the <b>Abort</b> button', buttons=QMessageBox.Retry | QMessageBox.Abort, default_button=QMessageBox.Retry, icon=QMessageBox.Warning) == QMessageBox.Retry: # we need to reconnect the device; first, we'll try to reconnect to HW without closing the interfering # application; it it doesn't help we'll display a message requesting the user to close the app if hw_session: hw_session.disconnect_hardware_wallet() if hw_session.connect_hardware_wallet(): hw_client = hw_session.hw_client else: raise Exception('Hardware wallet reconnect error.') else: raise Exception('Cannot reconnect Ledger device') else: break else: ok = True break if not ok: raise CancelException('Cancelled') try: signature = hw_client.signMessageSign() except BTChipException as e: if e.args and len(e.args) >= 2 and e.args[0] == 'Invalid status 6985': raise CancelException('Cancelled') else: raise except Exception as e: logging.exception('Exception while signing message with Ledger Nano S') raise Exception( 'Exception while signing message with Ledger Nano S. Details: ' + str(e)) try: pubkey = hw_client.getWalletPublicKey(bip32_path) except Exception as e: logging.exception( 'Could not get public key for BIP32 path from Ledger Nano S') raise Exception( 'Could not get public key for BIP32 path from Ledger Nano S. Details: ' + str(e)) if len(signature) > 4: r_length = signature[3] r = signature[4:4 + r_length] if len(signature) > 4 + r_length + 1: s_length = signature[4 + r_length + 1] if len(signature) > 4 + r_length + 2: s = signature[4 + r_length + 2:] if r_length == 33: r = r[1:] if s_length == 33: s = s[1:] else: logging.error( 'client.signMessageSign() returned invalid response (code 3): ' + signature.hex()) raise Exception('Invalid signature returned (code 3).') else: logging.error( 'client.signMessageSign() returned invalid response (code 2): ' + signature.hex()) raise Exception('Invalid signature returned (code 2).') else: logging.error( 'client.signMessageSign() returned invalid response (code 1): ' + signature.hex()) raise Exception('Invalid signature returned (code 1).') addr = _ledger_extract_address(pubkey.get('address')) return MessageSignature( addr, bytes(chr(27 + 4 + (signature[0] & 0x01)), "utf-8") + r + s)
def read_file_encrypted( file_name: str, ret_attrs: dict, hw_session: HwSessionInfo) -> Generator[bytes, None, None]: ret_attrs['encrypted'] = False try: hw_session.save_state() with open(file_name, 'rb') as f_ptr: data = f_ptr.read(len(DMT_ENCRYPTED_DATA_PREFIX)) if data == DMT_ENCRYPTED_DATA_PREFIX: ret_attrs['encrypted'] = True protocol = read_varint_from_file(f_ptr) if protocol == 1: # with Trezor method + Fernet hw_type_bin = read_varint_from_file(f_ptr) hw_type = { 1: HWType.trezor, 2: HWType.keepkey, 3: HWType.ledger_nano }.get(hw_type_bin) if hw_type: # connect hardware wallet, choosing the type compatible with the type read from # the encrypted file if hw_session.hw_client: if (hw_type in (HWType.trezor, HWType.keepkey) and hw_session.hw_type not in (HWType.trezor, HWType.keepkey)) or \ (hw_type == HWType.ledger_nano and hw_type != hw_session.hw_type): # if the currently connected hardware wallet type is not compatible with the # type from the encrypted file, disconnect it to give a user a chance to choose # the correct one in the code below hw_session.disconnect_hardware_wallet() if not hw_session.hw_client: if hw_type in (HWType.trezor, HWType.keepkey): hw_session.set_hw_types_allowed( (HWType.trezor, HWType.keepkey)) else: hw_session.set_hw_types_allowed((hw_type, )) if not hw_session.connect_hardware_wallet(): raise HWNotConnectedException( f'This file was encrypted with {HWType.get_desc(hw_type)} hardware wallet, ' f'which has to be connected to the computer decrypt the file.' ) data_label_bin = read_bytes_from_file(f_ptr) label = base64.urlsafe_b64decode( data_label_bin).decode('utf-8') encrypted_key_bin = read_bytes_from_file(f_ptr) bip32_path_n = read_int_list_from_file(f_ptr) pub_key_hash_hdr = read_bytes_from_file(f_ptr) while True: if not hw_session.hw_client: raise HWNotConnectedException( f'This file was encrypted with {HWType.get_desc(hw_type)} hardware wallet, ' f'which has to be connected to the computer decrypt the file.' ) if hw_session.hw_type in (HWType.trezor, HWType.keepkey): key_bin, pub_key = hw_session.hw_decrypt_value( bip32_path_n, label=label, value=encrypted_key_bin) elif hw_session.hw_type == HWType.ledger_nano: display_label = f'<b>Click the sign message confirmation button on the <br>' \ f'hardware wallet to decrypt \'{label}\'.</b>' bip32_path_str = bip32_path_n_to_string( bip32_path_n) sig = hw_sign_message( hw_session, 'Dash', bip32_path_str, encrypted_key_bin.hex(), display_label=display_label) adr_pk = get_address_and_pubkey( hw_session, 'Dash', bip32_path_str) pub_key = adr_pk.get('publicKey') key_bin = SHA256.new(sig.signature).digest() else: raise Exception( 'Invalid hardware wallet type.') pub_key_hash = SHA256.new(pub_key).digest() if pub_key_hash_hdr == pub_key_hash: break url = get_note_url('DMT0003') if WndUtils.query_dlg( message= 'Inconsistency between encryption and decryption keys.\n\n' 'The reason may be using a different passphrase than it was used ' 'for encryption or running another application communicating with the ' 'device simultaneously, like Trezor web wallet (see <a href="{url}">' 'here</a>).\n\n' 'Do you want to try again?', buttons=QMessageBox.Yes | QMessageBox.Cancel, default_button=QMessageBox.Cancel, icon=QMessageBox.Warning ) == QMessageBox.Cancel: raise CancelException('User cancelled.') hw_session.disconnect_hardware_wallet() hw_session.connect_hardware_wallet() key = base64.urlsafe_b64encode(key_bin) fer = Fernet(key) while True: # data is written in blocks; if front of each block there is a block size value data_bin = f_ptr.read(8) if len(data_bin) == 0: break # end of file elif len(data_bin) < 8: raise ValueError( 'File end before read completed.') data_chunk_size = int.from_bytes( data_bin, byteorder='little') if data_chunk_size < 0 or data_chunk_size > 2000000000: raise ValueError( 'Data corrupted: invalid data chunk size.') data_bin = f_ptr.read(data_chunk_size) if data_chunk_size != len(data_bin): raise ValueError( 'File end before read completed.') data_base64 = base64.urlsafe_b64encode(data_bin) try: data_decr = fer.decrypt(data_base64) except InvalidToken: raise Exception( 'Couldn\'t decrypt file (InvalidToken error). The file is probably ' 'corrupted or is encrypted with a different encryption method.' ) yield data_decr else: raise ValueError('Invalid hardware wallet type value.') else: raise ValueError('Invalid protocol value.') else: # the data inside the file isn't encrypted # read and yield raw data while True: # data is written in blocks; if front of each block there is a block size value data += f_ptr.read(ENC_FILE_BLOCK_SIZE) if not len(data): break yield data data = bytes() finally: hw_session.restore_state()