Beispiel #1
0
    def _get_address(ctrl, hw_session: HwSessionInfo, bip32_path: str, show_display: bool = False,
                     message_to_display: str = None):
        if ctrl:
            ctrl.dlg_config_fun(dlg_title=DEFAULT_HW_BUSY_TITLE, show_progress_bar=False)
            if message_to_display:
                ctrl.display_msg_fun(message_to_display)
            else:
                ctrl.display_msg_fun('<b>Click the confirmation button on your hardware wallet to exit...</b>')

        client = hw_session.hw_client
        if client:
            if isinstance(bip32_path, str):
                bip32_path.strip()
                if bip32_path.lower().find('m/') >= 0:
                    # removing m/ prefix because of keepkey library
                    bip32_path = bip32_path[2:]

            if hw_session.app_config.hw_type == HWType.trezor:

                from trezorlib import btc
                from trezorlib import exceptions

                try:
                    if isinstance(bip32_path, str):
                        bip32_path = gewel_utils.bip32_path_string_to_n(bip32_path)
                    ret = btc.get_address(client, hw_session.app_config.hw_coin_name, bip32_path, show_display)
                    return ret
                except (CancelException, exceptions.Cancelled):
                    raise CancelException()

            elif hw_session.app_config.hw_type == HWType.keepkey:

                from keepkeylib.client import CallException

                try:
                    if isinstance(bip32_path, str):
                        bip32_path = gewel_utils.bip32_path_string_to_n(bip32_path)
                    return client.get_address(hw_session.app_config.hw_coin_name, bip32_path, show_display)
                except CallException as e:
                    if isinstance(e.args, tuple) and len(e.args) >= 2 and isinstance(e.args[1], str) and \
                            e.args[1].find('cancel') >= 0:
                        raise CancelException('Cancelled')

            elif hw_session.app_config.hw_type == HWType.ledger_nano_s:
                import hw_intf_ledgernano as ledger

                if isinstance(bip32_path, list):
                    # ledger requires bip32 path argument as a string
                    bip32_path = bip32_path_n_to_string(bip32_path)

                adr_pubkey = ledger.get_address_and_pubkey(client, bip32_path, show_display)
                return adr_pubkey.get('address')
            else:
                raise Exception('Unknown hardware wallet type: ' + hw_session.app_config.hw_type)
        else:
            raise Exception('HW client not open.')
def sign_message(hw_session: HwSessionInfo, bip32path, message):
    client = hw_session.hw_client
    address_n = dash_utils.bip32_path_string_to_n(bip32path)
    try:
        return btc.sign_message(client, hw_session.app_config.hw_coin_name, address_n, message)
    except exceptions.Cancelled:
        raise CancelException('Cancelled')
Beispiel #3
0
    def decrypt(ctrl, hw_session: HwSessionInfo, bip32_path_n: List[int], label: str, value: bytearray):
        ctrl.dlg_config_fun(dlg_title="Data decryption", show_progress_bar=False)
        ctrl.display_msg_fun(f'<b>Decrypting \'{label}\'...</b><br><br>Enter the hardware wallet PIN/passphrase '
                             f'(if needed)<br> and click the confirmation button to decrypt data.')

        if hw_session.hw_type == HWType.trezor:

            from trezorlib import misc, btc
            from trezorlib import exceptions

            try:
                client = hw_session.hw_client
                data = misc.decrypt_keyvalue(client, bip32_path_n, label, value, ask_on_encrypt, ask_on_decrypt)
                pub_key = btc.get_public_node(client, bip32_path_n).node.public_key
                return data, pub_key
            except (CancelException, exceptions.Cancelled):
                raise CancelException()

        elif hw_session.hw_type == HWType.keepkey:

            client = hw_session.hw_client
            data = client.decrypt_keyvalue(bip32_path_n, label, value, ask_on_encrypt, ask_on_decrypt)
            pub_key = client.get_public_node(bip32_path_n).node.public_key
            return data, pub_key

        elif hw_session.hw_type == HWType.ledger_nano_s:

            raise Exception('Feature not available for Ledger Nano S.')

        else:
            raise Exception('Invalid HW type: ' + str(hw_session))
Beispiel #4
0
 def callback_PassphraseRequest(self, msg):
     passphrase = self.ask_for_pass_fun()
     if passphrase is None:
         raise CancelException('Cancelled')
     else:
         if self.passphrase_encoding in ('NFKD', 'NFC'):
             passphrase = unicodedata.normalize(self.passphrase_encoding, passphrase)
         else:
             raise Exception('Invalid passphrase encoding value: ' + self.passphrase_encoding)
     return keepkey_proto.PassphraseAck(passphrase=passphrase)
