plugin: trezor: handle passphrase_always_on_device
What changed, and why it matters
This commit fixes a bug in how Electrum talks to Trezor hardware wallets when a device setting forces passphrase entry on the Trezor itself instead of in Electrum. Previously, older Trezor devices could ask for the passphrase twice, and newer Safe 7 devices would fail to set up. The fix checks the device's setting and tells the Trezor library to handle the passphrase on the device when appropriate. It is a usability/compatibility fix, not a vulnerability patch.
Treat as a normal bug-fix / compatibility commit. No urgent security action required. Reviewers may want to verify that PassphraseSetting.ON_DEVICE is only used when the device explicitly reports passphrase_always_on_device, to avoid changing behavior for users who type passphrases in Electrum.
Security signals we found
Hardware wallet integration change
Passphrase handling change
No cryptographic or authorization bypass visible in diff
No mention of CVE, security advisory, or researcher attribution
Evidence from the diff
The change in electrum/plugins/trezor/clientbase.py restructures session opening for Trezor. It first unlocks the device so features are populated, then checks features.passphrase_protection and features.passphrase_always_on_device. If passphrase_always_on_device is enabled, it passes PassphraseSetting.ON_DEVICE to client.get_session(); otherwise it falls back to the previous logic. This removes a redundant unlock-and-derive step and avoids a double passphrase prompt on V1 devices and a setup error on THP devices (Safe 7).
Changed components
electrum/plugins/trezor/clientbase.pyTrezor hardware wallet pluginSession establishment / passphrase flowInspect captured patch +12 / −16
### electrum/plugins/trezor/clientbase.py
@@ -110,25 +110,21 @@ def session(self):
if self._session is None:
assert self.handler is not None, "No UI handler for session"
self.pair_if_needed()
-
- # If needed, unlock the device (triggering PIN entry dialog for legacy model).
- with self.client.get_session(passphrase=PassphraseSetting.STANDARD_WALLET) as session:
- session.ensure_unlocked()
-
- passphrase = PassphraseSetting.STANDARD_WALLET # (empty passphrase)
- if self.client.features.passphrase_protection:
- passphrase = self.get_passphrase(Capability.PassphraseEntry in self.client.features.capabilities)
-
- # Then, derive a session for this wallet (possibly with a passphrase)
- if passphrase == PassphraseSetting.STANDARD_WALLET:
- self._session = session # reuse the session above to avoid re-derivation
- self.logger.info("Opened standard %s", self._session)
+ self.client.ensure_unlocked() # unlock device so features are populated and we know about passphrase
+
+ features = self.client.features
+ if not features.passphrase_protection:
+ passphrase = PassphraseSetting.STANDARD_WALLET # (empty passphrase)
+ elif features.passphrase_always_on_device:
+ # the device asks for the passphrase itself, prompting in electrum as well would make the user enter it twice
+ passphrase = PassphraseSetting.ON_DEVICE
else:
- self._session = self.client.get_session(passphrase)
- self.logger.info("Re-opened passphrase %s", self._session)
+ passphrase = self.get_passphrase(Capability.PassphraseEntry in features.capabilities)
- return self._session
+ self._session = self.client.get_session(passphrase=passphrase)
+ self.logger.info(f"Opened {self._session} ({features.passphrase_protection=}, on_device={passphrase is PassphraseSetting.ON_DEVICE})")
+ return self._session
def run_flow(self, message=None, creating_wallet=False):
if self.in_flow:Why this scored 22/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.