trezor: single passphrase entry
Only require the user to input the passphrase once, unless creating a wallet. Should they mis-enter the passphrase, they will be warned Electrum couldn't pair the device, and when they actually need to use it they will be prompted again. Fixes #1672
This commit is contained in:
@@ -418,10 +418,11 @@ class DeviceMgr(ThreadJob, PrintError):
|
|||||||
# The user input has wrong PIN or passphrase, or cancelled input,
|
# The user input has wrong PIN or passphrase, or cancelled input,
|
||||||
# or it is not pairable
|
# or it is not pairable
|
||||||
raise DeviceUnpairableError(
|
raise DeviceUnpairableError(
|
||||||
_('Unable to pair with your %s.\n\n'
|
_('Electrum cannot pair with your %s.\n\n'
|
||||||
'Ensure you are able to pair it, or you have the seed phrase, '
|
'Before you request bitcoins to be sent to addresses in this '
|
||||||
'before you request bitcoins to be sent to this wallet.'
|
'wallet, ensure you can pair with your device, or that you have '
|
||||||
) % plugin.device)
|
'its seed (and passphrase, if any). Otherwise all bitcoins you '
|
||||||
|
'receive will be unspendable.') % plugin.device)
|
||||||
|
|
||||||
def unpaired_device_infos(self, handler, plugin, devices=None):
|
def unpaired_device_infos(self, handler, plugin, devices=None):
|
||||||
'''Returns a list of DeviceInfo objects: one for each connected,
|
'''Returns a list of DeviceInfo objects: one for each connected,
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ from electrum_gui.qt.util import *
|
|||||||
|
|
||||||
from electrum.i18n import _
|
from electrum.i18n import _
|
||||||
from electrum.util import PrintError
|
from electrum.util import PrintError
|
||||||
from electrum.wallet import BIP44_Wallet
|
|
||||||
|
|
||||||
# The trickiest thing about this handler was getting windows properly
|
# The trickiest thing about this handler was getting windows properly
|
||||||
# parented on MacOSX.
|
# parented on MacOSX.
|
||||||
@@ -80,17 +79,29 @@ class QtHandlerBase(QObject, PrintError):
|
|||||||
self.done.wait()
|
self.done.wait()
|
||||||
return self.word
|
return self.word
|
||||||
|
|
||||||
def get_passphrase(self, msg):
|
def get_passphrase(self, msg, confirm):
|
||||||
self.done.clear()
|
self.done.clear()
|
||||||
self.win.emit(SIGNAL('passphrase_dialog'), msg)
|
self.win.emit(SIGNAL('passphrase_dialog'), msg, confirm)
|
||||||
self.done.wait()
|
self.done.wait()
|
||||||
return self.passphrase
|
return self.passphrase
|
||||||
|
|
||||||
def passphrase_dialog(self, msg):
|
def passphrase_dialog(self, msg, confirm):
|
||||||
d = PasswordDialog(self.top_level_window(), None, msg, PW_PASSPHRASE)
|
# If confirm is true, require the user to enter the passphrase twice
|
||||||
confirmed, p, passphrase = d.run()
|
parent = self.top_level_window()
|
||||||
if confirmed:
|
if confirm:
|
||||||
passphrase = BIP44_Wallet.normalize_passphrase(passphrase)
|
d = PasswordDialog(parent, None, msg, PW_PASSPHRASE)
|
||||||
|
confirmed, p, passphrase = d.run()
|
||||||
|
else:
|
||||||
|
d = WindowModalDialog(parent, _("Enter Passphrase"))
|
||||||
|
pw = QLineEdit()
|
||||||
|
pw.setEchoMode(2)
|
||||||
|
pw.setMinimumWidth(200)
|
||||||
|
vbox = QVBoxLayout()
|
||||||
|
vbox.addWidget(WWLabel(msg))
|
||||||
|
vbox.addWidget(pw)
|
||||||
|
vbox.addLayout(Buttons(CancelButton(d), OkButton(d)))
|
||||||
|
d.setLayout(vbox)
|
||||||
|
passphrase = unicode(pw.text()) if d.exec_() else None
|
||||||
self.passphrase = passphrase
|
self.passphrase = passphrase
|
||||||
self.done.set()
|
self.done.set()
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from electrum.util import print_msg
|
|||||||
|
|
||||||
class KeepKeyCmdLineHandler:
|
class KeepKeyCmdLineHandler:
|
||||||
|
|
||||||
def get_passphrase(self, msg):
|
def get_passphrase(self, msg, confirm):
|
||||||
import getpass
|
import getpass
|
||||||
print_msg(msg)
|
print_msg(msg)
|
||||||
return getpass.getpass('')
|
return getpass.getpass('')
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import time
|
|||||||
|
|
||||||
from electrum.i18n import _
|
from electrum.i18n import _
|
||||||
from electrum.util import PrintError, UserCancelled
|
from electrum.util import PrintError, UserCancelled
|
||||||
|
from electrum.wallet import BIP44_Wallet
|
||||||
|
|
||||||
|
|
||||||
class GuiMixin(object):
|
class GuiMixin(object):
|
||||||
@@ -52,10 +53,17 @@ class GuiMixin(object):
|
|||||||
return self.proto.PinMatrixAck(pin=pin)
|
return self.proto.PinMatrixAck(pin=pin)
|
||||||
|
|
||||||
def callback_PassphraseRequest(self, req):
|
def callback_PassphraseRequest(self, req):
|
||||||
msg = _("Please enter your %s passphrase")
|
if self.creating_wallet:
|
||||||
passphrase = self.handler.get_passphrase(msg % self.device)
|
msg = _("Enter a passphrase to generate this wallet. Each time "
|
||||||
|
"you use this wallet your %s will prompt you for the "
|
||||||
|
"passphrase. If you forget the passphrase you cannot "
|
||||||
|
"access the bitcoins in the wallet.") % self.device
|
||||||
|
else:
|
||||||
|
msg = _("Enter the passphrase to unlock this wallet:")
|
||||||
|
passphrase = self.handler.get_passphrase(msg, self.creating_wallet)
|
||||||
if passphrase is None:
|
if passphrase is None:
|
||||||
return self.proto.Cancel()
|
return self.proto.Cancel()
|
||||||
|
passphrase = BIP44_Wallet.normalize_passphrase(passphrase)
|
||||||
return self.proto.PassphraseAck(passphrase=passphrase)
|
return self.proto.PassphraseAck(passphrase=passphrase)
|
||||||
|
|
||||||
def callback_WordRequest(self, msg):
|
def callback_WordRequest(self, msg):
|
||||||
@@ -72,6 +80,7 @@ class GuiMixin(object):
|
|||||||
return self.proto.Cancel()
|
return self.proto.Cancel()
|
||||||
return self.proto.CharacterAck(**char_info)
|
return self.proto.CharacterAck(**char_info)
|
||||||
|
|
||||||
|
|
||||||
class TrezorClientBase(GuiMixin, PrintError):
|
class TrezorClientBase(GuiMixin, PrintError):
|
||||||
|
|
||||||
def __init__(self, handler, plugin, proto):
|
def __init__(self, handler, plugin, proto):
|
||||||
@@ -82,6 +91,7 @@ class TrezorClientBase(GuiMixin, PrintError):
|
|||||||
self.tx_api = plugin
|
self.tx_api = plugin
|
||||||
self.types = plugin.types
|
self.types = plugin.types
|
||||||
self.msg = None
|
self.msg = None
|
||||||
|
self.creating_wallet = False
|
||||||
self.used()
|
self.used()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@@ -175,6 +185,10 @@ class TrezorClientBase(GuiMixin, PrintError):
|
|||||||
self.print_error("clear_session: ignoring error", str(e))
|
self.print_error("clear_session: ignoring error", str(e))
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def get_public_node(self, address_n, creating):
|
||||||
|
self.creating_wallet = creating
|
||||||
|
return super(TrezorClientBase, self).get_public_node(address_n)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
'''Called when Our wallet was closed or the device removed.'''
|
'''Called when Our wallet was closed or the device removed.'''
|
||||||
self.print_error("closing client")
|
self.print_error("closing client")
|
||||||
@@ -200,6 +214,7 @@ class TrezorClientBase(GuiMixin, PrintError):
|
|||||||
finally:
|
finally:
|
||||||
self.used()
|
self.used()
|
||||||
self.handler.finished()
|
self.handler.finished()
|
||||||
|
self.creating_wallet = False
|
||||||
self.msg = None
|
self.msg = None
|
||||||
|
|
||||||
return wrapped
|
return wrapped
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from electrum.util import print_msg
|
|||||||
|
|
||||||
class TrezorCmdLineHandler:
|
class TrezorCmdLineHandler:
|
||||||
|
|
||||||
def get_passphrase(self, msg):
|
def get_passphrase(self, msg, confirm):
|
||||||
import getpass
|
import getpass
|
||||||
print_msg(msg)
|
print_msg(msg)
|
||||||
return getpass.getpass('')
|
return getpass.getpass('')
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ class TrezorCompatibleWallet(BIP44_HW_Wallet):
|
|||||||
def get_public_key(self, bip32_path):
|
def get_public_key(self, bip32_path):
|
||||||
client = self.get_client()
|
client = self.get_client()
|
||||||
address_n = client.expand_path(bip32_path)
|
address_n = client.expand_path(bip32_path)
|
||||||
node = client.get_public_node(address_n).node
|
creating = self.next_account_number() == 0
|
||||||
|
node = client.get_public_node(address_n, creating).node
|
||||||
xpub = ("0488B21E".decode('hex') + chr(node.depth)
|
xpub = ("0488B21E".decode('hex') + chr(node.depth)
|
||||||
+ self.i4b(node.fingerprint) + self.i4b(node.child_num)
|
+ self.i4b(node.fingerprint) + self.i4b(node.child_num)
|
||||||
+ node.chain_code + node.public_key)
|
+ node.chain_code + node.public_key)
|
||||||
|
|||||||
Reference in New Issue
Block a user