Beispiel #5
0
def sign_message(hw_session: HwSessionInfo, bip32path, message):
    client = hw_session.hw_client
    address_n = client.expand_path(clean_bip32_path(bip32path))
    try:
        return client.sign_message(hw_session.app_config.hw_coin_name, address_n, message)
    except CallException as e:
        if e.args and len(e.args) >= 2 and e.args[1].lower().find('cancelled') >= 0:
            raise CancelException('Cancelled')
        else:
            raise
Beispiel #6
0
def sign_message(hw_client, hw_coin_name: str, bip32path: str, message: str):
    address_n = hw_client.expand_path(clean_bip32_path(bip32path))
    try:
        return hw_client.sign_message(hw_coin_name, address_n, message)
    except CallException as e:
        if e.args and len(
                e.args) >= 2 and e.args[1].lower().find('cancelled') >= 0:
            raise CancelException('Cancelled')
        else:
            raise
Beispiel #7
0
def sign_message(hw_client, hw_coin_name: str, bip32path: str, message: str):
    address_n = dash_utils.bip32_path_string_to_n(bip32path)
    try:
        return btc.sign_message(hw_client, hw_coin_name, address_n, message)
    except exceptions.Cancelled:
        raise CancelException('Cancelled')
    except exceptions.TrezorFailure as e:
        if e.failure.message == 'Device not initialized':
            raise HwNotInitialized(e.failure.message)
        else:
            raise
Beispiel #8
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 callback_PinMatrixRequest(self, msg):
     if msg.type == 1:
         desc = 'Enter current PIN'
     elif msg.type == 2:
         desc = 'Enter new PIN'
     elif msg.type == 3:
         desc = 'Enter new PIN again'
     else:
         desc = 'Enter PIN'
     pin = self.ask_for_pin_fun(desc)
     if not pin:
         raise CancelException('Cancelled')
     return keepkey_proto.PinMatrixAck(pin=pin)
Beispiel #10
0
def change_pin(hw_client, remove=False):
    if hw_client:
        try:
            device.change_pin(hw_client, remove)
        except exceptions.Cancelled:
            raise CancelException('Cancelled')
        except exceptions.TrezorFailure as e:
            if e.failure.message == 'Device not initialized':
                raise HwNotInitialized(e.failure.message)
            else:
                raise
    else:
        raise Exception('HW client not set.')
Beispiel #11
0
def cancel_hw_thread_dialog(hw_client):
    try:
        hw_type = get_hw_type(hw_client)
        if hw_type == HWType.trezor:
            hw_client.cancel()
        elif hw_type == HWType.keepkey:
            hw_client.cancel()
        elif hw_type == HWType.ledger_nano_s:
            return False
        raise CancelException('Cancel')
    except CancelException:
        raise
    except Exception as e:
        logging.warning('Error when canceling hw session. Details: %s', str(e))
        return True
Beispiel #12
0
    def firmware_update(self, fingerprint: str, firmware_data: bytes):
        """Adapted version of the 'firmware_update' function from trezorlib"""
        f = self.features
        bootloader_version = (f.major_version, f.minor_version,
                              f.patch_version)
        bootloader_onev2 = f.major_version == 1 and bootloader_version >= (
            1, 8, 0)
        model = f.model or "1"

        try:
            version, fw = trezorlib.firmware.parse(firmware_data)
        except Exception as e:
            raise

        self.validate_firmware_internal(version, fw, fingerprint)

        if (bootloader_onev2
                and version == trezorlib.firmware.FirmwareFormat.TREZOR_ONE
                and not fw.embedded_onev2):
            raise Exception("Firmware is too old for your device. Aborting.")
        elif not bootloader_onev2 and version == trezorlib.firmware.FirmwareFormat.TREZOR_ONE_V2:
            raise Exception("You need to upgrade to bootloader 1.8.0 first.")

        if f.major_version not in ALLOWED_FIRMWARE_FORMATS:
            raise Exception("DMT doesn't know your device version. Aborting.")
        elif version not in ALLOWED_FIRMWARE_FORMATS[f.major_version]:
            raise Exception("Firmware does not match your device, aborting.")

        if bootloader_onev2 and firmware_data[:4] == b"TRZR" and firmware_data[
                256:256 + 4] == b"TRZF":
            log.debug("Extracting embedded firmware image.")
            firmware_data = firmware_data[256:]

        try:
            # if f.major_version == 1 and f.firmware_present is not False:
            #     # Trezor One does not send ButtonRequest
            #     "Please confirm the action on your Trezor device"
            trezorlib.firmware.update(self, firmware_data)
            return True

        except exceptions.Cancelled:
            raise CancelException("Update aborted on device.")
        except exceptions.TrezorException as e:
            raise Exception("Update failed: {}".format(e))
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()
Beispiel #14
0
 def _callback_pin(self, msg):
     try:
         return TrezorClient._callback_pin(self, msg)
     except exceptions.Cancelled:
         raise CancelException('Cancelled')
