bugfix: recognize temporary secrets in teleport and Seed XOR
What changed, and why it matters
This commit fixes a bug in the COLDCARD hardware wallet where two seed-management features (teleport and Seed XOR) did not properly recognize temporary/ephemeral wallets. Previously, the code only checked whether the secure element (SE) held a permanent seed. If a user was operating from a temporary seed while the SE was blank, the device treated the wallet as empty. In teleport, this could cause an incoming seed to overwrite the current temporary seed as the master seed. In Seed XOR, it could cause the reconstructed seed to be saved as the master seed or fail to warn that the result would be temporary. The fix makes both features check whether any secret—stored or temporary—is active.
Users relying on temporary/ephemeral seeds should upgrade to a firmware release containing this fix. Reviewers should verify that `has_secrets()` is consistently used across all seed-import, backup, and wipe flows to ensure temporary seeds are never treated as blank.
Security signals we found
Logic flaw: temporary/ephemeral secrets not considered in security-critical branch
Potential unintended overwrite of active temporary wallet with incoming/master seed
Potential missing warning when reconstructed XOR seed would be temporary
Fix aligns authorization checks with actual secret state
Evidence from the diff
The patch changes two call sites. In shared/teleport.py, has_se_secrets() is replaced with has_secrets(), which returns true if either a secure-element seed or an ephemeral/temporary seed is present. In shared/xor_seed.py, pa.is_secret_blank() is replaced with not pa.has_secrets() in the save path, and not pa.is_secret_blank() is replaced with pa.has_secrets() in the warning path. The intent is to prevent temporary-seed state from being ignored when deciding whether to overwrite/save a seed or warn the user that the result will be temporary.
Changed components
shared/teleport.pyshared/xor_seed.pyCOLDCARD seed management / secure element policyInspect captured patch +7 / −4
### shared/teleport.py
@@ -306,7 +306,7 @@ async def kt_accept_values(dtype, raw):
- `p` - binary PSBT to be signed
- `b` - complete system backup file (text, internal format)
'''
- from flow import has_se_secrets, goto_top_menu
+ from flow import has_secrets, goto_top_menu
from pincodes import pa
enc = None
@@ -416,7 +416,8 @@ async def kt_accept_values(dtype, raw):
from seed import set_ephemeral_seed, set_seed_value
- if not has_se_secrets():
+ # A temporary wallet still counts as a secret when the SE is blank.
+ if not has_secrets():
# unit has nothing, so this will be the master seed
set_seed_value(encoded=enc)
ok = True
### shared/xor_seed.py
@@ -190,7 +190,8 @@ async def xor_all_done(data, force_tmp, done_cb):
enc = SecretStash.encode(seed_phrase=seed)
- if pa.is_secret_blank() and not force_tmp:
+ # A temporary wallet still counts as a secret when the SE is blank.
+ if not pa.has_secrets() and not force_tmp:
# save it since they have no other secret
set_seed_value(encoded=enc)
# update menu contents now that wallet defined
@@ -268,7 +269,8 @@ async def done_cb(data):
from glob import dis
escape = ""
- if not pa.is_secret_blank():
+ # Warn when either a stored or temporary secret is active.
+ if pa.has_secrets():
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.")Why this scored 59/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.