Beispiel #1
0
def get_address_and_pubkey(main_ui, bip32_path):
    client = main_ui.hw_client
    if client:
        bip32_path.strip()
        if bip32_path.lower().find('m/') >= 0:
            # removing m/ prefix because of keepkey library
            bip32_path = bip32_path[2:]

        if main_ui.config.hw_type in (HWType.trezor, HWType.keepkey):
            if isinstance(bip32_path, str):
                # trezor/keepkey require bip32 path argument as an array of integers
                bip32_path = client.expand_path(bip32_path)

            return {
                'address': client.get_address('Dash', bip32_path, False),
                'publicKey': client.get_public_node(bip32_path).node.public_key
            }

        elif main_ui.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)

            return ledger.get_address_and_pubkey(client, bip32_path)
        else:
            raise Exception('Unknown hwardware wallet type: ' +
                            main_ui.config.hw_type)
Beispiel #2
0
def get_address(hw_session: HwSessionInfo, bip32_path):
    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 in (HWType.trezor, HWType.keepkey):
            if isinstance(bip32_path, str):
                # trezor/keepkey require bip32 path argument as an array of integers
                bip32_path = client.expand_path(bip32_path)

            return client.get_address(hw_session.app_config.hw_coin_name,
                                      bip32_path, False)

        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)
            return adr_pubkey.get('address')
        else:
            raise Exception('Unknown hwardware wallet type: ' +
                            hw_session.app_config.hw_type)
    else:
        raise Exception('HW client not open.')
def get_xpub(hw_session: HwSessionInfo, bip32_path):
    client = hw_session.hw_client
    if client:
        if isinstance(bip32_path, str):
            bip32_path.strip()
            if bip32_path.lower().find('m/') >= 0:
                bip32_path = bip32_path[2:]

        if hw_session.app_config.hw_type == HWType.trezor:
            from trezorlib import btc
            if isinstance(bip32_path, str):
                bip32_path = dash_utils.bip32_path_string_to_n(bip32_path)

            return btc.get_public_node(client, bip32_path).xpub

        elif hw_session.app_config.hw_type == HWType.keepkey:
            if isinstance(bip32_path, str):
                bip32_path = dash_utils.bip32_path_string_to_n(bip32_path)

            return client.get_public_node(bip32_path).xpub

        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)

            return ledger.get_xpub(client, bip32_path)
        else:
            raise Exception('Unknown hardware wallet type: ' + hw_session.app_config.hw_type)
    else:
        raise Exception('HW client not open.')
def prepare_hw_encryption_attrs(hw_session: HwSessionInfo, label: str) -> \
        Tuple[int, int, List[int], bytes, bytes, bytes]:
    """

    :param hw_session:
    :param label:
    :return: 0: protocol id
             1: hw type id (see below)
             2: bip32 path to the entryption key
             3: encryption key hash
             4: encryption key binary
             5: pub key hash of the encryption key
    """
    # generate a new random password which will be used to encrypt with Trezor method + Fernet
    protocol = 1
    hw_type_bin = {
        HWType.trezor: 1,
        HWType.keepkey: 2,
        HWType.ledger_nano: 3
    }[hw_session.hw_type]

    key = Fernet.generate_key()  # encryption key
    key_bin = base64.urlsafe_b64decode(key)

    bip32_path_n = [10, 100, 1000]

    if hw_session.hw_type in (HWType.trezor, HWType.keepkey):
        # for trezor method, for encryption we use the raw key and the key encrypted with a device
        # will be part of a header
        encrypted_key_bin, pub_key = hw_session.hw_encrypt_value(bip32_path_n,
                                                                 label=label,
                                                                 value=key_bin)
        pub_key_hash = SHA256.new(pub_key).digest()
        return protocol, hw_type_bin, bip32_path_n, key, encrypted_key_bin, pub_key_hash

    elif hw_session.hw_type == HWType.ledger_nano:
        # Ledger Nano S does not have encryption/decryption features, so for encryption and decryption will use
        # a hash of a signed message, where the message the raw key itself;
        # The raw key will be part of the encrypted header.

        display_label = f'<b>Click the sign message confirmation button on the <br>hardware wallet to ' \
                        f'encrypt \'{label}\'.</b>'
        bip32_path_str = bip32_path_n_to_string(bip32_path_n)
        sig = hw_sign_message(hw_session,
                              'Dash',
                              bip32_path_str,
                              key_bin.hex(),
                              display_label=display_label)
        adr_pk = get_address_and_pubkey(hw_session, 'Dash', bip32_path_str)

        pub_key_hash = SHA256.new(adr_pk.get('publicKey')).digest()
        enc_key_hash = SHA256.new(sig.signature).digest()
        enc_key_hash = base64.urlsafe_b64encode(enc_key_hash)

        return protocol, hw_type_bin, bip32_path_n, enc_key_hash, key_bin, pub_key_hash
    def get_child_entry(self, index) -> 'Bip44Entry':
        child = self.child_entries.get(index)
        if not child:
            key = self.get_bip32key()
            child_key = key.ChildKey(index)
            child_xpub = child_key.ExtendedKey(False, True)
            if self.bip32_path:
                bip32_path_n = bip32_path_string_to_n(self.bip32_path)
                bip32_path_n.append(index)
                bip32_path = bip32_path_n_to_string(bip32_path_n)
            else:
                raise Exception('Unknown BIP32 path of the parrent')

            child = Bip44Entry(tree_id=self.tree_id, id=None, parent=self, xpub=child_xpub, address_index=index,
                               bip32_path=bip32_path, bip32_key=child_key)
            self.child_entries[index] = child
        return child