Beispiel #15
0
def sign_tx(hw_session: HwSessionInfo,
            utxos_to_spend: List[wallet_common.UtxoType],
            tx_outputs: List[wallet_common.TxOutputType], tx_fee):
    """
    Creates a signed transaction.
    :param hw_session:
    :param utxos_to_spend: list of utxos to send
    :param tx_outputs: list of transaction outputs
    :param tx_fee: transaction fee
    :return: tuple (serialized tx, total transaction amount in satoshis)
    """
    def load_prev_txes(tx_api, skip_cache: bool = False):
        txes = {}
        tx_api.skip_cache = skip_cache
        for utxo in utxos_to_spend:
            prev_hash = bytes.fromhex(utxo.txid)
            if prev_hash not in txes:
                tx = tx_api[prev_hash]
                txes[prev_hash] = tx
        return txes

    insight_network = 'insight_polis'
    if hw_session.app_config.is_testnet():
        insight_network += '_testnet'
    polis_network = hw_session.app_config.polis_network

    c_name = hw_session.app_config.hw_coin_name
    coin = coins.by_name[c_name]
    url = hw_session.app_config.get_tx_api_url()
    coin['bitcore'].clear()
    coin['bitcore'].append(url)

    tx_api = MyTxApiInsight(coin, '', hw_session.polisd_intf,
                            hw_session.app_config.tx_cache_dir)
    client = hw_session.hw_client
    inputs = []
    outputs = []
    inputs_amount = 0
    for utxo_index, utxo in enumerate(utxos_to_spend):
        if not utxo.bip32_path:
            raise Exception('No BIP32 path for UTXO ' + utxo.txid)

        address_n = polis_utils.bip32_path_string_to_n(utxo.bip32_path)
        it = trezor_proto.TxInputType(address_n=address_n,
                                      prev_hash=binascii.unhexlify(utxo.txid),
                                      prev_index=int(utxo.output_index))

        inputs.append(it)
        inputs_amount += utxo.satoshis

    outputs_amount = 0
    for out in tx_outputs:
        outputs_amount += out.satoshis
        if out.address[0] in polis_utils.get_chain_params(
                polis_network).B58_PREFIXES_SCRIPT_ADDRESS:
            stype = trezor_proto.OutputScriptType.PAYTOSCRIPTHASH
            logging.debug('Transaction type: PAYTOSCRIPTHASH' + str(stype))
        elif out.address[0] in polis_utils.get_chain_params(
                polis_network).B58_PREFIXES_PUBKEY_ADDRESS:
            stype = trezor_proto.OutputScriptType.PAYTOADDRESS
            logging.debug('Transaction type: PAYTOADDRESS ' + str(stype))
        else:
            raise Exception('Invalid prefix of the destination address.')
        if out.bip32_path:
            address_n = polis_utils.bip32_path_string_to_n(out.bip32_path)
        else:
            address_n = None

        ot = trezor_proto.TxOutputType(
            address=out.address if address_n is None else None,
            address_n=address_n,
            amount=out.satoshis,
            script_type=stype)
        outputs.append(ot)

    if outputs_amount + tx_fee != inputs_amount:
        raise Exception(
            'Transaction validation failure: inputs + fee != outputs')

    try:
        for skip_cache in (False, True):
            txes = load_prev_txes(tx_api, skip_cache)
            try:
                signed = btc.sign_tx(client,
                                     hw_session.app_config.hw_coin_name,
                                     inputs,
                                     outputs,
                                     prev_txes=txes)
                return signed[1], inputs_amount
            except exceptions.Cancelled:
                raise
            except Exception as e:
                if skip_cache:
                    raise
                log.exception(
                    'Exception occurred while signing transaction. Turning off the transaction cache '
                    'and retrying...')
        raise Exception('Internal error: transaction not signed')
    except exceptions.Cancelled:
        raise CancelException('Cancelled')
