mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-08-02 11:58:32 +00:00
feat(core/sdbackup): backup to unallocated space.
- backup and restore - basic unit test - WIP
This commit is contained in:
parent
8f26f9d9a8
commit
83b4066f55
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,6 +2,8 @@
|
|||||||
.cache/
|
.cache/
|
||||||
.venv/
|
.venv/
|
||||||
.idea/
|
.idea/
|
||||||
|
.ycm_extra_conf.py
|
||||||
|
.vimspector.json
|
||||||
.mypy_cache/
|
.mypy_cache/
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
.vscode/
|
.vscode/
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "embed/extmod/trezorobj.h"
|
#include "embed/extmod/trezorobj.h"
|
||||||
#include "py/mperrno.h"
|
#include "py/mperrno.h"
|
||||||
|
#include "py/obj.h"
|
||||||
#include "py/objstr.h"
|
#include "py/objstr.h"
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
@ -94,14 +95,10 @@ const PARTITION VolToPart[] = {
|
|||||||
{0, 1} // Logical Volume 0 => Physical Disk 0, Partition 1
|
{0, 1} // Logical Volume 0 => Physical Disk 0, Partition 1
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper function to create a partition spanned over a portition (in
|
// Helper function to create a partition on a SD card.
|
||||||
// percentage) of the card.
|
void make_partition(int pt_size) {
|
||||||
void make_partition(int disk_portion_p100) {
|
|
||||||
if ((disk_portion_p100 <= 0) || (disk_portion_p100 > 100)) {
|
|
||||||
FATFS_RAISE(FatFSError, FR_MKFS_ABORTED);
|
|
||||||
}
|
|
||||||
uint8_t working_buf[FF_MAX_SS] = {0};
|
uint8_t working_buf[FF_MAX_SS] = {0};
|
||||||
LBA_t plist[] = {disk_portion_p100, 0};
|
LBA_t plist[] = {pt_size, 0};
|
||||||
FRESULT res = f_fdisk(0, plist, working_buf);
|
FRESULT res = f_fdisk(0, plist, working_buf);
|
||||||
if (res != FR_OK) {
|
if (res != FR_OK) {
|
||||||
FATFS_RAISE(FatFSError, res);
|
FATFS_RAISE(FatFSError, res);
|
||||||
@ -562,15 +559,19 @@ STATIC mp_obj_t mod_trezorio_fatfs_mkfs(size_t n_args, const mp_obj_t *args) {
|
|||||||
|
|
||||||
// create partition
|
// create partition
|
||||||
if (n_args > 0 && args[0] == mp_const_true) {
|
if (n_args > 0 && args[0] == mp_const_true) {
|
||||||
// for SD card backup: we need small partition and keep the rest unallocated
|
// for SD card backup: we make a small partition and keep the rest
|
||||||
make_partition(60); // TODO decide on the exact portion
|
// unallocated
|
||||||
|
// TODO: seems like not big enough for Windows (problem detected popup)
|
||||||
|
const int n_clusters = 0xFFF5 + 1 + 549; // MAX_FAT16 + 1 + overhead
|
||||||
|
make_partition(n_clusters);
|
||||||
} else {
|
} else {
|
||||||
// for other use (SD salt): make the partitio over the whole space.
|
// for other use (SD salt): make the partition over the whole space.
|
||||||
make_partition(100);
|
make_partition(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create FAT volume mapped to the created partition
|
// create FAT volume mapped to the created partition
|
||||||
MKFS_PARM params = {FM_FAT32, 0, 0, 0, 0};
|
MKFS_PARM params = {
|
||||||
|
.fmt = FM_FAT32, .n_fat = 0, .align = 0, .n_root = 0, .au_size = 0};
|
||||||
uint8_t working_buf[FF_MAX_SS] = {0};
|
uint8_t working_buf[FF_MAX_SS] = {0};
|
||||||
FRESULT res = f_mkfs("", ¶ms, working_buf, sizeof(working_buf));
|
FRESULT res = f_mkfs("", ¶ms, working_buf, sizeof(working_buf));
|
||||||
if (res != FR_OK) {
|
if (res != FR_OK) {
|
||||||
@ -582,6 +583,31 @@ STATIC mp_obj_t mod_trezorio_fatfs_mkfs(size_t n_args, const mp_obj_t *args) {
|
|||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorio_fatfs_mkfs_obj, 0, 1,
|
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorio_fatfs_mkfs_obj, 0, 1,
|
||||||
mod_trezorio_fatfs_mkfs);
|
mod_trezorio_fatfs_mkfs);
|
||||||
|
|
||||||
|
/// def get_capacity() -> int:
|
||||||
|
/// """
|
||||||
|
/// Get total filesystem size in bytes.
|
||||||
|
/// """
|
||||||
|
STATIC mp_obj_t mod_trezorio_fatfs_get_capacity() {
|
||||||
|
FATFS_ONLY_MOUNTED;
|
||||||
|
/* printf("csize: %d\n", fs_instance.csize); */
|
||||||
|
/* printf("fatent: %d\n", fs_instance.n_fatent); */
|
||||||
|
/* printf("free clusters: %d\n", fs_instance.free_clst); */
|
||||||
|
/* printf("volbase: %d\n", fs_instance.volbase); */
|
||||||
|
/* printf("fatbase: %d\n", fs_instance.fatbase); */
|
||||||
|
/* printf("dirbase: %d\n", fs_instance.dirbase); */
|
||||||
|
/* printf("database: %d\n", fs_instance.database); */
|
||||||
|
/* printf("winsect: %d\n", fs_instance.winsect); */
|
||||||
|
// total number of clusters in the filesystem
|
||||||
|
DWORD total_clusters = fs_instance.n_fatent - 2;
|
||||||
|
// size of each cluster in bytes
|
||||||
|
DWORD cluster_size = fs_instance.csize * SDCARD_BLOCK_SIZE;
|
||||||
|
DWORD total_size = total_clusters * cluster_size;
|
||||||
|
|
||||||
|
return mp_obj_new_int_from_uint(total_size);
|
||||||
|
}
|
||||||
|
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_fatfs_get_capacity_obj,
|
||||||
|
mod_trezorio_fatfs_get_capacity);
|
||||||
|
|
||||||
/// def setlabel(label: str) -> None:
|
/// def setlabel(label: str) -> None:
|
||||||
/// """
|
/// """
|
||||||
/// Set volume label
|
/// Set volume label
|
||||||
@ -621,6 +647,8 @@ STATIC const mp_rom_map_elem_t mod_trezorio_fatfs_globals_table[] = {
|
|||||||
{MP_ROM_QSTR(MP_QSTR_is_mounted),
|
{MP_ROM_QSTR(MP_QSTR_is_mounted),
|
||||||
MP_ROM_PTR(&mod_trezorio_fatfs_is_mounted_obj)},
|
MP_ROM_PTR(&mod_trezorio_fatfs_is_mounted_obj)},
|
||||||
{MP_ROM_QSTR(MP_QSTR_mkfs), MP_ROM_PTR(&mod_trezorio_fatfs_mkfs_obj)},
|
{MP_ROM_QSTR(MP_QSTR_mkfs), MP_ROM_PTR(&mod_trezorio_fatfs_mkfs_obj)},
|
||||||
|
{MP_ROM_QSTR(MP_QSTR_get_capacity),
|
||||||
|
MP_ROM_PTR(&mod_trezorio_fatfs_get_capacity_obj)},
|
||||||
{MP_ROM_QSTR(MP_QSTR_setlabel),
|
{MP_ROM_QSTR(MP_QSTR_setlabel),
|
||||||
MP_ROM_PTR(&mod_trezorio_fatfs_setlabel_obj)},
|
MP_ROM_PTR(&mod_trezorio_fatfs_setlabel_obj)},
|
||||||
|
|
||||||
|
@ -174,6 +174,13 @@ def mkfs(for_sd_backup: bool=False) -> None:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# extmod/modtrezorio/modtrezorio-fatfs.h
|
||||||
|
def get_capacity() -> int:
|
||||||
|
"""
|
||||||
|
Get total filesystem size in bytes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
# extmod/modtrezorio/modtrezorio-fatfs.h
|
# extmod/modtrezorio/modtrezorio-fatfs.h
|
||||||
def setlabel(label: str) -> None:
|
def setlabel(label: str) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -63,6 +63,8 @@ storage.resident_credentials
|
|||||||
import storage.resident_credentials
|
import storage.resident_credentials
|
||||||
storage.sd_salt
|
storage.sd_salt
|
||||||
import storage.sd_salt
|
import storage.sd_salt
|
||||||
|
storage.sd_seed_backup
|
||||||
|
import storage.sd_seed_backup
|
||||||
trezor
|
trezor
|
||||||
import trezor
|
import trezor
|
||||||
trezor.crypto
|
trezor.crypto
|
||||||
|
@ -86,7 +86,9 @@ async def confirm_retry_sd(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def ensure_sdcard(ensure_filesystem: bool = True) -> None:
|
async def ensure_sdcard(
|
||||||
|
ensure_filesystem: bool = True, for_sd_backup: bool = False
|
||||||
|
) -> None:
|
||||||
"""Ensure a SD card is ready for use.
|
"""Ensure a SD card is ready for use.
|
||||||
|
|
||||||
This function runs the UI flow needed to ask the user to insert a SD card if there
|
This function runs the UI flow needed to ask the user to insert a SD card if there
|
||||||
@ -95,6 +97,9 @@ async def ensure_sdcard(ensure_filesystem: bool = True) -> None:
|
|||||||
If `ensure_filesystem` is True (the default), it also tries to mount the SD card
|
If `ensure_filesystem` is True (the default), it also tries to mount the SD card
|
||||||
filesystem, and allows the user to format the card if a filesystem cannot be
|
filesystem, and allows the user to format the card if a filesystem cannot be
|
||||||
mounted.
|
mounted.
|
||||||
|
|
||||||
|
In addition, if 'for_sd_backup' is True (False by default), the card is formatted
|
||||||
|
for SD backup feature.
|
||||||
"""
|
"""
|
||||||
from trezor import sdcard
|
from trezor import sdcard
|
||||||
|
|
||||||
@ -120,9 +125,9 @@ async def ensure_sdcard(ensure_filesystem: bool = True) -> None:
|
|||||||
|
|
||||||
# Proceed to formatting. Failure is caught by the outside OSError handler
|
# Proceed to formatting. Failure is caught by the outside OSError handler
|
||||||
with sdcard.filesystem(mounted=False):
|
with sdcard.filesystem(mounted=False):
|
||||||
fatfs.mkfs()
|
fatfs.mkfs(for_sd_backup)
|
||||||
fatfs.mount()
|
fatfs.mount()
|
||||||
fatfs.setlabel("TREZOR")
|
fatfs.setlabel("BACKUP" if for_sd_backup else "TREZOR")
|
||||||
|
|
||||||
# format and mount succeeded
|
# format and mount succeeded
|
||||||
return
|
return
|
||||||
|
@ -214,10 +214,9 @@ def _compute_secret_from_entropy(
|
|||||||
|
|
||||||
|
|
||||||
async def _backup_bip39_sdcard(mnemonic: str) -> None:
|
async def _backup_bip39_sdcard(mnemonic: str) -> None:
|
||||||
from apps.management.sd_backup import sdcard_backup_seed, sdcard_verify_backup
|
from apps.management.sd_backup import sdcard_backup_seed
|
||||||
await sdcard_backup_seed(mnemonic)
|
backup_success: bool = await sdcard_backup_seed(mnemonic)
|
||||||
backup_succes = sdcard_verify_backup(mnemonic)
|
if not backup_success:
|
||||||
if not backup_succes:
|
|
||||||
raise ProcessError("SD Card backup could not be verified.")
|
raise ProcessError("SD Card backup could not be verified.")
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,45 +1,21 @@
|
|||||||
from trezor import io, utils
|
from trezor import io, utils
|
||||||
from trezor.sdcard import with_filesystem
|
|
||||||
|
from storage.sd_seed_backup import store_seed_on_sdcard, recover_seed_from_sdcard
|
||||||
|
|
||||||
if utils.USE_SD_CARD:
|
if utils.USE_SD_CARD:
|
||||||
fatfs = io.fatfs # global_import_cache
|
fatfs = io.fatfs # global_import_cache
|
||||||
|
|
||||||
|
|
||||||
async def sdcard_backup_seed(mnemonic_secret: bytes) -> None:
|
async def sdcard_backup_seed(mnemonic_secret: bytes) -> bool:
|
||||||
from apps.common.sdcard import ensure_sdcard
|
from apps.common.sdcard import ensure_sdcard
|
||||||
|
|
||||||
await ensure_sdcard()
|
await ensure_sdcard(ensure_filesystem=True, for_sd_backup=True)
|
||||||
_write_seed_plain_text(mnemonic_secret)
|
return store_seed_on_sdcard(mnemonic_secret)
|
||||||
|
|
||||||
|
|
||||||
async def sdcard_recover_seed() -> str | None:
|
async def sdcard_recover_seed() -> str | None:
|
||||||
from apps.common.sdcard import ensure_sdcard
|
from apps.common.sdcard import ensure_sdcard
|
||||||
|
|
||||||
await ensure_sdcard(ensure_filesystem=False)
|
await ensure_sdcard(ensure_filesystem=False)
|
||||||
return _read_seed_plain_text()
|
seed_read = recover_seed_from_sdcard()
|
||||||
|
return seed_read
|
||||||
|
|
||||||
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')
|
|
||||||
|
174
core/src/storage/sd_seed_backup.py
Normal file
174
core/src/storage/sd_seed_backup.py
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
from micropython import const
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
import storage.device
|
||||||
|
from trezor import io, utils
|
||||||
|
from trezor.sdcard import with_filesystem
|
||||||
|
from trezorcrypto import crc
|
||||||
|
|
||||||
|
if utils.USE_SD_CARD:
|
||||||
|
fatfs = io.fatfs # global_import_cache
|
||||||
|
sdcard = io.sdcard # global_import_cache
|
||||||
|
SDCARD_BLOCK_SIZE_B = sdcard.BLOCK_SIZE # global_import_cache
|
||||||
|
SDBACKUP_BLOCK_START = 66_138
|
||||||
|
SDBACKUP_BLOCK_OFFSET = 130 # TODO arbitrary for now
|
||||||
|
SDBACKUP_MAGIC = b"TRZS"
|
||||||
|
SDBACKUP_VERSION = b"00"
|
||||||
|
|
||||||
|
# TODO with_filesystem can be just with_sdcard, unnecessary to mount FS for recovery
|
||||||
|
|
||||||
|
|
||||||
|
@with_filesystem
|
||||||
|
def store_seed_on_sdcard(mnemonic_secret: bytes) -> bool:
|
||||||
|
_write_seed_unalloc(mnemonic_secret)
|
||||||
|
_write_seed_plain_text(mnemonic_secret)
|
||||||
|
if _verify_backup(mnemonic_secret):
|
||||||
|
_write_readme()
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@with_filesystem
|
||||||
|
def recover_seed_from_sdcard() -> str | None:
|
||||||
|
return _read_seed_unalloc()
|
||||||
|
|
||||||
|
|
||||||
|
@with_filesystem
|
||||||
|
def _verify_backup(mnemonic_secret: bytes) -> bool:
|
||||||
|
mnemonic_read_plain = _read_seed_plain_text()
|
||||||
|
mnemonic_read_unalloc = _read_seed_unalloc()
|
||||||
|
if mnemonic_read_plain is None:
|
||||||
|
return False
|
||||||
|
if mnemonic_read_unalloc is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return (
|
||||||
|
mnemonic_read_plain.encode() == mnemonic_secret
|
||||||
|
and mnemonic_read_unalloc.encode() == mnemonic_secret
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_filesystem
|
||||||
|
def _write_seed_unalloc(mnemonic_secret: bytes) -> None:
|
||||||
|
block_to_write = _encode_mnemonic_to_backup_block(mnemonic_secret)
|
||||||
|
for block_idx in _storage_blocks_gen():
|
||||||
|
# print(f"block_idx: {block_idx}, writing: {block_to_write[10:10+4]}")
|
||||||
|
sdcard.write(block_idx, block_to_write)
|
||||||
|
|
||||||
|
|
||||||
|
@with_filesystem
|
||||||
|
def _read_seed_unalloc() -> str | None:
|
||||||
|
block_buffer = bytearray(SDCARD_BLOCK_SIZE_B)
|
||||||
|
for block_idx in _storage_blocks_gen():
|
||||||
|
try:
|
||||||
|
sdcard.read(block_idx, block_buffer)
|
||||||
|
mnemonic_read = _decode_mnemonic_from_backup_block(block_buffer)
|
||||||
|
if mnemonic_read is not None:
|
||||||
|
break
|
||||||
|
except fatfs.FatFSError:
|
||||||
|
return None
|
||||||
|
# print(f"_read_seed_unalloc: block_read: {block_read}")
|
||||||
|
mnemonic_read_decoded = mnemonic_read.decode("utf-8").rstrip("\x00")
|
||||||
|
# print(f"_read_seed_unalloc: mnemonic_read_decoded: {mnemonic_read_decoded}")
|
||||||
|
return mnemonic_read_decoded
|
||||||
|
|
||||||
|
|
||||||
|
def _storage_blocks_gen() -> Generator:
|
||||||
|
cap = sdcard.capacity()
|
||||||
|
if cap == 0:
|
||||||
|
raise ProcessError
|
||||||
|
BLOCK_END = cap // SDCARD_BLOCK_SIZE_B - 1
|
||||||
|
return range(SDBACKUP_BLOCK_START, BLOCK_END, SDBACKUP_BLOCK_OFFSET)
|
||||||
|
|
||||||
|
"""
|
||||||
|
Backup Memory Block Layout:
|
||||||
|
+----------------------+----------------------+----------------------+-------------------------------+
|
||||||
|
| SDBACKUP_MAGIC (4B) | SDBACKUP_VERSION (2B)| SEED_LENGTH (4B) | MNEMONIC (variable length) |
|
||||||
|
+----------------------+----------------------+----------------------+-------------------------------+
|
||||||
|
| CHECKSUM (4B) | Padding (variable) |
|
||||||
|
+----------------------------------------------------------------------------+----------------------------+
|
||||||
|
|
||||||
|
- SDBACKUP_MAGIC: 4 bytes magic number identifying the backup block
|
||||||
|
- SDBACKUP_VERSION: 2 bytes representing the version of the backup format
|
||||||
|
- SEED_LENGTH: 4 bytes (big-endian) indicating the length of the mnemonic
|
||||||
|
- MNEMONIC: Variable length field containing the mnemonic
|
||||||
|
- CHECKSUM: 4 bytes CRC32 checksum of all previous fields
|
||||||
|
- Padding: Remaining bytes of the block (if any) are padding
|
||||||
|
|
||||||
|
The total size of the block is defined by SDCARD_BLOCK_SIZE_B.
|
||||||
|
"""
|
||||||
|
# Constants for offsets and lengths
|
||||||
|
MAGIC_OFFSET = 0
|
||||||
|
MAGIC_LENGTH = 4
|
||||||
|
VERSION_OFFSET = MAGIC_OFFSET + MAGIC_LENGTH
|
||||||
|
VERSION_LENGTH = 2
|
||||||
|
SEED_LEN_OFFSET = VERSION_OFFSET + VERSION_LENGTH
|
||||||
|
SEED_LEN_LENGTH = 4
|
||||||
|
MNEMONIC_OFFSET = SEED_LEN_OFFSET + SEED_LEN_LENGTH
|
||||||
|
CHECKSUM_LENGTH = 4
|
||||||
|
|
||||||
|
|
||||||
|
def _encode_mnemonic_to_backup_block(mnemonic: bytes) -> bytes:
|
||||||
|
ret = bytearray(SDCARD_BLOCK_SIZE_B)
|
||||||
|
magic = SDBACKUP_MAGIC + SDBACKUP_VERSION
|
||||||
|
seed_len = len(mnemonic)
|
||||||
|
ret[MAGIC_OFFSET : MAGIC_OFFSET + MAGIC_LENGTH] = magic
|
||||||
|
ret[SEED_LEN_OFFSET : SEED_LEN_OFFSET + SEED_LEN_LENGTH] = seed_len.to_bytes(
|
||||||
|
SEED_LEN_LENGTH, "big"
|
||||||
|
)
|
||||||
|
ret[MNEMONIC_OFFSET : MNEMONIC_OFFSET + seed_len] = mnemonic
|
||||||
|
checksum = crc.crc32(ret[: MNEMONIC_OFFSET + seed_len])
|
||||||
|
ret[
|
||||||
|
MNEMONIC_OFFSET + seed_len : MNEMONIC_OFFSET + seed_len + CHECKSUM_LENGTH
|
||||||
|
] = checksum.to_bytes(CHECKSUM_LENGTH, "big")
|
||||||
|
return bytes(ret)
|
||||||
|
|
||||||
|
|
||||||
|
def _decode_mnemonic_from_backup_block(block: bytes) -> bytes | None:
|
||||||
|
assert len(block) == SDCARD_BLOCK_SIZE_B
|
||||||
|
if len(block) != SDCARD_BLOCK_SIZE_B:
|
||||||
|
return None
|
||||||
|
if block[MAGIC_OFFSET : MAGIC_OFFSET + MAGIC_LENGTH] != SDBACKUP_MAGIC:
|
||||||
|
return None
|
||||||
|
seed_len = int.from_bytes(
|
||||||
|
block[SEED_LEN_OFFSET : SEED_LEN_OFFSET + SEED_LEN_LENGTH], "big"
|
||||||
|
)
|
||||||
|
checksum_expected = crc.crc32(block[: MNEMONIC_OFFSET + seed_len])
|
||||||
|
checksum_read = int.from_bytes(
|
||||||
|
block[
|
||||||
|
MNEMONIC_OFFSET + seed_len : MNEMONIC_OFFSET + seed_len + CHECKSUM_LENGTH
|
||||||
|
],
|
||||||
|
"big",
|
||||||
|
)
|
||||||
|
if checksum_expected == checksum_read:
|
||||||
|
return block[MNEMONIC_OFFSET : MNEMONIC_OFFSET + seed_len]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@with_filesystem
|
||||||
|
def _write_readme() -> None:
|
||||||
|
with fatfs.open("README.txt", "w") as f:
|
||||||
|
f.write("This is a Trezor backup SD card.")
|
||||||
|
|
||||||
|
|
||||||
|
@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(SDCARD_BLOCK_SIZE_B)
|
||||||
|
try:
|
||||||
|
with fatfs.open("/trezor/seed.txt", "r") as f:
|
||||||
|
f.read(mnemonic_read)
|
||||||
|
except fatfs.FatFSError:
|
||||||
|
return None
|
||||||
|
# print(f"_read_seed_plain_text: mnemonic_read: {mnemonic_read}")
|
||||||
|
return mnemonic_read.decode("utf-8").rstrip("\x00")
|
29
core/tests/test_storage.sd_seed_backup.py
Normal file
29
core/tests/test_storage.sd_seed_backup.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
from common import *
|
||||||
|
|
||||||
|
from trezorio import sdcard, fatfs
|
||||||
|
from storage.sd_seed_backup import *
|
||||||
|
|
||||||
|
|
||||||
|
class TestStorageSdSeedBackup(unittest.TestCase):
|
||||||
|
# TODO add more tests, also with partly damaged backup
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.mnemonic = (
|
||||||
|
"crane mesh that gain predict open dice defy lottery toddler coin upgrade"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_backup_and_restore(self):
|
||||||
|
# with self.assertRaises(fatfs.FatFSError):
|
||||||
|
# store_seed_on_sdcard(self.mnemonic.encode("utf-8"))
|
||||||
|
|
||||||
|
sdcard.power_on()
|
||||||
|
fatfs.mkfs(True)
|
||||||
|
success = store_seed_on_sdcard(self.mnemonic.encode("utf-8"))
|
||||||
|
self.assertTrue(success)
|
||||||
|
|
||||||
|
restored = recover_seed_from_sdcard()
|
||||||
|
self.assertEqual(self.mnemonic, restored)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
Loading…
Reference in New Issue
Block a user