Skip to content

Commit

Permalink
Merge c811c5c into 743ef9e
Browse files Browse the repository at this point in the history
  • Loading branch information
SomberNight committed Jan 28, 2018
2 parents 743ef9e + c811c5c commit 7d7c870
Show file tree
Hide file tree
Showing 20 changed files with 508 additions and 147 deletions.
8 changes: 7 additions & 1 deletion electrum
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ def init_daemon(config_options):
print_msg("Type 'electrum create' to create a new wallet, or provide a path to a wallet with the -w option")
sys.exit(0)
if storage.is_encrypted():
if storage.is_encrypted_with_hw_device():
raise NotImplementedError("CLI functionality of encrypted hw wallets")
if config.get('password'):
password = config.get('password')
else:
Expand Down Expand Up @@ -236,6 +238,8 @@ def init_cmdline(config_options, server):
# commands needing password
if (cmd.requires_wallet and storage.is_encrypted() and server is None)\
or (cmd.requires_password and (storage.get('use_encryption') or storage.is_encrypted())):
if storage.is_encrypted_with_hw_device():
raise NotImplementedError("CLI functionality of encrypted hw wallets")
if config.get('password'):
password = config.get('password')
else:
Expand All @@ -262,12 +266,14 @@ def run_offline_command(config, config_options):
if cmd.requires_wallet:
storage = WalletStorage(config.get_wallet_path())
if storage.is_encrypted():
if storage.is_encrypted_with_hw_device():
raise NotImplementedError("CLI functionality of encrypted hw wallets")
storage.decrypt(password)
wallet = Wallet(storage)
else:
wallet = None
# check password
if cmd.requires_password and storage.get('use_encryption'):
if cmd.requires_password and wallet.has_password():
try:
seed = wallet.check_password(password)
except InvalidPassword:
Expand Down
2 changes: 1 addition & 1 deletion gui/kivy/uix/dialogs/installwizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -807,7 +807,7 @@ def password_dialog(self, message, callback):
popup.init(message, callback)
popup.open()

def request_password(self, run_next):
def request_password(self, run_next, force_disable_encrypt_cb=False):
def callback(pin):
if pin:
self.run('confirm_password', pin, run_next)
Expand Down
85 changes: 63 additions & 22 deletions gui/qt/installwizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@

from electrum import Wallet, WalletStorage
from electrum.util import UserCancelled, InvalidPassword
from electrum.base_wizard import BaseWizard
from electrum.base_wizard import BaseWizard, HWD_SETUP_DECRYPT_WALLET
from electrum.i18n import _

from .seed_dialog import SeedLayout, KeysLayout
from .network_dialog import NetworkChoiceLayout
from .util import *
from .password_dialog import PasswordLayout, PW_NEW
from .password_dialog import PasswordLayout, PasswordLayoutForHW, PW_NEW


