Example #1
0
 def unfinished_confirm_password(self, run_next):
     vbox = QVBoxLayout()
     msg_label = WWLabel(_('Please enter wallet password before continue'))
     vbox.addWidget(msg_label)
     hbox = QHBoxLayout()
     pw_e = PasswordLineEdit('', self)
     pw_e.setFixedWidth(17 * char_width_in_lineedit())
     pw_label = QLabel(_('Password') + ':')
     hbox.addWidget(pw_label)
     hbox.addWidget(pw_e)
     hbox.addStretch()
     vbox.addLayout(hbox)
     self.set_layout(vbox, title=_('Confirm wallet password'))
     try:
         while True:
             pw_e.setFocus()
             if self.loop.exec_() != 2:  # 2 = next
                 raise UserCancelled()
             password = pw_e.text()
             try:
                 self.unfinished_check_password(password)
                 break
             except InvalidPassword as e:
                 self.show_message(title=_('Error'), msg=str(e))
                 continue
             except BaseException as e:
                 self.logger.exception('')
                 self.show_message(title=_('Error'), msg=repr(e))
                 raise UserCancelled()
     finally:
         pw_e.clear()
     return password
Example #2
0
 def run_user_interaction_loop():
     while True:
         if self.loop.exec_() != 2:  # 2 = next
             raise UserCancelled()
         assert temp_storage
         if temp_storage.file_exists(
         ) and not temp_storage.is_encrypted():
             break
         if not temp_storage.file_exists():
             break
         wallet_from_memory = get_wallet_from_daemon(temp_storage.path)
         if wallet_from_memory:
             raise WalletAlreadyOpenInMemory(wallet_from_memory)
         if temp_storage.file_exists() and temp_storage.is_encrypted():
             if temp_storage.is_encrypted_with_user_pw():
                 password = pw_e.text()
                 try:
                     temp_storage.decrypt(password)
                     break
                 except InvalidPassword as e:
                     self.show_message(title=_('Error'), msg=str(e))
                     continue
                 except BaseException as e:
                     self.logger.exception('')
                     self.show_message(title=_('Error'), msg=repr(e))
                     raise UserCancelled()
             elif temp_storage.is_encrypted_with_hw_device():
                 try:
                     self.run('choose_hw_device',
                              HWD_SETUP_DECRYPT_WALLET,
                              storage=temp_storage)
                 except InvalidPassword as e:
                     self.show_message(
                         title=_('Error'),
                         msg=
                         _('Failed to decrypt using this hardware device.'
                           ) + '\n' +
                         _('If you use a passphrase, make sure it is correct.'
                           ))
                     self.reset_stack()
                     return self.select_storage(path,
                                                get_wallet_from_daemon)
                 except (UserCancelled, GoBack):
                     raise
                 except BaseException as e:
                     self.logger.exception('')
                     self.show_message(title=_('Error'), msg=repr(e))
                     raise UserCancelled()
                 if temp_storage.is_past_initial_decryption():
                     break
                 else:
                     raise UserCancelled()
             else:
                 raise Exception('Unexpected encryption version')
Example #3
0
    def check_device_dialog(self):
        match = re.search(
            r'v([0-9])+\.[0-9]+\.[0-9]+',
            run_in_hwd_thread(self.dbb_hid.get_serial_number_string))
        if match is None:
            raise Exception("error detecting firmware version")
        major_version = int(match.group(1))
        if major_version < MIN_MAJOR_VERSION:
            raise Exception(
                "Please upgrade to the newest firmware using the BitBox Desktop app: https://shiftcrypto.ch/start"
            )
        # Set password if fresh device
        if self.password is None and not self.dbb_has_password():
            if not self.setupRunning:
                return False  # A fresh device cannot connect to an existing wallet
            msg = _("An uninitialized Digital Bitbox is detected.") + " " + \
                  _("Enter a new password below.") + "\n\n" + \
                  _("REMEMBER THE PASSWORD!") + "\n\n" + \
                  _("You cannot access your coins or a backup without the password.") + "\n" + \
                  _("A backup is saved automatically when generating a new wallet.")
            if self.password_dialog(msg):
                reply = self.hid_send_plain(b'{"password":"******"}')
            else:
                return False

        # Get password from user if not yet set
        msg = _("Enter your Digital Bitbox password:"******"led":"blink"}')
            if 'error' in reply:
                self.password = None
                if reply['error']['code'] == 109:
                    msg = _("Incorrect password entered.") + "\n\n" + \
                          reply['error']['message'] + "\n\n" + \
                          _("Enter your Digital Bitbox password:"******"Unexpected error occurred.") + "\n\n" + \
                          reply['error']['message'] + "\n\n" + \
                          _("Enter your Digital Bitbox password:"******"device":"info"}')
            if reply['device']['id'] != "":
                self.recover_or_erase_dialog()  # Already seeded
            else:
                self.seed_device_dialog()  # Seed if not initialized
            self.mobile_pairing_dialog()
        return self.isInitialized
