fix: block Delta Mode secret consumers
What changed, and why it matters
This commit fixes two ways a Coldcard in 'Delta Mode' (a restricted mode that hides the wallet seed) could still let a user pick or import secret seed phrases. One path was through 'Key Teleport' secret selection, and another was through importing a 'CCC key C' from the Seed Vault. The patch blocks both so Delta Mode cannot be bypassed to reveal or use stored secrets.
Treat as a security hardening fix and include in the next firmware release. No immediate user action is required beyond applying the update. Review other flows that consume Seed Vault secrets to ensure Delta Mode guards are consistently applied.
Security signals we found
Delta Mode bypass hardening
Seed Vault secret exposure prevention
UI menu option restricted under security mode
Defensive wipe helper invoked at sensitive entry point
Evidence from the diff
The change adds Delta Mode guards in shared/ccc.py and shared/teleport.py. In ccc.py, the CCC Key C generation/import menu now checks pa.is_deltamode() before offering the Seed Vault import option and removes ‘6’ from the escape keys. In teleport.py, the Key Teleport secret picker now calls wipe_if_deltamode() at entry. A test verifies the Seed Vault option is absent in Delta Mode and that pressing ‘6’ has no effect.
Changed components
shared/ccc.pyshared/teleport.pyCCC Key C import flowKey Teleport secret pickerSeed Vault integrationInspect captured patch +33 / −3
### releases/Next-ChangeLog.md
@@ -32,6 +32,8 @@ This lists the new changes that have not yet been published in a normal release.
- Bugfix: Prevent access to Seed Vault entries through Seed XOR restore in Delta Mode. Thanks to
Rety for reporting this.
- Bugfix: Wipe seed in Delta Mode when saved BIP-39 passphrases are listed, instead of revealing them.
+- Bugfix: Block Key Teleport’s secret picker and CCC key-C import from Seed
+ Vault in Delta Mode.
- Bugfix: BIP-322 message signing now rejects non-ASCII and other unsupported
message text before approval. Thanks to @KirillCherikov for reporting.
- Bugfix: Prevent duplicate WIF Store entries after restarting
### shared/ccc.py
@@ -858,15 +858,18 @@ async def gen_or_import():
# returns 12 words, or None to abort
from seed import WordNestMenu, generate_seed_with_user_entropy, approve_word_list
from seed import SeedVaultChooserMenu, PURPOSE_CCC
+ from pincodes import pa
msg = "Press %s to generate a new 12-word seed phrase to be used "\
"as the Coldcard Co-Signing Secret (key C).\n\nOr press (1) to import existing "\
"12-words or (2) for 24-words import." % OK
- if settings.master_get("seedvault", False):
+ esc = "12"
+ if not pa.is_deltamode() and settings.master_get("seedvault", False):
+ esc += "6"
msg += ' Press (6) to import from Seed Vault.'
- ch = await ux_show_story(msg, escape='126', title="CCC Key C")
+ ch = await ux_show_story(msg, escape=esc, title="CCC Key C")
if ch in '12':
nwords = 24 if ch == '2' else 12
### shared/teleport.py
@@ -4,7 +4,7 @@
# secure environment of two Q's.
#
import ngu, aes256ctr, bip39, json, ndef, chains, stash
-from utils import xfp2str, deserialize_secret
+from utils import xfp2str, deserialize_secret, wipe_if_deltamode
from ubinascii import unhexlify as a2b_hex
from ubinascii import hexlify as b2a_hex
from glob import settings, dis
@@ -516,6 +516,8 @@ def __init__(self, rx_pubkey):
from pincodes import pa
assert not pa.hobbled_mode
+ wipe_if_deltamode() # shouldn't be here in delta mode
+
from flow import word_based_seed, is_tmp, has_se_secrets
has_notes = bool(NoteContentBase.count())
has_sv = bool(settings.get('seedvault', False))
### testing/test_ccc.py
@@ -1361,6 +1361,29 @@ def test_c_key_from_seed_vault(has_candidates, setup_ccc, build_test_seed_vault,
press_select()
+def test_c_key_from_seed_vault_hidden_in_deltamode(
+ goto_home, goto_ccc_menu, settings_set, set_deltamode, press_select,
+ need_keypress, cap_story):
+ goto_home()
+ settings_set("ccc", None)
+ settings_set("seedvault", True)
+ set_deltamode(True)
+
+ goto_ccc_menu()
+ press_select()
+ time.sleep(.2)
+
+ title, story = cap_story()
+ assert title == "CCC Key C"
+ assert "import from Seed Vault" not in story
+
+ need_keypress("6")
+ time.sleep(.2)
+ new_title, new_story = cap_story()
+ assert new_title == title
+ assert new_story == story
+
+
@pytest.mark.parametrize("way", ["sd", "qr"])
@pytest.mark.parametrize("ftype", ["cc", "bsms"])
@pytest.mark.parametrize("is_bbqr", [True, False])Why this scored 62/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.