Skip to content

Commit

Permalink
trezor: single passphrase entry
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Neil Booth committed Feb 11, 2016
1 parent 2bbe829 commit 5359561
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 17 deletions.
9 changes: 5 additions & 4 deletions lib/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,10 +418,11 @@ def force_pair_wallet(self, plugin, wallet, devices):
# The user input has wrong PIN or passphrase, or cancelled input,
# or it is not pairable
raise DeviceUnpairableError(
_('Unable to 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 this wallet.'
) % plugin.device)
_('Electrum cannot pair with your %s.\n\n'
'Before you request bitcoins to be sent to addresses in this '
'wallet, ensure you can pair with your device, or that you have '
'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):
'''Returns a list of DeviceInfo objects: one for each connected,
Expand Down
27 changes: 19 additions & 8 deletions plugins/hw_wallet/qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@

from electrum.i18n import _
from electrum.util import PrintError
from electrum.wallet import BIP44_Wallet

# The trickiest thing about this handler was getting windows properly
# parented on MacOSX.
Expand Down Expand Up @@ -80,17 +79,29 @@ def get_word(self, msg):
self.done.wait()
return self.word

def get_passphrase(self, msg):
def get_passphrase(self, msg, confirm):
self.done.clear()
self.win.emit(SIGNAL('passphrase_dialog'), msg)
self.win.emit(SIGNAL('passphrase_dialog'), msg, confirm)
self.done.wait()
return self.passphrase

def passphrase_dialog(self, msg):
d = PasswordDialog(self.top_level_window(), None, msg, PW_PASSPHRASE)
confirmed, p, passphrase = d.run()
if confirmed:
passphrase = BIP44_Wallet.normalize_passphrase(passphrase)
def passphrase_dialog(self, msg, confirm):
# If confirm is true, require the user to enter the passphrase twice
parent = self.top_level_window()
if confirm:
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.done.set()

Expand Down
2 changes: 1 addition & 1 deletion plugins/keepkey/cmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

class KeepKeyCmdLineHandler:

def get_passphrase(self, msg):
def get_passphrase(self, msg, confirm):
import getpass
print_msg(msg)
return getpass.getpass('')
Expand Down
19 changes: 17 additions & 2 deletions plugins/trezor/clientbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from electrum.i18n import _
from electrum.util import PrintError, UserCancelled
from electrum.wallet import BIP44_Wallet


class GuiMixin(object):
Expand Down Expand Up @@ -52,10 +53,17 @@ def callback_PinMatrixRequest(self, msg):
return self.proto.PinMatrixAck(pin=pin)

def callback_PassphraseRequest(self, req):
msg = _("Please enter your %s passphrase")
passphrase = self.handler.get_passphrase(msg % self.device)
if self.creating_wallet:
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:
return self.proto.Cancel()
passphrase = BIP44_Wallet.normalize_passphrase(passphrase)
return self.proto.PassphraseAck(passphrase=passphrase)

def callback_WordRequest(self, msg):
Expand All @@ -72,6 +80,7 @@ def callback_CharacterRequest(self, msg):
return self.proto.Cancel()
return self.proto.CharacterAck(**char_info)


class TrezorClientBase(GuiMixin, PrintError):

def __init__(self, handler, plugin, proto):
Expand All @@ -82,6 +91,7 @@ def __init__(self, handler, plugin, proto):
self.tx_api = plugin
self.types = plugin.types
self.msg = None
self.creating_wallet = False
self.used()

def __str__(self):
Expand Down Expand Up @@ -175,6 +185,10 @@ def clear_session(self):
self.print_error("clear_session: ignoring error", str(e))
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):
'''Called when Our wallet was closed or the device removed.'''
self.print_error("closing client")
Expand All @@ -200,6 +214,7 @@ def wrapped(self, *args, **kwargs):
finally:
self.used()
self.handler.finished()
self.creating_wallet = False
self.msg = None

return wrapped
Expand Down
2 changes: 1 addition & 1 deletion plugins/trezor/cmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

class TrezorCmdLineHandler:

def get_passphrase(self, msg):
def get_passphrase(self, msg, confirm):
import getpass
print_msg(msg)
return getpass.getpass('')
Expand Down
3 changes: 2 additions & 1 deletion plugins/trezor/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ class TrezorCompatibleWallet(BIP44_HW_Wallet):
def get_public_key(self, bip32_path):
client = self.get_client()
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)
+ self.i4b(node.fingerprint) + self.i4b(node.child_num)
+ node.chain_code + node.public_key)
Expand Down

0 comments on commit 5359561

Please sign in to comment.