wallet: minor clean-up and sanity checks for unlock/lock
What changed, and why it matters
This commit tightens up how Electrum wallets handle being 'unlocked' with a password. It makes the unlock() function itself treat an empty string the same as no password, ensures the function that returns the in-memory password validates it first, and adds a helper to check whether the wallet is currently unlocked. These are defensive sanity checks rather than fixes for a known active attack.
Review callers of get_unlocked_password() and unlock() to confirm they no longer perform their own empty-string normalization, and verify that the new InvalidPassword-to-Exception conversion does not mask legitimate authentication failures. No urgent deployment action is indicated.
Security signals we found
defensive input normalization (empty password handling)
state-consistency validation before returning sensitive value
addition of explicit unlock-state predicate
Evidence from the diff
The patch modifies Abstract_Wallet in electrum/wallet.py. unlock() now coerces password = password or None so empty-string passwords are normalized before check_password() is called. get_unlocked_password() now returns None if the wallet is not unlocked and re-validates the cached password, raising an exception if the cached password has become inconsistent. A new is_unlocked() method returns True when either a password is cached in memory or the wallet has no password. The changes reduce the chance that downstream callers act on stale or invalid password state.
Changed components
electrum/wallet.pyAbstract_Wallet.unlock()Abstract_Wallet.get_unlocked_password()Abstract_Wallet.is_unlocked()Inspect captured patch +14 / −3
diff --git a/electrum/wallet.py b/electrum/wallet.py
index cf7874f..c852603 100644
--- a/electrum/wallet.py
+++ b/electrum/wallet.py
@@ -3532,16 +3532,27 @@ class Abstract_Wallet(ABC, Logger, EventListener):
"""Returns the number of new addresses we generated."""
return 0
- def unlock(self, password):
+ def unlock(self, password: Optional[str]) -> None:
self.logger.info(f'unlocking wallet')
+ password = password or None
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
+ def get_unlocked_password(self) -> Optional[str]:
+ pw = self._password_in_memory
+ if not self.is_unlocked():
+ return None
+ try:
+ self.check_password(pw)
+ except InvalidPassword as e:
+ raise Exception("inconsistent _password_in_memory") from e
+ return pw
+
+ def is_unlocked(self) -> bool:
+ return self._password_in_memory is not None or not self.has_password()
def get_text_not_enough_funds_mentioning_frozen(
self,
Why this scored 29/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.