What changed, and why it matters
This commit only adds automated tests for an existing firmware feature called 'deltamode' trick PINs. It does not change the actual COLDCARD firmware code that users run. The tests confirm that when a deltamode trick PIN is active, the device produces an intentionally invalid Bitcoin signature and does not wipe the seed. No security bug is introduced or fixed here.
No security action required. This is a test-only commit. Reviewers may optionally confirm the new tests accurately reflect the intended deltamode behavior documented elsewhere.
Security signals we found
No changes to production firmware or device behavior
Adds regression tests for existing deltamode trick-PIN behavior
Verifies deltamode produces invalid signatures rather than leaking real keys
Verifies device does not wipe seed in deltamode signing path
Evidence from the diff
The diff adds pytest fixtures (get_deltamode, set_deltamode) and two test cases. test_deltamode_toggle verifies the simulator can toggle the TC_DELTA_MODE flag (pa.delay_required bit 0x0400). test_deltamode_signature verifies that, under SSSP (Seed Vault / trick PIN) conditions with deltamode active, PSBT signing returns a different, invalid signature (rejected by testmempoolaccept with ‘Signature must be zero’) and that the normal signature remains valid. The commit removes an obsolete TODO comment in test_hobble.py. No production firmware source files are modified.
Changed components
testing/conftest.pytesting/test_hobble.pytesting/test_se2.pytesting/test_sssp.pyInspect captured patch +82 / −2
diff --git a/testing/conftest.py b/testing/conftest.py
index ccfd5c6..1916b22 100644
--- a/testing/conftest.py
+++ b/testing/conftest.py
@@ -2627,6 +2627,27 @@ def build_test_seed_vault():
return sv
return doit
+@pytest.fixture
+def get_deltamode(sim_exec):
+ # get current "deltamode" status: T or F
+ def doit():
+ return eval(sim_exec('RV.write(repr(pa.is_deltamode()))'))
+ return doit
+
+@pytest.fixture
+def set_deltamode(sim_exec):
+ # control current "deltamode" status: T or F
+ def doit(val):
+ # TC_DELTA_MODE = const(0x0400)
+ if val:
+ sim_exec('pa.delay_required |= 0x400')
+ else:
+ sim_exec('pa.delay_required &= ~0x400')
+
+ yield doit
+
+ doit(False)
+
# useful fixtures
from test_backup import backup_system
diff --git a/testing/test_hobble.py b/testing/test_hobble.py
index 3cba9fe..dc4ed9e 100644
--- a/testing/test_hobble.py
+++ b/testing/test_hobble.py
@@ -17,6 +17,7 @@ from test_ux import word_menu_entry
#
# - test_teleport.py::test_teleport_ms_sign
# - verifies: MS psbt KT should still work in hobbled mode
+#
# - test_teleport.py::test_hobble_limited
# - verifies: scan a KT and have it rejected if not PSBT type: so R and E types
@@ -392,8 +393,6 @@ def test_h_usbcmds(en_okeys, set_hobble, dev):
got = dev.send_recv(cmd)
assert 'Spending policy in effect' in str(ee)
-# TODO: verify that PSBT can be "signed" when SP enabled and delta-mode pin is active. seed not wiped.
-
# TODO verify whitelist of QR types is correct when in hobbled mode
# - no private key material, no teleport starting, unless "okeys" is set
diff --git a/testing/test_se2.py b/testing/test_se2.py
index 75f1825..78d640a 100644
--- a/testing/test_se2.py
+++ b/testing/test_se2.py
@@ -916,6 +916,14 @@ def build_duress_wallets(request, seed_vault=False):
return 4
+def test_deltamode_toggle(get_deltamode, set_deltamode):
+ # check test fixture works.
+ assert get_deltamode() == False
+ set_deltamode(True)
+ assert get_deltamode() == True
+ set_deltamode(False)
+ assert get_deltamode() == False
+
# TODO
# - make trick and do login, check arrives right state?
diff --git a/testing/test_sssp.py b/testing/test_sssp.py
index 264c207..b14d469 100644
--- a/testing/test_sssp.py
+++ b/testing/test_sssp.py
@@ -566,4 +566,56 @@ def test_use_trick_pin_as_unlock(hide, setup_sssp, cap_story, new_trick_pin, pic
assert "already in use" in story
assert "PIN codes must be unique" in story
+
+
+@pytest.mark.parametrize("active_policy", [False, True])
+def test_deltamode_signature(active_policy, setup_sssp, bitcoind, settings_set,
+ start_sign, end_sign,
+ set_deltamode, bitcoind_d_sim_watch, settings_get):
+
+ # verify that "deltamode" trick pins will work in SSSP mode
+ # - and that resulting signature is bad
+ # - device should **not** wipe itself
+
+ dest = "bcrt1qlk39jrclgnawa42tvhu2n7se987qm96qg8v76e"
+ wo = bitcoind_d_sim_watch
+ wo.keypoolrefill(20)
+
+ settings_set("chain", "XRT")
+
+ if active_policy:
+ setup_sssp("11-11", mag=100)
+
+ bitcoind.supply_wallet.sendtoaddress(address=wo.getnewaddress(), amount=2)
+ bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress())
+
+ # create funded PSBT, first tx
+ # - within active policy.
+ init_block_height = bitcoind.supply_wallet.getblockchaininfo()["blocks"] # block height
+ psbt_resp = wo.walletcreatefundedpsbt([], [{dest: 0.06}],
+ init_block_height, {"fee_rate":2, "replaceable": False})
+ psbt = psbt_resp.get("psbt")
+
+ po = BasicPSBT().parse(base64.b64decode(psbt))
+ assert po.parsed_txn.nLockTime == init_block_height
+
+ start_sign(base64.b64decode(psbt), finalize=True)
+ signed = end_sign(accept=True, finalize=True)
+
+ set_deltamode(True)
+
+ start_sign(base64.b64decode(psbt), finalize=True)
+ signed2 = end_sign(accept=True, finalize=True)
+
+ # check wrong signature happened
+ assert signed != signed2
+ probs = wo.testmempoolaccept([signed2.hex()])[0]
+ assert 'Signature must be zero' in probs['reject-reason'], probs
+ assert not probs['allowed']
+
+ # check right signature
+ no_probs = wo.testmempoolaccept([signed.hex()])[0]
+ assert no_probs['allowed']
+
+
# EOF
Why this scored 15/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.