Example #4
0
 def callback_Failure(self, msg):
     # BaseClient's unfortunate call() implementation forces us to
     # raise exceptions on failure in order to unwind the stack.
     # However, making the user acknowledge they cancelled
     # gets old very quickly, so we suppress those.  The NotInitialized
     # one is misnamed and indicates a passphrase request was cancelled.
     if msg.code in (self.types.FailureType.PinCancelled,
                     self.types.FailureType.ActionCancelled,
                     self.types.FailureType.NotInitialized):
         raise UserCancelled()
     raise RuntimeError(msg.message)
Example #5
0
 def __exit__(self, exc_type, e, traceback):
     self.end_flow()
     if e is not None:
         if isinstance(e, Cancelled):
             raise UserCancelled() from e
         elif isinstance(e, TrezorFailure):
             raise RuntimeError(str(e)) from e
         elif isinstance(e, OutdatedFirmwareError):
             raise OutdatedHwFirmwareException(e) from e
         else:
             return False
     return True
Example #6
0
    def get_seed(self):
        password = None
        if self.wallet.has_keystore_encryption():
            password = self.password_dialog(parent=self.d.parent())
            if not password:
                raise UserCancelled()

        keystore = self.wallet.get_keystore()
        if not keystore or not keystore.has_seed():
            return
        self.extension = bool(keystore.get_passphrase(password))
        return keystore.get_seed(password)
Example #7
0
 def f(method):
     import threading
     settings = self.request_trezor_init_settings(wizard, method, self.device)
     t = threading.Thread(target=self._initialize_device_safe, args=(settings, method, device_id, wizard, handler))
     t.setDaemon(True)
     t.start()
     exit_code = wizard.loop.exec_()
     if exit_code != 0:
         # this method (initialize_device) was called with the expectation
         # of leaving the device in an initialized state when finishing.
         # signal that this is not the case:
         raise UserCancelled()
Example #8
0
 def exec_layout(self,
                 layout,
                 title=None,
                 raise_on_cancel=True,
                 next_enabled=True):
     self.set_layout(layout, title, next_enabled)
     result = self.loop.exec_()
     if not result and raise_on_cancel:
         raise UserCancelled()
     if result == 1:
         raise GoBack from None
     self.title.setVisible(False)
     self.back_button.setEnabled(False)
     self.next_button.setEnabled(False)
     self.main_widget.setVisible(False)
     self.please_wait.setVisible(True)
     self.refresh_gui()
     return result
Example #9
0
    def run_upgrades(self, storage: WalletStorage, db: 'WalletDB') -> None:
        path = storage.path
        if db.requires_split():
            self.hide()
            msg = _(
                "The wallet '{}' contains multiple accounts, which are no longer supported since Firo Electrum 2.7.\n\n"
                "Do you want to split your wallet into multiple files?"
            ).format(path)
            if not self.question(msg):
                return
            file_list = db.split_accounts(path)
            msg = _('Your accounts have been moved to') + ':\n' + '\n'.join(
                file_list) + '\n\n' + _(
                    'Do you want to delete the old file') + ':\n' + path
            if self.question(msg):
                os.remove(path)
                self.show_warning(_('The file was removed'))
            # raise now, to avoid having the old storage opened
            raise UserCancelled()

        action = db.get_action()
        if action and db.requires_upgrade():
            raise WalletFileException(
                'Incomplete wallet files cannot be upgraded.')
        if action:
            self.hide()
            msg = _("The file '{}' contains an incompletely created wallet.\n"
                    "Do you want to complete its creation now?").format(path)
            if not self.question(msg):
                if self.question(
                        _("Do you want to delete '{}'?").format(path)):
                    os.remove(path)
                    self.show_warning(_('The file was removed'))
                return
            self.show()
            self.data = json.loads(storage.read())
            self.run(action)
            for k, v in self.data.items():
                db.put(k, v)
            db.write(storage)
            return

        if db.requires_upgrade():
            self.upgrade_db(storage, db)
