bugfix: keep CKCC backup restores temporary
What changed, and why it matters
This commit fixes a logic bug in how COLDCARD decides whether to restore a backup as a temporary wallet or as the device's main wallet. Previously, the code checked whether the master secret was blank using an older method. The fix changes that check to use a more accurate function that detects whether any secrets are active. The risk is that a backup could be restored to the wrong place—permanently overwriting the main wallet when the user expected a temporary restore, or failing to create a temporary wallet when one was intended. The commit message calls this a bugfix but does not describe it as a security issue.
Treat as a functional bugfix with possible security side effects. Review related backup-restore test coverage to confirm the old condition could not be triggered by user input to overwrite the main seed unexpectedly. No immediate emergency response is indicated by the diff alone, but a firmware update note should mention the corrected restore behavior.
Security signals we found
Logic correction in wallet restore path
Change from is_secret_blank() to has_secrets()
Potential for unintended master-wallet overwrite if old logic misfired
No explicit security framing by vendor
Evidence from the diff
In shared/auth.py, the to_tmp() method now uses pa.has_secrets() instead of pa.is_secret_blank() to decide the restore target. The old check appears to have been an inverted or less precise condition: it returned ‘master’ when the secret was blank and temporary mode was not forced. The new condition returns ‘master’ only when no secrets are active and temporary mode is not forced; otherwise it returns ‘temporary’. This is a small semantic correction in backup-restore flow logic. The commit title and message frame it as a bugfix with no security disclosure.
Changed components
shared/auth.pyBackup restore flowTemporary wallet restore featureInspect captured patch +4 / −5
### shared/auth.py
@@ -1228,13 +1228,12 @@ def to_words(self):
def to_tmp(self):
# conversion to "temporary" argument of "restore_complete" function
from pincodes import pa
- if pa.is_secret_blank() and not self.force_tmp:
- # no master secret & not forcing tmp
- # will load backup as master seed
+ # A temporary wallet makes master writes no-op, so only select master restore
+ # when no secret is active and the caller did not force temporary mode.
+ if not pa.has_secrets() and not self.force_tmp:
return False, "master"
- # has master secret --> load backup as tmp
- # secret is blank but user forcing tmp
+ # A master/temporary secret is active, or the caller forced temporary mode.
return True, "temporary"
async def interact(self):Why this scored 44/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.