During wallet creation, do not write seed on disk before it is encrypted
authorthomasv <thomasv@gitorious>
Fri, 13 Dec 2013 16:30:34 +0000 (17:30 +0100)
committerthomasv <thomasv@gitorious>
Fri, 13 Dec 2013 16:30:34 +0000 (17:30 +0100)
electrum
gui/android.py
gui/gtk.py
gui/qt/installwizard.py
gui/qt/password_dialog.py
lib/account.py
lib/wallet.py

index 5b3710c..2ee0425 100755 (executable)
--- a/electrum
+++ b/electrum
@@ -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()
@@ -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)
 
index 4a50935..0994cbb 100644 (file)
@@ -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':
@@ -911,7 +911,7 @@ class ElectrumGui:
                 if not seed:
                     exit()
                 wallet.init_seed(str(seed))
-                wallet.save_seed()
+                wallet.save_seed(None)
             else:
                 exit()
 
@@ -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)
index 3273850..322e9e1 100644 (file)
@@ -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
@@ -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()
@@ -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"
 
@@ -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)
 
@@ -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):
@@ -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)
@@ -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)
@@ -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)
@@ -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):
 
index 7886b99..c36cc52 100644 (file)
@@ -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):
@@ -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
@@ -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
@@ -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
index 6f30b17..95479be 100644 (file)
@@ -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
 
 
 
@@ -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'))
+
 
 
 
index 9a262bf..51f42b4 100644 (file)
@@ -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
 
index c6b525a..297f1ed 100644 (file)
@@ -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):
@@ -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'/")
@@ -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)