1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-08-05 05:15:27 +00:00

core: wipe before reset and recovery; introduce 'intialized' field

This commit is contained in:
Tomas Susanka 2020-06-16 07:20:06 +00:00
parent 033d49aa22
commit a6acefbdf5
23 changed files with 99 additions and 79 deletions

View File

@ -65,12 +65,10 @@ def get_features() -> Features:
Capability.PassphraseEntry, Capability.PassphraseEntry,
] ]
f.sd_card_present = sdcard.is_present() f.sd_card_present = sdcard.is_present()
f.initialized = storage.device.is_initialized()
# private fields: # private fields:
if config.is_unlocked(): if config.is_unlocked():
# While this is technically not private, we can't reliably find the value while
# locked. Instead of sending always False, we choose to not send it.
f.initialized = storage.is_initialized()
f.needs_backup = storage.device.needs_backup() f.needs_backup = storage.device.needs_backup()
f.unfinished_backup = storage.device.unfinished_backup() f.unfinished_backup = storage.device.unfinished_backup()

View File

@ -1,5 +1,4 @@
import storage from storage import cache, device
from storage import cache
from trezor import wire from trezor import wire
from trezor.crypto import bip32 from trezor.crypto import bip32
@ -39,7 +38,7 @@ class Keychain:
@cache.stored_async(cache.APP_CARDANO_ROOT) @cache.stored_async(cache.APP_CARDANO_ROOT)
async def get_keychain(ctx: wire.Context) -> Keychain: async def get_keychain(ctx: wire.Context) -> Keychain:
if not storage.is_initialized(): if not device.is_initialized():
raise wire.NotInitialized("Device is not initialized") raise wire.NotInitialized("Device is not initialized")
passphrase = await get_passphrase(ctx) passphrase = await get_passphrase(ctx)

View File

@ -1,5 +1,4 @@
import storage from storage import cache, device
from storage import cache
from trezor import wire from trezor import wire
from trezor.crypto import bip32, hashlib, hmac from trezor.crypto import bip32, hashlib, hmac
@ -113,7 +112,7 @@ class Keychain:
@cache.stored_async(cache.APP_COMMON_SEED) @cache.stored_async(cache.APP_COMMON_SEED)
async def _get_seed(ctx: wire.Context) -> bytes: async def _get_seed(ctx: wire.Context) -> bytes:
if not storage.is_initialized(): if not device.is_initialized():
raise wire.NotInitialized("Device is not initialized") raise wire.NotInitialized("Device is not initialized")
passphrase = await get_passphrase(ctx) passphrase = await get_passphrase(ctx)
return mnemonic.get_seed(passphrase) return mnemonic.get_seed(passphrase)
@ -121,7 +120,7 @@ async def _get_seed(ctx: wire.Context) -> bytes:
@cache.stored(cache.APP_COMMON_SEED_WITHOUT_PASSPHRASE) @cache.stored(cache.APP_COMMON_SEED_WITHOUT_PASSPHRASE)
def _get_seed_without_passphrase() -> bytes: def _get_seed_without_passphrase() -> bytes:
if not storage.is_initialized(): if not device.is_initialized():
raise Exception("Device is not initialized") raise Exception("Device is not initialized")
return mnemonic.get_seed(progress_bar=False) return mnemonic.get_seed(progress_bar=False)

View File

@ -54,7 +54,7 @@ async def load_device(ctx, msg):
def _validate(msg) -> int: def _validate(msg) -> int:
if storage.is_initialized(): if storage.device.is_initialized():
raise wire.UnexpectedMessage("Already initialized") raise wire.UnexpectedMessage("Already initialized")
if not msg.mnemonics: if not msg.mnemonics:

View File

@ -12,18 +12,18 @@ async def homescreen() -> None:
class Homescreen(HomescreenBase): class Homescreen(HomescreenBase):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
if not storage.is_initialized(): if not storage.device.is_initialized():
self.label = "Go to trezor.io/start" self.label = "Go to trezor.io/start"
def on_render(self) -> None: def on_render(self) -> None:
# warning bar on top # warning bar on top
if storage.is_initialized() and storage.device.no_backup(): if storage.device.is_initialized() and storage.device.no_backup():
ui.header_error("SEEDLESS") ui.header_error("SEEDLESS")
elif storage.is_initialized() and storage.device.unfinished_backup(): elif storage.device.is_initialized() and storage.device.unfinished_backup():
ui.header_error("BACKUP FAILED!") ui.header_error("BACKUP FAILED!")
elif storage.is_initialized() and storage.device.needs_backup(): elif storage.device.is_initialized() and storage.device.needs_backup():
ui.header_warning("NEEDS BACKUP!") ui.header_warning("NEEDS BACKUP!")
elif storage.is_initialized() and not config.has_pin(): elif storage.device.is_initialized() and not config.has_pin():
ui.header_warning("PIN NOT SET!") ui.header_warning("PIN NOT SET!")
else: else:
ui.display.bar(0, 0, ui.WIDTH, ui.HEIGHT, ui.BG) ui.display.bar(0, 0, ui.WIDTH, ui.HEIGHT, ui.BG)

View File

