mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-23 06:48:16 +00:00
storage: remove pbuf, add pin lock
This commit is contained in:
parent
be7ee61ddd
commit
05f832cae7
@ -1,115 +1,166 @@
|
|||||||
import protobuf as p
|
|
||||||
from micropython import const
|
from micropython import const
|
||||||
|
import ustruct
|
||||||
|
import utime
|
||||||
|
|
||||||
from trezor import config
|
from trezor import config
|
||||||
|
from trezor import utils
|
||||||
|
|
||||||
_APP_COMMON = const(1)
|
_APP = const(1)
|
||||||
|
|
||||||
_CFG_ID = const(0)
|
_DEVICE_ID = const(0) # str
|
||||||
_CFG_VERSION = const(1)
|
_VERSION = const(1) # varint
|
||||||
_CFG_MNEMONIC = const(2)
|
_MNEMONIC = const(2) # str
|
||||||
_CFG_LANGUAGE = const(3)
|
_LANGUAGE = const(3) # str
|
||||||
_CFG_LABEL = const(4)
|
_LABEL = const(4) # str
|
||||||
_CFG_PIN = const(5)
|
_PIN = const(5) # bytes
|
||||||
_CFG_PIN_ATTEMPTS = const(6)
|
_PIN_FAILS = const(6) # varint
|
||||||
_CFG_PASSPHRASE_PROTECTION = const(7)
|
_PASSPHRASE_PROTECTION = const(7) # varint
|
||||||
|
|
||||||
_types = {
|
|
||||||
_CFG_ID: p.UnicodeType,
|
# pin lock
|
||||||
_CFG_VERSION: p.UVarintType,
|
# ===
|
||||||
_CFG_MNEMONIC: p.UnicodeType,
|
|
||||||
_CFG_LANGUAGE: p.UnicodeType,
|
_locked = True
|
||||||
_CFG_LABEL: p.UnicodeType,
|
|
||||||
_CFG_PIN: p.UnicodeType,
|
|
||||||
_CFG_PIN_ATTEMPTS: p.UVarintType,
|
def is_locked() -> bool:
|
||||||
_CFG_PASSPHRASE_PROTECTION: p.BoolType,
|
return is_protected_by_pin() and _locked
|
||||||
}
|
|
||||||
|
|
||||||
|
def unlock(user_pin: str, failure_callback=None) -> bool:
|
||||||
|
global _locked
|
||||||
|
|
||||||
|
if not is_protected_by_pin():
|
||||||
|
return True
|
||||||
|
|
||||||
|
# increment the pin fail counter before checking the pin
|
||||||
|
fails = bytes_to_int(config_get(_PIN_FAILS)) + 1
|
||||||
|
config_set_checked(_PIN_FAILS, int_to_bytes(fails))
|
||||||
|
|
||||||
|
if const_equal(config_get(_PIN), user_pin.encode()):
|
||||||
|
# unlock and reset the counter
|
||||||
|
_locked = False
|
||||||
|
config_set(_PIN_FAILS, int_to_bytes(0))
|
||||||
|
return True
|
||||||
|
|
||||||
|
else:
|
||||||
|
# lock, run the callback (ie for ui) and sleep for a quadratic delay
|
||||||
|
_locked = True
|
||||||
|
delay_ms = fails * fails * 1000
|
||||||
|
if failure_callback:
|
||||||
|
try:
|
||||||
|
failure_callback(delay_ms)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
utime.sleep_ms(delay_ms)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def lock():
|
||||||
|
global _locked
|
||||||
|
_locked = True
|
||||||
|
|
||||||
|
|
||||||
|
def const_equal(a: bytes, b: bytes) -> bool:
|
||||||
|
return a == b
|
||||||
|
|
||||||
|
|
||||||
|
# settings
|
||||||
|
# ===
|
||||||
|
|
||||||
|
|
||||||
def get_device_id() -> str:
|
def get_device_id() -> str:
|
||||||
devid = _get(_CFG_ID)
|
dev_id = config_get(_DEVICE_ID).decode()
|
||||||
if devid is None:
|
if dev_id is None:
|
||||||
devid = _new_device_id()
|
dev_id = new_device_id()
|
||||||
_set(_CFG_ID, devid)
|
config_set(_DEVICE_ID, dev_id.encode())
|
||||||
return devid
|
return dev_id
|
||||||
|
|
||||||
|
|
||||||
def is_initialized() -> bool:
|
def is_initialized() -> bool:
|
||||||
return _get(_CFG_VERSION) is not None
|
return bool(config_get(_VERSION))
|
||||||
|
|
||||||
|
|
||||||
def is_protected_by_pin() -> bool:
|
def is_protected_by_pin() -> bool:
|
||||||
return _get(_CFG_PIN) is not None
|
return bool(config_get(_PIN))
|
||||||
|
|
||||||
|
|
||||||
def is_protected_by_passphrase() -> bool:
|
def is_protected_by_passphrase() -> bool:
|
||||||
return _get(_CFG_PASSPHRASE_PROTECTION) is True
|
return bool(bytes_to_int(config_get(_PASSPHRASE_PROTECTION)))
|
||||||
|
|
||||||
|
|
||||||
def check_pin(pin: str) -> bool:
|
|
||||||
return _get(_CFG_PIN) == pin
|
|
||||||
|
|
||||||
|
|
||||||
def get_pin() -> str:
|
def get_pin() -> str:
|
||||||
return _get(_CFG_PIN)
|
return config_get(_PIN).decode()
|
||||||
|
|
||||||
|
|
||||||
def get_label() -> str:
|
def get_label() -> str:
|
||||||
return _get(_CFG_LABEL)
|
return config_get(_LABEL).decode()
|
||||||
|
|
||||||
|
|
||||||
def get_mnemonic() -> str:
|
def get_mnemonic() -> str:
|
||||||
return _get(_CFG_MNEMONIC)
|
utils.ensure(is_initialized())
|
||||||
|
utils.ensure(not is_locked())
|
||||||
|
|
||||||
|
return config_get(_MNEMONIC).decode()
|
||||||
|
|
||||||
|
|
||||||
|
# settings configuration
|
||||||
|
# ===
|
||||||
|
|
||||||
|
|
||||||
def load_mnemonic(mnemonic: str):
|
def load_mnemonic(mnemonic: str):
|
||||||
if is_initialized():
|
utils.ensure(not is_initialized())
|
||||||
raise Exception('Device is already initialized')
|
|
||||||
_set(_CFG_VERSION, 1)
|
config_set(_VERSION, int_to_bytes(1))
|
||||||
_set(_CFG_MNEMONIC, mnemonic)
|
config_set(_MNEMONIC, mnemonic.encode())
|
||||||
|
|
||||||
|
|
||||||
def load_settings(language: str,
|
def load_settings(language: str=None,
|
||||||
label: str,
|
label: str=None,
|
||||||
pin: str,
|
pin: str=None,
|
||||||
passphrase_protection: bool):
|
passphrase_protection: bool=None):
|
||||||
if not is_initialized():
|
utils.ensure(is_initialized())
|
||||||
raise Exception('Device is not initialized')
|
utils.ensure(not is_locked())
|
||||||
_set(_CFG_LANGUAGE, language or None)
|
|
||||||
_set(_CFG_LABEL, label or None)
|
if language is not None:
|
||||||
_set(_CFG_PIN, pin or None)
|
config_set(_LANGUAGE, language.encode())
|
||||||
_set(_CFG_PIN_ATTEMPTS, None)
|
if label is not None:
|
||||||
_set(_CFG_PASSPHRASE_PROTECTION, passphrase_protection)
|
config_set(_LABEL, label.encode())
|
||||||
|
if pin is not None:
|
||||||
|
config_set(_PIN, pin.encode())
|
||||||
|
if passphrase_protection is not None:
|
||||||
|
config_set(_PASSPHRASE_PROTECTION,
|
||||||
|
int_to_bytes(passphrase_protection))
|
||||||
|
|
||||||
|
|
||||||
def wipe():
|
def wipe():
|
||||||
_set(_CFG_ID, _new_device_id())
|
config.wipe()
|
||||||
_set(_CFG_VERSION, None)
|
|
||||||
_set(_CFG_MNEMONIC, None)
|
|
||||||
_set(_CFG_LANGUAGE, None)
|
|
||||||
_set(_CFG_LABEL, None)
|
|
||||||
_set(_CFG_PIN, None)
|
|
||||||
_set(_CFG_PIN_ATTEMPTS, None)
|
|
||||||
_set(_CFG_PASSPHRASE_PROTECTION, None)
|
|
||||||
|
|
||||||
|
|
||||||
def _get(key: int):
|
def new_device_id() -> str:
|
||||||
buf = config.get(_APP_COMMON, key)
|
|
||||||
if buf:
|
|
||||||
val = _types[key].loads(buf)
|
|
||||||
else:
|
|
||||||
val = None
|
|
||||||
return val
|
|
||||||
|
|
||||||
|
|
||||||
def _set(key: int, val):
|
|
||||||
if val is not None:
|
|
||||||
buf = _types[key].dumps(val)
|
|
||||||
else:
|
|
||||||
buf = b''
|
|
||||||
config.set(_APP_COMMON, key, buf)
|
|
||||||
|
|
||||||
|
|
||||||
def _new_device_id() -> str:
|
|
||||||
from ubinascii import hexlify
|
from ubinascii import hexlify
|
||||||
from trezor.crypto import random
|
from trezor.crypto import random
|
||||||
return hexlify(random.bytes(12)).decode('ascii').upper()
|
return hexlify(random.bytes(12)).decode('ascii').upper()
|
||||||
|
|
||||||
|
|
||||||
|
def config_get(key: int) -> bytes:
|
||||||
|
return config.get(_APP, key)
|
||||||
|
|
||||||
|
|
||||||
|
def config_set(key: int, value: bytes):
|
||||||
|
config.set(_APP, key, value)
|
||||||
|
|
||||||
|
|
||||||
|
def config_set_checked(key, value: bytes):
|
||||||
|
config_set(key, value)
|
||||||
|
check = config_get(key)
|
||||||
|
if check != value:
|
||||||
|
utils.halt('config.set failed')
|
||||||
|
|
||||||
|
|
||||||
|
def int_to_bytes(i: int) -> bytes:
|
||||||
|
return ustruct.pack('>L', i) if i else bytes()
|
||||||
|
|
||||||
|
|
||||||
|
def bytes_to_int(b: bytes) -> int:
|
||||||
|
return ustruct.unpack('>L', b) if b else 0
|
||||||
|
@ -46,3 +46,8 @@ def unimport(func):
|
|||||||
def chunks(l, n):
|
def chunks(l, n):
|
||||||
for i in range(0, len(l), n):
|
for i in range(0, len(l), n):
|
||||||
yield l[i:i + n]
|
yield l[i:i + n]
|
||||||
|
|
||||||
|
|
||||||
|
def ensure(cond):
|
||||||
|
if not cond:
|
||||||
|
raise AssertionError()
|
||||||
|
Loading…
Reference in New Issue
Block a user