revert SSSP bypass PIN login

This commit is contained in:
scgbckbone 2026-06-25 10:26:32 +02:00 committed by doc-hex
parent 0ef6413cd8
commit 542dcd32c7
3 changed files with 211 additions and 16 deletions

View File

@ -33,7 +33,6 @@ This lists the new changes that have not yet been published in a normal release.
- Bugfix: Non-standard OP_RETURN outputs shown as "null-data", hiding part of the script - Bugfix: Non-standard OP_RETURN outputs shown as "null-data", hiding part of the script
- Bugfix: Over-limit CCC address-whitelist import was rejected but still modified the policy - Bugfix: Over-limit CCC address-whitelist import was rejected but still modified the policy
- Bugfix: Deleting a file right after renaming it (List Files) blanked the old name, leaving the renamed file - Bugfix: Deleting a file right after renaming it (List Files) blanked the old name, leaving the renamed file
- Bugfix: SSSP bypass PIN alone could complete login into a no-secret session. Second prompt now requires a PIN that loads secrets.
- Bugfix: Reordered `multi(...)` multisig with same keys was misreported as name-only change. Now blocked as duplicate. - Bugfix: Reordered `multi(...)` multisig with same keys was misreported as name-only change. Now blocked as duplicate.
- Bugfix: Max WIF store capacity limit was ignored if saving via QR WIF visualization - Bugfix: Max WIF store capacity limit was ignored if saving via QR WIF visualization
- Bugfix: Force Seed XOR restore from Temporary Seed menu to remain temporary even when master seed is blank - Bugfix: Force Seed XOR restore from Temporary Seed menu to remain temporary even when master seed is blank

View File

@ -820,15 +820,12 @@ async def start_login_sequence():
sp_unlock = tp.was_sp_unlock() sp_unlock = tp.was_sp_unlock()
if sp_unlock: if sp_unlock:
# Trying to unlock spending policy: ask for main PIN next. # Trying to unlock spending policy: ask for main PIN next.
while True: await ux_show_story("Spending Policy Unlock: Please provide Main PIN next.")
await ux_show_story("Spending Policy Unlock: Please provide Main PIN next.") pa.reset()
pa.reset() await block_until_login()
await block_until_login()
if pa.has_secrets():
break
# Main or duress wallet PINs are acceptable here, but zero-secret # we don't really know if that was the Main PIN (could easily be the bypass
# trick PINs are not enough to disable spending policy. # PIN again) and if it's a duress wallet, that's cool...
# Do we need to do countdown delay? (real or otherwise) # Do we need to do countdown delay? (real or otherwise)
# - wiping has already occurred if that was selected by trick details # - wiping has already occurred if that was selected by trick details

View File

