URI: 
       tDuring wallet creation, do not write seed on disk before it is encrypted - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit f045490597243b056c35b56e0159588123dcbe5f
   DIR parent 60b6fd399d0af62c043e0a98f9e2ee141c052eb8
  HTML Author: thomasv <thomasv@gitorious>
       Date:   Fri, 13 Dec 2013 17:30:34 +0100
       
       During wallet creation, do not write seed on disk before it is encrypted
       
       Diffstat:
         M electrum                            |       7 ++-----
         M gui/android.py                      |       7 ++-----
         M gui/gtk.py                          |     102 +++++++++++++++++--------------
         M gui/qt/installwizard.py             |      15 +++++++--------
         M gui/qt/password_dialog.py           |      49 ++++++++++++++++++-------------
         M lib/account.py                      |       2 +-
         M lib/wallet.py                       |      30 ++++++++++++++++++------------
       
       7 files changed, 115 insertions(+), 97 deletions(-)
       ---
   DIR diff --git a/electrum b/electrum
       t@@ -235,7 +235,7 @@ if __name__ == '__main__':
                        sys.exit("Error: No seed")
        
                    wallet.init_seed(str(seed))
       -            wallet.save_seed()
       +            wallet.save_seed(password)
                    if not options.offline:
                        network = Network(config)
                        network.start()
       t@@ -254,16 +254,13 @@ if __name__ == '__main__':
        
                else:
                    wallet.init_seed(None)
       -            wallet.save_seed()
       +            wallet.save_seed(password)
                    wallet.synchronize()
                    print_msg("Your wallet generation seed is:\n\"%s\"" % wallet.get_mnemonic(None))
                    print_msg("Please keep it in a safe place; if you lose it, you will not be able to restore your wallet.")
        
                print_msg("Wallet saved in '%s'" % wallet.storage.path)
        
       -        if password:
       -            wallet.update_password(None, password)
       -
                # terminate
                sys.exit(0)
        
   DIR diff --git a/gui/android.py b/gui/android.py
       t@@ -903,7 +903,7 @@ class ElectrumGui:
                    if action == 'create':
                        wallet.init_seed(None)
                        self.show_seed()
       -                wallet.save_seed()
       +                wallet.save_seed(None)
                        wallet.synchronize()  # generate first addresses offline
                        
                    elif action == 'restore':
       t@@ -911,7 +911,7 @@ class ElectrumGui:
                        if not seed:
                            exit()
                        wallet.init_seed(str(seed))
       -                wallet.save_seed()
       +                wallet.save_seed(None)
                    else:
                        exit()
        
       t@@ -996,9 +996,6 @@ class ElectrumGui:
            def network_dialog(self):
                return True
        
       -    def verify_seed(self):
       -        wallet.save_seed()
       -        return True
                
            def show_seed(self):
                modal_dialog('Your seed is:', wallet.seed)
   DIR diff --git a/gui/gtk.py b/gui/gtk.py
       t@@ -69,7 +69,7 @@ def show_seed_dialog(wallet, password, parent):
                show_message("No seed")
                return
            try:
       -        seed = wallet.get_seed(password)
       +        mnemonic = wallet.get_mnemonic(password)
            except Exception:
                show_message("Incorrect password")
                return
       t@@ -77,9 +77,8 @@ def show_seed_dialog(wallet, password, parent):
                parent = parent,
                flags = gtk.DIALOG_MODAL, 
                buttons = gtk.BUTTONS_OK, 
       -        message_format = "Your wallet generation seed is:\n\n" + seed \
       -            + "\n\nPlease keep it in a safe place; if you lose it, you will not be able to restore your wallet.\n\n" \
       -            + "Equivalently, your wallet seed can be stored and recovered with the following mnemonic code:\n\n\"" + ' '.join(mnemonic.mn_encode(seed)) + "\"" )
       +        message_format = "Your wallet generation seed is:\n\n" + '"' + mnemonic + '"'\
       +            + "\n\nPlease keep it in a safe place; if you lose it, you will not be able to restore your wallet.\n\n" )
            dialog.set_title("Seed")
            dialog.show()
            dialog.run()
       t@@ -404,13 +403,11 @@ def password_dialog(parent):
            dialog.destroy()
            if result != gtk.RESPONSE_CANCEL: return pw
        
       -def change_password_dialog(wallet, parent, icon):
       -    if not wallet.seed:
       -        show_message("No seed")
       -        return
       +
       +def change_password_dialog(is_encrypted, parent):
        
            if parent:
       -        msg = 'Your wallet is encrypted. Use this dialog to change the password. To disable wallet encryption, enter an empty new password.' if wallet.use_encryption else 'Your wallet keys are not encrypted'
       +        msg = 'Your wallet is encrypted. Use this dialog to change the password. To disable wallet encryption, enter an empty new password.' if is_encrypted else 'Your wallet keys are not encrypted'
            else:
                msg = "Please choose a password to encrypt your wallet keys"
        
       t@@ -421,7 +418,7 @@ def change_password_dialog(wallet, parent, icon):
            image.show()
            dialog.set_image(image)
        
       -    if wallet.use_encryption:
       +    if is_encrypted:
                current_pw, current_pw_entry = password_line('Current password:')
                dialog.vbox.pack_start(current_pw, False, True, 0)
        
       t@@ -432,30 +429,22 @@ def change_password_dialog(wallet, parent, icon):
        
            dialog.show()
            result = dialog.run()
       -    password = current_pw_entry.get_text() if wallet.use_encryption else None
       +    password = current_pw_entry.get_text() if is_encrypted else None
            new_password = password_entry.get_text()
            new_password2 = password2_entry.get_text()
            dialog.destroy()
            if result == gtk.RESPONSE_CANCEL: 
                return
        
       -    try:
       -        wallet.get_seed(password)
       -    except Exception:
       -        show_message("Incorrect password")
       -        return
       -
            if new_password != new_password2:
                show_message("passwords do not match")
       -        return
       +        return change_password_dialog(is_encrypted, parent)
        
       -    wallet.update_password(password, new_password)
       +    if not new_password:
       +        new_password = None
       +
       +    return True, password, new_password
        
       -    if icon:
       -        if wallet.use_encryption:
       -            icon.set_tooltip_text('wallet is encrypted')
       -        else:
       -            icon.set_tooltip_text('wallet is unencrypted')
        
        
        def add_help_button(hbox, message):
       t@@ -548,16 +537,16 @@ class ElectrumWindow:
                prefs_button.show()
                self.status_bar.pack_end(prefs_button,False,False)
        
       -        pw_icon = gtk.Image()
       -        pw_icon.set_from_stock(gtk.STOCK_DIALOG_AUTHENTICATION, gtk.ICON_SIZE_MENU)
       -        pw_icon.set_alignment(0.5, 0.5)
       -        pw_icon.set_size_request(16,16 )
       -        pw_icon.show()
       +        self.pw_icon = gtk.Image()
       +        self.pw_icon.set_from_stock(gtk.STOCK_DIALOG_AUTHENTICATION, gtk.ICON_SIZE_MENU)
       +        self.pw_icon.set_alignment(0.5, 0.5)
       +        self.pw_icon.set_size_request(16,16 )
       +        self.pw_icon.show()
        
                if self.wallet.seed:
                    password_button = gtk.Button()
       -            password_button.connect("clicked", lambda x: change_password_dialog(self.wallet, self.window, pw_icon))
       -            password_button.add(pw_icon)
       +            password_button.connect("clicked", self.do_update_password)
       +            password_button.add(self.pw_icon)
                    password_button.set_relief(gtk.RELIEF_NONE)
                    password_button.show()
                    self.status_bar.pack_end(password_button,False,False)
       t@@ -606,6 +595,28 @@ class ElectrumWindow:
            def update_callback(self):
                self.wallet_updated = True
        
       +    def do_update_password(self):
       +        if not wallet.seed:
       +            show_message("No seed")
       +            return
       +
       +        res = change_password_dialog(self.wallet.use_encryption, self.window)
       +        if res:
       +            _, password, new_password = res
       +
       +            try:
       +                wallet.get_seed(password)
       +            except Exception:
       +                show_message("Incorrect password")
       +                return
       +
       +            wallet.update_password(password, new_password)
       +
       +            if wallet.use_encryption:
       +                self.pw_icon.set_tooltip_text('wallet is encrypted')
       +            else:
       +                self.pw_icon.set_tooltip_text('wallet is unencrypted')
       +
        
            def add_tab(self, page, name):
                tab_label = gtk.Label(name)
       t@@ -1293,23 +1304,33 @@ class ElectrumGui():
                        wallet.gap_limit = gap
                        wallet.storage.put('gap_limit', gap, True)
        
       -            self.wallet.start_threads(self.network)
        
                    if action == 'create':
                        wallet.init_seed(None)
       -                wallet.save_seed()
       +                show_seed_dialog(wallet, None, None)
       +                r = change_password_dialog(False, None)
       +                password = r[2] if r else None
       +                print "password", password
       +                wallet.save_seed(password)
                        wallet.synchronize()  # generate first addresses offline
       +
                    elif action == 'restore':
                        seed = self.seed_dialog()
                        wallet.init_seed(seed)
       -                wallet.save_seed()
       -                self.restore_wallet(wallet)
       +                r = change_password_dialog(False, None)
       +                password = r[2] if r else None
       +                wallet.save_seed(password)
                        
                    else:
                        exit()
                else:
                    self.wallet = Wallet(storage)
       -            self.wallet.start_threads(self.network)
       +            action = None
       +
       +        self.wallet.start_threads(self.network)
       +
       +        if action == 'restore':
       +            self.restore_wallet(wallet)
        
                w = ElectrumWindow(self.wallet, self.config, self.network)
                if url: w.set_url(url)
       t@@ -1321,18 +1342,9 @@ class ElectrumGui():
            def seed_dialog(self):
                return run_recovery_dialog()
        
       -    def verify_seed(self):
       -        self.wallet.save_seed()
       -        return True
       -
            def network_dialog(self):
                return run_network_dialog( self.network, parent=None )
        
       -    def show_seed(self):
       -        show_seed_dialog(self.wallet, None, None)
       -
       -    def password_dialog(self):
       -        change_password_dialog(self.wallet, None, None)
        
            def restore_wallet(self, wallet):
        
   DIR diff --git a/gui/qt/installwizard.py b/gui/qt/installwizard.py
       t@@ -247,8 +247,7 @@ class InstallWizard(QDialog):
                      +_("Leave these fields empty if you want to disable encryption.")
                from password_dialog import make_password_dialog, run_password_dialog
                self.set_layout( make_password_dialog(self, wallet, msg) )
       -
       -        run_password_dialog(self, wallet, self)
       +        return run_password_dialog(self, wallet, self)
        
        
            def run(self):
       t@@ -269,13 +268,14 @@ class InstallWizard(QDialog):
                        return
                    if not self.verify_seed(wallet):
                        return
       +            ok, _, password = self.password_dialog(wallet)
                    def create():
       -                wallet.save_seed()
       +                wallet.save_seed(password)
                        wallet.synchronize()  # generate first addresses offline
                    self.waiting_dialog(create)
        
       +
                elif action == 'restore':
       -            # ask for seed and gap.
                    seed = self.seed_dialog()
                    if not seed:
                        return
       t@@ -287,10 +287,11 @@ class InstallWizard(QDialog):
                        QMessageBox.warning(None, _('Error'), _('Incorrect seed'), _('OK'))
                        return
        
       -            wallet.save_seed()
       +            ok, _, password = self.password_dialog(wallet)
       +            wallet.save_seed(password)
       +
        
                elif action == 'watching':
       -            # ask for seed and gap.
                    mpk = self.mpk_dialog()
                    if not mpk:
                        return
       t@@ -318,6 +319,4 @@ class InstallWizard(QDialog):
                    else:
                        QMessageBox.information(None, _('Information'), _("This wallet was restored offline. It may contain more addresses than displayed."), _('OK'))
        
       -        self.password_dialog(wallet)
       -
                return wallet
   DIR diff --git a/gui/qt/password_dialog.py b/gui/qt/password_dialog.py
       t@@ -75,37 +75,24 @@ def run_password_dialog(self, wallet, parent):
                
            if not wallet.seed:
                QMessageBox.information(parent, _('Error'), _('No seed'), _('OK'))
       -        return
       +        return False, None, None
        
       -    if not self.exec_(): return
       +    if not self.exec_():
       +        return False, None, None
        
            password = unicode(self.pw.text()) if wallet.use_encryption else None
            new_password = unicode(self.new_pw.text())
            new_password2 = unicode(self.conf_pw.text())
        
       -    try:
       -        wallet.get_seed(password)
       -    except Exception:
       -        QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK'))
       -        return
       -
            if new_password != new_password2:
                QMessageBox.warning(parent, _('Error'), _('Passwords do not match'), _('OK'))
                # Retry
       -        run_password_dialog(self, wallet, parent) 
       -        return
       -
       -    try:
       -        wallet.update_password(password, new_password)
       -    except Exception:
       -        QMessageBox.warning(parent, _('Error'), _('Failed to update password'), _('OK'))
       -        return
       +        return run_password_dialog(self, wallet, parent)
        
       -    if new_password:
       -        QMessageBox.information(parent, _('Success'), _('Password was updated successfully'), _('OK'))
       -    else:
       -        QMessageBox.information(parent, _('Success'), _('This wallet is not encrypted'), _('OK'))
       +    if not new_password:
       +        new_password = None
        
       +    return True, password, new_password
        
        
        
       t@@ -123,7 +110,27 @@ class PasswordDialog(QDialog):
        
        
            def run(self):
       -        run_password_dialog(self, self.wallet, self.parent)
       +        ok, password, new_password = run_password_dialog(self, self.wallet, self.parent)
       +        if not ok:
       +            return
       +
       +        try:
       +            self.wallet.get_seed(password)
       +        except Exception:
       +            QMessageBox.warning(self.parent, _('Error'), _('Incorrect Password'), _('OK'))
       +            return False, None, None
       +
       +        try:
       +            self.wallet.update_password(password, new_password)
       +        except:
       +            QMessageBox.warning(self.parent, _('Error'), _('Failed to update password'), _('OK'))
       +            return
       +
       +        if new_password:
       +            QMessageBox.information(self.parent, _('Success'), _('Password was updated successfully'), _('OK'))
       +        else:
       +            QMessageBox.information(self.parent, _('Success'), _('This wallet is not encrypted'), _('OK'))
       +
        
        
        
   DIR diff --git a/lib/account.py b/lib/account.py
       t@@ -108,7 +108,7 @@ class OldAccount(Account):
                master_private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
                master_public_key = master_private_key.get_verifying_key().to_string()
                if master_public_key != self.mpk:
       -            print_error('invalid password (mpk)')
       +            print_error('invalid password (mpk)', self.mpk.encode('hex'), master_public_key.encode('hex'))
                    raise Exception('Invalid password')
                return True
        
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -340,10 +340,14 @@ class Wallet:
                #    self.seed = seed
                    
        
       -    def save_seed(self):
       +    def save_seed(self, password):
       +        if password: 
       +            self.seed = pw_encode( self.seed, password)
       +            self.use_encryption = True
                self.storage.put('seed', self.seed, True)
                self.storage.put('seed_version', self.seed_version, True)
       -        self.create_accounts()
       +        self.storage.put('use_encryption', self.use_encryption,True)
       +        self.create_accounts(password)
        
        
            def create_watching_only_wallet(self, params):
       t@@ -366,29 +370,31 @@ class Wallet:
                self.create_account('1','Main account')
        
        
       -    def create_accounts(self): 
       +    def create_accounts(self, password):
       +        seed = pw_decode(self.seed, password)
       +
                if self.seed_version == 4:
       -            mpk = OldAccount.mpk_from_seed(self.seed)
       +            mpk = OldAccount.mpk_from_seed(seed)
                    self.create_old_account(mpk)
                else:
                    # create default account
       -            self.create_master_keys('1')
       +            self.create_master_keys('1', password)
                    self.create_account('1','Main account')
        
        
       -    def create_master_keys(self, account_type):
       +    def create_master_keys(self, account_type, password):
                master_k, master_c, master_K, master_cK = bip32_init(self.get_seed(None))
                if account_type == '1':
                    k0, c0, K0, cK0 = bip32_private_derivation(master_k, master_c, "m/", "m/0'/")
                    self.master_public_keys["m/0'/"] = (c0, K0, cK0)
       -            self.master_private_keys["m/0'/"] = k0
       +            self.master_private_keys["m/0'/"] = pw_encode(k0, password)
                elif account_type == '2of2':
                    k1, c1, K1, cK1 = bip32_private_derivation(master_k, master_c, "m/", "m/1'/")
                    k2, c2, K2, cK2 = bip32_private_derivation(master_k, master_c, "m/", "m/2'/")
                    self.master_public_keys["m/1'/"] = (c1, K1, cK1)
                    self.master_public_keys["m/2'/"] = (c2, K2, cK2)
       -            self.master_private_keys["m/1'/"] = k1
       -            self.master_private_keys["m/2'/"] = k2
       +            self.master_private_keys["m/1'/"] = pw_encode(k1, password)
       +            self.master_private_keys["m/2'/"] = pw_encode(k2, password)
                elif account_type == '2of3':
                    k3, c3, K3, cK3 = bip32_private_derivation(master_k, master_c, "m/", "m/3'/")
                    k4, c4, K4, cK4 = bip32_private_derivation(master_k, master_c, "m/", "m/4'/")
       t@@ -396,9 +402,9 @@ class Wallet:
                    self.master_public_keys["m/3'/"] = (c3, K3, cK3)
                    self.master_public_keys["m/4'/"] = (c4, K4, cK4)
                    self.master_public_keys["m/5'/"] = (c5, K5, cK5)
       -            self.master_private_keys["m/3'/"] = k3
       -            self.master_private_keys["m/4'/"] = k4
       -            self.master_private_keys["m/5'/"] = k5
       +            self.master_private_keys["m/3'/"] = pw_encode(k3, password)
       +            self.master_private_keys["m/4'/"] = pw_encode(k4, password)
       +            self.master_private_keys["m/5'/"] = pw_encode(k5, password)
        
                self.storage.put('master_public_keys', self.master_public_keys, True)
                self.storage.put('master_private_keys', self.master_private_keys, True)