Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions ports/stm32/boards/Passport/modules/tasks/restore_backup_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@
from constants import MAX_BACKUP_FILE_SIZE
from pincodes import SE_SECRET_LEN

NON_RESTORABLE_BACKUP_SETTINGS = ('bip39_passphrase', 'root_xfp', 'xfp', 'xpub')


def restore_settings_from_backup(vals, settings):
for k in vals:
if not k.startswith('setting.'):
continue

setting_key = k[8:]
if setting_key in NON_RESTORABLE_BACKUP_SETTINGS:
continue

settings.set(setting_key, vals[k])


async def restore_backup_task(on_done, decryption_password, backup_file_path):
from common import pa, settings
Expand Down Expand Up @@ -114,17 +128,15 @@ async def restore_backup_task(on_done, decryption_password, backup_file_path):
await pa.new_main_secret(raw, chain)

# Finally, restore the settings
for idx, k in enumerate(vals):
if not k.startswith('setting.'):
continue

if k == 'xfp' or k == 'xpub':
continue

settings.set(k[8:], vals[k])
restore_settings_from_backup(vals, settings)

# This would be true in the old backup, but false for this new device
settings.set('backup_quiz', False)

# Wallet identity metadata is derived from the restored secret, never trusted from the backup.
with stash.SensitiveValues(raw) as sv:
sv.chain = chain
sv.capture_xpub(save=True)

# Success!
await on_done(None)
4 changes: 4 additions & 0 deletions ports/stm32/boards/Passport/modules/tests/test_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,7 @@ def test_ui(test):

def test_foundation(test):
assert test('foundation.py') == b'OK'


def test_restore_backup(test):
assert test('restore_backup.py') == b'OK'
37 changes: 37 additions & 0 deletions ports/stm32/boards/Passport/modules/tests/unit/restore_backup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# SPDX-FileCopyrightText: 2026 Foundation Devices, Inc. <hello@foundation.xyz>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# Test backup restore settings filtering.

from tasks.restore_backup_task import restore_settings_from_backup


class FakeSettings:
def __init__(self):
self.values = {}

def set(self, key, value):
self.values[key] = value


vals = {
'chain': 'BTC',
'xfp': 'top-level metadata is ignored here',
'setting.xfp': 0x11111111,
'setting.xpub': 'attacker-xpub',
'setting.root_xfp': 0x22222222,
'setting.bip39_passphrase': 'runtime-only',
'setting.units': 'sats',
'setting.backup_quiz': True,
}

settings = FakeSettings()
restore_settings_from_backup(vals, settings)

assert settings.values == {
'units': 'sats',
'backup_quiz': True,
}

return_value.write(b'OK')