electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 121ac07b0106bbfcdb86ad9a1b1921257d22da5f
parent 0fd813f2294c650caed0dfdf22559ff81c459737
Author: ThomasV <thomasv@electrum.org>
Date:   Fri, 30 Sep 2016 01:15:28 +0200

wizard: add checkbox for passphrases. allow passphrases with 2fa seeds

Diffstat:
Mgui/kivy/uix/dialogs/installwizard.py | 4++--
Mgui/qt/installwizard.py | 21++++++++++++++++-----
Mlib/base_wizard.py | 39++++++++++++++++++---------------------
Mlib/bitcoin.py | 4++--
Mplugins/trustedcoin/trustedcoin.py | 40++++++++++++++++++++++------------------
5 files changed, 60 insertions(+), 48 deletions(-)

diff --git a/gui/kivy/uix/dialogs/installwizard.py b/gui/kivy/uix/dialogs/installwizard.py @@ -530,7 +530,7 @@ class ShowSeedDialog(WizardDialog): self._back = _back = partial(self.ids.back.dispatch, 'on_release') def get_params(self, b): - return (self.seed_text,) + return (True,) class WordButton(Button): @@ -643,7 +643,7 @@ class RestoreSeedDialog(WizardDialog): tis.focus = False def get_params(self, b): - return (self.get_text(), False) + return (self.get_text(), False, True) class ConfirmSeedDialog(RestoreSeedDialog): diff --git a/gui/qt/installwizard.py b/gui/qt/installwizard.py @@ -252,9 +252,13 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard): slayout = SeedInputLayout(self, message, is_seed) vbox = QVBoxLayout() vbox.addLayout(slayout.layout()) - if self.opt_bip39: + if self.opt_ext or self.opt_bip39: vbox.addStretch(1) vbox.addWidget(QLabel(_('Options') + ':')) + if self.opt_ext: + cb_pass = QCheckBox(_('Add a passphrase to this seed')) + vbox.addWidget(cb_pass) + if self.opt_bip39: def f(b): if b: msg = ' '.join([ @@ -278,7 +282,8 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard): self.set_main_layout(vbox, title, next_enabled=False) seed = slayout.get_seed() is_bip39 = cb_bip39.isChecked() if self.opt_bip39 else False - return seed, is_bip39 + is_ext = cb_pass.isChecked() if self.opt_ext else False + return seed, is_bip39, is_ext @wizard_dialog def add_xpub_dialog(self, title, message, is_valid, run_next): @@ -308,14 +313,20 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard): _('If you lose your seed, your money will be permanently lost.'), _('To make sure that you have properly saved your seed, please retype it here.') ]) - seed, is_bip39 = self.seed_input(title, message, test) + seed, is_bip39, is_pass = self.seed_input(title, message, test) return seed @wizard_dialog def show_seed_dialog(self, run_next, seed_text): + vbox = QVBoxLayout() slayout = CreateSeedLayout(seed_text) - self.set_main_layout(slayout.layout()) - return seed_text + vbox.addLayout(slayout.layout()) + vbox.addStretch(1) + vbox.addWidget(QLabel('<b>'+_('Option') + '</b>:')) + cb_pass = QCheckBox(_('Add a passphrase to this seed')) + vbox.addWidget(cb_pass) + self.set_main_layout(vbox) + return cb_pass.isChecked() def pw_layout(self, msg, kind): playout = PasswordLayout(None, msg, kind, self.next_button) diff --git a/lib/base_wizard.py b/lib/base_wizard.py @@ -249,9 +249,10 @@ class BaseWizard(object): self.on_keystore(k) def passphrase_dialog(self, run_next): + title = _('Passphrase') message = '\n'.join([ - _('Your seed may be extended with a passphrase.'), - _('If that is the case, enter it here.'), + _('You may extend your seed with a passphrase.'), + _('The passphrase must be saved together with your seed.'), ]) warning = '\n'.join([ _('Note that this is NOT your encryption password.'), @@ -261,18 +262,19 @@ class BaseWizard(object): def restore_from_seed(self): self.opt_bip39 = True + self.opt_ext = True test = bitcoin.is_seed if self.wallet_type == 'standard' else bitcoin.is_new_seed self.restore_seed_dialog(run_next=self.on_restore_seed, test=test) - def on_restore_seed(self, seed, is_bip39): + def on_restore_seed(self, seed, is_bip39, is_ext): if is_bip39: - f = lambda x: self.on_restore_bip39(seed, x) - self.passphrase_dialog(run_next=f) + f = lambda passphrase: self.on_restore_bip39(seed, passphrase) + self.passphrase_dialog(run_next=f) if is_ext else f('') else: seed_type = bitcoin.seed_type(seed) if seed_type == 'standard': - f = lambda x: self.run('create_keystore', seed, x) - self.passphrase_dialog(run_next=f) + f = lambda passphrase: self.run('create_keystore', seed, passphrase) + self.passphrase_dialog(run_next=f) if is_ext else f('') elif seed_type == 'old': self.run('create_keystore', seed, passphrase) elif seed_type == '2fa': @@ -281,7 +283,7 @@ class BaseWizard(object): self.run('restore_from_seed') else: self.load_2fa() - self.run('on_restore_seed', seed) + self.run('on_restore_seed', seed, is_ext) else: raise @@ -355,20 +357,15 @@ class BaseWizard(object): from electrum.mnemonic import Mnemonic seed = Mnemonic('en').make_seed() self.opt_bip39 = False - self.show_seed_dialog(run_next=self.request_passphrase, seed_text=seed) + f = lambda x: self.request_passphrase(seed, x) + self.show_seed_dialog(run_next=f, seed_text=seed) - def request_passphrase(self, seed): - title = _('Passphrase') - message = '\n'.join([ - _('You may extend your seed with a passphrase.'), - _('The passphrase must be saved together with your seed.'), - ]) - warning = '\n'.join([ - _('Note that this is NOT your encryption password.'), - _('If you do not know what this is, leave this field empty.'), - ]) - f = lambda x: self.confirm_seed(seed, x) - self.line_dialog(run_next=f, title=title, message=message, warning=warning, default='', test=lambda x:True) + def request_passphrase(self, seed, opt_passphrase): + if opt_passphrase: + f = lambda x: self.confirm_seed(seed, x) + self.passphrase_dialog(run_next=f) + else: + self.run('confirm_seed', seed, '') def confirm_seed(self, seed, passphrase): f = lambda x: self.confirm_passphrase(seed, passphrase) diff --git a/lib/bitcoin.py b/lib/bitcoin.py @@ -825,8 +825,8 @@ def bip32_private_key(sequence, k, chain): return SecretToASecret(k, True) -def xkeys_from_seed(seed, derivation): +def xkeys_from_seed(seed, passphrase, derivation): from mnemonic import Mnemonic - xprv, xpub = bip32_root(Mnemonic.mnemonic_to_seed(seed, '')) + xprv, xpub = bip32_root(Mnemonic.mnemonic_to_seed(seed, passphrase)) xprv, xpub = bip32_private_derivation(xprv, "m/", derivation) return xprv, xpub diff --git a/plugins/trustedcoin/trustedcoin.py b/plugins/trustedcoin/trustedcoin.py @@ -348,33 +348,31 @@ class TrustedCoinPlugin(BasePlugin): ('create_seed', _('Create a new seed')), ('restore_wallet', _('I already have a seed')), ] - wizard.opt_bip39 = False - wizard.opt_ext = False wizard.choice_dialog(title=title, message=message, choices=choices, run_next=wizard.run) def create_seed(self, wizard): seed = self.make_seed() - f = lambda x: wizard.confirm_seed(seed, '') + f = lambda x: wizard.request_passphrase(seed, x) wizard.show_seed_dialog(run_next=f, seed_text=seed) - def xkeys_from_seed(self, seed): + def xkeys_from_seed(self, seed, passphrase): words = seed.split() n = len(words) # old version use long seed phrases if n >= 24: - xprv1, xpub1 = keystore.xkeys_from_seed(' '.join(words[0:12]), "m/") - xprv2, xpub2 = keystore.xkeys_from_seed(' '.join(words[12:]), "m/") + assert passphrase == '' + xprv1, xpub1 = keystore.xkeys_from_seed(' '.join(words[0:12]), '', "m/") + xprv2, xpub2 = keystore.xkeys_from_seed(' '.join(words[12:]), '', "m/") elif n==12: - xprv1, xpub1 = keystore.xkeys_from_seed(seed, "m/0'/") - xprv2, xpub2 = keystore.xkeys_from_seed(seed, "m/1'/") + xprv1, xpub1 = keystore.xkeys_from_seed(seed, passphrase, "m/0'/") + xprv2, xpub2 = keystore.xkeys_from_seed(seed, passphrase, "m/1'/") else: raise BaseException('unrecognized seed length') return xprv1, xpub1, xprv2, xpub2 def create_keystore(self, wizard, seed, passphrase): - assert passphrase == '' # this overloads the wizard's method - xprv1, xpub1, xprv2, xpub2 = self.xkeys_from_seed(seed) + xprv1, xpub1, xprv2, xpub2 = self.xkeys_from_seed(seed, passphrase) k1 = keystore.from_xprv(xprv1) k2 = keystore.from_xpub(xpub2) wizard.request_password(run_next=lambda pw: self.on_password(wizard, pw, k1, k2)) @@ -399,11 +397,17 @@ class TrustedCoinPlugin(BasePlugin): wizard.confirm_dialog(title='', message=msg, run_next = lambda x: wizard.run('create_remote_key')) def restore_wallet(self, wizard): + wizard.opt_bip39 = False + wizard.opt_ext = True title = _("Restore two-factor Wallet") - f = lambda x, y: wizard.run('on_restore_seed', x) + f = lambda seed, is_bip39, is_ext: wizard.run('on_restore_seed', seed, is_ext) wizard.restore_seed_dialog(run_next=f, test=self.is_valid_seed) - def on_restore_seed(self, wizard, seed): + def on_restore_seed(self, wizard, seed, is_ext): + f = lambda x: self.restore_choice(wizard, seed, x) + wizard.passphrase_dialog(run_next=f) if is_ext else f('') + + def restore_choice(self, wizard, seed, passphrase): wizard.set_icon(':icons/trustedcoin.png') wizard.stack = [] title = _('Restore 2FA wallet') @@ -413,19 +417,19 @@ class TrustedCoinPlugin(BasePlugin): 'or do you want to disable it, and have two master private keys in your wallet?' ]) choices = [('keep', 'Keep'), ('disable', 'Disable')] - f = lambda x: self.on_choice(wizard, seed, x) + f = lambda x: self.on_choice(wizard, seed, passphrase, x) wizard.choice_dialog(choices=choices, message=msg, title=title, run_next=f) - def on_choice(self, wizard, seed, x): + def on_choice(self, wizard, seed, passphrase, x): if x == 'disable': - f = lambda pw: wizard.run('on_restore_pw', seed, pw) + f = lambda pw: wizard.run('on_restore_pw', seed, passphrase, pw) wizard.request_password(run_next=f) else: - self.create_keystore(wizard, seed, '') + self.create_keystore(wizard, seed, passphrase) - def on_restore_pw(self, wizard, seed, password): + def on_restore_pw(self, wizard, seed, passphrase, password): storage = wizard.storage - xprv1, xpub1, xprv2, xpub2 = self.xkeys_from_seed(seed) + xprv1, xpub1, xprv2, xpub2 = self.xkeys_from_seed(seed, passphrase) k1 = keystore.from_xprv(xprv1) k2 = keystore.from_xprv(xprv2) k1.add_seed(seed)