feat(core): propagate method to choose backup handler
What changed, and why it matters
This commit refactors how the Trezor firmware selects a backup handler when creating or restoring a wallet backup. Previously the device always used a display-based backup handler. Now it accepts a 'method' parameter from the host and routes to the appropriate handler, with only display-based backup implemented today and a placeholder for a future method. There is no direct security fix here; it is a feature/refactoring commit that adds a TODO for user prompting when no method is specified.
Treat as a non-security refactoring commit. Monitor the follow-up work that implements the TODO user prompt and any new backup methods (e.g., N4W1-based) for proper authorization and validation, since host-controlled backup method selection could become a security boundary.
Security signals we found
New host-controlled method parameter routed into backup handler selection
Debug-only warning for unsupported BackupMethod values; no runtime enforcement
TODO comment indicating user-prompt logic for method=None is not yet implemented
No validation or sanitization of the BackupMethod enum beyond an in-debug assert-like check
Evidence from the diff
The change introduces a new choose_backup_handler() helper in reset_device/layout.py that currently returns _DisplayBackup() for any input, logs a warning in debug builds for unsupported methods, and has a TODO to prompt the user when method is None. The BackupDevice, ResetDevice, and recovery homescreen flows are updated to pass msg.backup_method or None through to this helper. The actual backup logic is otherwise unchanged.
Changed components
core/src/apps/management/backup_device.pycore/src/apps/management/recovery_device/homescreen.pycore/src/apps/management/reset_device/__init__.pycore/src/apps/management/reset_device/layout.pyInspect captured patch +33 / −6
diff --git a/core/src/apps/management/backup_device.py b/core/src/apps/management/backup_device.py
index 2b5a8a74..fd8b567c 100644
--- a/core/src/apps/management/backup_device.py
+++ b/core/src/apps/management/backup_device.py
@@ -6,11 +6,12 @@ from trezor.enums import BackupType
if TYPE_CHECKING:
from typing import Sequence
- from trezor.messages import BackupDevice, Success
+ from trezor.messages import BackupDevice, BackupMethod, Success
async def perform_backup(
is_repeated_backup: bool,
+ method: BackupMethod | None,
group_threshold: int | None = None,
groups: Sequence[tuple[int, int]] = (),
) -> None:
@@ -53,7 +54,9 @@ async def perform_backup(
backup.deactivate_repeated_backup()
storage_device.set_backed_up()
- handler = layout.DisplayBackup()
+ # Choose backup handler (prompt the user if method is `None`)
+ handler = await layout.choose_backup_handler(method)
+
if group_threshold is not None:
# Parameters provided from host side.
assert backup_types.is_slip39_backup_type(backup_type)
@@ -116,6 +119,11 @@ async def backup_device(msg: BackupDevice) -> Success:
# avoid failing backup process due to I/O-related errors
with wire.context.continue_on_errors("Backup in progress"):
- await perform_backup(is_repeated_backup, group_threshold, groups)
+ await perform_backup(
+ is_repeated_backup=is_repeated_backup,
+ method=msg.backup_method,
+ group_threshold=group_threshold,
+ groups=groups,
+ )
return Success(message="Seed successfully backed up")
diff --git a/core/src/apps/management/recovery_device/homescreen.py b/core/src/apps/management/recovery_device/homescreen.py
index c011f9d6..53783ec9 100644
--- a/core/src/apps/management/recovery_device/homescreen.py
+++ b/core/src/apps/management/recovery_device/homescreen.py
@@ -83,7 +83,8 @@ async def _continue_repeated_backup() -> None:
)
try:
- await perform_backup(is_repeated_backup=True)
+ # During on-device flow, the backup method will be chosen later.
+ await perform_backup(is_repeated_backup=True, method=None)
finally:
backup.deactivate_repeated_backup()
diff --git a/core/src/apps/management/reset_device/__init__.py b/core/src/apps/management/reset_device/__init__.py
index b2b685e1..cb6861e5 100644
--- a/core/src/apps/management/reset_device/__init__.py
+++ b/core/src/apps/management/reset_device/__init__.py
@@ -125,8 +125,10 @@ async def reset_device(msg: ResetDevice) -> Success:
# generate and display backup information for the master secret
if perform_backup:
+ # choose backup handler (prompt the user if method is `None`)
+ handler = await layout.choose_backup_handler(msg.backup_method)
await backup_seed(
- handler=layout.DisplayBackup(),
+ handler=handler,
backup_type=backup_type,
mnemonic_secret=secret,
)
diff --git a/core/src/apps/management/reset_device/layout.py b/core/src/apps/management/reset_device/layout.py
index 5eb4371c..cb1efadb 100644
--- a/core/src/apps/management/reset_device/layout.py
+++ b/core/src/apps/management/reset_device/layout.py
@@ -10,6 +10,9 @@ from trezor.ui.layouts.reset import ( # noqa: F401
slip39_show_checklist,
)
+if TYPE_CHECKING:
+ from trezor.messages import BackupMethod
+
_NUM_OF_CHOICES = const(3)
@@ -159,7 +162,7 @@ async def slip39_advanced_show_and_confirm_shares(
)
-class DisplayBackup:
+class _DisplayBackup:
async def intro(self, num_of_words: int | None = None) -> None:
from trezor.ui.layouts.reset import show_intro_backup
@@ -186,3 +189,16 @@ class DisplayBackup:
# make the user confirm words from the share
if await _share_words_confirmed(share):
break # this share is confirmed, go to next one
+
+
+async def choose_backup_handler(method: BackupMethod | None) -> BackupHandler:
+ # TODO: prompt the user if method is `None`.
+ if __debug__:
+ from trezor.enums import BackupMethod
+
+ if method not in (None, BackupMethod.Display):
+ from trezor import log
+
+ log.warning(__name__, "Unsupported backup method: %s", method)
+
+ return _DisplayBackup()
Why this scored 17/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.