electrum

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

commit 3ac357171a09adfdc3c563bd9cdcc0a34a986c51
parent b907a668ec59ac5500df5137f58201fbd1a8af4e
Author: ThomasV <thomasv@electrum.org>
Date:   Thu, 25 Aug 2016 12:18:51 +0200

wizard: add password only once all keystores are known

Diffstat:
Mlib/base_wizard.py | 66++++++++++++++++++++++++++++++++++++------------------------------
Mlib/keystore.py | 73++++++++++++++++++++++++++++++++++---------------------------------------
Mplugins/trustedcoin/trustedcoin.py | 35++++++++++++++++++++++-------------
3 files changed, 92 insertions(+), 82 deletions(-)

diff --git a/lib/base_wizard.py b/lib/base_wizard.py @@ -159,13 +159,8 @@ class BaseWizard(object): self.restore_keys_dialog(title=title, message=message, run_next=self.on_restore_from_key, is_valid=v) def on_restore_from_key(self, text): - def f(password): - k = keystore.from_keys(text, password) - self.on_keystore(k, password) - if keystore.is_private(text): - self.run('request_password', run_next=f) - else: - f(None) + k = keystore.from_keys(text) + self.on_keystore(k) def choose_hw_device(self): title = _('Hardware Keystore') @@ -228,43 +223,36 @@ class BaseWizard(object): def on_seed(self, seed, add_passphrase, is_bip39): self.is_bip39 = is_bip39 - f = lambda x: self.run('on_passphrase', seed, x) + f = lambda x: self.run('create_keystore', seed, x) if add_passphrase: self.request_passphrase(run_next=f) else: f('') - def on_passphrase(self, seed, passphrase): - f = lambda x: self.run('on_password', seed, passphrase, x) - self.request_password(run_next=f) - - def on_password(self, seed, passphrase, password): + def create_keystore(self, seed, passphrase): if self.is_bip39: - f = lambda account_id: self.run('on_bip44', seed, passphrase, password, account_id) + f = lambda account_id: self.run('on_bip44', seed, passphrase, account_id) self.account_id_dialog(run_next=f) else: - k = keystore.from_seed(seed, passphrase, password) - self.on_keystore(k, password) + k = keystore.from_seed(seed, passphrase) + self.on_keystore(k) - def on_bip44(self, seed, passphrase, password, account_id): + def on_bip44(self, seed, passphrase, account_id): import keystore k = keystore.BIP32_KeyStore({}) bip32_seed = keystore.bip39_to_seed(seed, passphrase) derivation = "m/44'/0'/%d'"%account_id - k.add_xprv_from_seed(bip32_seed, derivation, password) - self.on_keystore(k, password) + k.add_xprv_from_seed(bip32_seed, derivation) + self.on_keystore(k) - def on_keystore(self, k, password): + def on_keystore(self, k): if self.wallet_type == 'standard': - self.storage.put('keystore', k.dump()) - self.wallet = Standard_Wallet(self.storage) - self.run('create_addresses') + self.keystores.append(k) + self.run('create_wallet') elif self.wallet_type == 'multisig': - if k.xpub in map(lambda x: x.xpub, self.keystores): raise BaseException('duplicate key') self.keystores.append(k) - if len(self.keystores) == 1: xpub = k.get_master_public_key() self.stack = [] @@ -272,11 +260,29 @@ class BaseWizard(object): elif len(self.keystores) < self.n: self.run('choose_keystore') else: - for i, k in enumerate(self.keystores): - self.storage.put('x%d/'%(i+1), k.dump()) - self.storage.write() - self.wallet = Multisig_Wallet(self.storage) - self.run('create_addresses') + self.run('create_wallet') + + def create_wallet(self): + if any(k.may_have_password() for k in self.keystores): + self.request_password(run_next=self.on_password) + else: + self.on_password(None) + + def on_password(self, password): + self.storage.put('use_encryption', bool(password)) + for k in self.keystores: + if k.may_have_password(): + k.update_password(None, password) + if self.wallet_type == 'standard': + self.storage.put('keystore', k.dump()) + self.wallet = Standard_Wallet(self.storage) + self.run('create_addresses') + elif self.wallet_type == 'multisig': + for i, k in enumerate(self.keystores): + self.storage.put('x%d/'%(i+1), k.dump()) + self.storage.write() + self.wallet = Multisig_Wallet(self.storage) + self.run('create_addresses') def show_xpub_and_add_cosigners(self, xpub): self.show_xpub_dialog(xpub=xpub, run_next=lambda x: self.run('choose_keystore')) diff --git a/lib/keystore.py b/lib/keystore.py @@ -49,12 +49,14 @@ class KeyStore(PrintError): def can_import(self): return False - class Software_KeyStore(KeyStore): def __init__(self): KeyStore.__init__(self) + def may_have_password(self): + return not self.is_watching_only() + def sign_message(self, sequence, message, password): sec = self.get_private_key(sequence, password) key = regenerate_key(sec) @@ -161,12 +163,10 @@ class Deterministic_KeyStore(Software_KeyStore): def can_change_password(self): return not self.is_watching_only() - def add_seed(self, seed, password): + def add_seed(self, seed): if self.seed: raise Exception("a seed exists") self.seed_version, self.seed = self.format_seed(seed) - if password: - self.seed = pw_encode(self.seed, password) def get_seed(self, password): return pw_decode(self.seed, password).encode('utf8') @@ -179,9 +179,6 @@ class Xpub: self.xpub_receive = None self.xpub_change = None - def add_master_public_key(self, xpub): - self.xpub = xpub - def get_master_public_key(self): return self.xpub @@ -237,9 +234,6 @@ class BIP32_KeyStore(Deterministic_KeyStore, Xpub): d['xprv'] = self.xprv return d - def add_master_private_key(self, xprv, password): - self.xprv = pw_encode(xprv, password) - def get_master_private_key(self, password): return pw_decode(self.xprv, password) @@ -297,15 +291,14 @@ class BIP32_KeyStore(Deterministic_KeyStore, Xpub): def get_mnemonic(self, password): return self.get_seed(password) - def add_xprv(self, xprv, password): - xpub = bitcoin.xpub_from_xprv(xprv) - self.add_master_private_key(xprv, password) - self.add_master_public_key(xpub) + def add_xprv(self, xprv): + self.xprv = xprv + self.xpub = bitcoin.xpub_from_xprv(xprv) - def add_xprv_from_seed(self, bip32_seed, derivation, password): + def add_xprv_from_seed(self, bip32_seed, derivation): xprv, xpub = bip32_root(bip32_seed) xprv, xpub = bip32_private_derivation(xprv, "m/", derivation) - self.add_xprv(xprv, password) + self.add_xprv(xprv) def can_sign(self, xpub): return xpub == self.xpub and self.xprv is not None @@ -328,9 +321,9 @@ class Old_KeyStore(Deterministic_KeyStore): d['mpk'] = self.mpk.encode('hex') return d - def add_seed(self, seed, password): - Deterministic_KeyStore.add_seed(self, seed, password) - self.mpk = self.mpk_from_seed(self.get_seed(password)) + def add_seed(self, seed): + Deterministic_KeyStore.add_seed(self, seed) + self.mpk = self.mpk_from_seed(seed) def add_master_public_key(self, mpk): self.mpk = mpk.decode('hex') @@ -469,6 +462,9 @@ class Hardware_KeyStore(KeyStore, Xpub): self.derivation = d.get('derivation') self.handler = None + def may_have_password(self): + return False + def is_deterministic(self): return True @@ -623,22 +619,21 @@ is_bip32_key = lambda x: is_xprv(x) or is_xpub(x) def bip44_derivation(account_id): return "m/44'/0'/%d'"% int(account_id) -def from_seed(seed, passphrase, password): +def from_seed(seed, passphrase): if is_old_seed(seed): keystore = Old_KeyStore({}) - keystore.add_seed(seed, password) + keystore.add_seed(seed) elif is_new_seed(seed): keystore = BIP32_KeyStore({}) - keystore.add_seed(seed, password) + keystore.add_seed(seed) bip32_seed = Mnemonic.mnemonic_to_seed(seed, passphrase) - keystore.add_xprv_from_seed(bip32_seed, "m/", password) + keystore.add_xprv_from_seed(bip32_seed, "m/") return keystore -def from_private_key_list(text, password): +def from_private_key_list(text): keystore = Imported_KeyStore({}) for x in text.split(): keystore.import_key(x, None) - keystore.update_password(None, password) return keystore def from_old_mpk(mpk): @@ -647,36 +642,36 @@ def from_old_mpk(mpk): return keystore def from_xpub(xpub): - keystore = BIP32_KeyStore({}) - keystore.add_master_public_key(xpub) - return keystore + k = BIP32_KeyStore({}) + k.xpub = xpub + return k -def from_xprv(xprv, password): +def from_xprv(xprv): xpub = bitcoin.xpub_from_xprv(xprv) - keystore = BIP32_KeyStore({}) - keystore.add_master_private_key(xprv, password) - keystore.add_master_public_key(xpub) - return keystore + k = BIP32_KeyStore({}) + k.xprv = xprv + k.xpub = xpub + return k -def xprv_from_seed(seed, password): +def xprv_from_seed(seed): # do not store the seed, only the master xprv xprv, xpub = bip32_root(Mnemonic.mnemonic_to_seed(seed, '')) - return from_xprv(xprv, password) + return from_xprv(xprv) -def xpub_from_seed(seed, passphrase): +def xpub_from_seed(seed): # store only master xpub xprv, xpub = bip32_root(Mnemonic.mnemonic_to_seed(seed,'')) return from_xpub(xpub) -def from_keys(text, password): +def from_keys(text): if is_xprv(text): - k = from_xprv(text, password) + k = from_xprv(text) elif is_old_mpk(text): k = from_old_mpk(text) elif is_xpub(text): k = from_xpub(text) elif is_private_key_list(text): - k = from_private_key_list(text, password) + k = from_private_key_list(text) else: raise BaseException('Invalid key') return k diff --git a/plugins/trustedcoin/trustedcoin.py b/plugins/trustedcoin/trustedcoin.py @@ -352,14 +352,20 @@ class TrustedCoinPlugin(BasePlugin): seed = self.make_seed() wizard.show_seed_dialog(run_next=wizard.confirm_seed, seed_text=seed) - def create_keystore(self, wizard, seed, password): + def create_keystore(self, wizard, seed, passphrase): + assert passphrase == '' # this overloads the wizard's method words = seed.split() n = len(words)/2 - keystore1 = keystore.xprv_from_seed(' '.join(words[0:n]), password) - keystore2 = keystore.xpub_from_seed(' '.join(words[n:])) - wizard.storage.put('x1/', keystore1.dump()) - wizard.storage.put('x2/', keystore2.dump()) + k1 = keystore.xprv_from_seed(' '.join(words[0:n])) + k2 = keystore.xpub_from_seed(' '.join(words[n:])) + wizard.request_password(run_next=lambda pw: self.on_password(wizard, pw, k1, k2)) + + def on_password(self, wizard, password, k1, k2): + k1.update_password(None, password) + wizard.storage.put('use_encryption', bool(password)) + wizard.storage.put('x1/', k1.dump()) + wizard.storage.put('x2/', k2.dump()) wizard.storage.write() msg = [ _("Your wallet file is: %s.")%os.path.abspath(wizard.storage.path), @@ -389,14 +395,17 @@ class TrustedCoinPlugin(BasePlugin): storage = wizard.storage words = seed.split() n = len(words)/2 - keystore1 = keystore.xprv_from_seed(' '.join(words[0:n]), password) - keystore2 = keystore.xprv_from_seed(' '.join(words[n:]), password) - storage.put('x1/', keystore1.dump()) - storage.put('x2/', keystore2.dump()) + k1 = keystore.xprv_from_seed(' '.join(words[0:n])) + k2 = keystore.xprv_from_seed(' '.join(words[n:])) + k1.update_password(None, password) + k2.update_password(None, password) long_user_id, short_id = get_user_id(storage) xpub3 = make_xpub(signing_xpub, long_user_id) - keystore3 = keystore.from_xpub(xpub3) - storage.put('x3/', keystore3.dump()) + k3 = keystore.from_xpub(xpub3) + storage.put('use_encryption', bool(password)) + storage.put('x1/', k1.dump()) + storage.put('x2/', k2.dump()) + storage.put('x3/', k3.dump()) wizard.wallet = Wallet(storage) wizard.create_addresses() @@ -436,8 +445,8 @@ class TrustedCoinPlugin(BasePlugin): if not self.setup_google_auth(wizard, short_id, otp_secret): wizard.show_message("otp error") return - keystore3 = keystore.from_xpub(xpub3) - wizard.storage.put('x3/', keystore3.dump()) + k3 = keystore.from_xpub(xpub3) + wizard.storage.put('x3/', k3.dump()) wizard.storage.put('use_trustedcoin', True) wizard.storage.write() wizard.wallet = Wallet(wizard.storage)