What changed, and why it matters
This commit fixes a display layout problem on the COLDCARD Q hardware wallet. When showing 24 special 'TRNG words' on the Q's screen, the list was being split across two screens in a confusing way, so a user might not see all words together. The change adds a hint telling the user to scroll, and adjusts the text so the 24-word grid starts cleanly on a new screen. It is a usability fix for viewing sensitive seed material, not a fix for a code-execution or theft vulnerability.
Treat as a minor usability/reliability improvement rather than a security patch requiring urgent deployment. Users relying on the TRNG words screen on COLDCARD Q should ensure firmware includes this change so all 24 words are visible together after scrolling. No immediate rotation of seeds or incident response is warranted.
Security signals we found
Sensitive seed material displayed to user
UI pagination could cause user to miss part of 24-word seed
No change to entropy generation, storage, or cryptographic operations
Evidence from the diff
In shared/seed.py, the view_trng_words() function builds a story page shown via ux_show_story(). On the COLDCARD Q (is_q1), the long preamble text pushed the rendered 24-word grid across a page boundary, splitting the words across screens. The patch adds an explicit line ‘Scroll to see TRNG words.’ and a comment explaining that the line count is intentionally kept so the 24-word grid begins on a fresh Q screen. The test is updated to assert the new hint and, on Q1, to verify that pressing down twice reveals word numbers 1-24 on one screen. No cryptographic, input-validation, or access-control logic is changed.
Changed components
shared/seed.py:view_trng_words()COLDCARD Q display UXtesting/test_ux.py:test_view_trng_words_verifies_dice_mixInspect captured patch +12 / −2
### shared/seed.py
@@ -793,11 +793,12 @@ async def view_trng_words(_menu, _idx, item):
from ux import ux_render_words
words = bip39.b2a_words(item.arg).split(' ')
+ # Keep all story lines: their count makes the 24-word grid start on a fresh Q screen.
msg = ('These 24 words encode the full 256-bit device seed (STM32 TRNG + SE1 + SE2) '
'before user entropy is mixed in.\n\nAll 256 bits are used, even for a 12-word wallet.'
'\n\nUse them to verify dice-roll or coin-flip mixing.\n\nKEEP SECRET: Anyone with '
'these words and your complete user entropy can recreate your wallet seed.'
- '\n\n%s' % ux_render_words(words))
+ '\n\nScroll to see TRNG words.\n\n%s' % ux_render_words(words))
try:
await ux_show_story(msg, title='TRNG Words', sensitive=True)
finally:
### testing/test_ux.py
@@ -473,7 +473,8 @@ def finish_entropy():
@pytest.mark.parametrize('nwords', [12, 24])
def test_view_trng_words_verifies_dice_mix(nwords, pick_menu_item, cap_menu, cap_story, unit_test,
- press_select, need_keypress, seed_story_to_words, is_q1):
+ press_select, need_keypress, seed_story_to_words, is_q1,
+ cap_screen, press_down):
unit_test('devtest/clear_seed.py')
pick_menu_item('New Seed Words')
pick_menu_item(f'{nwords} Words')
@@ -489,6 +490,14 @@ def test_view_trng_words_verifies_dice_mix(nwords, pick_menu_item, cap_menu, cap
assert 'All 256 bits are used' in body
assert 'STM32 TRNG + SE1 + SE2' in body
assert 'KEEP SECRET' in body
+ assert 'Scroll to see TRNG words.' in body
+
+ if is_q1:
+ press_down()
+ press_down()
+ time.sleep(0.1)
+ shown = {int(n) for n in re.findall(r'(?<!\d)(\d{1,2}):', cap_screen())}
+ assert shown == set(range(1, 25))
press_select()
time.sleep(0.1)Why this scored 19/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.