@ -1,12 +1,11 @@
import storage.device import storage.device
from storage.device import set_flags from storage.device import set_flags
from trezor import wire
from trezor.messages.Success import Success from trezor.messages.Success import Success
import wire
async def apply_flags(ctx, msg): async def apply_flags(ctx, msg):
if not storage.is_initialized(): if not storage.device.is_initialized():
raise wire.NotInitialized("Device is not initialized") raise wire.NotInitialized("Device is not initialized")
set_flags(msg.flags) set_flags(msg.flags)
return Success(message="Flags applied") return Success(message="Flags applied")

View File

@ -12,7 +12,7 @@ if False:
async def apply_settings(ctx: wire.Context, msg: ApplySettings): async def apply_settings(ctx: wire.Context, msg: ApplySettings):
if not storage.is_initialized(): if not storage.device.is_initialized():
raise wire.NotInitialized("Device is not initialized") raise wire.NotInitialized("Device is not initialized")
if ( if (
msg.homescreen is None msg.homescreen is None

View File

@ -8,7 +8,7 @@ from apps.management.reset_device import backup_seed, layout
async def backup_device(ctx, msg): async def backup_device(ctx, msg):
if not storage.is_initialized(): if not storage.device.is_initialized():
raise wire.NotInitialized("Device is not initialized") raise wire.NotInitialized("Device is not initialized")
if not storage.device.needs_backup(): if not storage.device.needs_backup():
raise wire.ProcessError("Seed already backed up") raise wire.ProcessError("Seed already backed up")

View File

@ -1,4 +1,4 @@
from storage import is_initialized from storage.device import is_initialized
from trezor import config, ui, wire from trezor import config, ui, wire
from trezor.messages.Success import Success from trezor.messages.Success import Success
from trezor.pin import pin_to_int from trezor.pin import pin_to_int

View File

@ -1,4 +1,4 @@
from storage import is_initialized from storage.device import is_initialized
from trezor import config, ui, wire from trezor import config, ui, wire
from trezor.messages.Success import Success from trezor.messages.Success import Success
from trezor.pin import pin_to_int from trezor.pin import pin_to_int

View File

@ -11,7 +11,7 @@ from apps.common.confirm import require_confirm
async def get_next_u2f_counter( async def get_next_u2f_counter(
ctx: wire.Context, msg: GetNextU2FCounter ctx: wire.Context, msg: GetNextU2FCounter
) -> NextU2FCounter: ) -> NextU2FCounter:
if not storage.is_initialized(): if not storage.device.is_initialized():
raise wire.NotInitialized("Device is not initialized") raise wire.NotInitialized("Device is not initialized")
text = Text("Get next U2F counter", ui.ICON_CONFIG) text = Text("Get next U2F counter", ui.ICON_CONFIG)
text.normal("Do you really want to") text.normal("Do you really want to")

View File

@ -42,6 +42,10 @@ async def recovery_device(ctx: wire.Context, msg: RecoveryDevice) -> Success:
await _continue_dialog(ctx, msg) await _continue_dialog(ctx, msg)
if not msg.dry_run:
# wipe storage to make sure the device is in a clear state
storage.reset()
# for dry run pin needs to be entered # for dry run pin needs to be entered
if msg.dry_run: if msg.dry_run:
curpin, salt = await request_pin_and_sd_salt(ctx, "Enter PIN") curpin, salt = await request_pin_and_sd_salt(ctx, "Enter PIN")
@ -68,9 +72,9 @@ async def recovery_device(ctx: wire.Context, msg: RecoveryDevice) -> Success:
def _validate(msg: RecoveryDevice) -> None: def _validate(msg: RecoveryDevice) -> None:
if not msg.dry_run and storage.is_initialized(): if not msg.dry_run and storage.device.is_initialized():
raise wire.UnexpectedMessage("Already initialized") raise wire.UnexpectedMessage("Already initialized")
if msg.dry_run and not storage.is_initialized(): if msg.dry_run and not storage.device.is_initialized():
raise wire.NotInitialized("Device is not initialized") raise wire.NotInitialized("Device is not initialized")
if msg.enforce_wordlist is False: if msg.enforce_wordlist is False:

View File

@ -28,6 +28,9 @@ async def reset_device(ctx: wire.Context, msg: ResetDevice) -> Success:
# make sure user knows they're setting up a new wallet # make sure user knows they're setting up a new wallet
await layout.show_reset_device_warning(ctx, msg.backup_type) await layout.show_reset_device_warning(ctx, msg.backup_type)
# wipe storage to make sure the device is in a clear state
storage.reset()
# request and set new PIN # request and set new PIN
if msg.pin_protection: if msg.pin_protection:
newpin = await request_pin_confirm(ctx) newpin = await request_pin_confirm(ctx)
@ -164,7 +167,7 @@ def _validate_reset_device(msg: ResetDevice) -> None:
raise wire.ProcessError("Invalid strength (has to be 128, 192 or 256 bits)") raise wire.ProcessError("Invalid strength (has to be 128, 192 or 256 bits)")
if msg.display_random and (msg.skip_backup or msg.no_backup): if msg.display_random and (msg.skip_backup or msg.no_backup):
raise wire.ProcessError("Can't show internal entropy when backup is skipped") raise wire.ProcessError("Can't show internal entropy when backup is skipped")
if storage.is_initialized(): if storage.device.is_initialized():
raise wire.UnexpectedMessage("Already initialized") raise wire.UnexpectedMessage("Already initialized")

View File

@ -41,7 +41,7 @@ async def _set_salt(
async def sd_protect(ctx: wire.Context, msg: SdProtect) -> Success: async def sd_protect(ctx: wire.Context, msg: SdProtect) -> Success:
if not storage.is_initialized(): if not storage.device.is_initialized():
raise wire.NotInitialized("Device is not initialized") raise wire.NotInitialized("Device is not initialized")
if msg.operation == SdProtectOperationType.ENABLE: if msg.operation == SdProtectOperationType.ENABLE:

View File

@ -9,7 +9,7 @@ from apps.common.confirm import require_confirm
async def set_u2f_counter(ctx: wire.Context, msg: SetU2FCounter) -> Success: async def set_u2f_counter(ctx: wire.Context, msg: SetU2FCounter) -> Success:
if not storage.is_initialized(): if not storage.device.is_initialized():
raise wire.NotInitialized("Device is not initialized") raise wire.NotInitialized("Device is not initialized")
if msg.u2f_counter is None: if msg.u2f_counter is None:
raise wire.ProcessError("No value provided") raise wire.ProcessError("No value provided")

View File

@ -1,4 +1,4 @@
import storage import storage.device
from trezor import ui, wire from trezor import ui, wire
from trezor.messages.Success import Success from trezor.messages.Success import Success
from trezor.messages.WebAuthnAddResidentCredential import WebAuthnAddResidentCredential from trezor.messages.WebAuthnAddResidentCredential import WebAuthnAddResidentCredential
@ -31,7 +31,7 @@ class ConfirmAddCredential(ConfirmInfo):
async def add_resident_credential( async def add_resident_credential(
ctx: wire.Context, msg: WebAuthnAddResidentCredential ctx: wire.Context, msg: WebAuthnAddResidentCredential
) -> Success: ) -> Success:
if not storage.is_initialized(): if not storage.device.is_initialized():
raise wire.NotInitialized("Device is not initialized") raise wire.NotInitialized("Device is not initialized")
if not msg.credential_id: if not msg.credential_id:
raise wire.ProcessError("Missing credential ID parameter.") raise wire.ProcessError("Missing credential ID parameter.")

View File

@ -1221,7 +1221,7 @@ def msg_register(req: Msg, dialog_mgr: DialogManager) -> Cmd:
dialog_mgr.set_state(new_state) dialog_mgr.set_state(new_state)
return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED) return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED)
if not storage.is_initialized(): if not storage.device.is_initialized():
if __debug__: if __debug__:
log.warning(__name__, "not initialized") log.warning(__name__, "not initialized")
# There is no standard way to decline a U2F request, but responding with ERR_CHANNEL_BUSY # There is no standard way to decline a U2F request, but responding with ERR_CHANNEL_BUSY
@ -1302,7 +1302,7 @@ def msg_authenticate(req: Msg, dialog_mgr: DialogManager) -> Cmd:
dialog_mgr.set_state(new_state) dialog_mgr.set_state(new_state)
return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED) return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED)
if not storage.is_initialized(): if not storage.device.is_initialized():
if __debug__: if __debug__:
log.warning(__name__, "not initialized") log.warning(__name__, "not initialized")
# Device is not registered with the RP. # Device is not registered with the RP.
@ -1483,7 +1483,7 @@ def cbor_make_credential_process(
) -> Union[State, Cmd]: ) -> Union[State, Cmd]:
from apps.webauthn import knownapps from apps.webauthn import knownapps
if not storage.is_initialized(): if not storage.device.is_initialized():
if __debug__: if __debug__:
log.warning(__name__, "not initialized") log.warning(__name__, "not initialized")
return cbor_error(req.cid, _ERR_OTHER) return cbor_error(req.cid, _ERR_OTHER)
@ -1663,7 +1663,7 @@ def cbor_get_assertion(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]:
def cbor_get_assertion_process( def cbor_get_assertion_process(
req: Cmd, dialog_mgr: DialogManager req: Cmd, dialog_mgr: DialogManager
) -> Union[State, Cmd]: ) -> Union[State, Cmd]:
if not storage.is_initialized(): if not storage.device.is_initialized():
if __debug__: if __debug__:
log.warning(__name__, "not initialized") log.warning(__name__, "not initialized")
return cbor_error(req.cid, _ERR_OTHER) return cbor_error(req.cid, _ERR_OTHER)
@ -1924,7 +1924,7 @@ def cbor_client_pin(req: Cmd) -> Cmd:
def cbor_reset(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]: def cbor_reset(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]:
if not storage.is_initialized(): if not storage.device.is_initialized():
if __debug__: if __debug__:
log.warning(__name__, "not initialized") log.warning(__name__, "not initialized")
# Return success, because the authenticator is already in factory default state. # Return success, because the authenticator is already in factory default state.

