2019-01-10 13:32:28 +00:00
|
|
|
from trezor import ui, wire
|
2019-03-07 13:49:23 +00:00
|
|
|
from trezor.crypto import bip32
|
2018-07-03 14:20:26 +00:00
|
|
|
|
2019-04-05 09:23:06 +00:00
|
|
|
from apps.common import HARDENED, cache, mnemonic, storage
|
2018-02-27 15:35:21 +00:00
|
|
|
from apps.common.request_passphrase import protect_by_passphrase
|
2016-10-06 13:04:38 +00:00
|
|
|
|
2018-11-14 18:58:07 +00:00
|
|
|
allow = list
|
2016-10-06 13:04:38 +00:00
|
|
|
|
|
|
|
|
2018-11-13 16:09:52 +00:00
|
|
|
class Keychain:
|
2018-11-14 18:58:07 +00:00
|
|
|
"""
|
|
|
|
Keychain provides an API for deriving HD keys from previously allowed
|
|
|
|
key-spaces.
|
|
|
|
"""
|
2018-11-13 16:09:52 +00:00
|
|
|
|
2018-12-13 12:53:53 +00:00
|
|
|
def __init__(self, seed: bytes, namespaces: list):
|
|
|
|
self.seed = seed
|
|
|
|
self.namespaces = namespaces
|
|
|
|
self.roots = [None] * len(namespaces)
|
|
|
|
|
|
|
|
def __del__(self):
|
|
|
|
for root in self.roots:
|
|
|
|
if root is not None:
|
|
|
|
root.__del__()
|
|
|
|
del self.roots
|
|
|
|
del self.seed
|
2018-11-14 18:58:07 +00:00
|
|
|
|
2019-04-05 09:23:06 +00:00
|
|
|
def validate_path(self, checked_path: list, checked_curve: str):
|
2019-04-04 09:41:23 +00:00
|
|
|
for curve, *path in self.namespaces:
|
2019-04-05 09:23:06 +00:00
|
|
|
if path == checked_path[: len(path)] and curve == checked_curve:
|
|
|
|
if curve == "ed25519" and not _path_hardened(checked_path):
|
|
|
|
break
|
2019-04-04 09:41:23 +00:00
|
|
|
return
|
|
|
|
raise wire.DataError("Forbidden key path")
|
2019-04-02 14:00:51 +00:00
|
|
|
|
2018-11-14 18:58:07 +00:00
|
|
|
def derive(self, node_path: list, curve_name: str = "secp256k1") -> bip32.HDNode:
|
2018-12-13 12:53:53 +00:00
|
|
|
# find the root node index
|
2018-11-14 18:58:07 +00:00
|
|
|
root_index = 0
|
2018-12-13 12:53:53 +00:00
|
|
|
for curve, *path in self.namespaces:
|
2018-11-14 18:58:07 +00:00
|
|
|
prefix = node_path[: len(path)]
|
|
|
|
suffix = node_path[len(path) :]
|
|
|
|
if curve == curve_name and path == prefix:
|
|
|
|
break
|
|
|
|
root_index += 1
|
|
|
|
else:
|
|
|
|
raise wire.DataError("Forbidden key path")
|
2018-12-13 12:53:53 +00:00
|
|
|
|
|
|
|
# create the root node if not cached
|
|
|
|
root = self.roots[root_index]
|
|
|
|
if root is None:
|
|
|
|
root = bip32.from_seed(self.seed, curve_name)
|
|
|
|
root.derive_path(path)
|
|
|
|
self.roots[root_index] = root
|
|
|
|
|
2019-04-02 14:00:51 +00:00
|
|
|
# TODO check for ed25519?
|
2018-11-14 18:58:07 +00:00
|
|
|
# derive child node from the root
|
2018-12-13 12:53:53 +00:00
|
|
|
node = root.clone()
|
2018-11-14 18:58:07 +00:00
|
|
|
node.derive_path(suffix)
|
2018-11-13 16:09:52 +00:00
|
|
|
return node
|
2016-10-06 13:04:38 +00:00
|
|
|
|
|
|
|
|
2018-12-13 12:53:53 +00:00
|
|
|
async def get_keychain(ctx: wire.Context, namespaces: list) -> Keychain:
|
2018-05-28 13:20:31 +00:00
|
|
|
if not storage.is_initialized():
|
2018-07-03 14:20:58 +00:00
|
|
|
raise wire.ProcessError("Device is not initialized")
|
2018-11-13 16:09:52 +00:00
|
|
|
seed = cache.get_seed()
|
|
|
|
if seed is None:
|
2019-01-17 15:54:35 +00:00
|
|
|
seed = await _compute_seed(ctx)
|
2018-12-13 12:53:53 +00:00
|
|
|
keychain = Keychain(seed, namespaces)
|
2018-11-13 16:09:52 +00:00
|
|
|
return keychain
|
2017-05-23 10:44:36 +00:00
|
|
|
|
|
|
|
|
2019-04-05 09:23:06 +00:00
|
|
|
def _path_hardened(path: list) -> bool:
|
|
|
|
# TODO: move to paths.py after #538 is fixed
|
|
|
|
for i in path:
|
|
|
|
if not (i & HARDENED):
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
2019-02-21 12:22:57 +00:00
|
|
|
@ui.layout_no_slide
|
2019-01-17 15:54:35 +00:00
|
|
|
async def _compute_seed(ctx: wire.Context) -> bytes:
|
|
|
|
passphrase = cache.get_passphrase()
|
|
|
|
if passphrase is None:
|
|
|
|
passphrase = await protect_by_passphrase(ctx)
|
|
|
|
cache.set_passphrase(passphrase)
|
2019-03-07 13:49:23 +00:00
|
|
|
seed = mnemonic.get_seed(passphrase)
|
2019-01-17 15:54:35 +00:00
|
|
|
cache.set_seed(seed)
|
|
|
|
return seed
|
|
|
|
|
|
|
|
|
2018-07-03 14:20:58 +00:00
|
|
|
def derive_node_without_passphrase(
|
2018-11-14 18:58:07 +00:00
|
|
|
path: list, curve_name: str = "secp256k1"
|
2018-07-03 14:20:58 +00:00
|
|
|
) -> bip32.HDNode:
|
2017-05-23 10:44:36 +00:00
|
|
|
if not storage.is_initialized():
|
2018-07-03 14:20:58 +00:00
|
|
|
raise Exception("Device is not initialized")
|
2019-03-29 13:23:46 +00:00
|
|
|
seed = mnemonic.get_seed(progress_bar=False)
|
2018-02-06 13:28:22 +00:00
|
|
|
node = bip32.from_seed(seed, curve_name)
|
|
|
|
node.derive_path(path)
|
|
|
|
return node
|
2018-05-11 13:31:53 +00:00
|
|
|
|
|
|
|
|
2018-06-18 12:23:33 +00:00
|
|
|
def remove_ed25519_prefix(pubkey: bytes) -> bytes:
|
2018-05-11 13:31:53 +00:00
|
|
|
# 0x01 prefix is not part of the actual public key, hence removed
|
|
|
|
return pubkey[1:]
|