Example #10
0
    def sign_transaction(self, tx, password):
        if tx.is_complete():
            return

        try:
            p2pkhTransaction = True
            inputhasharray = []
            hasharray = []
            pubkeyarray = []

            # Build hasharray from inputs
            for i, txin in enumerate(tx.inputs()):
                if txin.is_coinbase_input():
                    self.give_error(
                        "Coinbase not supported")  # should never happen

                if txin.script_type != 'p2pkh':
                    p2pkhTransaction = False

                my_pubkey, inputPath = self.find_my_pubkey_in_txinout(txin)
                if not inputPath:
                    self.give_error("No matching pubkey for sign_transaction"
                                    )  # should never happen
                inputPath = convert_bip32_intpath_to_strpath(inputPath)
                inputHash = sha256d(bfh(tx.serialize_preimage(i)))
                hasharray_i = {
                    'hash': to_hexstr(inputHash),
                    'keypath': inputPath
                }
                hasharray.append(hasharray_i)
                inputhasharray.append(inputHash)

            # Build pubkeyarray from outputs
            for txout in tx.outputs():
                assert txout.address
                if txout.is_change:
                    changePubkey, changePath = self.find_my_pubkey_in_txinout(
                        txout)
                    assert changePath
                    changePath = convert_bip32_intpath_to_strpath(changePath)
                    changePubkey = changePubkey.hex()
                    pubkeyarray_i = {
                        'pubkey': changePubkey,
                        'keypath': changePath
                    }
                    pubkeyarray.append(pubkeyarray_i)

            # Special serialization of the unsigned transaction for
            # the mobile verification app.
            # At the moment, verification only works for p2pkh transactions.
            if p2pkhTransaction:
                tx_copy = copy.deepcopy(tx)

                # monkey-patch method of tx_copy instance to change serialization
                def input_script(self,
                                 txin: PartialTxInput,
                                 *,
                                 estimate_size=False):
                    if txin.script_type == 'p2pkh':
                        return Transaction.get_preimage_script(txin)
                    raise Exception("unsupported type %s" % txin.script_type)

                tx_copy.input_script = input_script.__get__(
                    tx_copy, PartialTransaction)
                tx_dbb_serialized = tx_copy.serialize_to_network()
            else:
                # We only need this for the signing echo / verification.
                tx_dbb_serialized = None

            # Build sign command
            dbb_signatures = []
            steps = math.ceil(1.0 * len(hasharray) / self.maxInputs)
            for step in range(int(steps)):
                hashes = hasharray[step * self.maxInputs:(step + 1) *
                                   self.maxInputs]

                msg = {
                    "sign": {
                        "data": hashes,
                        "checkpub": pubkeyarray,
                    },
                }
                if tx_dbb_serialized is not None:
                    msg["sign"]["meta"] = to_hexstr(sha256d(tx_dbb_serialized))
                msg = json.dumps(msg).encode('ascii')
                dbb_client = self.plugin.get_client(self)

                if not dbb_client.is_paired():
                    raise Exception("Could not sign transaction.")

                reply = dbb_client.hid_send_encrypt(msg)
                if 'error' in reply:
                    raise Exception(reply['error']['message'])

                if 'echo' not in reply:
                    raise Exception("Could not sign transaction.")

                if self.plugin.is_mobile_paired(
                ) and tx_dbb_serialized is not None:
                    reply['tx'] = tx_dbb_serialized
                    self.plugin.comserver_post_notification(reply)

                if steps > 1:
                    self.handler.show_message(
                        _("Signing large transaction. Please be patient ...") +
                        "\n\n" +
                        _("To continue, touch the Digital Bitbox's blinking light for 3 seconds."
                          ) + " " +
                        _("(Touch {} of {})").format((step + 1), steps) +
                        "\n\n" +
                        _("To cancel, briefly touch the blinking light or wait for the timeout."
                          ) + "\n\n")
                else:
                    self.handler.show_message(
                        _("Signing transaction...") + "\n\n" +
                        _("To continue, touch the Digital Bitbox's blinking light for 3 seconds."
                          ) + "\n\n" +
                        _("To cancel, briefly touch the blinking light or wait for the timeout."
                          ))

                # Send twice, first returns an echo for smart verification
                reply = dbb_client.hid_send_encrypt(msg)
                self.handler.finished()

                if 'error' in reply:
                    if reply["error"].get('code') in (600, 601):
                        # aborted via LED short touch or timeout
                        raise UserCancelled()
                    raise Exception(reply['error']['message'])

                if 'sign' not in reply:
                    raise Exception("Could not sign transaction.")

                dbb_signatures.extend(reply['sign'])

            # Fill signatures
            if len(dbb_signatures) != len(tx.inputs()):
                raise Exception("Incorrect number of transactions signed."
                                )  # Should never occur
            for i, txin in enumerate(tx.inputs()):
                for pubkey_bytes in txin.pubkeys:
                    if txin.is_complete():
                        break
                    signed = dbb_signatures[i]
                    if 'recid' in signed:
                        # firmware > v2.1.1
                        recid = int(signed['recid'], 16)
                        s = binascii.unhexlify(signed['sig'])
                        h = inputhasharray[i]
                        pk = ecc.ECPubkey.from_sig_string(s, recid, h)
                        pk = pk.get_public_key_hex(compressed=True)
                    elif 'pubkey' in signed:
                        # firmware <= v2.1.1
                        pk = signed['pubkey']
                    if pk != pubkey_bytes.hex():
                        continue
                    sig_r = int(signed['sig'][:64], 16)
                    sig_s = int(signed['sig'][64:], 16)
                    sig = ecc.der_sig_from_r_and_s(sig_r, sig_s)
                    sig = to_hexstr(sig) + '01'
                    tx.add_signature_to_txin(txin_idx=i,
                                             signing_pubkey=pubkey_bytes.hex(),
                                             sig=sig)
        except UserCancelled:
            raise
        except BaseException as e:
            self.give_error(e, True)
        else:
            _logger.info(f"Transaction is_complete {tx.is_complete()}")