View File

@ -1,3 +1,4 @@
import storage.device
import storage.resident_credentials import storage.resident_credentials
from trezor import wire from trezor import wire
from trezor.messages.Success import Success from trezor.messages.Success import Success
@ -32,7 +33,7 @@ class ConfirmRemoveCredential(ConfirmInfo):
async def remove_resident_credential( async def remove_resident_credential(
ctx: wire.Context, msg: WebAuthnRemoveResidentCredential ctx: wire.Context, msg: WebAuthnRemoveResidentCredential
) -> Success: ) -> Success:
if not storage.is_initialized(): if not storage.device.is_initialized():
raise wire.NotInitialized("Device is not initialized") raise wire.NotInitialized("Device is not initialized")
if msg.index is None: if msg.index is None:
raise wire.ProcessError("Missing credential index parameter.") raise wire.ProcessError("Missing credential index parameter.")

View File

@ -6,10 +6,6 @@ def set_current_version() -> None:
device.set_version(common.STORAGE_VERSION_CURRENT) device.set_version(common.STORAGE_VERSION_CURRENT)
def is_initialized() -> bool:
return device.is_version_stored()
def wipe() -> None: def wipe() -> None:
config.wipe() config.wipe()
cache.clear_all() cache.clear_all()
@ -21,6 +17,20 @@ def init_unlocked() -> None:
if version == common.STORAGE_VERSION_01: if version == common.STORAGE_VERSION_01:
_migrate_from_version_01() _migrate_from_version_01()
# In FWs <= 2.3.1 'version' denoted whether the device is initialized or not.
# In 2.3.2 we have introduced a new field 'initialized' for that.
if device.is_version_stored() and not device.is_initialized():
common.set_bool(common.APP_DEVICE, device.INITIALIZED, True, public=True)
def reset() -> None:
"""
Wipes storage but keeps the device id unchanged.
"""
device_id = device.get_device_id()
wipe()
common.set(common.APP_DEVICE, device.DEVICE_ID, device_id.encode(), public=True)
def _migrate_from_version_01() -> None: def _migrate_from_version_01() -> None:
# Make the U2F counter public and writable even when storage is locked. # Make the U2F counter public and writable even when storage is locked.

