Pass expected_address through QR message signing path
What changed, and why it matters
This commit fixes a consistency issue in the QR-code message-signing flow of the Passport hardware wallet. Previously, when signing a message via QR code, the wallet did not pass the user-confirmed address down to the signing code as an explicit 'expected address' check. The microSD signing path already had this safety check. After the change, the raw, verified address is passed through and used to confirm the signature is for the correct address, while a stylized version is still shown on screen. This reduces the risk that a tricked or confused user signs a message with the wrong address.
Treat this as a security-hardening fix and include it in the next firmware release. Review whether any other message-signing flows (e.g., other QR protocols, USB, or Bluetooth paths) similarly omit expected_address. Consider adding regression tests that verify sign_text_file_task receives the raw expected_address for every user-facing signing path.
Security signals we found
Adds expected_address validation to a previously unvalidated code path
Mirrors an existing security control from another input path (microSD / PR #636)
Separates canonical/raw address used for verification from human-readable stylized address used for display
Prevents address mismatch between what the user confirms and what the signature actually covers
Small, targeted diff in a single signing flow file
Evidence from the diff
In sign_electrum_message_flow.py, the QR message-signing path previously called stylize_address() on self.address before the signing step, then invoked sign_text_file_task with only message, subpath, and addr_format. The microSD path (per PR #636) already passes an expected_address argument so the firmware can assert the derived/recovered address matches. This change keeps self.address as the raw expected address, introduces a local display_address for UI stylization, and adds self.address as the fourth argument to sign_text_file_task. This mirrors the existing WYSIWYG-signing assertion and closes a gap where QR-initiated signing did not verify address consistency at the task layer.
Changed components
ports/stm32/boards/Passport/modules/flows/sign_electrum_message_flow.pyQR-code Electrum message signing flowsign_text_file_task invocationInspect captured patch +3 / −3
diff --git a/ports/stm32/boards/Passport/modules/flows/sign_electrum_message_flow.py b/ports/stm32/boards/Passport/modules/flows/sign_electrum_message_flow.py
index 6333213..541e6cb 100644
--- a/ports/stm32/boards/Passport/modules/flows/sign_electrum_message_flow.py
+++ b/ports/stm32/boards/Passport/modules/flows/sign_electrum_message_flow.py
@@ -59,7 +59,7 @@ class SignElectrumMessageFlow(Flow):
node = sv.derive_path(self.subpath)
self.address = sv.chain.address(node, self.addr_format)
- self.address = stylize_address(self.address)
+ display_address = stylize_address(self.address)
result = await LongTextPage(centered=True,
text=('\n' + self.message),
@@ -69,7 +69,7 @@ class SignElectrumMessageFlow(Flow):
self.set_result(False)
return
- result = await LongQuestionPage(text='Sign message with this address?\n\n{}'.format(self.address),
+ result = await LongQuestionPage(text='Sign message with this address?\n\n{}'.format(display_address),
right_micron=microns.Sign,
margins=MARGIN_FOR_ADDRESSES,
top_margin=8).show()
@@ -82,7 +82,7 @@ class SignElectrumMessageFlow(Flow):
async def do_sign(self):
(sig, address, error) = await spinner_task('Signing message', sign_text_file_task,
- args=[self.message, self.subpath, self.addr_format])
+ args=[self.message, self.subpath, self.addr_format, self.address])
if error is None:
self.signature = sig
self.goto(self.show_signed)
Why this scored 59/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.