Beispiel #16
0
def connect_hw(hw_session: Optional[HwSessionInfo],
               hw_type: HWType,
               device_id: Optional[str] = 'NFC',
               passphrase_encoding: Optional[str] = None):
    """
    Initializes connection with a hardware wallet.
    :param hw_type: symbol of the hardware wallet type
    :param passphrase_encoding: (for Keepkey only) it allows forcing the passphrase encoding compatible with BIP-39
        standard (NFKD), which is used by Trezor devices; by default Keepkey uses non-standard encoding (NFC).
    :return:
    """
    def get_session_info_trezor(get_public_node_fun, hw_session: HwSessionInfo,
                                hw_client):
        nonlocal hw_type

        def call_get_public_node(ctrl, get_public_node_fun, path_n):
            pk = get_public_node_fun(path_n).node.public_key
            return pk

        path = fix_utils.get_default_bip32_base_path(
            hw_session.app_config.fix_network)
        path_n = fix_utils.bip32_path_string_to_n(path)

        # show message for Trezor T device while waiting for the user to choose the passphrase input method
        pub = WndUtils.run_thread_dialog(call_get_public_node,
                                         (get_public_node_fun, path_n),
                                         title=DEFAULT_HW_BUSY_TITLE,
                                         text=DEFAULT_HW_BUSY_MESSAGE,
                                         force_close_dlg_callback=partial(
                                             cancel_hw_thread_dialog,
                                             hw_client),
                                         show_window_delay_ms=1000)

        if pub:
            hw_session.set_base_info(path, pub)
        else:
            raise Exception('Couldn\'t read data from the hardware wallet.')

    control_trezor_keepkey_libs(hw_type)
    if hw_type == HWType.trezor:
        import hw_intf_trezor as trezor
        import trezorlib.client as client
        from trezorlib import btc, exceptions
        try:
            if hw_session and hw_session.app_config:
                use_webusb = hw_session.app_config.trezor_webusb
                use_bridge = hw_session.app_config.trezor_bridge
                use_udp = hw_session.app_config.trezor_udp
                use_hid = hw_session.app_config.trezor_hid
            else:
                use_webusb = True
                use_bridge = True
                use_udp = True
                use_hid = True

            cli = trezor.connect_trezor(device_id=device_id,
                                        use_webusb=use_webusb,
                                        use_bridge=use_bridge,
                                        use_udp=use_udp,
                                        use_hid=use_hid)
            if cli and hw_session:
                try:
                    get_public_node_fun = partial(btc.get_public_node, cli)
                    get_session_info_trezor(get_public_node_fun, hw_session,
                                            cli)
                except (CancelException, exceptions.Cancelled):
                    # cancel_hw_operation(cli)
                    disconnect_hw(cli)
                    raise CancelException()
                except Exception as e:
                    # in the case of error close the session
                    disconnect_hw(cli)
                    raise
            return cli
        except exceptions.PinException as e:
            raise HardwareWalletPinException(e.args[1])

    elif hw_type == HWType.keepkey:
        import hw_intf_keepkey as keepkey
        import keepkeylib.client as client
        try:
            cli = keepkey.connect_keepkey(
                passphrase_encoding=passphrase_encoding, device_id=device_id)
            if cli and hw_session:
                try:
                    get_session_info_trezor(cli.get_public_node, hw_session,
                                            cli)
                except CancelException:
                    cancel_hw_operation(cli)
                    disconnect_hw(cli)
                    raise
                except Exception:
                    # in the case of error close the session
                    disconnect_hw(cli)
                    raise
            return cli

        except client.PinException as e:
            raise HardwareWalletPinException(e.args[1])

    elif hw_type == HWType.ledger_nano_s:
        import hw_intf_ledgernano as ledger
        cli = ledger.connect_ledgernano()
        if cli and hw_session:
            try:
                path = fix_utils.get_default_bip32_base_path(
                    hw_session.app_config.fix_network)
                ap = ledger.get_address_and_pubkey(cli, path)
                hw_session.set_base_info(path, ap['publicKey'])
            except CancelException:
                cancel_hw_operation(cli)
                disconnect_hw(cli)
                raise
            except Exception:
                # in the case of error close the session
                disconnect_hw(cli)
                raise
        return cli

    else:
        raise Exception('Invalid HW type: ' + str(hw_type))
 def callback_WordRequest(self, msg):
     msg = "Enter one word of mnemonic: "
     word = ask_for_word_callback(msg, self.__mnemonic.wordlist)
     if not word:
         raise CancelException('Cancelled')
     return keepkey_proto.WordAck(word=word)
