allow viewing QR codes for XOR split mnemonics
What changed, and why it matters
This commit adds a small user-interface convenience: after splitting a secret backup phrase into XOR parts on a COLDCARD hardware wallet, users can now view those parts as QR codes by pressing the '4' key (in addition to the existing QR key on Q models). It is a feature enhancement, not a security fix or vulnerability.
No security action required; treat as normal feature release.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change modifies shared/xor_seed.py so that the story screen shown after XOR seed splitting accepts both KEY_QR and the ‘4’ key as triggers for displaying QR-encoded seed parts. It also updates the on-screen instructions and tests to reflect the new behavior. No cryptographic, input-validation, or access-control logic is changed.
Changed components
shared/xor_seed.pytesting/test_seed_xor.pyreleases/Next-ChangeLog.mdInspect captured patch +11 / −6
diff --git a/releases/Next-ChangeLog.md b/releases/Next-ChangeLog.md
index 4685571..5bcf6d9 100644
--- a/releases/Next-ChangeLog.md
+++ b/releases/Next-ChangeLog.md
@@ -7,12 +7,11 @@ This lists the new changes that have not yet been published in a normal release.
- New Feature: Export [BIP-380](https://github.com/bitcoin/bips/blob/master/bip-0380.mediawiki) extended key expression.
Navigate to `Advanced/Tools -> Export Wallet -> Key Expression`
-
# Mk4 Specific Changes
## 5.4.5 - 2025-12-xx
-- tbd
+- Enhancement: Show QR of XOR-split seeds
# Q Specific Changes
diff --git a/shared/xor_seed.py b/shared/xor_seed.py
index a89bde1..0b2663c 100644
--- a/shared/xor_seed.py
+++ b/shared/xor_seed.py
@@ -99,7 +99,8 @@ Otherwise, press {ok} to continue.'''.format(n=num_parts, ok=OK), escape='2')
if await ux_confirm("Stop and forget those words?"):
return
continue
- if ch == KEY_QR:
+
+ if ch in "4"+KEY_QR:
qrs = []
for wl in word_parts:
qrs.append(encode_seed_qr(wl))
@@ -221,7 +222,7 @@ class XORWordNestMenu(WordNestMenu):
async def show_n_parts(parts, chk_word):
num_parts = len(parts)
seed_len = len(parts[0])
- msg = 'Record these %d lists of %d-words each:' % (num_parts, seed_len)
+ msg = '%d lists of %d-words each:' % (num_parts, seed_len)
for n,words in enumerate(parts):
msg += '\n\nPart %s:\n' % chr(65+n)
@@ -231,8 +232,12 @@ async def show_n_parts(parts, chk_word):
' which we recommend recording:\n\n%d: %s\n\n' % (seed_len, chk_word))
msg += 'Please check and double check your notes. There will be a test! '
+ if not version.has_qwerty:
+ msg += 'Press (4) to view QR Codes. '
- return await ux_show_story(msg, sensitive=True)
+ # allow QR codes on both Mk4 & Q
+ return await ux_show_story(msg, title="Record these:", sensitive=True, escape="4",
+ hint_icons=KEY_QR)
async def xor_restore_start(*a):
# shown on import menu when no seed of any kind yet
diff --git a/testing/test_seed_xor.py b/testing/test_seed_xor.py
index ec82390..d14cf88 100644
--- a/testing/test_seed_xor.py
+++ b/testing/test_seed_xor.py
@@ -253,7 +253,8 @@ def test_xor_split(num_words, qty, trng, goto_home, pick_menu_item, cap_story, n
time.sleep(.01)
title, body = cap_story()
- assert f'Record these {qty} lists of {num_words}-words' in body
+ assert "Record these" in title
+ assert f'{qty} lists of {num_words}-words' in body
assert all((f'Part {chr(n+65)}:' in body) for n in range(qty))
if is_q1:
Why this scored 18/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.