def get_address_and_pubkey(hw_session: HwSessionInfo, bip32_path):
    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
            if isinstance(bip32_path, str):
                bip32_path = dash_utils.bip32_path_string_to_n(bip32_path)
            return {
                'address':
                btc.get_address(client, hw_session.app_config.hw_coin_name,
                                bip32_path, False),
                'publicKey':
                btc.get_public_node(client, bip32_path).node.public_key
            }

        elif hw_session.app_config.hw_type == HWType.keepkey:
            if isinstance(bip32_path, str):
                bip32_path = dash_utils.bip32_path_string_to_n(bip32_path)
            return {
                'address':
                client.get_address(hw_session.app_config.hw_coin_name,
                                   bip32_path, False),
                'publicKey':
                client.get_public_node(bip32_path).node.public_key
            }

        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)

            return ledger.get_address_and_pubkey(client, bip32_path)
        else:
            raise Exception('Unknown hardware wallet type: ' +
                            hw_session.app_config.hw_type)
    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="Please confirm", 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
                if isinstance(bip32_path, str):
                    bip32_path = dash_utils.bip32_path_string_to_n(bip32_path)
                return btc.get_address(client, hw_session.app_config.hw_coin_name, bip32_path, show_display)

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

                if isinstance(bip32_path, str):
                    bip32_path = dash_utils.bip32_path_string_to_n(bip32_path)
                return client.get_address(hw_session.app_config.hw_coin_name, bip32_path, show_display)

            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)
                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 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 #9
0
        def get_addresses_to_scan(self, thread_ctrl: CtrlObject, addr_scan_ctrl: dict):
            """
            :param self:
            :param addr_scan_ctrl: (only for self.utxo_src_mode == 2) penultimate element of bip32 path to scan, used
                to switch sanning between normal and change addresses
            :return: yield List[Tuple[str (address), str (bip32 path)]]
            """
            try:
                if self.utxo_src_mode == 1:

                    if self.mn_src_index is not None:
                        if self.mn_src_index == len(self.masternode_addresses):
                            # show addresses of all masternodes
                            for chunk_nr in range(int(math.ceil(len(self.masternode_addresses) / ADDRESS_CHUNK))):
                                if self.finishing or thread_ctrl.finish:
                                    return
                                yield [x for x in self.masternode_addresses[
                                                  chunk_nr * ADDRESS_CHUNK : (chunk_nr + 1) * ADDRESS_CHUNK] if x[0] and x[1]]
                        elif self.mn_src_index < len(self.masternode_addresses) and self.mn_src_index >= 0:
                            if self.finishing or thread_ctrl.finish:
                                return
                            if self.masternode_addresses[self.mn_src_index][0] and \
                                self.masternode_addresses[self.mn_src_index][1]:
                                yield [self.masternode_addresses[self.mn_src_index]]

                elif self.utxo_src_mode == 2:
                    # hw wallet account: scan all addresses and change addresses for a specific account
                    # stop when a defined number of subsequent address has balance 0

                    addr_count = 0
                    addr_n = dash_utils.bip32_path_string_to_n(self.hw_account_base_bip32_path)
                    db_cur = self.db_intf.get_cursor()

                    try:
                        bip32_path_n = addr_n[:] + [self.hw_account_number + 0x80000000, 0, 0]
                        cur_addr_buf = []
                        last_level2_nr = addr_scan_ctrl.get('level2')
                        while True:
                            restart_iteration = False
                            for nr in range(1000):
                                if self.finishing or thread_ctrl.finish:
                                    return
                                if last_level2_nr != addr_scan_ctrl.get('level2'):
                                    last_level2_nr = addr_scan_ctrl.get('level2')
                                    restart_iteration = True
                                    break
                                bip32_path_n[-2] = addr_scan_ctrl.get('level2')
                                bip32_path_n[-1] = nr

                                cur_addr = hw_intf.get_address_ext(self.main_ui.hw_session, bip32_path_n, db_cur,
                                                                   self.app_config.hw_encrypt_string,
                                                                   self.app_config.hw_decrypt_string)

                                bip32_path = dash_utils.bip32_path_n_to_string(bip32_path_n)
                                cur_addr_buf.append((cur_addr, bip32_path))
                                addr_count += 1
                                if len(cur_addr_buf) >= ADDRESS_CHUNK:
                                    yield cur_addr_buf
                                    cur_addr_buf.clear()
                            if restart_iteration:
                                continue
                            if cur_addr_buf:
                                yield cur_addr_buf
                            break
                    finally:
                        if db_cur.connection.total_changes > 0:
                            self.db_intf.commit()
                        self.db_intf.release_cursor()

                elif self.utxo_src_mode == 3:

                    db_cur = self.db_intf.get_cursor()
                    try:
                        # address from a specific bip32 path
                        bip32_path_n = dash_utils.bip32_path_string_to_n(self.hw_src_bip32_path)
                        cur_addr = hw_intf.get_address_ext(self.main_ui.hw_session, bip32_path_n, db_cur,
                                                           self.app_config.hw_encrypt_string,
                                                           self.app_config.hw_decrypt_string)
                        self.hw_src_address = cur_addr
                        yield [(cur_addr, self.hw_src_bip32_path)]

                    finally:
                        if db_cur.connection.total_changes > 0:
                            self.db_intf.commit()
                        self.db_intf.release_cursor()

            except Exception as e:
                logging.exception('Exception occurred')
                raise