View File

@ -14,7 +14,7 @@ _NAMESPACE = common.APP_DEVICE
# fmt: off # fmt: off
# Keys: # Keys:
_DEVICE_ID = const(0x00) # bytes DEVICE_ID = const(0x00) # bytes
_VERSION = const(0x01) # int _VERSION = const(0x01) # int
_MNEMONIC_SECRET = const(0x02) # bytes _MNEMONIC_SECRET = const(0x02) # bytes
_LANGUAGE = const(0x03) # str _LANGUAGE = const(0x03) # str
@ -33,6 +33,7 @@ _ROTATION = const(0x0F) # int
_SLIP39_IDENTIFIER = const(0x10) # bool _SLIP39_IDENTIFIER = const(0x10) # bool
_SLIP39_ITERATION_EXPONENT = const(0x11) # int _SLIP39_ITERATION_EXPONENT = const(0x11) # int
_SD_SALT_AUTH_KEY = const(0x12) # bytes _SD_SALT_AUTH_KEY = const(0x12) # bytes
INITIALIZED = const(0x13) # bool (0x01 or empty)
_DEFAULT_BACKUP_TYPE = BackupType.Bip39 _DEFAULT_BACKUP_TYPE = BackupType.Bip39
# fmt: on # fmt: on
@ -60,15 +61,19 @@ def set_version(version: bytes) -> None:
common.set(_NAMESPACE, _VERSION, version) common.set(_NAMESPACE, _VERSION, version)
def is_initialized() -> bool:
return common.get_bool(_NAMESPACE, INITIALIZED, public=True)
def _new_device_id() -> str: def _new_device_id() -> str:
return hexlify(random.bytes(12)).decode().upper() return hexlify(random.bytes(12)).decode().upper()
def get_device_id() -> str: def get_device_id() -> str:
dev_id = common.get(_NAMESPACE, _DEVICE_ID, True) # public dev_id = common.get(_NAMESPACE, DEVICE_ID, public=True)
if not dev_id: if not dev_id:
dev_id = _new_device_id().encode() dev_id = _new_device_id().encode()
common.set(_NAMESPACE, _DEVICE_ID, dev_id, True) # public common.set(_NAMESPACE, DEVICE_ID, dev_id, public=True)
return dev_id.decode() return dev_id.decode()
@ -123,6 +128,7 @@ def store_mnemonic_secret(
common.set(_NAMESPACE, _MNEMONIC_SECRET, secret) common.set(_NAMESPACE, _MNEMONIC_SECRET, secret)
common.set_uint8(_NAMESPACE, _BACKUP_TYPE, backup_type) common.set_uint8(_NAMESPACE, _BACKUP_TYPE, backup_type)
common.set_true_or_delete(_NAMESPACE, _NO_BACKUP, no_backup) common.set_true_or_delete(_NAMESPACE, _NO_BACKUP, no_backup)
common.set_bool(_NAMESPACE, INITIALIZED, True, public=True)
if not no_backup: if not no_backup:
common.set_true_or_delete(_NAMESPACE, _NEEDS_BACKUP, needs_backup) common.set_true_or_delete(_NAMESPACE, _NEEDS_BACKUP, needs_backup)

View File

@ -17,7 +17,7 @@ salt = None
config.unlock(1, salt) config.unlock(1, salt)
storage.init_unlocked() storage.init_unlocked()
storage.cache.start_session() storage.cache.start_session()
print("is_initialized: ", storage.is_initialized()) print("is_initialized: ", storage.device.is_initialized())
print("version: ", storage.device.is_version_stored()) print("version: ", storage.device.is_version_stored())
print("version: ", storage.device.get_version()) print("version: ", storage.device.get_version())
print("needs backup: ", storage.device.needs_backup()) print("needs backup: ", storage.device.needs_backup())

View File

@ -1,5 +1,6 @@
from common import * from common import *
import storage import storage
import storage.device
from apps.common import mnemonic from apps.common import mnemonic
from apps.webauthn.credential import Fido2Credential, U2fCredential, NAME_MAX_LENGTH from apps.webauthn.credential import Fido2Credential, U2fCredential, NAME_MAX_LENGTH
from apps.webauthn.fido2 import distinguishable_cred_list from apps.webauthn.fido2 import distinguishable_cred_list
@ -11,7 +12,7 @@ class TestCredential(unittest.TestCase):
def test_fido2_credential_decode(self): def test_fido2_credential_decode(self):
mnemonic_secret = b"all all all all all all all all all all all all" mnemonic_secret = b"all all all all all all all all all all all all"
mnemonic.get_secret = lambda: mnemonic_secret mnemonic.get_secret = lambda: mnemonic_secret
storage.is_initialized = lambda: True storage.device.is_initialized = lambda: True
cred_id = ( cred_id = (
b"f1d0020013e65c865634ad8abddf7a66df56ae7d8c3afd356f76426801508b2e" b"f1d0020013e65c865634ad8abddf7a66df56ae7d8c3afd356f76426801508b2e"

View File

@ -169,38 +169,38 @@
"test_msg_recoverydevice_bip39_dryrun.py::test_seed_mismatch": "85c61f5304a32e8b84a37ef80d035cfdcbf89a8631bde53409b1ec7f1013740c", "test_msg_recoverydevice_bip39_dryrun.py::test_seed_mismatch": "85c61f5304a32e8b84a37ef80d035cfdcbf89a8631bde53409b1ec7f1013740c",
"test_msg_recoverydevice_bip39_dryrun.py::test_uninitialized": "14fcdd2ded299ca099a35966cc9f21204b31de8d6bab9ec91cb64537bd70440c", "test_msg_recoverydevice_bip39_dryrun.py::test_uninitialized": "14fcdd2ded299ca099a35966cc9f21204b31de8d6bab9ec91cb64537bd70440c",
"test_msg_recoverydevice_bip39_t2.py::test_already_initialized": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586", "test_msg_recoverydevice_bip39_t2.py::test_already_initialized": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586",
"test_msg_recoverydevice_bip39_t2.py::test_tt_nopin_nopassphrase": "48e9e30277913b9c2a7a2bfcb51cadad20a888e31d08208e87ad1f5e90c0d807", "test_msg_recoverydevice_bip39_t2.py::test_tt_nopin_nopassphrase": "86e52bb95d0f53193cc83e828f6e6baea59ebcaa26e06784bbb4f6873ee442ac",
"test_msg_recoverydevice_bip39_t2.py::test_tt_pin_passphrase": "f0106d33287cd438298c8b56b08cfc8593a442b90e9414c59af6a1cff8efbd5a", "test_msg_recoverydevice_bip39_t2.py::test_tt_pin_passphrase": "7a7b9d20cc5b2d6fcdf0e35d90cfcd46bfe536067becdea5568fd7f3d102306f",
"test_msg_recoverydevice_slip39_advanced.py::test_abort": "793dde7fb47e9c4ad36369be396da20332560f29083d7f9a2b0582173371c9ed", "test_msg_recoverydevice_slip39_advanced.py::test_abort": "a54d4f29cf1fc3ce26831f52d0ae98a30a2f3e108f822cce08a9bfdd3319356e",
"test_msg_recoverydevice_slip39_advanced.py::test_extra_share_entered": "00a94e20b786346c45f987860b2465f299075d7c6de4971f42a4749e1cc8bfc0", "test_msg_recoverydevice_slip39_advanced.py::test_extra_share_entered": "c972403fc15f00527f12b3226bdb918a5c29315ba88e496982f09a4fdac43218",
"test_msg_recoverydevice_slip39_advanced.py::test_group_threshold_reached": "3b075a276c4e0d53fbc51ce1f29594bbd474d25f47c0f6a32caac41ba0ba2138", "test_msg_recoverydevice_slip39_advanced.py::test_group_threshold_reached": "137427360db303e288035972866df29ab0b272d30c8b11108bc68252f1aef748",
"test_msg_recoverydevice_slip39_advanced.py::test_noabort": "d2d17674ec4bbfdc922570148281903abfd57f0354f21eae21079253fb301bf8", "test_msg_recoverydevice_slip39_advanced.py::test_noabort": "78a8cc92a79f90b45c3e14f01c1c57ba0fbef63438c9abe9cd1feb35b0e03c0a",
"test_msg_recoverydevice_slip39_advanced.py::test_same_share": "de288365352fc0676b94df3e2c78b28893b1821c505b36f9c1a28dc117af2954", "test_msg_recoverydevice_slip39_advanced.py::test_same_share": "1ed63220ab59dd2feab4a42ffa565a9ff50980a72da022c35f6134869534c0fe",
"test_msg_recoverydevice_slip39_advanced.py::test_secret[shares0-c2d2e26ad06023c60145f150abe2dd2b]": "3a0803601280ca9eb72bccabc0328637ce4b7c20323ffcdde82341b09e170763", "test_msg_recoverydevice_slip39_advanced.py::test_secret[shares0-c2d2e26ad06023c60145f150abe2dd2b]": "c69e74416015afdeb589d257511c3a8a693c1f584717d948f93a3250d6713ef6",
"test_msg_recoverydevice_slip39_advanced.py::test_secret[shares1-c41d5cf80fed71a008a3a0ae0458ff0c6d62": "147fe87091f2b1a1e2cca6bd96432273a2ee98fd73bcd0f8503c578c1d101e5b", "test_msg_recoverydevice_slip39_advanced.py::test_secret[shares1-c41d5cf80fed71a008a3a0ae0458ff0c6d62": "9131fad9e499bb4cb3ee18c5535b89647d149746464334e0854052410b6a33d8",
"test_msg_recoverydevice_slip39_advanced_dryrun.py::test_2of3_dryrun": "fdf2733eac6e1cc6f5758cf599dc6a02e3000145cd83150f0727602d98744b8d", "test_msg_recoverydevice_slip39_advanced_dryrun.py::test_2of3_dryrun": "fdf2733eac6e1cc6f5758cf599dc6a02e3000145cd83150f0727602d98744b8d",
"test_msg_recoverydevice_slip39_advanced_dryrun.py::test_2of3_invalid_seed_dryrun": "950a00e2a14070cb9c78658dd13064cf860cd125d604df242cf8a22ce9cf7a5e", "test_msg_recoverydevice_slip39_advanced_dryrun.py::test_2of3_invalid_seed_dryrun": "950a00e2a14070cb9c78658dd13064cf860cd125d604df242cf8a22ce9cf7a5e",
"test_msg_recoverydevice_slip39_basic.py::test_1of1": "a72967338372da0829b521e384ffea4087a088cd0a461748bbe5581cb1894082", "test_msg_recoverydevice_slip39_basic.py::test_1of1": "de184147e0786f76c324019964ffebd0f170474d0e1a72b0aa120daa36c624d7",
"test_msg_recoverydevice_slip39_basic.py::test_abort": "793dde7fb47e9c4ad36369be396da20332560f29083d7f9a2b0582173371c9ed", "test_msg_recoverydevice_slip39_basic.py::test_abort": "a54d4f29cf1fc3ce26831f52d0ae98a30a2f3e108f822cce08a9bfdd3319356e",
"test_msg_recoverydevice_slip39_basic.py::test_ask_word_number": "8e9d9fd75e17f6b44829ae2d7b0eb9e60b48577f975abc6d75116f8365241082", "test_msg_recoverydevice_slip39_basic.py::test_ask_word_number": "01b6945fab5f321da8858b58e7ea9f2fb1e7391884545cb563d1a34aab0c3e7a",
"test_msg_recoverydevice_slip39_basic.py::test_noabort": "d374a9b85c03a0cc1bbb59130e454406513fc35f4f43b968db4920414de1bb72", "test_msg_recoverydevice_slip39_basic.py::test_noabort": "3db993abfb7e8d35e4a0acf1d8975d42fe51d1bee630639238f642b5c6c5f26d",
"test_msg_recoverydevice_slip39_basic.py::test_recover_with_pin_passphrase": "59de0866cceef43519b01ebec5f4575b21df13628d878479b21c9899234bb773", "test_msg_recoverydevice_slip39_basic.py::test_recover_with_pin_passphrase": "45330e1d06ad7b4fc5710c0cd44fdd40afd9bbb7ce1e1c291eadb0306536719a",
"test_msg_recoverydevice_slip39_basic.py::test_same_share": "e6a54429fdbedea9efca9cbed736aada07f95f3b20f895f9c1c5ec056a2be014", "test_msg_recoverydevice_slip39_basic.py::test_same_share": "3a5317f3bcf96931bb9b262f31fd3461d14560dce0a6c068e545283e9bf526a0",
"test_msg_recoverydevice_slip39_basic.py::test_secret[shares0-491b795b80fc21ccdf466c0fbc98c8fc]": "54581a91b55ab531b215cba61052fd77c505232c510f170080760605eb9b8c46", "test_msg_recoverydevice_slip39_basic.py::test_secret[shares0-491b795b80fc21ccdf466c0fbc98c8fc]": "c3cbc4aa0243f89d421de05ee02a941b44e0794ae1f9ca064d7ecea6b3dd4176",
"test_msg_recoverydevice_slip39_basic.py::test_secret[shares1-b770e0da1363247652de97a39bdbf2463be0878": "f73bef254762d761db27df46bff62641a9d2ac0602c34fc4e465262bf26ed08f", "test_msg_recoverydevice_slip39_basic.py::test_secret[shares1-b770e0da1363247652de97a39bdbf2463be0878": "c7151e24b74ddb70ce6d10459f5ba318e8a7947cbc8abecc90df97d5abdb7609",
"test_msg_recoverydevice_slip39_basic.py::test_wrong_nth_word[0]": "df88cf7855ebf39f1742b6e5c49a1c9e3394564903de1935e19555d22f818cfc", "test_msg_recoverydevice_slip39_basic.py::test_wrong_nth_word[0]": "3164a3744b29cdd345cbae18b8963a008e89c4d4bcebe98d2c320bf714c9c299",
"test_msg_recoverydevice_slip39_basic.py::test_wrong_nth_word[1]": "bd8f5062e0e48c8c29add65a6308c6ead84ae3fdce332506900ea52f8c941890", "test_msg_recoverydevice_slip39_basic.py::test_wrong_nth_word[1]": "b85543b48047ebb93b1b8c509d0596205d193bf99b3cd1c6650b24d97f6bd6d4",
"test_msg_recoverydevice_slip39_basic.py::test_wrong_nth_word[2]": "d8feb5ab8f9c5ca9bbe9830a68237d5b621bff8b6d1736ebd04ca678c050a08b", "test_msg_recoverydevice_slip39_basic.py::test_wrong_nth_word[2]": "6fff99c5997b08bc18d6f6dbfe67a141eda00a848168af5927b46eff48e46770",
"test_msg_recoverydevice_slip39_basic_dryrun.py::test_2of3_dryrun": "d84427489f691ecc222b62f83af3e97fa09097404dcba07772a43b5eb0c689e8", "test_msg_recoverydevice_slip39_basic_dryrun.py::test_2of3_dryrun": "d84427489f691ecc222b62f83af3e97fa09097404dcba07772a43b5eb0c689e8",
"test_msg_recoverydevice_slip39_basic_dryrun.py::test_2of3_invalid_seed_dryrun": "55f2dd6b4958659f071c3f57e06286f872ac38af4828f446a0f4e91c657dfccc", "test_msg_recoverydevice_slip39_basic_dryrun.py::test_2of3_invalid_seed_dryrun": "55f2dd6b4958659f071c3f57e06286f872ac38af4828f446a0f4e91c657dfccc",
"test_msg_resetdevice_bip39_t2.py-test_already_initialized": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586", "test_msg_resetdevice_bip39_t2.py-test_already_initialized": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586",
"test_msg_resetdevice_bip39_t2.py-test_failed_pin": "6b13f98de76ce3b75670e6e5fee7b11b4017165e25aa8d17e5c2f3fdca3846a3", "test_msg_resetdevice_bip39_t2.py-test_failed_pin": "ff7fe2e2d69a8e0dda7d9ec811ff0164aa5f85f9c56fe693932749b9be92c868",
"test_msg_resetdevice_bip39_t2.py-test_reset_device": "c5fc35eab68889c0b3315cad09afd573ce32c1e94c2cb942388638414145f631", "test_msg_resetdevice_bip39_t2.py-test_reset_device": "5f1b6cdc46e416430df1afd114bceda57fb644108d594ce1f466460ba4917b41",
"test_msg_resetdevice_bip39_t2.py-test_reset_device_192": "febbacc3370cf9219faa49bbc542a7aa1280d9fc3e6e9776dacbcd2f09231636", "test_msg_resetdevice_bip39_t2.py-test_reset_device_192": "d304d9902accbe23af2dcf73758a2e81b00da17e89f754ca83f209bac8eb8ee1",
"test_msg_resetdevice_bip39_t2.py-test_reset_device_pin": "cdc6e4d80a0988b367c4e37fb10e122f72507ba3cce75b0d8e4b3cbfeb4fd32a", "test_msg_resetdevice_bip39_t2.py-test_reset_device_pin": "1e4cef983f25b66931db5a8286c83f8faa3e09eb865896c7e946d2c8992e6d28",
"test_msg_resetdevice_slip39_advanced.py-test_reset_device_slip39_advanced": "77fae06d2427b6626debb71448e0b79c81afb99da10df9b9e987259eb045edf2", "test_msg_resetdevice_slip39_advanced.py-test_reset_device_slip39_advanced": "92dde100ab37934b7c39a9bfff15a4e73419b4f029c377f25f0b428c6e4a005c",
"test_msg_resetdevice_slip39_basic.py-test_reset_device_slip39_basic": "ad0d71e21112456fa99fdbd36563aa1ccbf49343a94ca1ed74068b2ec04ebd61", "test_msg_resetdevice_slip39_basic.py-test_reset_device_slip39_basic": "650ebacd885fe8c34237aceb835827472a845d8951f2ee573d94c97030603db3",
"test_msg_resetdevice_slip39_basic.py-test_reset_device_slip39_basic_256": "5f21f628ada58d9b519aec96f99a087df1098825de33421ddb36777dc4f578f1", "test_msg_resetdevice_slip39_basic.py-test_reset_device_slip39_basic_256": "aa7f19f34dafbd5bf205858cd8f694f3278b309a3c8837e6596759d6a1066634",
"test_msg_ripple_get_address.py-test_ripple_get_address": "2bb7d7bf48f1218530b4d7045d48480cad6411e110df537551b2f80b342007f2", "test_msg_ripple_get_address.py-test_ripple_get_address": "2bb7d7bf48f1218530b4d7045d48480cad6411e110df537551b2f80b342007f2",
"test_msg_ripple_get_address.py-test_ripple_get_address_other": "2bb7d7bf48f1218530b4d7045d48480cad6411e110df537551b2f80b342007f2", "test_msg_ripple_get_address.py-test_ripple_get_address_other": "2bb7d7bf48f1218530b4d7045d48480cad6411e110df537551b2f80b342007f2",
"test_msg_ripple_sign_tx.py-test_ripple_sign_invalid_fee": "1c0ca08b857da6121f43cfb1632c7f7e1d189ef1fdb665db7ba2cdfa7a59ea7c", "test_msg_ripple_sign_tx.py-test_ripple_sign_invalid_fee": "1c0ca08b857da6121f43cfb1632c7f7e1d189ef1fdb665db7ba2cdfa7a59ea7c",
@ -349,12 +349,12 @@
"test_passphrase_slip39_advanced.py::test_256bit_passphrase": "3a92115b6bfb2d53f2445a67c9c5df6b6b5ff97769de98e3fac9e1bf424c5669", "test_passphrase_slip39_advanced.py::test_256bit_passphrase": "3a92115b6bfb2d53f2445a67c9c5df6b6b5ff97769de98e3fac9e1bf424c5669",
"test_passphrase_slip39_basic.py::test_2of5_passphrase": "612dad8ab8762162a186ec9279d7de0bdfc589c52b4e4f4eba0545a00f21c3f0", "test_passphrase_slip39_basic.py::test_2of5_passphrase": "612dad8ab8762162a186ec9279d7de0bdfc589c52b4e4f4eba0545a00f21c3f0",
"test_passphrase_slip39_basic.py::test_3of6_passphrase": "612dad8ab8762162a186ec9279d7de0bdfc589c52b4e4f4eba0545a00f21c3f0", "test_passphrase_slip39_basic.py::test_3of6_passphrase": "612dad8ab8762162a186ec9279d7de0bdfc589c52b4e4f4eba0545a00f21c3f0",
"test_reset_backup.py::test_skip_backup_manual[0-backup_flow_bip39]": "80a614b20d172be640c28cdcee6ba7fc7dadd5329fa08e1ec8f6a55a80ceb2f9", "test_reset_backup.py::test_skip_backup_manual[0-backup_flow_bip39]": "9c5cded50e6ebe51dc6ecdaa6b793da9ec5527df582acbdc189494b809ee9f47",
"test_reset_backup.py::test_skip_backup_manual[1-backup_flow_slip39_basic]": "7695c4ffca95c263baa82ae17ce6faf9335bd07c6964beb900a75b61a515c245", "test_reset_backup.py::test_skip_backup_manual[1-backup_flow_slip39_basic]": "bf9ea5281234d622b39f388715dad86e95f0a0e1efbd3d4d6b60478a79edcc23",
"test_reset_backup.py::test_skip_backup_manual[2-backup_flow_slip39_advanced]": "e0a4139cfc344909deb2b6b2398d223ce4ddf0cdb2d9975d03454537d9be2152", "test_reset_backup.py::test_skip_backup_manual[2-backup_flow_slip39_advanced]": "e3fb56f53d04edde94aa11e5eac1e2dc732bddcd39def3981e03cffcbef1a96c",
"test_reset_backup.py::test_skip_backup_msg[0-backup_flow_bip39]": "30a8d39c13b89aa0bf2820bf780b8962829c9d8cd70b8c95fc98b6ef7b3ead9c", "test_reset_backup.py::test_skip_backup_msg[0-backup_flow_bip39]": "913ab51cbefc22938190a86865793e7e99420e9c99d6714ab94c8aba6edab3ea",
"test_reset_backup.py::test_skip_backup_msg[1-backup_flow_slip39_basic]": "e08feca49017cbe4a2ff22b7255fb9f8db659c79709affcf2954f2ade6924a57", "test_reset_backup.py::test_skip_backup_msg[1-backup_flow_slip39_basic]": "189a01dbe3f11061b13562d5773971ec44c0737695bfe1a4dc1c5dc1b3ffd8c0",
"test_reset_backup.py::test_skip_backup_msg[2-backup_flow_slip39_advanced]": "b980c758e8c0aacf6cbb047a9f1acaf3a1d1f3ab84a01ee9e52e5c466caf344c", "test_reset_backup.py::test_skip_backup_msg[2-backup_flow_slip39_advanced]": "cd6c1248d9ee4d6416c57026a96190a84ac8608af04fd42c9c8c6b7275226aba",
"test_sdcard.py::test_sd_format": "6bb7486932a5d38cdbb9b1368ee92aca3fad384115c744feadfade80c1605dd8", "test_sdcard.py::test_sd_format": "6bb7486932a5d38cdbb9b1368ee92aca3fad384115c744feadfade80c1605dd8",
"test_sdcard.py::test_sd_no_format": "f47e897caee95cf98c1b4506732825f853c4b8afcdc2713e38e3b4055973c9ac", "test_sdcard.py::test_sd_no_format": "f47e897caee95cf98c1b4506732825f853c4b8afcdc2713e38e3b4055973c9ac",
"test_sdcard.py::test_sd_protect_unlock": "9b98ad83499e38acaa9d73b0ef3261abde6e3b4b46194c32f323f28a79705077", "test_sdcard.py::test_sd_protect_unlock": "9b98ad83499e38acaa9d73b0ef3261abde6e3b4b46194c32f323f28a79705077",