Each namespace now has its own file in apps.common.storage and storage/__init__ serves as an entry point. Updates #274.pull/295/head
parent
14063ee494
commit
46e4c02602
@ -1,346 +0,0 @@
|
||||
from micropython import const
|
||||
from ubinascii import hexlify
|
||||
|
||||
from trezor import config
|
||||
from trezor.crypto import random, slip39
|
||||
|
||||
from apps.common import cache
|
||||
|
||||
HOMESCREEN_MAXSIZE = 16384
|
||||
|
||||
_STORAGE_VERSION = b"\x02"
|
||||
_FALSE_BYTE = b"\x00"
|
||||
_TRUE_BYTE = b"\x01"
|
||||
_COUNTER_HEAD_LEN = 4
|
||||
_COUNTER_TAIL_LEN = 8
|
||||
|
||||
# fmt: off
|
||||
_APP = const(0x01) # app namespace
|
||||
_DEVICE_ID = const(0x00) # bytes
|
||||
_VERSION = const(0x01) # int
|
||||
_MNEMONIC_SECRET = const(0x02) # bytes
|
||||
_LANGUAGE = const(0x03) # str
|
||||
_LABEL = const(0x04) # str
|
||||
_USE_PASSPHRASE = const(0x05) # bool (0x01 or empty)
|
||||
_HOMESCREEN = const(0x06) # bytes
|
||||
_NEEDS_BACKUP = const(0x07) # bool (0x01 or empty)
|
||||
_FLAGS = const(0x08) # int
|
||||
_U2F_COUNTER = const(0x09) # int
|
||||
_PASSPHRASE_SOURCE = const(0x0A) # int
|
||||
_UNFINISHED_BACKUP = const(0x0B) # bool (0x01 or empty)
|
||||
_AUTOLOCK_DELAY_MS = const(0x0C) # int
|
||||
_NO_BACKUP = const(0x0D) # bool (0x01 or empty)
|
||||
_MNEMONIC_TYPE = const(0x0E) # int
|
||||
_ROTATION = const(0x0F) # int
|
||||
|
||||
_SLIP39 = const(0x02) # SLIP-39 namespace
|
||||
_SLIP39_IN_PROGRESS = const(0x00) # bool
|
||||
_SLIP39_IDENTIFIER = const(0x01) # bytes
|
||||
_SLIP39_THRESHOLD = const(0x02) # int
|
||||
_SLIP39_REMAINING = const(0x03) # int
|
||||
_SLIP39_WORDS_COUNT = const(0x04) # int
|
||||
_SLIP39_ITERATION_EXPONENT = const(0x05) # int
|
||||
|
||||
# Mnemonics stored during SLIP-39 recovery process.
|
||||
# Each mnemonic is stored under key = index.
|
||||
_SLIP39_MNEMONICS = const(0x03) # SLIP-39 mnemonics namespace
|
||||
|
||||
# fmt: on
|
||||
|
||||
|
||||
def set_slip39_in_progress(val: bool):
|
||||
_set_bool(_SLIP39, _SLIP39_IN_PROGRESS, val)
|
||||
|
||||
|
||||
def is_slip39_in_progress():
|
||||
return _get_bool(_SLIP39, _SLIP39_IN_PROGRESS)
|
||||
|
||||
|
||||
def set_slip39_identifier(identifier: int):
|
||||
_set_uint16(_SLIP39, _SLIP39_IDENTIFIER, identifier)
|
||||
|
||||
|
||||
def get_slip39_identifier() -> int:
|
||||
return _get_uint16(_SLIP39, _SLIP39_IDENTIFIER)
|
||||
|
||||
|
||||
def set_slip39_threshold(threshold: int):
|
||||
_set_uint8(_SLIP39, _SLIP39_THRESHOLD, threshold)
|
||||
|
||||
|
||||
def get_slip39_threshold() -> int:
|
||||
return _get_uint8(_SLIP39, _SLIP39_THRESHOLD)
|
||||
|
||||
|
||||
def set_slip39_remaining(remaining: int):
|
||||
_set_uint8(_SLIP39, _SLIP39_REMAINING, remaining)
|
||||
|
||||
|
||||
def get_slip39_remaining() -> int:
|
||||
return _get_uint8(_SLIP39, _SLIP39_REMAINING)
|
||||
|
||||
|
||||
def set_slip39_words_count(count: int):
|
||||
_set_uint8(_SLIP39, _SLIP39_WORDS_COUNT, count)
|
||||
|
||||
|
||||
def get_slip39_words_count() -> int:
|
||||
return _get_uint8(_SLIP39, _SLIP39_WORDS_COUNT)
|
||||
|
||||
|
||||
def set_slip39_iteration_exponent(exponent: int):
|
||||
# TODO: check if not > 5 bits
|
||||
_set_uint8(_SLIP39, _SLIP39_ITERATION_EXPONENT, exponent)
|
||||
|
||||
|
||||
def get_slip39_iteration_exponent() -> int:
|
||||
return _get_uint8(_SLIP39, _SLIP39_ITERATION_EXPONENT)
|
||||
|
||||
|
||||
def set_slip39_mnemonic(index: int, mnemonic: str):
|
||||
config.set(_SLIP39_MNEMONICS, index, mnemonic.encode())
|
||||
|
||||
|
||||
def get_slip39_mnemonic(index: int) -> str:
|
||||
m = config.get(_SLIP39_MNEMONICS, index)
|
||||
if m:
|
||||
return m.decode()
|
||||
return False
|
||||
|
||||
|
||||
def get_slip39_mnemonics() -> list:
|
||||
mnemonics = []
|
||||
for index in range(0, slip39.MAX_SHARE_COUNT):
|
||||
m = get_slip39_mnemonic(index)
|
||||
if m:
|
||||
mnemonics.append(m)
|
||||
return mnemonics
|
||||
|
||||
|
||||
def clear_slip39_data():
|
||||
config.delete(_SLIP39, _SLIP39_IN_PROGRESS)
|
||||
config.delete(_SLIP39, _SLIP39_REMAINING)
|
||||
config.delete(_SLIP39, _SLIP39_THRESHOLD)
|
||||
config.delete(_SLIP39, _SLIP39_WORDS_COUNT)
|
||||
for index in (0, slip39.MAX_SHARE_COUNT):
|
||||
config.delete(_SLIP39_MNEMONICS, index)
|
||||
|
||||
|
||||
def _set_bool(app: int, key: int, value: bool, public: bool = False) -> None:
|
||||
if value:
|
||||
config.set(app, key, _TRUE_BYTE, public)
|
||||
else:
|
||||
config.set(app, key, _FALSE_BYTE, public)
|
||||
|
||||
|
||||
def _get_bool(app: int, key: int, public: bool = False) -> bool:
|
||||
return config.get(app, key, public) == _TRUE_BYTE
|
||||
|
||||
|
||||
def _set_uint8(app: int, key: int, val: int):
|
||||
config.set(app, key, val.to_bytes(1, "big"))
|
||||
|
||||
|
||||
def _get_uint8(app: int, key: int) -> int:
|
||||
val = config.get(app, key)
|
||||
if not val:
|
||||
return None
|
||||
return int.from_bytes(val, "big")
|
||||
|
||||
|
||||
def _set_uint16(app: int, key: int, val: int):
|
||||
config.set(app, key, val.to_bytes(2, "big"))
|
||||
|
||||
|
||||
def _get_uint16(app: int, key: int) -> int:
|
||||
val = config.get(app, key)
|
||||
if not val:
|
||||
return None
|
||||
return int.from_bytes(val, "big")
|
||||
|
||||
|
||||
def _new_device_id() -> str:
|
||||
return hexlify(random.bytes(12)).decode().upper()
|
||||
|
||||
|
||||
def get_device_id() -> str:
|
||||
dev_id = config.get(_APP, _DEVICE_ID, True) # public
|
||||
if not dev_id:
|
||||
dev_id = _new_device_id().encode()
|
||||
config.set(_APP, _DEVICE_ID, dev_id, True) # public
|
||||
return dev_id.decode()
|
||||
|
||||
|
||||
def get_rotation() -> int:
|
||||
rotation = config.get(_APP, _ROTATION, True) # public
|
||||
if not rotation:
|
||||
return 0
|
||||
return int.from_bytes(rotation, "big")
|
||||
|
||||
|
||||
def is_initialized() -> bool:
|
||||
return bool(config.get(_APP, _VERSION)) and not bool(
|
||||
config.get(_SLIP39, _SLIP39_IN_PROGRESS)
|
||||
)
|
||||
|
||||
|
||||
def get_label() -> str:
|
||||
label = config.get(_APP, _LABEL, True) # public
|
||||
if label is None:
|
||||
return None
|
||||
return label.decode()
|
||||
|
||||
|
||||
def get_mnemonic_secret() -> bytes:
|
||||
return config.get(_APP, _MNEMONIC_SECRET)
|
||||
|
||||
|
||||
def get_mnemonic_type() -> int:
|
||||
return _get_uint8(_APP, _MNEMONIC_TYPE)
|
||||
|
||||
|
||||
def has_passphrase() -> bool:
|
||||
return _get_bool(_APP, _USE_PASSPHRASE)
|
||||
|
||||
|
||||
def get_homescreen() -> bytes:
|
||||
return config.get(_APP, _HOMESCREEN, True) # public
|
||||
|
||||
|
||||
def store_mnemonic(
|
||||
secret: bytes,
|
||||
mnemonic_type: int,
|
||||
needs_backup: bool = False,
|
||||
no_backup: bool = False,
|
||||
) -> None:
|
||||
config.set(_APP, _MNEMONIC_SECRET, secret)
|
||||
_set_uint8(_APP, _MNEMONIC_TYPE, mnemonic_type)
|
||||
_init(needs_backup, no_backup)
|
||||
|
||||
|
||||
def _init(needs_backup=False, no_backup=False):
|
||||
config.set(_APP, _VERSION, _STORAGE_VERSION)
|
||||
_set_bool(_APP, _NO_BACKUP, no_backup)
|
||||
if not no_backup:
|
||||
_set_bool(_APP, _NEEDS_BACKUP, needs_backup)
|
||||
|
||||
|
||||
def needs_backup() -> bool:
|
||||
return _get_bool(_APP, _NEEDS_BACKUP)
|
||||
|
||||
|
||||
def set_backed_up() -> None:
|
||||
config.set(_APP, _NEEDS_BACKUP, b"")
|
||||
|
||||
|
||||
def unfinished_backup() -> bool:
|
||||
return _get_bool(_APP, _UNFINISHED_BACKUP)
|
||||
|
||||
|
||||
def set_unfinished_backup(state: bool) -> None:
|
||||
_set_bool(_APP, _UNFINISHED_BACKUP, state)
|
||||
|
||||
|
||||
def no_backup() -> bool:
|
||||
return _get_bool(_APP, _NO_BACKUP)
|
||||
|
||||
|
||||
def get_passphrase_source() -> int:
|
||||
b = config.get(_APP, _PASSPHRASE_SOURCE)
|
||||
if b == b"\x01":
|
||||
return 1
|
||||
elif b == b"\x02":
|
||||
return 2
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def load_settings(
|
||||
label: str = None,
|
||||
use_passphrase: bool = None,
|
||||
homescreen: bytes = None,
|
||||
passphrase_source: int = None,
|
||||
display_rotation: int = None,
|
||||
) -> None:
|
||||
if label is not None:
|
||||
config.set(_APP, _LABEL, label.encode(), True) # public
|
||||
if use_passphrase is not None:
|
||||
_set_bool(_APP, _USE_PASSPHRASE, use_passphrase)
|
||||
if homescreen is not None:
|
||||
if homescreen[:8] == b"TOIf\x90\x00\x90\x00":
|
||||
if len(homescreen) <= HOMESCREEN_MAXSIZE:
|
||||
config.set(_APP, _HOMESCREEN, homescreen, True) # public
|
||||
else:
|
||||
config.set(_APP, _HOMESCREEN, b"", True) # public
|
||||
if passphrase_source is not None:
|
||||
if passphrase_source in (0, 1, 2):
|
||||
config.set(_APP, _PASSPHRASE_SOURCE, bytes([passphrase_source]))
|
||||
if display_rotation is not None:
|
||||
if display_rotation not in (0, 90, 180, 270):
|
||||
raise ValueError(
|
||||
"Unsupported display rotation degrees: %d" % display_rotation
|
||||
)
|
||||
else:
|
||||
config.set(
|
||||
_APP, _ROTATION, display_rotation.to_bytes(2, "big"), True
|
||||
) # public
|
||||
|
||||
|
||||
def get_flags() -> int:
|
||||
b = config.get(_APP, _FLAGS)
|
||||
if b is None:
|
||||
return 0
|
||||
else:
|
||||
return int.from_bytes(b, "big")
|
||||
|
||||
|
||||
def set_flags(flags: int) -> None:
|
||||
b = config.get(_APP, _FLAGS)
|
||||
if b is None:
|
||||
b = 0
|
||||
else:
|
||||
b = int.from_bytes(b, "big")
|
||||
flags = (flags | b) & 0xFFFFFFFF
|
||||
if flags != b:
|
||||
config.set(_APP, _FLAGS, flags.to_bytes(4, "big"))
|
||||
|
||||
|
||||
def get_autolock_delay_ms() -> int:
|
||||
b = config.get(_APP, _AUTOLOCK_DELAY_MS)
|
||||
if b is None:
|
||||
return 10 * 60 * 1000
|
||||
else:
|
||||
return int.from_bytes(b, "big")
|
||||
|
||||
|
||||
def set_autolock_delay_ms(delay_ms: int) -> None:
|
||||
if delay_ms < 60 * 1000:
|
||||
delay_ms = 60 * 1000
|
||||
config.set(_APP, _AUTOLOCK_DELAY_MS, delay_ms.to_bytes(4, "big"))
|
||||
|
||||
|
||||
def next_u2f_counter() -> int:
|
||||
return config.next_counter(_APP, _U2F_COUNTER, True) # writable when locked
|
||||
|
||||
|
||||
def set_u2f_counter(cntr: int) -> None:
|
||||
config.set_counter(_APP, _U2F_COUNTER, cntr, True) # writable when locked
|
||||
|
||||
|
||||
def wipe():
|
||||
config.wipe()
|
||||
cache.clear()
|
||||
|
||||
|
||||
def init_unlocked():
|
||||
# Check for storage version upgrade.
|
||||
version = config.get(_APP, _VERSION)
|
||||
if version == b"\x01":
|
||||
# Make the U2F counter public and writable even when storage is locked.
|
||||
counter = config.get(_APP, _U2F_COUNTER)
|
||||
if counter is not None:
|
||||
config.set_counter(
|
||||
_APP, _U2F_COUNTER, int.from_bytes(counter, "big"), True
|
||||
) # writable when locked
|
||||
config.delete(_APP, _U2F_COUNTER)
|
||||
config.set(_APP, _VERSION, _STORAGE_VERSION)
|
@ -0,0 +1,35 @@
|
||||
from trezor import config
|
||||
|
||||
from apps.common import cache
|
||||
from apps.common.storage import common, device, slip39
|
||||
|
||||
|
||||
def set_current_version():
|
||||
device.set_version(common._STORAGE_VERSION_CURRENT)
|
||||
|
||||
|
||||
def is_initialized() -> bool:
|
||||
return device.is_version_stored() and not slip39.is_in_progress()
|
||||
|
||||
|
||||
def wipe():
|
||||
config.wipe()
|
||||
cache.clear()
|
||||
|
||||
|
||||
def init_unlocked():
|
||||
# Check for storage version upgrade.
|
||||
version = device.get_version()
|
||||
if version == common._STORAGE_VERSION_01:
|
||||
_migrate_from_version_01()
|
||||
|
||||
|
||||
def _migrate_from_version_01():
|
||||
# Make the U2F counter public and writable even when storage is locked.
|
||||
# U2F counter wasn't public, so we are intentionally not using storage.device module.
|
||||
counter = common._get(common._APP_DEVICE, device._U2F_COUNTER)
|
||||
if counter is not None:
|
||||
device.set_u2f_counter(int.from_bytes(counter, "big"))
|
||||
# Delete the old, non-public U2F_COUNTER.
|
||||
common._delete(common._APP_DEVICE, device._U2F_COUNTER)
|
||||
set_current_version()
|
@ -0,0 +1,75 @@
|
||||
from trezor import config
|
||||
|
||||
# Namespaces:
|
||||
# fmt: off
|
||||
# Intentionally not using const() to allow import in submodules.
|
||||
_APP_DEVICE = 0x01
|
||||
_APP_SLIP39 = 0x02
|
||||
_APP_SLIP39_MNEMONICS = 0x03
|
||||
# fmt: on
|
||||
|
||||
_FALSE_BYTE = b"\x00"
|
||||
_TRUE_BYTE = b"\x01"
|
||||
|
||||
_STORAGE_VERSION_01 = b"\x01"
|
||||
_STORAGE_VERSION_CURRENT = b"\x02"
|
||||
|
||||
|
||||
def _set(app: int, key: int, data: bytes, public: bool = False):
|
||||
config.set(app, key, data, public)
|
||||
|
||||
|
||||
def _get(app: int, key: int, public: bool = False):
|
||||
return config.get(app, key, public)
|
||||
|
||||
|
||||
def _delete(app: int, key: int):
|
||||
config.delete(app, key)
|
||||
|
||||
|
||||
def _set_true_or_delete(app: int, key: int, value: bool):
|
||||
if value:
|
||||
_set_bool(app, key, value)
|
||||
else:
|
||||
_delete(app, key)
|
||||
|
||||
|
||||
def _set_bool(app: int, key: int, value: bool, public: bool = False) -> None:
|
||||
if value:
|
||||
_set(app, key, _TRUE_BYTE, public)
|
||||
else:
|
||||
_set(app, key, _FALSE_BYTE, public)
|
||||
|
||||
|
||||
def _get_bool(app: int, key: int, public: bool = False) -> bool:
|
||||
return _get(app, key, public) == _TRUE_BYTE
|
||||
|
||||
|
||||
def _set_uint8(app: int, key: int, val: int):
|
||||
_set(app, key, val.to_bytes(1, "big"))
|
||||
|
||||
|
||||
def _get_uint8(app: int, key: int) -> int:
|
||||
val = _get(app, key)
|
||||
if not val:
|
||||
return None
|
||||
return int.from_bytes(val, "big")
|
||||
|
||||
|
||||
def _set_uint16(app: int, key: int, val: int):
|
||||
_set(app, key, val.to_bytes(2, "big"))
|
||||
|
||||
|
||||
def _get_uint16(app: int, key: int) -> int:
|
||||
val = _get(app, key)
|
||||
if not val:
|
||||
return None
|
||||
return int.from_bytes(val, "big")
|
||||
|
||||
|
||||
def _next_counter(app: int, key: int, public: bool = False):
|
||||
return config.next_counter(app, key, public)
|
||||
|
||||
|
||||
def _set_counter(app: int, key: int, count: int, public: bool = False) -> None:
|
||||
config.set_counter(app, key, count, public)
|
@ -0,0 +1,201 @@
|
||||
from micropython import const
|
||||
from ubinascii import hexlify
|
||||
|
||||
from trezor.crypto import random
|
||||
|
||||
from apps.common.storage import common
|
||||
|
||||
# Namespace:
|
||||
_NAMESPACE = common._APP_DEVICE
|
||||
|
||||
# fmt: off
|
||||
# Keys:
|
||||
_DEVICE_ID = const(0x00) # bytes
|
||||
_VERSION = const(0x01) # int
|
||||
_MNEMONIC_SECRET = const(0x02) # bytes
|
||||
_LANGUAGE = const(0x03) # str
|
||||
_LABEL = const(0x04) # str
|
||||
_USE_PASSPHRASE = const(0x05) # bool (0x01 or empty)
|
||||
_HOMESCREEN = const(0x06) # bytes
|
||||
_NEEDS_BACKUP = const(0x07) # bool (0x01 or empty)
|
||||
_FLAGS = const(0x08) # int
|
||||
_U2F_COUNTER = const(0x09) # int
|
||||
_PASSPHRASE_SOURCE = const(0x0A) # int
|
||||
_UNFINISHED_BACKUP = const(0x0B) # bool (0x01 or empty)
|
||||
_AUTOLOCK_DELAY_MS = const(0x0C) # int
|
||||
_NO_BACKUP = const(0x0D) # bool (0x01 or empty)
|
||||
_MNEMONIC_TYPE = const(0x0E) # int
|
||||
_ROTATION = const(0x0F) # int
|
||||
# fmt: on
|
||||
|
||||
HOMESCREEN_MAXSIZE = 16384
|
||||
|
||||
|
||||
def is_version_stored() -> bool:
|
||||
return bool(common._get(_NAMESPACE, _VERSION))
|
||||
|
||||
|
||||
def get_version() -> bool:
|
||||
return common._get(_NAMESPACE, _VERSION)
|
||||
|
||||
|
||||
def set_version(version: bytes) -> bool:
|
||||
return common._set(_NAMESPACE, _VERSION, version)
|
||||
|
||||
|
||||
def _new_device_id() -> str:
|
||||
return hexlify(random.bytes(12)).decode().upper()
|
||||
|
||||
|
||||
def get_device_id() -> str:
|
||||
dev_id = common._get(_NAMESPACE, _DEVICE_ID, True) # public
|
||||
if not dev_id:
|
||||
dev_id = _new_device_id().encode()
|
||||
common._set(_NAMESPACE, _DEVICE_ID, dev_id, True) # public
|
||||
return dev_id.decode()
|
||||
|
||||
|
||||
def get_rotation() -> int:
|
||||
rotation = common._get(_NAMESPACE, _ROTATION, True) # public
|
||||
if not rotation:
|
||||
return 0
|
||||
return int.from_bytes(rotation, "big")
|
||||
|
||||
|
||||
def get_label() -> str:
|
||||
label = common._get(_NAMESPACE, _LABEL, True) # public
|
||||
if label is None:
|
||||
return None
|
||||
return label.decode()
|
||||
|
||||
|
||||
def get_mnemonic_secret() -> bytes:
|
||||
return common._get(_NAMESPACE, _MNEMONIC_SECRET)
|
||||
|
||||
|
||||
def get_mnemonic_type() -> int:
|
||||
return common._get_uint8(_NAMESPACE, _MNEMONIC_TYPE)
|
||||
|
||||
|
||||
def has_passphrase() -> bool:
|
||||
return common._get_bool(_NAMESPACE, _USE_PASSPHRASE)
|
||||
|
||||
|
||||
def get_homescreen() -> bytes:
|
||||
return common._get(_NAMESPACE, _HOMESCREEN, True) # public
|
||||
|
||||
|
||||
def store_mnemonic_secret(
|
||||
secret: bytes,
|
||||
mnemonic_type: int,
|
||||
needs_backup: bool = False,
|
||||
no_backup: bool = False,
|
||||
) -> None:
|
||||
set_version(common._STORAGE_VERSION_CURRENT)
|
||||
common._set(_NAMESPACE, _MNEMONIC_SECRET, secret)
|
||||
common._set_uint8(_NAMESPACE, _MNEMONIC_TYPE, mnemonic_type)
|
||||
common._set_true_or_delete(_NAMESPACE, _NO_BACKUP, no_backup)
|
||||
if not no_backup:
|
||||
common._set_true_or_delete(_NAMESPACE, _NEEDS_BACKUP, needs_backup)
|
||||
|
||||
|
||||
def needs_backup() -> bool:
|
||||
return common._get_bool(_NAMESPACE, _NEEDS_BACKUP)
|
||||
|
||||
|
||||
def set_backed_up() -> None:
|
||||
common._delete(_NAMESPACE, _NEEDS_BACKUP)
|
||||
|
||||
|
||||
def unfinished_backup() -> bool:
|
||||
return common._get_bool(_NAMESPACE, _UNFINISHED_BACKUP)
|
||||
|
||||
|
||||
def set_unfinished_backup(state: bool) -> None:
|
||||
common._set_bool(_NAMESPACE, _UNFINISHED_BACKUP, state)
|
||||
|
||||
|
||||
def no_backup() -> bool:
|
||||
return common._get_bool(_NAMESPACE, _NO_BACKUP)
|
||||
|
||||
|
||||
def get_passphrase_source() -> int:
|
||||
b = common._get(_NAMESPACE, _PASSPHRASE_SOURCE)
|
||||
if b == b"\x01":
|
||||
return 1
|
||||
elif b == b"\x02":
|
||||
return 2
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def load_settings(
|
||||
label: str = None,
|
||||
use_passphrase: bool = None,
|
||||
homescreen: bytes = None,
|
||||
passphrase_source: int = None,
|
||||
display_rotation: int = None,
|
||||
) -> None:
|
||||
if label is not None:
|
||||
common._set(_NAMESPACE, _LABEL, label.encode(), True) # public
|
||||
if use_passphrase is not None:
|
||||
common._set_bool(_NAMESPACE, _USE_PASSPHRASE, use_passphrase)
|
||||
if homescreen is not None:
|
||||
if homescreen[:8] == b"TOIf\x90\x00\x90\x00":
|
||||
if len(homescreen) <= HOMESCREEN_MAXSIZE:
|
||||
common._set(_NAMESPACE, _HOMESCREEN, homescreen, True) # public
|
||||
else:
|
||||
common._set(_NAMESPACE, _HOMESCREEN, b"", True) # public
|
||||
if passphrase_source is not None:
|
||||
if passphrase_source in (0, 1, 2):
|
||||
common._set(_NAMESPACE, _PASSPHRASE_SOURCE, bytes([passphrase_source]))
|
||||
if display_rotation is not None:
|
||||
if display_rotation not in (0, 90, 180, 270):
|
||||
raise ValueError(
|
||||
"Unsupported display rotation degrees: %d" % display_rotation
|
||||
)
|
||||
else:
|
||||
common._set(
|
||||
_NAMESPACE, _ROTATION, display_rotation.to_bytes(2, "big"), True
|
||||
) # public
|
||||
|
||||
|
||||
def get_flags() -> int:
|
||||
b = common._get(_NAMESPACE, _FLAGS)
|
||||
if b is None:
|
||||
return 0
|
||||
else:
|
||||
return int.from_bytes(b, "big")
|
||||
|
||||
|
||||
def set_flags(flags: int) -> None:
|
||||
b = common._get(_NAMESPACE, _FLAGS)
|
||||
if b is None:
|
||||
b = 0
|
||||
else:
|
||||
b = int.from_bytes(b, "big")
|
||||
flags = (flags | b) & 0xFFFFFFFF
|
||||
if flags != b:
|
||||
common._set(_NAMESPACE, _FLAGS, flags.to_bytes(4, "big"))
|
||||
|
||||
|
||||
def get_autolock_delay_ms() -> int:
|
||||
b = common._get(_NAMESPACE, _AUTOLOCK_DELAY_MS)
|
||||
if b is None:
|
||||
return 10 * 60 * 1000
|
||||
else:
|
||||
return int.from_bytes(b, "big")
|
||||
|
||||
|
||||
def set_autolock_delay_ms(delay_ms: int) -> None:
|
||||
if delay_ms < 60 * 1000:
|
||||
delay_ms = 60 * 1000
|
||||
common._set(_NAMESPACE, _AUTOLOCK_DELAY_MS, delay_ms.to_bytes(4, "big"))
|
||||
|
||||
|
||||
def next_u2f_counter() -> int:
|
||||
return common._next_counter(_NAMESPACE, _U2F_COUNTER, True) # writable when locked
|
||||
|
||||
|
||||
def set_u2f_counter(count: int) -> None:
|
||||
common._set_counter(_NAMESPACE, _U2F_COUNTER, count, True) # writable when locked
|
@ -0,0 +1,72 @@
|
||||
from micropython import const
|
||||
|
||||
from apps.common.storage import common, slip39_mnemonics
|
||||
|
||||
# Namespace:
|
||||
_NAMESPACE = common._APP_SLIP39
|
||||
|
||||
# fmt: off
|
||||
# Keys:
|
||||
_SLIP39_IN_PROGRESS = const(0x00) # bool
|
||||
_SLIP39_IDENTIFIER = const(0x01) # bytes
|
||||
_SLIP39_THRESHOLD = const(0x02) # int
|
||||
_SLIP39_REMAINING = const(0x03) # int
|
||||
_SLIP39_WORDS_COUNT = const(0x04) # int
|
||||
_SLIP39_ITERATION_EXPONENT = const(0x05) # int
|
||||
# fmt: on
|
||||
|
||||
|
||||
def set_in_progress(val: bool):
|
||||
common._set_bool(_NAMESPACE, _SLIP39_IN_PROGRESS, val)
|
||||
|
||||
|
||||
def is_in_progress():
|
||||
return common._get_bool(_NAMESPACE, _SLIP39_IN_PROGRESS)
|
||||
|
||||
|
||||
def set_identifier(identifier: int):
|
||||
common._set_uint16(_NAMESPACE, _SLIP39_IDENTIFIER, identifier)
|
||||
|
||||
|
||||
def get_identifier() -> int:
|
||||
return common._get_uint16(_NAMESPACE, _SLIP39_IDENTIFIER)
|
||||
|
||||
|
||||
def set_threshold(threshold: int):
|
||||
common._set_uint8(_NAMESPACE, _SLIP39_THRESHOLD, threshold)
|
||||
|
||||
|
||||
def get_threshold() -> int:
|
||||
return common._get_uint8(_NAMESPACE, _SLIP39_THRESHOLD)
|
||||
|
||||
|
||||
def set_remaining(remaining: int):
|
||||
common._set_uint8(_NAMESPACE, _SLIP39_REMAINING, remaining)
|
||||
|
||||
|
||||
def get_remaining() -> int:
|
||||
return common._get_uint8(_NAMESPACE, _SLIP39_REMAINING)
|
||||
|
||||
|
||||
def set_words_count(count: int):
|
||||
common._set_uint8(_NAMESPACE, _SLIP39_WORDS_COUNT, count)
|
||||
|
||||
|
||||
def get_words_count() -> int:
|
||||
return common._get_uint8(_NAMESPACE, _SLIP39_WORDS_COUNT)
|
||||
|
||||
|
||||
def set_iteration_exponent(exponent: int):
|
||||
common._set_uint8(_NAMESPACE, _SLIP39_ITERATION_EXPONENT, exponent)
|
||||
|
||||
|
||||
def get_iteration_exponent() -> int:
|
||||
return common._get_uint8(_NAMESPACE, _SLIP39_ITERATION_EXPONENT)
|
||||
|
||||
|
||||
def delete_progress():
|
||||
common._delete(_NAMESPACE, _SLIP39_IN_PROGRESS)
|
||||
common._delete(_NAMESPACE, _SLIP39_REMAINING)
|
||||
common._delete(_NAMESPACE, _SLIP39_THRESHOLD)
|
||||
common._delete(_NAMESPACE, _SLIP39_WORDS_COUNT)
|
||||
slip39_mnemonics.delete()
|
@ -0,0 +1,31 @@
|
||||
from trezor.crypto import slip39
|
||||
|
||||
from apps.common.storage import common
|
||||
|
||||
# Mnemonics stored during SLIP-39 recovery process.
|
||||
# Each mnemonic is stored under key = index.
|
||||
|
||||
|
||||
def set(index: int, mnemonic: str):
|
||||
common._set(common._APP_SLIP39_MNEMONICS, index, mnemonic.encode())
|
||||
|
||||
|
||||
def get(index: int) -> str:
|
||||
m = common._get(common._APP_SLIP39_MNEMONICS, index)
|
||||
if m:
|
||||
return m.decode()
|
||||
return False
|
||||
|
||||
|
||||
def fetch() -> list:
|
||||
mnemonics = []
|
||||
for index in range(0, slip39.MAX_SHARE_COUNT):
|
||||
m = get(index)
|
||||
if m:
|
||||
mnemonics.append(m)
|
||||
return mnemonics
|
||||
|
||||
|
||||
def delete():
|
||||
for index in range(0, slip39.MAX_SHARE_COUNT):
|
||||
common._delete(common._APP_SLIP39_MNEMONICS, index)
|
Loading…
Reference in new issue