seedxor allowed when hobbled

This commit is contained in:
Peter D. Gray 2025-09-22 11:57:21 -04:00 committed by scgbckbone
parent 6cb1173671
commit e4eaaf16fa
2 changed files with 44 additions and 14 deletions

View File

@ -120,13 +120,15 @@ Quiz Passed!\n
You have confirmed the details of the new split.''')
# list of seed phrases
# stores encoded secret bytes (not word lists)
# - stores encoded secret bytes (not word lists)
import_xor_parts = []
async def xor_all_done(data):
# So we have another part, might be done or not.
global import_xor_parts
chk_words = None
if data is None:
# special case, needs something already in import_xor_parts
target_words = len_to_numwords(len(import_xor_parts[0]))
@ -144,7 +146,7 @@ async def xor_all_done(data):
if num_parts >= 2:
chk_words = bip39.b2a_words(seed).split(' ')
chk_word = chk_words[-1]
msg += "If you stop now, the %dth word of the XOR-combined seed phrase\nwill be:\n\n" % target_words
msg += "If you stop now, the %dth word of the XOR-combined seed phrase will be:\n\n" % target_words
msg += "%d: %s\n\n" % (target_words, chk_word)
if all((not x) for x in seed):
@ -236,12 +238,12 @@ async def xor_restore_start(*a):
# shown on import menu when no seed of any kind yet
# - or operational system
ch = await ux_show_story('''\
To import a seed split using XOR, you must import all the parts.
It does not matter the order (A/B/C or C/A/B) and the Coldcard
cannot determine when you have all the parts. You may stop at
any time and you will have a valid wallet. Combined seed parts
have to be equal length. No way to combine seed parts of different
length. Press %s for 24 words XOR, press (1) for 12 words XOR,
To import a seed split using XOR, you must import all the parts. \
It does not matter the order (A/B/C or C/A/B) and the Coldcard \
cannot determine when you have all the parts. You may stop at \
any time and you will have a valid wallet. Combined seed parts \
have to be equal length.\n
Press %s for 24 words XOR, press (1) for 12 words XOR, \
or press (2) for 18 words XOR.''' % OK, escape="12")
if ch == 'x': return
@ -251,8 +253,6 @@ or press (2) for 18 words XOR.''' % OK, escape="12")
elif ch == "2":
desired_num_words = 18
curr_num_words = settings.get('words', desired_num_words)
global import_xor_parts
import_xor_parts.clear()
@ -264,15 +264,20 @@ or press (2) for 18 words XOR.''' % OK, escape="12")
msg = ("Since you have a seed already on this Coldcard, the reconstructed XOR seed will be "
"temporary and not saved. Wipe the seed first if you want to commit the new value "
"into the secure element.")
if curr_num_words == desired_num_words:
curr_num_words = settings.get('words', desired_num_words)
if (curr_num_words == desired_num_words) and not pa.hobbled_mode:
escape += "1"
msg += ("\nPress (1) to include this Coldcard's seed words into the XOR seed set, "
msg += ("\n\nPress (1) to include this Coldcard's seed words into the XOR seed set, "
"or %s to continue without." % OK)
ch = await ux_show_story(msg, escape=escape)
if ch == 'x': return
if ch == 'x':
return
if ch == '1':
assert not pa.hobbled_mode
dis.fullscreen("Wait...")
with SensitiveValues(enforce_delta=True) as sv:
if sv.mode == 'words':

View File

@ -273,7 +273,8 @@ def test_h_tempseeds(mode, set_hobble, pick_menu_item, cap_menu, settings_set, i
m = cap_menu()
assert 'Generate Words' not in m
assert all(i.startswith("Import ") or i.endswith(' Backup') for i in m), m
assert all((i.startswith("Import ") or i.endswith(' Backup') or i == 'Restore Seed XOR')
for i in m), m
words, expect_xfp = WORDLISTS[12]
@ -451,5 +452,29 @@ def test_h_qrscan(en_okeys, set_hobble, scan_a_qr, need_keypress, press_cancel,
else:
scr = cap_screen() # stays in scanning mode
assert 'KT Blocked' in scr
def test_h_seedxor(set_hobble, need_keypress, press_cancel, cap_screen, only_q1,
cap_story, press_select, pick_menu_item, settings_set):
# can start import via seed XOR, but cannot include master seed phrase
# as part of it.
settings_set('seedvault', True)
settings_set('seeds', [])
set_hobble(True, {'okeys'})
pick_menu_item("Advanced/Tools")
pick_menu_item('Temporary Seed')
pick_menu_item('Restore Seed XOR')
title, story = cap_story()
assert 'A/B/C' in story
press_select() # select 24 words
title, story = cap_story()
assert 'Since you have' in story
assert "include this Coldcard's seed" not in story # WEAK: fragile if UX changes
press_cancel()
# EOF