@ -501,7 +501,7 @@ def test_login_integration(request, nick, randomize, login_ctdwn, kill_btn, kill
def test_calc_login(request): def test_calc_login(request):
is_Q = request.config.getoption('--Q') is_Q = request.config.getoption('--Q')
if not is_Q: raise pytest.skip("Q only") if not is_Q: raise pytest.skip("Q only")
clean_sim_data() # remove all from previous clean_sim_data() # remove all from previous
sim = ColdcardSimulator(args=["--q1"]) sim = ColdcardSimulator(args=["--q1"])
sim.start(start_wait=6) sim.start(start_wait=6)
@ -734,13 +734,10 @@ def test_sssp_bypass_pin_alone_no_login(request):
_login(device, is_Q, bypass_pin) # bypass PIN a 2nd time, instead of main PIN _login(device, is_Q, bypass_pin) # bypass PIN a 2nd time, instead of main PIN
time.sleep(1.0) time.sleep(1.0)
# With the bug the device lands on the EmptyWallet menu (no-secret session). # device lands on the EmptyWallet menu (no-secret session).
# With the fix the zero-secret PIN is rejected and login does not complete.
scr = _cap_screen(device) scr = _cap_screen(device)
assert "New Seed Words" not in scr assert "New Seed Words" in scr
assert "Import Existing" not in scr assert "Import Existing" in scr
assert "provide Main PIN" in scr
sim.stop() sim.stop()
device.close() device.close()
@ -895,4 +892,206 @@ def test_sssp_trick_pins(request):
sim.stop() sim.stop()
device.close() device.close()
def test_trick_countdown_twice(request):
# countdown TP used again after countdown does not land in empty seed menu
ct_pin = "89-89"
is_Q = request.config.getoption('--Q')
headless = request.config.getoption('--headless')
clean_sim_data() # remove all from previous
sim = ColdcardSimulator(args=["--q1" if is_Q else "", "--pin", "22-22", "--early-usb"],
headless=headless)
sim.start(start_wait=6)
device = ColdcardDevice(is_simulator=True)
_login(device, is_Q, "22-22")
_pick_menu_item(device, is_Q, "Settings")
_pick_menu_item(device, is_Q, "Login Settings")
_pick_menu_item(device, is_Q, "Trick PINs")
_pick_menu_item(device, is_Q, "Add New Trick")
time.sleep(.1)
for ch in ct_pin[:2]:
_need_keypress(device, ch)
time.sleep(.1)
_press_select(device, is_Q)
if not is_Q:
# anti-phishing words
_press_select(device, is_Q)
for ch in ct_pin[-2:]:
_need_keypress(device, ch)
time.sleep(.1)
_press_select(device, is_Q)
_pick_menu_item(device, is_Q, "Login Countdown")
_press_select(device, is_Q)
time.sleep(.1)
_pick_menu_item(device, is_Q, "Just Countdown")
for _ in range(2):
_press_select(device, is_Q)
time.sleep(.1)
# adjust countdown to lowest possible value
_pick_menu_item(device, is_Q, f'{ct_pin}')
_pick_menu_item(device, is_Q, '↳Countdown')
_need_keypress(device, "4")
_pick_menu_item(device, is_Q, " 5 minutes")
time.sleep(2)
sim.stop()
device.close()
sim = ColdcardSimulator(args=["--q1" if is_Q else "", "--pin", "22-22", "--early-usb"],
headless=headless)
sim.start(start_wait=6)
device = ColdcardDevice(is_simulator=True)
_login(device, is_Q, ct_pin)
time.sleep(.15)
scr = " ".join(_cap_screen(device).split("\n"))
assert "Login countdown in effect" in scr
assert "Must wait:" in scr
assert "5s" in scr
time.sleep(6)
_login(device, is_Q, ct_pin)
time.sleep(.15)
scr = _cap_screen(device)
assert "New Seed Words" in scr
assert "Import Existing" in scr
sim.stop()
device.close()
def test_wipe_countdown_trick_pin_finishes_blank(request):
ct_pin = "89-90"
is_Q = request.config.getoption('--Q')
headless = request.config.getoption('--headless')
clean_sim_data() # remove all from previous
sim = ColdcardSimulator(args=["--q1" if is_Q else "", "--pin", "22-22", "--early-usb"],
headless=headless)
sim.start(start_wait=6)
device = ColdcardDevice(is_simulator=True)
_login(device, is_Q, "22-22")
_pick_menu_item(device, is_Q, "Settings")
_pick_menu_item(device, is_Q, "Login Settings")
_pick_menu_item(device, is_Q, "Trick PINs")
_pick_menu_item(device, is_Q, "Add New Trick")
time.sleep(.1)
for ch in ct_pin[:2]:
_need_keypress(device, ch)
time.sleep(.1)
_press_select(device, is_Q)
if not is_Q:
# anti-phishing words
_press_select(device, is_Q)
for ch in ct_pin[-2:]:
_need_keypress(device, ch)
time.sleep(.1)
_press_select(device, is_Q)
_pick_menu_item(device, is_Q, "Login Countdown")
_press_select(device, is_Q)
time.sleep(.1)
_pick_menu_item(device, is_Q, "Wipe & Countdown")
for _ in range(2):
_press_select(device, is_Q)
time.sleep(.1)
# adjust countdown to lowest possible value
_pick_menu_item(device, is_Q, f'{ct_pin}')
_pick_menu_item(device, is_Q, '↳Countdown')
_need_keypress(device, "4")
_pick_menu_item(device, is_Q, " 5 minutes")
time.sleep(2)
sim.stop()
device.close()
sim = ColdcardSimulator(args=["--q1" if is_Q else "", "--pin", "22-22", "--early-usb"],
headless=headless)
sim.start(start_wait=6)
device = ColdcardDevice(is_simulator=True)
_login(device, is_Q, ct_pin)
time.sleep(.15)
scr = " ".join(_cap_screen(device).split("\n"))
assert "Login countdown in effect" in scr
assert "Must wait:" in scr
assert "5s" in scr
time.sleep(6)
_login(device, is_Q, ct_pin)
time.sleep(3)
m = _cap_menu(device)
assert "New Seed Words" in m
assert "Import Existing" in m
sim.stop()
device.close()
def test_look_blank_trick_pin_empty_menu(request):
blank_pin = "55-55"
is_Q = request.config.getoption('--Q')
clean_sim_data() # remove all from previous
sim = ColdcardSimulator(args=["--q1" if is_Q else "", "--pin", "22-22", "--early-usb"])
sim.start(start_wait=6)
device = ColdcardDevice(is_simulator=True)
_login(device, is_Q, "22-22")
_pick_menu_item(device, is_Q, "Settings")
_pick_menu_item(device, is_Q, "Login Settings")
_pick_menu_item(device, is_Q, "Trick PINs")
_pick_menu_item(device, is_Q, "Add New Trick")
time.sleep(.1)
for ch in blank_pin[:2]:
_need_keypress(device, ch)
time.sleep(.1)
_press_select(device, is_Q)
if not is_Q:
# anti-phishing words
_press_select(device, is_Q)
for ch in blank_pin[-2:]:
_need_keypress(device, ch)
time.sleep(.1)
_press_select(device, is_Q)
_pick_menu_item(device, is_Q, "Look Blank")
for _ in range(2):
_press_select(device, is_Q)
time.sleep(.1)
time.sleep(2)
sim.stop()
device.close()
sim = ColdcardSimulator(args=["--q1" if is_Q else "", "--pin", "22-22", "--early-usb"])
sim.start(start_wait=6)
device = ColdcardDevice(is_simulator=True)
_login(device, is_Q, blank_pin)
time.sleep(3)
m = _cap_menu(device)
assert "New Seed Words" in m
assert "Import Existing" in m
sim.stop()
device.close()
# EOF # EOF