예제 #1
0
    def btnSignMessageClick(self):
        try:
            msg_to_sign = self.edtMessageToSign.toPlainText()
            if msg_to_sign:
                # for ledger HW check if the message contains non-ascii characters
                if self.main_ui.config.hw_type == HWType.ledger_nano_s:
                    try:
                        msg_to_sign.encode('ascii')
                    except UnicodeEncodeError:
                        self.warnMsg('Ledger wallets cannot sign non-ASCII and non-printable characters. Please '
                                     'remove them from your message and try again.')
                        return
                    if len(msg_to_sign) > 140:
                        self.warnMsg('Ledger wallets cannot sign messages longer than 140 characters. Please '
                                     'remove any extra characters and try again.')
                        return

                sig = hw_intf.hw_sign_message(self.main_ui.hw_session, self.bip32path, msg_to_sign)
                signed = base64.b64encode(sig.signature)
                # hex_message = binascii.hexlify(sig.signature).decode('base64')
                self.edtSignedMessage.setPlainText(signed.decode('ascii'))
                if sig.address != self.address:
                    self.warnMsg('Message signed but signing address (%s) for BIP32 path (%s) differs from '
                                 'required one: %s\n\nDid you enter correct passphrase?' % (sig.address, self.bip32path, self.address))
            else:
                self.errorMsg('Empty message cannot be signed.')

        except HardwareWalletCancelException:
            logging.warning('HardwareWalletCancelException')

        except Exception as e:
            logging.exception('Sign message exception:')
            self.errorMsg(str(e))
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
예제 #3
0
    def btnSignMessageClick(self):
        try:
            msg_to_sign = self.edtMessageToSign.toPlainText()
            if msg_to_sign:
                # for ledger HW check if the message contains non-ascii characters
                if self.hw_session and self.bip32path and self.address:
                    if self.hw_session.hw_type == HWType.ledger_nano:
                        try:
                            msg_to_sign.encode('ascii')
                        except UnicodeEncodeError:
                            self.warn_msg(
                                'Ledger wallets cannot sign non-ASCII and non-printable characters. Please '
                                'remove them from your message and try again.')
                            return
                        if len(msg_to_sign) > 140:
                            self.warn_msg(
                                'Ledger wallets cannot sign messages longer than 140 characters. Please '
                                'remove any extra characters and try again.')
                            return

                    sig = hw_intf.hw_sign_message(self.hw_session,
                                                  self.rt_data.hw_coin_name,
                                                  self.bip32path, msg_to_sign)
                    signed = base64.b64encode(sig.signature)
                    self.edtSignedMessage.setPlainText(signed.decode('ascii'))
                    self.edtSignedMessage.update()
                    if sig.address != self.address:
                        self.warn_msg(
                            'Message signed but signing address (%s) for BIP32 path (%s) differs from '
                            'required one: %s\n\nDid you enter correct passphrase?'
                            % (sig.address, self.bip32path, self.address))
                elif self.private_key:
                    sig = ecdsa_sign(msg_to_sign, self.private_key,
                                     self.app_config.dash_network)
                    self.edtSignedMessage.setPlainText(sig)
                    self.edtSignedMessage.update()
                else:
                    raise Exception('Invalid arguments')
            else:
                self.error_msg('Empty message cannot be signed.')

        except CancelException:
            logging.warning('CancelException')

        except Exception as e:
            logging.exception('Sign message exception:')
            self.error_msg(str(e))
    def btnSignMessageClick(self):
        try:
            msg_to_sign = self.edtMessageToSign.toPlainText()
            if msg_to_sign:
                # for ledger HW check if the message contains non-ascii characters
                if self.hw_session.app_config.hw_type == HWType.ledger_nano_s:
                    try:
                        msg_to_sign.encode('ascii')
                    except UnicodeEncodeError:
                        if len(msg_to_sign) > 140:
                            self.warnMsg(
                                'Ledger wallets cannot sign non-ASCII and non-printable characters, and cannot'
                                'sign more than 140 characters. Please change your message and try again.'
                            )
                        else:
                            self.warnMsg(
                                'Ledger wallets cannot sign non-ASCII and non-printable characters. Please '
                                'remove them from your message and try again.')
                        return
                    if len(msg_to_sign) > 140:
                        self.warnMsg(
                            'Ledger wallets cannot sign messages longer than 140 characters. Please '
                            'remove any extra characters and try again.')
                        return

                sig = hw_intf.hw_sign_message(self.hw_session, self.bip32path,
                                              msg_to_sign)
                signed = base64.b64encode(sig.signature)
                # hex_message = binascii.hexlify(sig.signature).decode('base64')
                self.edtSignedMessage.setPlainText(signed.decode('ascii'))
                if sig.address != self.address:
                    self.warnMsg(
                        'The message was signed, but the signing address for the BIP32 path (%s) differs from the one '
                        'you intended.\n\nMaybe you entered a bad passphrase, and are not using the correct device for the current config?\n\n'
                        'Intended: %s\nActual: %s' %
                        (self.bip32path, self.address, sig.address))
            else:
                self.errorMsg('Empty message cannot be signed.')

        except CancelException:
            logging.warning('CancelException')

        except Exception as e:
            logging.exception('Sign message exception:')
            self.errorMsg(str(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()