Beispiel #18
0
def sign_message(hw_session: HwSessionInfo, bip32_path, message):

    client = hw_session.hw_client
    # 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 = client.signMessagePrepare(bip32_path, message)
        if info['confirmationNeeded'] and info['confirmationType'] == 34:
            if i == 1 or \
                WndUtils.queryDlg('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 intefering
                # application; it it doesn't help we'll display a message requesting the user to close the app
                hw_session.hw_disconnect()
                if hw_session.hw_connect():
                    client = hw_session.hw_client
                else:
                    raise Exception('Hardware wallet reconnect error.')
            else:
                break
        else:
            ok = True
            break

    if not ok:
        raise CancelException('Cancelled')

    try:
        signature = client.signMessageSign()
    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 = 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).')

    return MessageSignature(
        pubkey.get('address').decode('ascii'),
        bytes(chr(27 + 4 + (signature[0] & 0x01)), "utf-8") + r + s)
Beispiel #19
0
def sign_tx(hw_session: HWSessionBase, rt_data: AppRuntimeData,
            utxos_to_spend: List[wallet_common.UtxoType],
            tx_outputs: List[wallet_common.TxOutputType], tx_fee):
    """
    Creates a signed transaction.
    :param hw_session:
    :param utxos_to_spend: list of utxos to send
    :param tx_outputs: list of transaction outputs
    :param tx_fee: transaction fee
    :return: tuple (serialized tx, total transaction amount in satoshis)
    """
    def load_prev_txes(tx_api_, skip_cache_: bool = False):
        txes_ = {}
        tx_api_.skip_cache = skip_cache_
        for utxo_ in utxos_to_spend:
            prev_hash_bin = bytes.fromhex(utxo_.txid)
            if prev_hash_bin not in txes_:
                tx = tx_api_.get_tx(utxo_.txid)
                txes_[prev_hash_bin] = tx
        return txes_

    insight_network = 'insight_dash'
    if rt_data.is_testnet:
        insight_network += '_testnet'
    dash_network = rt_data.dash_network

    tx_api = MyTxApiInsight(rt_data.dashd_intf, rt_data.tx_cache_dir)
    client = hw_session.hw_client
    inputs = []
    outputs = []
    inputs_amount = 0
    for utxo_index, utxo in enumerate(utxos_to_spend):
        if not utxo.bip32_path:
            raise Exception('No BIP32 path for UTXO ' + utxo.txid)

        address_n = dash_utils.bip32_path_string_to_n(utxo.bip32_path)
        it = trezor_proto.TxInputType(address_n=address_n,
                                      amount=utxo.satoshis,
                                      prev_hash=binascii.unhexlify(utxo.txid),
                                      prev_index=int(utxo.output_index))

        inputs.append(it)
        inputs_amount += utxo.satoshis

    outputs_amount = 0
    for out in tx_outputs:
        outputs_amount += out.satoshis
        if out.address[0] in dash_utils.get_chain_params(
                dash_network).B58_PREFIXES_SCRIPT_ADDRESS:
            stype = trezor_proto.OutputScriptType.PAYTOSCRIPTHASH
            logging.debug('Transaction type: PAYTOSCRIPTHASH' + str(stype))
        elif out.address[0] in dash_utils.get_chain_params(
                dash_network).B58_PREFIXES_PUBKEY_ADDRESS:
            stype = trezor_proto.OutputScriptType.PAYTOADDRESS
            logging.debug('Transaction type: PAYTOADDRESS ' + str(stype))
        else:
            raise Exception('Invalid prefix of the destination address.')
        if out.bip32_path:
            address_n = dash_utils.bip32_path_string_to_n(out.bip32_path)
        else:
            address_n = None

        ot = trezor_proto.TxOutputType(
            address=out.address if address_n is None else None,
            address_n=address_n,
            amount=out.satoshis,
            script_type=stype)
        outputs.append(ot)

    if outputs_amount + tx_fee != inputs_amount:
        raise Exception(
            'Transaction validation failure: inputs + fee != outputs')

    try:
        for skip_cache in (False, True):
            txes = load_prev_txes(tx_api, skip_cache)
            try:
                signed = btc.sign_tx(client,
                                     rt_data.hw_coin_name,
                                     inputs,
                                     outputs,
                                     prev_txes=txes)
                return signed[1], inputs_amount
            except exceptions.Cancelled:
                raise
            except Exception:
                if skip_cache:
                    raise
                log.exception(
                    'Exception occurred while signing transaction. Turning off the transaction cache '
                    'and retrying...')
        raise Exception('Internal error: transaction not signed')
    except exceptions.Cancelled:
        raise CancelException('Cancelled')
    except exceptions.TrezorFailure as e:
        if e.failure.message == 'Device not initialized':
            raise HwNotInitialized(e.failure.message)
        else:
            raise