Beispiel #10
0
def get_address_ext(hw_session: HwSessionInfo, bip32_path_n: List[int],
                    db_cursor: sqlite3.Cursor, encrypt_fun: Callable,
                    decrypt_fun: Callable):
    """
    Reads address of a specific bip32 path from hardware wallet, using db cache to speed-up operation
    by avoiding utilization the hardware wallet device as quite slow for this operation.
    :param hw_session:
    :param bip32_path_n:
    :param db_cursor:
    :param encrypt_fun:
    :param decrypt_fun:
    :return:
    """
    global hd_tree_db_map, bip32_address_map

    def get_hd_tree_db_id(tree_ident: str):
        db_id = hd_tree_db_map.get(tree_ident)
        if not db_id:
            db_cursor.execute('select id from ADDRESS_HD_TREE where ident=?',
                              (tree_ident, ))
            row = db_cursor.fetchone()
            if not row:
                db_cursor.execute(
                    'insert into ADDRESS_HD_TREE(ident) values(?)',
                    (tree_ident, ))
                db_id = db_cursor.lastrowid
                hd_tree_db_map[tree_ident] = db_id
            else:
                db_id = row[0]
        return db_id

    try:
        map_dict = bip32_address_map.get(hw_session.hd_tree_ident)
        if not map_dict:
            map_dict = {}
            bip32_address_map[hw_session.hd_tree_ident] = map_dict

        path_str = dash_utils.bip32_path_n_to_string(bip32_path_n)
        address = map_dict.get(path_str)
        db_id = None
        if not address:
            # look for address in db cache
            hd_tree_id = get_hd_tree_db_id(hw_session.hd_tree_ident)
            db_cursor.execute(
                'select id, address from ADDRESS where tree_id=? and path=?',
                (hd_tree_id, path_str))
            row = db_cursor.fetchone()
            if row:
                db_id, address = row
                # address is encrypted; try to decrypt it
                try:
                    address = decrypt_fun(address).decode('ascii')
                    if not dash_utils.validate_address(
                            address, hw_session.app_config.dash_network):
                        address = None
                except Exception:
                    address = None

            if not address:
                address = get_address(hw_session, bip32_path_n)
                map_dict[path_str] = address
                address_encrypted = encrypt_fun(bytes(address, 'ascii'))
                if db_id:
                    # update db record: it was encrypted with no longer valid encryption key
                    db_cursor.execute(
                        'update ADDRESS set address=? where id=?',
                        (address_encrypted, db_id))
                else:
                    db_cursor.execute(
                        'insert into ADDRESS(tree_id, path, address) values(?,?,?)',
                        (hd_tree_id, path_str, address_encrypted))
        return address
    except Exception as e:
        logging.exception('Unhandled exception occurred')
        return get_address(hw_session, bip32_path_n)