class GoBack(Exception):
Expand All @@ -29,6 +29,10 @@ class GoBack(Exception):
MSG_COSIGNER = _("Please enter the master public key of cosigner #%d:")
MSG_ENTER_PASSWORD = _("Choose a password to encrypt your wallet keys.") + '\n'\
+ _("Leave this field empty if you want to disable encryption.")
MSG_HW_STORAGE_ENCRYPTION = _("Set wallet file encryption.") + '\n'\
+ _("Your wallet file does not contain secrets, mostly just metadata. ") \
+ _("It also contains your master public key that allows watching your addresses.") + '\n\n'\
+ _("Note: If you enable this setting, you will need your hardware device to open your wallet.")
MSG_RESTORE_PASSPHRASE = \
_("Please enter your seed derivation passphrase. "
"Note: this is NOT your encryption password. "
Expand Down Expand Up @@ -196,12 +200,18 @@ def on_filename(filename):
msg =_("This file does not exist.") + '\n' \
+ _("Press 'Next' to create this wallet, or choose another file.")
pw = False
elif self.storage.file_exists() and self.storage.is_encrypted():
msg = _("This file is encrypted.") + '\n' + _('Enter your password or choose another file.')
pw = True
else:
msg = _("Press 'Next' to open this wallet.")
pw = False
if self.storage.is_encrypted_with_user_pw():
msg = _("This file is encrypted with a password.") + '\n' \
+ _('Enter your password or choose another file.')
pw = True
elif self.storage.is_encrypted_with_hw_device():
msg = _("This file is encrypted using a hardware device.") + '\n' \
+ _("Press 'Next' to choose device to decrypt.")
pw = False
else:
msg = _("Press 'Next' to open this wallet.")
pw = False
else:
msg = _('Cannot read file')
pw = False
Expand All @@ -227,17 +237,40 @@ def on_filename(filename):
if not self.storage.file_exists():
break
if self.storage.file_exists() and self.storage.is_encrypted():
password = self.pw_e.text()
try:
self.storage.decrypt(password)
break
except InvalidPassword as e:
QMessageBox.information(None, _('Error'), str(e))
continue
except BaseException as e:
traceback.print_exc(file=sys.stdout)
QMessageBox.information(None, _('Error'), str(e))
return
if self.storage.is_encrypted_with_user_pw():
password = self.pw_e.text()
try:
self.storage.decrypt(password)
break
except InvalidPassword as e:
QMessageBox.information(None, _('Error'), str(e))
continue
except BaseException as e:
traceback.print_exc(file=sys.stdout)
QMessageBox.information(None, _('Error'), str(e))
return
elif self.storage.is_encrypted_with_hw_device():
try:
self.run('choose_hw_device', HWD_SETUP_DECRYPT_WALLET)
except InvalidPassword as e:
# FIXME if we get here because of mistyped passphrase
# then that passphrase gets "cached"
QMessageBox.information(
None, _('Error'),
_('Failed to decrypt using this hardware device.') + '\n' +
_('If you use a passphrase, make sure it is correct.'))
self.stack = []
return self.run_and_get_wallet()
except BaseException as e:
traceback.print_exc(file=sys.stdout)
QMessageBox.information(None, _('Error'), str(e))
return
if self.storage.is_past_initial_decryption():
break
else:
return
else:
raise Exception('Unexpected encryption version')

path = self.storage.path
if self.storage.requires_split():
Expand Down Expand Up @@ -386,17 +419,25 @@ def show_seed_dialog(self, run_next, seed_text):
self.exec_layout(slayout)
return slayout.is_ext

def pw_layout(self, msg, kind):
playout = PasswordLayout(None, msg, kind, self.next_button)
def pw_layout(self, msg, kind, force_disable_encrypt_cb):
playout = PasswordLayout(None, msg, kind, self.next_button,
force_disable_encrypt_cb=force_disable_encrypt_cb)
playout.encrypt_cb.setChecked(True)
self.exec_layout(playout.layout())
return playout.new_password(), playout.encrypt_cb.isChecked()

@wizard_dialog
def request_password(self, run_next):
def request_password(self, run_next, force_disable_encrypt_cb=False):
"""Request the user enter a new password and confirm it. Return
the password or None for no password."""
return self.pw_layout(MSG_ENTER_PASSWORD, PW_NEW)
return self.pw_layout(MSG_ENTER_PASSWORD, PW_NEW, force_disable_encrypt_cb)

@wizard_dialog
def request_storage_encryption(self, run_next):
playout = PasswordLayoutForHW(None, MSG_HW_STORAGE_ENCRYPTION, PW_NEW, self.next_button)
playout.encrypt_cb.setChecked(True)
self.exec_layout(playout.layout())
return playout.encrypt_cb.isChecked()

def show_restore(self, wallet, network):
# FIXME: these messages are shown after the install wizard is
Expand Down
53 changes: 37 additions & 16 deletions gui/qt/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ def watching_only_changed(self):
extra.append(_('watching only'))
title += ' [%s]'% ', '.join(extra)
self.setWindowTitle(title)
self.password_menu.setEnabled(self.wallet.can_change_password())
self.password_menu.setEnabled(self.wallet.may_have_password())
self.import_privkey_menu.setVisible(self.wallet.can_import_privkey())
self.import_address_menu.setVisible(self.wallet.can_import_address())
self.export_menu.setEnabled(self.wallet.can_export())
Expand Down Expand Up @@ -877,14 +877,15 @@ def sign_payment_request(self, addr):
if alias_addr:
if self.wallet.is_mine(alias_addr):
msg = _('This payment request will be signed.') + '\n' + _('Please enter your password')
password = self.password_dialog(msg)
if password:
try:
self.wallet.sign_payment_request(addr, alias, alias_addr, password)
except Exception as e:
self.show_error(str(e))
password = None
if self.wallet.has_keystore_encryption():
password = self.password_dialog(msg)
if not password:
return
else:
try:
self.wallet.sign_payment_request(addr, alias, alias_addr, password)
except Exception as e:
self.show_error(str(e))
return
else:
return
Expand Down Expand Up @@ -1372,7 +1373,7 @@ def protected(func):
def request_password(self, *args, **kwargs):
parent = self.top_level_window()
password = None
while self.wallet.has_password():
while self.wallet.has_keystore_encryption():
password = self.password_dialog(parent=parent)
if password is None:
# User cancelled password input
Expand Down Expand Up @@ -1506,7 +1507,7 @@ def do_send(self, preview = False):
if fee > confirm_rate * tx.estimated_size() / 1000:
msg.append(_('Warning') + ': ' + _("The fee for this transaction seems unusually high."))

if self.wallet.has_password():
if self.wallet.has_keystore_encryption():
msg.append("")
msg.append(_("Enter your password to proceed"))
password = self.password_dialog('\n'.join(msg))
Expand Down Expand Up @@ -1909,25 +1910,45 @@ def update_lock_icon(self):

def update_buttons_on_seed(self):
self.seed_button.setVisible(self.wallet.has_seed())
self.password_button.setVisible(self.wallet.can_change_password())
self.password_button.setVisible(self.wallet.may_have_password())
self.send_button.setVisible(not self.wallet.is_watching_only())

def change_password_dialog(self):
from .password_dialog import ChangePasswordDialog
d = ChangePasswordDialog(self, self.wallet)
ok, password, new_password, encrypt_file = d.run()
from electrum.storage import STO_EV_XPUB_PW
if self.wallet.get_available_storage_encryption_version() == STO_EV_XPUB_PW:
from .password_dialog import ChangePasswordDialogForHW
d = ChangePasswordDialogForHW(self, self.wallet)
ok, encrypt_file = d.run()
if not ok:
return

try:
hw_dev_pw = self.wallet.keystore.get_password_for_storage_encryption()
except UserCancelled:
return
except BaseException as e:
traceback.print_exc(file=sys.stderr)
self.show_error(str(e))
return
old_password = hw_dev_pw if self.wallet.has_password() else None
new_password = hw_dev_pw if encrypt_file else None
else:
from .password_dialog import ChangePasswordDialogForSW
d = ChangePasswordDialogForSW(self, self.wallet)
ok, old_password, new_password, encrypt_file = d.run()

if not ok:
return
try:
self.wallet.update_password(password, new_password, encrypt_file)
self.wallet.update_password(old_password, new_password, encrypt_file)
except BaseException as e:
self.show_error(str(e))
return
except:
traceback.print_exc(file=sys.stdout)
self.show_error(_('Failed to update password'))
return
msg = _('Password was updated successfully') if new_password else _('Password is disabled, this wallet is not protected')
msg = _('Password was updated successfully') if self.wallet.has_password() else _('Password is disabled, this wallet is not protected')
self.show_message(msg, title=_("Success"))
self.update_lock_icon()

Expand Down
Loading

0 comments on commit 7d7c870

Please sign in to comment.