mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-06-27 10:22:34 +00:00
feat(core/sdbackup): SD card backup basic flow
- WIP - saving to plain text for now
This commit is contained in:
parent
071e1fa903
commit
d6791dcfc7
@ -345,6 +345,8 @@ apps.management.reset_device
|
|||||||
import apps.management.reset_device
|
import apps.management.reset_device
|
||||||
apps.management.reset_device.layout
|
apps.management.reset_device.layout
|
||||||
import apps.management.reset_device.layout
|
import apps.management.reset_device.layout
|
||||||
|
apps.management.sd_backup
|
||||||
|
import apps.management.sd_backup
|
||||||
apps.management.sd_protect
|
apps.management.sd_protect
|
||||||
import apps.management.sd_protect
|
import apps.management.sd_protect
|
||||||
apps.management.set_u2f_counter
|
apps.management.set_u2f_counter
|
||||||
|
@ -21,7 +21,11 @@ async def recovery_device(msg: RecoveryDevice) -> Success:
|
|||||||
import storage.recovery as storage_recovery
|
import storage.recovery as storage_recovery
|
||||||
from trezor import config, wire, workflow
|
from trezor import config, wire, workflow
|
||||||
from trezor.enums import ButtonRequestType
|
from trezor.enums import ButtonRequestType
|
||||||
from trezor.ui.layouts import confirm_action, confirm_reset_device
|
from trezor.ui.layouts import (
|
||||||
|
confirm_action,
|
||||||
|
confirm_reset_device,
|
||||||
|
choose_backup_medium,
|
||||||
|
)
|
||||||
|
|
||||||
from apps.common.request_pin import (
|
from apps.common.request_pin import (
|
||||||
error_pin_invalid,
|
error_pin_invalid,
|
||||||
|
@ -44,11 +44,22 @@ async def recovery_process() -> Success:
|
|||||||
async def _continue_recovery_process() -> Success:
|
async def _continue_recovery_process() -> Success:
|
||||||
from trezor import utils
|
from trezor import utils
|
||||||
from trezor.errors import MnemonicError
|
from trezor.errors import MnemonicError
|
||||||
|
from trezor.ui.layouts import choose_backup_medium
|
||||||
|
|
||||||
# gather the current recovery state from storage
|
# gather the current recovery state from storage
|
||||||
dry_run = storage_recovery.is_dry_run()
|
dry_run = storage_recovery.is_dry_run()
|
||||||
word_count, backup_type = recover.load_slip39_state()
|
word_count, backup_type = recover.load_slip39_state()
|
||||||
|
|
||||||
|
# ask the user for backup type (words/SD card)
|
||||||
|
backup_medium: str = await choose_backup_medium(recovery=True)
|
||||||
|
if backup_medium == "sdcard":
|
||||||
|
from apps.management.sd_backup import sdcard_recover_seed
|
||||||
|
|
||||||
|
words = await sdcard_recover_seed()
|
||||||
|
if not words:
|
||||||
|
raise wire.ProcessError("SD card backup could not be recovered.")
|
||||||
|
secret, backup_type = await _process_words(words)
|
||||||
|
else:
|
||||||
# Both word_count and backup_type are derived from the same data. Both will be
|
# Both word_count and backup_type are derived from the same data. Both will be
|
||||||
# either set or unset. We use 'backup_type is None' to detect status of both.
|
# either set or unset. We use 'backup_type is None' to detect status of both.
|
||||||
# The following variable indicates that we are (re)starting the first recovery step,
|
# The following variable indicates that we are (re)starting the first recovery step,
|
||||||
|
@ -213,10 +213,24 @@ def _compute_secret_from_entropy(
|
|||||||
return secret
|
return secret
|
||||||
|
|
||||||
|
|
||||||
|
async def _backup_bip39_sdcard(mnemonic: str) -> None:
|
||||||
|
from apps.management.sd_backup import sdcard_backup_seed, sdcard_verify_backup
|
||||||
|
await sdcard_backup_seed(mnemonic)
|
||||||
|
backup_succes = sdcard_verify_backup(mnemonic)
|
||||||
|
if not backup_succes:
|
||||||
|
raise ProcessError("SD Card backup could not be verified.")
|
||||||
|
|
||||||
|
|
||||||
async def backup_seed(backup_type: BackupType, mnemonic_secret: bytes) -> None:
|
async def backup_seed(backup_type: BackupType, mnemonic_secret: bytes) -> None:
|
||||||
if backup_type == BAK_T_SLIP39_BASIC:
|
if backup_type == BAK_T_SLIP39_BASIC:
|
||||||
await _backup_slip39_basic(mnemonic_secret)
|
await _backup_slip39_basic(mnemonic_secret)
|
||||||
elif backup_type == BAK_T_SLIP39_ADVANCED:
|
elif backup_type == BAK_T_SLIP39_ADVANCED:
|
||||||
await _backup_slip39_advanced(mnemonic_secret)
|
await _backup_slip39_advanced(mnemonic_secret)
|
||||||
else:
|
else:
|
||||||
|
backup_medium: str = await layout.bip39_choose_backup_medium()
|
||||||
|
if backup_medium == "sdcard":
|
||||||
|
await _backup_bip39_sdcard(mnemonic_secret)
|
||||||
|
elif backup_medium == "words":
|
||||||
await layout.bip39_show_and_confirm_mnemonic(mnemonic_secret.decode())
|
await layout.bip39_show_and_confirm_mnemonic(mnemonic_secret.decode())
|
||||||
|
else:
|
||||||
|
raise ProcessError("Invalid backup medium.")
|
||||||
|
@ -158,6 +158,12 @@ async def show_backup_success() -> None:
|
|||||||
# BIP39
|
# BIP39
|
||||||
# ===
|
# ===
|
||||||
|
|
||||||
|
async def bip39_choose_backup_medium() -> str:
|
||||||
|
# TODO this will be general, not only for BIP39
|
||||||
|
from trezor.ui.layouts import choose_backup_medium
|
||||||
|
|
||||||
|
return await choose_backup_medium()
|
||||||
|
|
||||||
|
|
||||||
async def bip39_show_and_confirm_mnemonic(mnemonic: str) -> None:
|
async def bip39_show_and_confirm_mnemonic(mnemonic: str) -> None:
|
||||||
# warn user about mnemonic safety
|
# warn user about mnemonic safety
|
||||||
|
45
core/src/apps/management/sd_backup.py
Normal file
45
core/src/apps/management/sd_backup.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
from trezor import io, utils
|
||||||
|
from trezor.sdcard import with_filesystem
|
||||||
|
|
||||||
|
if utils.USE_SD_CARD:
|
||||||
|
fatfs = io.fatfs # global_import_cache
|
||||||
|
|
||||||
|
|
||||||
|
async def sdcard_backup_seed(mnemonic_secret: bytes) -> None:
|
||||||
|
from apps.common.sdcard import ensure_sdcard
|
||||||
|
|
||||||
|
await ensure_sdcard()
|
||||||
|
_write_seed_plain_text(mnemonic_secret)
|
||||||
|
|
||||||
|
|
||||||
|
async def sdcard_recover_seed() -> str | None:
|
||||||
|
from apps.common.sdcard import ensure_sdcard
|
||||||
|
|
||||||
|
await ensure_sdcard(ensure_filesystem=False)
|
||||||
|
return _read_seed_plain_text()
|
||||||
|
|
||||||
|
|
||||||
|
def sdcard_verify_backup(mnemonic_secret: bytes) -> bool:
|
||||||
|
mnemonic_read = _read_seed_plain_text()
|
||||||
|
if mnemonic_read is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return mnemonic_read.encode() == mnemonic_secret
|
||||||
|
|
||||||
|
@with_filesystem
|
||||||
|
def _write_seed_plain_text(mnemonic_secret: bytes) -> None:
|
||||||
|
# TODO to be removed, just for testing purposes
|
||||||
|
fatfs.mkdir("/trezor", True)
|
||||||
|
with fatfs.open("/trezor/seed.txt", "w") as f:
|
||||||
|
f.write(mnemonic_secret)
|
||||||
|
|
||||||
|
@with_filesystem
|
||||||
|
def _read_seed_plain_text() -> str | None:
|
||||||
|
# TODO to be removed, just for testing purposes
|
||||||
|
mnemonic_read = bytearray(512)
|
||||||
|
try:
|
||||||
|
with fatfs.open("/trezor/seed.txt", "r") as f:
|
||||||
|
f.read(mnemonic_read)
|
||||||
|
except fatfs.FatFSError:
|
||||||
|
return None
|
||||||
|
return mnemonic_read.decode('utf-8').rstrip('\x00')
|
@ -575,6 +575,35 @@ async def show_success(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def choose_backup_medium(recovery: bool = False) -> str:
|
||||||
|
# TODO what br type
|
||||||
|
br_type = "br_type"
|
||||||
|
if recovery:
|
||||||
|
br_code: ButtonRequestType = ButtonRequestType.RecoveryHomepage
|
||||||
|
description: str = "Do you have written words or SD card for recovery?"
|
||||||
|
else:
|
||||||
|
br_code: ButtonRequestType = ButtonRequestType.ResetDevice
|
||||||
|
description: str = "You will be able to backup on the 2n medium later."
|
||||||
|
|
||||||
|
result = await interact(
|
||||||
|
RustLayout(
|
||||||
|
trezorui2.confirm_action(
|
||||||
|
title="Backup medium", # TODO naming convention (backup medium?)
|
||||||
|
action="Choose backup medium.",
|
||||||
|
description=description,
|
||||||
|
verb="SD card",
|
||||||
|
verb_cancel="Words",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
br_type,
|
||||||
|
br_code,
|
||||||
|
)
|
||||||
|
if result is CONFIRMED:
|
||||||
|
return "sdcard"
|
||||||
|
else:
|
||||||
|
return "words"
|
||||||
|
|
||||||
|
|
||||||
async def confirm_output(
|
async def confirm_output(
|
||||||
address: str,
|
address: str,
|
||||||
amount: str,
|
amount: str,
|
||||||
|
@ -913,6 +913,10 @@ class InputFlowBip39Backup(InputFlowBase):
|
|||||||
self.mnemonic = None
|
self.mnemonic = None
|
||||||
|
|
||||||
def input_flow_common(self) -> BRGeneratorType:
|
def input_flow_common(self) -> BRGeneratorType:
|
||||||
|
# choose Words
|
||||||
|
received = yield
|
||||||
|
self.debug.press_no()
|
||||||
|
|
||||||
# 1. Confirm Reset
|
# 1. Confirm Reset
|
||||||
yield from click_through(self.debug, screens=1, code=B.ResetDevice)
|
yield from click_through(self.debug, screens=1, code=B.ResetDevice)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user