2016-09-29 10:29:43 +00:00
|
|
|
from micropython import const
|
2016-11-23 13:46:55 +00:00
|
|
|
import ustruct
|
|
|
|
import utime
|
|
|
|
|
2016-09-26 11:06:28 +00:00
|
|
|
from trezor import config
|
2016-11-23 13:46:55 +00:00
|
|
|
from trezor import utils
|
|
|
|
|
|
|
|
_APP = const(1)
|
|
|
|
|
2017-01-24 13:11:32 +00:00
|
|
|
DEVICE_ID = const(0) # str
|
|
|
|
VERSION = const(1) # varint
|
|
|
|
MNEMONIC = const(2) # str
|
|
|
|
LANGUAGE = const(3) # str
|
|
|
|
LABEL = const(4) # str
|
|
|
|
PIN = const(5) # bytes
|
|
|
|
PIN_FAILS = const(6) # varint
|
|
|
|
PASSPHRASE_PROTECTION = const(7) # varint
|
2016-11-23 13:46:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
# pin lock
|
|
|
|
# ===
|
|
|
|
|
|
|
|
_locked = True
|
|
|
|
|
|
|
|
|
|
|
|
def is_locked() -> bool:
|
|
|
|
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
|
2017-01-24 13:11:32 +00:00
|
|
|
fails = bytes_to_int(config_get(PIN_FAILS)) + 1
|
|
|
|
config_set_checked(PIN_FAILS, int_to_bytes(fails))
|
2016-11-23 13:46:55 +00:00
|
|
|
|
2017-01-24 13:11:32 +00:00
|
|
|
if const_equal(config_get(PIN), user_pin.encode()):
|
2016-11-23 13:46:55 +00:00
|
|
|
# unlock and reset the counter
|
|
|
|
_locked = False
|
2017-01-24 13:11:32 +00:00
|
|
|
config_set(PIN_FAILS, int_to_bytes(0))
|
2016-11-23 13:46:55 +00:00
|
|
|
return True
|
|
|
|
|
|
|
|
else:
|
|
|
|
# lock, run the callback (ie for ui) and sleep for a quadratic delay
|
|
|
|
_locked = True
|
|
|
|
delay_ms = fails * fails * 1000
|
2016-12-12 14:20:20 +00:00
|
|
|
try:
|
|
|
|
if failure_callback:
|
2016-11-23 13:46:55 +00:00
|
|
|
failure_callback(delay_ms)
|
2016-12-12 14:20:20 +00:00
|
|
|
finally:
|
|
|
|
utime.sleep_ms(delay_ms)
|
2016-11-23 13:46:55 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def lock():
|
|
|
|
global _locked
|
|
|
|
_locked = True
|
|
|
|
|
|
|
|
|
|
|
|
def const_equal(a: bytes, b: bytes) -> bool:
|
2017-01-17 16:43:08 +00:00
|
|
|
return a == b # TODO: proper const equal
|
2016-11-23 13:46:55 +00:00
|
|
|
|
2016-09-26 11:06:28 +00:00
|
|
|
|
2016-11-23 13:46:55 +00:00
|
|
|
# settings
|
|
|
|
# ===
|
2016-09-26 11:06:28 +00:00
|
|
|
|
|
|
|
|
2016-10-14 13:35:44 +00:00
|
|
|
def get_device_id() -> str:
|
2017-01-24 13:11:32 +00:00
|
|
|
dev_id = config_get(DEVICE_ID).decode()
|
2016-12-15 11:33:39 +00:00
|
|
|
if not dev_id:
|
2016-11-23 13:46:55 +00:00
|
|
|
dev_id = new_device_id()
|
2017-01-24 13:11:32 +00:00
|
|
|
config_set(DEVICE_ID, dev_id.encode())
|
2016-11-23 13:46:55 +00:00
|
|
|
return dev_id
|
2016-09-26 11:06:28 +00:00
|
|
|
|
|
|
|
|
2016-10-14 13:35:44 +00:00
|
|
|
def is_initialized() -> bool:
|
2017-01-24 13:11:32 +00:00
|
|
|
return bool(config_get(VERSION))
|
2016-09-26 11:06:28 +00:00
|
|
|
|
2016-10-06 13:03:54 +00:00
|
|
|
|
2016-10-14 13:35:44 +00:00
|
|
|
def is_protected_by_pin() -> bool:
|
2017-01-24 13:11:32 +00:00
|
|
|
return bool(config_get(PIN))
|
2016-10-06 13:03:54 +00:00
|
|
|
|
2016-10-14 13:35:44 +00:00
|
|
|
|
|
|
|
def is_protected_by_passphrase() -> bool:
|
2017-01-24 13:11:32 +00:00
|
|
|
return bool(bytes_to_int(config_get(PASSPHRASE_PROTECTION)))
|
2016-10-14 13:35:44 +00:00
|
|
|
|
|
|
|
|
2016-11-15 10:50:45 +00:00
|
|
|
def get_pin() -> str:
|
2017-01-24 13:11:32 +00:00
|
|
|
return config_get(PIN).decode()
|
2016-11-15 10:50:45 +00:00
|
|
|
|
|
|
|
|
2016-10-14 13:35:44 +00:00
|
|
|
def get_label() -> str:
|
2017-01-24 13:11:32 +00:00
|
|
|
return config_get(LABEL).decode()
|
2016-10-14 13:35:44 +00:00
|
|
|
|
|
|
|
|
2016-12-15 11:48:33 +00:00
|
|
|
def get_language() -> str:
|
2017-01-24 13:11:32 +00:00
|
|
|
return config_get(LANGUAGE).decode() or _DEFAULT_LANGUAGE
|
2016-12-15 11:48:33 +00:00
|
|
|
|
|
|
|
|
2016-10-14 13:35:44 +00:00
|
|
|
def get_mnemonic() -> str:
|
2016-11-23 13:46:55 +00:00
|
|
|
utils.ensure(is_initialized())
|
|
|
|
utils.ensure(not is_locked())
|
|
|
|
|
2017-01-24 13:11:32 +00:00
|
|
|
return config_get(MNEMONIC).decode()
|
2016-11-23 13:46:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
# settings configuration
|
|
|
|
# ===
|
2016-10-14 13:35:44 +00:00
|
|
|
|
|
|
|
|
|
|
|
def load_mnemonic(mnemonic: str):
|
2016-11-23 13:46:55 +00:00
|
|
|
utils.ensure(not is_initialized())
|
2016-10-14 13:35:44 +00:00
|
|
|
|
2017-01-24 13:11:32 +00:00
|
|
|
config_set(VERSION, int_to_bytes(1))
|
|
|
|
config_set(MNEMONIC, mnemonic.encode())
|
2016-10-14 13:35:44 +00:00
|
|
|
|
|
|
|
|
2016-12-15 11:48:33 +00:00
|
|
|
_ALLOWED_LANGUAGES = ('english')
|
|
|
|
_DEFAULT_LANGUAGE = 'english'
|
|
|
|
|
|
|
|
|
2016-11-23 13:46:55 +00:00
|
|
|
def load_settings(language: str=None,
|
|
|
|
label: str=None,
|
|
|
|
pin: str=None,
|
|
|
|
passphrase_protection: bool=None):
|
|
|
|
utils.ensure(is_initialized())
|
|
|
|
utils.ensure(not is_locked())
|
2016-10-14 13:35:44 +00:00
|
|
|
|
2016-12-15 11:48:33 +00:00
|
|
|
if language is not None and language in _ALLOWED_LANGUAGES:
|
|
|
|
if language is _DEFAULT_LANGUAGE:
|
2017-01-24 13:11:32 +00:00
|
|
|
config_set(LANGUAGE, b'')
|
2016-12-15 11:48:33 +00:00
|
|
|
else:
|
2017-01-24 13:11:32 +00:00
|
|
|
config_set(LANGUAGE, language.encode())
|
2016-11-23 13:46:55 +00:00
|
|
|
if label is not None:
|
2017-01-24 13:11:32 +00:00
|
|
|
config_set(LABEL, label.encode())
|
2016-11-23 13:46:55 +00:00
|
|
|
if pin is not None:
|
2017-01-24 13:11:32 +00:00
|
|
|
config_set(PIN, pin.encode())
|
2016-11-23 13:46:55 +00:00
|
|
|
if passphrase_protection is not None:
|
2017-01-24 13:11:32 +00:00
|
|
|
config_set(PASSPHRASE_PROTECTION,
|
2016-11-23 13:46:55 +00:00
|
|
|
int_to_bytes(passphrase_protection))
|
2016-10-14 13:35:44 +00:00
|
|
|
|
|
|
|
|
2016-11-23 13:46:55 +00:00
|
|
|
def wipe():
|
2016-12-15 11:33:39 +00:00
|
|
|
lock()
|
2016-11-23 13:46:55 +00:00
|
|
|
config.wipe()
|
2016-10-14 13:35:44 +00:00
|
|
|
|
|
|
|
|
2016-11-23 13:46:55 +00:00
|
|
|
def new_device_id() -> str:
|
2016-10-14 13:35:44 +00:00
|
|
|
from ubinascii import hexlify
|
|
|
|
from trezor.crypto import random
|
2016-11-12 14:03:45 +00:00
|
|
|
return hexlify(random.bytes(12)).decode('ascii').upper()
|
2016-11-23 13:46:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
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')
|
|
|
|
|
|
|
|
|
2016-12-12 14:20:20 +00:00
|
|
|
# TODO: store ints as varints
|
|
|
|
|
2016-11-23 13:46:55 +00:00
|
|
|
def int_to_bytes(i: int) -> bytes:
|
|
|
|
return ustruct.pack('>L', i) if i else bytes()
|
|
|
|
|
|
|
|
|
|
|
|
def bytes_to_int(b: bytes) -> int:
|
2017-01-17 16:43:08 +00:00
|
|
|
return ustruct.unpack('>L', b)[0] if b else 0
|