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()
Exemple #2
0
    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()
Exemple #7
0
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()