1
0

Qt: do not require password in memory

- do not require full encryption
 - do not store password on startup
 - add lock/unlock functions to qt GUI
This commit is contained in:
ThomasV
2025-04-18 12:37:19 +02:00
parent 1c3268c2ff
commit 07ba0e6329
4 changed files with 49 additions and 29 deletions

View File

@@ -470,8 +470,6 @@ class Daemon(Logger):
if wallet := self._wallets.get(wallet_key):
return wallet
wallet = self._load_wallet(path, password, upgrade=upgrade, config=self.config)
if wallet.requires_unlock() and password is not None:
wallet.unlock(password)
wallet.start_network(self.network)
self.add_wallet(wallet)
self.update_recently_opened_wallets(path)

View File

@@ -308,7 +308,6 @@ class ElectrumGui(BaseElectrumGui, Logger):
self.build_tray_menu()
w.warn_if_testnet()
w.warn_if_watching_only()
w.require_full_encryption()
return w
def count_wizards_in_progress(func):

View File

@@ -141,7 +141,7 @@ def protected(func):
password = None
msg = kwargs.get('message')
while self.wallet.has_keystore_encryption():
password = self.password_dialog(parent=parent, msg=msg)
password = self.wallet.get_unlocked_password() or self.password_dialog(parent=parent, msg=msg)
if password is None:
# User cancelled password input
return
@@ -330,6 +330,45 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
self._coroutines_scheduled[fut] = name
self.need_update.set()
def toggle_lock(self):
if self.wallet.get_unlocked_password():
self.lock_wallet()
else:
msg = ' '.join([
_('Your wallet is locked.'),
_('If you unlock it, its password will not be required to sign transactions.'),
_('Enter your password to unlock your wallet:')
])
self.unlock_wallet(message=msg)
def update_lock_menu(self):
self.lock_menu.setEnabled(self.wallet.has_password())
text = _('Lock') if self.wallet.get_unlocked_password() else _('Unlock')
self.lock_menu.setText(text)
@protected
def unlock_wallet(self, password, message=None):
self.wallet.unlock(password)
self.update_lock_icon()
self.update_lock_menu()
icon = read_QIcon("unlock.png")
msg = ' '.join([
_('Your wallet is unlocked.'),
_('Its password will not be required to sign transactions.'),
])
self.show_message(msg, icon=icon.pixmap(30))
def lock_wallet(self):
self.wallet.lock_wallet()
self.update_lock_icon()
self.update_lock_menu()
icon = read_QIcon("lock.png")
msg = ' '.join([
_('Your wallet is locked.'),
_('Its password will be required to sign transactions.'),
])
self.show_message(msg, icon=icon.pixmap(30))
def on_fx_history(self):
self.history_model.refresh('fx_history')
self.address_list.refresh_all()
@@ -580,24 +619,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
])
self.show_warning(msg, title=_('Watch-only wallet'))
def require_full_encryption(self):
if self.wallet.has_keystore_encryption() and not self.wallet.has_storage_encryption():
msg = ' '.join([
_("Your wallet is password-protected, but the wallet file is not encrypted."),
_("This is no longer supported."),
_("Please enter your password in order to encrypt your wallet file."),
])
while True:
password = self.password_dialog(msg)
if not password:
self.close()
raise UserCancelled()
try:
self.wallet.update_password(password, password, encrypt_storage=True)
break
except InvalidPassword as e:
self.show_error(str(e))
def warn_if_testnet(self):
if not constants.net.TESTNET:
return
@@ -724,6 +745,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
self.wallet_menu.addSeparator()
self.password_menu = self.wallet_menu.addAction(_("&Password"), self.change_password_dialog)
self.lock_menu = self.wallet_menu.addAction(_("&Unlock"), self.toggle_lock)
self.update_lock_menu()
self.seed_menu = self.wallet_menu.addAction(_("&Seed"), self.show_seed_dialog)
self.private_keys_menu = self.wallet_menu.addMenu(_("&Private keys"))
self.private_keys_menu.addAction(_("&Sweep"), self.sweep_key_dialog)
@@ -1864,7 +1887,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
"Payments are more likely to succeed with a more complete graph."))
def update_lock_icon(self):
icon = read_QIcon("lock.png") if self.wallet.has_password() else read_QIcon("unlock.png")
icon = read_QIcon("lock.png") if self.wallet.has_password() and (self.wallet.get_unlocked_password() is None) else read_QIcon("unlock.png")
self.password_button.setIcon(icon)
def update_buttons_on_seed(self):
@@ -1897,6 +1920,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
return
self._update_wallet_password(
old_password=old_password, new_password=new_password, encrypt_storage=encrypt_file)
self.update_lock_menu()
def _update_wallet_password(self, *, old_password, new_password, encrypt_storage: bool):
try:

View File

@@ -502,9 +502,6 @@ class Abstract_Wallet(ABC, Logger, EventListener):
def has_channels(self):
return self.lnworker is not None and len(self.lnworker._channels) > 0
def requires_unlock(self):
return self.config.ENABLE_ANCHOR_CHANNELS and self.has_channels()
def can_have_lightning(self) -> bool:
""" whether this wallet can create new channels """
# we want static_remotekey to be a wallet address
@@ -3098,9 +3095,8 @@ class Abstract_Wallet(ABC, Logger, EventListener):
# save changes. force full rewrite to rm remnants of old password
if self.storage and self.storage.file_exists():
self.db.write_and_force_consolidation()
# if wallet was previously unlocked, update password in memory
if self.requires_unlock():
self.unlock(new_pw)
# if wallet was previously unlocked, reset password_in_memory
self.lock_wallet()
@abstractmethod
def _update_password_for_keystore(self, old_pw: Optional[str], new_pw: Optional[str]) -> None:
@@ -3418,6 +3414,9 @@ class Abstract_Wallet(ABC, Logger, EventListener):
self.check_password(password)
self._password_in_memory = password
def lock_wallet(self):
self._password_in_memory = None
def get_unlocked_password(self):
return self._password_in_memory