bugfix: do not offer to show QR code of TXID if txn is not finalized
What changed, and why it matters
This commit fixes a small user-interface bug in the COLDCARD hardware wallet. Previously, after signing a transaction that was not yet finalized, the device still offered a menu option to display a QR code of the transaction ID (TXID). Because the transaction ID can only be reliably computed once the transaction is finalized, offering the QR code earlier could mislead the user or show invalid/incomplete data. The fix hides that QR-code option unless the transaction is finalized. There is no direct evidence of funds being stolen or a remote attack.
Treat as a routine bugfix with low security impact. Include in normal firmware release notes. No urgent patching required unless the project considers misleading QR-code display a usability/security concern worth highlighting.
Security signals we found
UI-only bugfix with no cryptographic or authorization bypass
Prevents display of potentially invalid/incomplete TXID QR code before transaction finalization
No buffer overflow, injection, or privilege escalation signals in diff
No vendor disclosure of security relevance in commit message or diff
Evidence from the diff
In shared/auth.py’s done_signing(), the code now sets key6=None when txid is absent, and only passes key6=’for QR Code of TXID’ when txid exists. The import_export_prompt() helper uses key6 as the label for the (6) key menu entry. A non-finalized PSBT signing does not produce a txid, so the menu entry is now suppressed. A regression test in testing/test_sign.py verifies that ‘(6) for QR Code of TXID’ is absent when finalize=False and present when finalize=True.
Changed components
shared/auth.py: done_signing() post-signing prompt flowCOLDCARD firmware user interface: signed transaction export menuInspect captured patch +20 / −2
diff --git a/shared/auth.py b/shared/auth.py
index cd267dc..76900b3 100644
--- a/shared/auth.py
+++ b/shared/auth.py
@@ -861,9 +861,11 @@ async def done_signing(psbt, tx_req, input_method=None, filename=None,
if not ch:
# show all possible export options (based on hardware enabled, features)
intro = []
+ key6 = None
if msg:
intro.append(msg)
if txid:
+ key6 = "for QR Code of TXID"
intro.append('TXID:\n' + txid)
# "force_prompt" is needed after first iteration as we can be Mk4, with NFC,Vdisk off,
@@ -871,8 +873,7 @@ async def done_signing(psbt, tx_req, input_method=None, filename=None,
# In that case this would just return dict and keep producing signed
# files on SD infinitely (would never actually prompt).
ch = await import_export_prompt(noun, intro="\n\n".join(intro), offer_kt=offer_kt,
- key6="for QR Code of TXID", title=title,
- force_prompt=not first_time,
+ key6=key6, title=title, force_prompt=not first_time,
no_qr=not version.has_qwerty)
if ch == KEY_CANCEL:
UserAuthorizedAction.cleanup()
diff --git a/testing/test_sign.py b/testing/test_sign.py
index b031761..f97a270 100644
--- a/testing/test_sign.py
+++ b/testing/test_sign.py
@@ -3657,4 +3657,21 @@ def test_duplicate_inputs(segwit_in, num_ins, fake_txn, start_sign, end_sign, ca
else:
assert title == "OK TO SEND?"
+
+def test_txid_qr(fake_txn, start_sign, cap_story, press_cancel, press_select):
+ psbt = fake_txn(1, 2, change_outputs=[0])
+ start_sign(psbt, finalize=False)
+ press_select() # confirm signing
+ time.sleep(.1)
+ title, story = cap_story()
+ assert "(6) for QR Code of TXID" not in story
+ press_cancel() # refuse
+ time.sleep(.1)
+ start_sign(psbt, finalize=True)
+ press_select() # confirm signing
+ time.sleep(.1)
+ title, story = cap_story()
+ assert "(6) for QR Code of TXID" in story
+ press_cancel()
+
# EOF
Why this scored 28/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.