feat(core/sdbackup): verify random seed blocks

pull/3441/head
obrusvit 9 months ago
parent 144a4e812d
commit 8415f51a7c

@ -19,6 +19,8 @@ if utils.USE_SD_CARD:
SDCARD_BLOCK_SIZE_B = sdcard.BLOCK_SIZE # global_import_cache SDCARD_BLOCK_SIZE_B = sdcard.BLOCK_SIZE # global_import_cache
SDBACKUP_BLOCK_START = sdcard.BACKUP_BLOCK_START # global_import_cache SDBACKUP_BLOCK_START = sdcard.BACKUP_BLOCK_START # global_import_cache
SDBACKUP_N_WRITINGS = 100 # TODO arbitrary for now SDBACKUP_N_WRITINGS = 100 # TODO arbitrary for now
SDBACKUP_N_VERIFY = 10
assert SDBACKUP_N_WRITINGS > SDBACKUP_N_VERIFY
SDBACKUP_MAGIC = b"TRZM" SDBACKUP_MAGIC = b"TRZM"
SDBACKUP_VERSION = 0 SDBACKUP_VERSION = 0
@ -29,7 +31,7 @@ class BackupMedium(IntEnum):
@with_filesystem @with_filesystem
def store_seed_on_sdcard(mnemonic_secret: bytes, backup_type: BackupType): def store_seed_on_sdcard(mnemonic_secret: bytes, backup_type: BackupType) -> None:
_write_seed_unalloc(mnemonic_secret, backup_type) _write_seed_unalloc(mnemonic_secret, backup_type)
if _verify_backup(mnemonic_secret, backup_type): if _verify_backup(mnemonic_secret, backup_type):
_write_readme() _write_readme()
@ -49,10 +51,19 @@ def is_backup_present() -> bool:
def _verify_backup(mnemonic_secret: bytes, backup_type: BackupType) -> bool: def _verify_backup(mnemonic_secret: bytes, backup_type: BackupType) -> bool:
decoded_mnemonic, decoded_backup_type = _read_seed_unalloc() from trezor.crypto import random
if decoded_mnemonic is None or decoded_backup_type is None:
return False block_buffer = bytearray(SDCARD_BLOCK_SIZE_B)
return decoded_mnemonic == mnemonic_secret and decoded_backup_type == backup_type all_backup_blocks = list(_storage_blocks_gen())
for _ in range(SDBACKUP_N_VERIFY):
block_idx = random.uniform(len(all_backup_blocks))
sdcard.read(all_backup_blocks[block_idx], block_buffer)
decoded_mnemonic, decoded_backup_type = _decode_backup_block(block_buffer)
if decoded_mnemonic is None or decoded_backup_type is None:
return False
if decoded_mnemonic != mnemonic_secret or decoded_backup_type != backup_type:
return False
return True
def _write_seed_unalloc(mnemonic_secret: bytes, backup_type: BackupType) -> None: def _write_seed_unalloc(mnemonic_secret: bytes, backup_type: BackupType) -> None:
@ -63,18 +74,15 @@ def _write_seed_unalloc(mnemonic_secret: bytes, backup_type: BackupType) -> None
def _read_seed_unalloc() -> tuple[bytes | None, BackupType | None]: def _read_seed_unalloc() -> tuple[bytes | None, BackupType | None]:
block_buffer = bytearray(SDCARD_BLOCK_SIZE_B) block_buffer = bytearray(SDCARD_BLOCK_SIZE_B)
restored_block = None (decoded_mnemonic, decoded_backup_type) = (None, None)
for block_idx in _storage_blocks_gen(): for block_idx in _storage_blocks_gen():
try: try:
sdcard.read(block_idx, block_buffer) sdcard.read(block_idx, block_buffer)
restored_block = _decode_backup_block(block_buffer) decoded_mnemonic, decoded_backup_type = _decode_backup_block(block_buffer)
if restored_block is not None: if (decoded_mnemonic, decoded_backup_type) != (None, None):
break break
except Exception: except Exception:
return (None, None) return (None, None)
if restored_block is None:
return (None, None)
decoded_mnemonic, decoded_backup_type = restored_block
return (decoded_mnemonic, decoded_backup_type) return (decoded_mnemonic, decoded_backup_type)
@ -130,14 +138,14 @@ def _encode_backup_block(mnemonic: bytes, backup_type: BackupType) -> bytes:
return bytes(ret) return bytes(ret)
def _decode_backup_block(block: bytes) -> tuple[bytes, BackupType] | None: def _decode_backup_block(block: bytes) -> tuple[bytes | None, BackupType | None]:
from trezor.enums import BackupType from trezor.enums import BackupType
assert len(block) == SDCARD_BLOCK_SIZE_B assert len(block) == SDCARD_BLOCK_SIZE_B
try: try:
r = utils.BufferReader(block) r = utils.BufferReader(block)
if r.read_memoryview(MAGIC_LEN) != SDBACKUP_MAGIC: if r.read_memoryview(MAGIC_LEN) != SDBACKUP_MAGIC:
return None return (None, None)
r.read_memoryview(VERSION_LEN) # skip the version for now r.read_memoryview(VERSION_LEN) # skip the version for now
backup_type = int.from_bytes(r.read_memoryview(BACKUPTYPE_LEN), "big") backup_type = int.from_bytes(r.read_memoryview(BACKUPTYPE_LEN), "big")
seed_len = int.from_bytes(r.read_memoryview(SEEDLEN_LEN), "big") seed_len = int.from_bytes(r.read_memoryview(SEEDLEN_LEN), "big")
@ -156,7 +164,7 @@ def _decode_backup_block(block: bytes) -> tuple[bytes, BackupType] | None:
): ):
return (mnemonic, backup_type) return (mnemonic, backup_type)
else: else:
return None return (None, None)
except (ValueError, EOFError): except (ValueError, EOFError):
raise DataError("Trying to decode invalid SD card block.") raise DataError("Trying to decode invalid SD card block.")

@ -2,7 +2,6 @@ import pytest
from trezorlib import device, messages from trezorlib import device, messages
from trezorlib.debuglink import TrezorClientDebugLink as Client from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.messages import BackupType
from ...common import MNEMONIC12 from ...common import MNEMONIC12
from ...input_flows import InputFlowBip39RecoverySdCard from ...input_flows import InputFlowBip39RecoverySdCard

@ -49,7 +49,6 @@ def reset(client: Client, strength: int = 128, skip_backup: bool = False) -> Non
assert client.features.passphrase_protection is False assert client.features.passphrase_protection is False
def recover(client: Client): def recover(client: Client):
with client: with client:
IF = InputFlowBip39RecoverySdCard(client) IF = InputFlowBip39RecoverySdCard(client)

@ -1578,7 +1578,9 @@ class InputFlowSlip39BasicRecovery(InputFlowBase):
class InputFlowSlip39BasicRecoverySdCard(InputFlowBase): class InputFlowSlip39BasicRecoverySdCard(InputFlowBase):
def __init__(self, client: Client, sdcard_numbers: list[int], pin: str | None = None): def __init__(
self, client: Client, sdcard_numbers: list[int], pin: str | None = None
):
super().__init__(client) super().__init__(client)
self.sdcard_numbers = sdcard_numbers self.sdcard_numbers = sdcard_numbers
self.pin = pin self.pin = pin

Loading…
Cancel
Save