|
|
|
@ -1,10 +1,10 @@
|
|
|
|
|
from storage import cache, device
|
|
|
|
|
from trezor import wire
|
|
|
|
|
from trezor.crypto import bip32
|
|
|
|
|
from trezor.crypto import bip32, cardano
|
|
|
|
|
|
|
|
|
|
from apps.common import mnemonic
|
|
|
|
|
from apps.common.passphrase import get as get_passphrase
|
|
|
|
|
from apps.common.seed import get_seed
|
|
|
|
|
from apps.common.seed import get_seed, derive_and_store_roots
|
|
|
|
|
|
|
|
|
|
from .helpers import paths
|
|
|
|
|
|
|
|
|
@ -24,12 +24,19 @@ class Keychain:
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, root: bip32.HDNode) -> None:
|
|
|
|
|
self.byron_root = derive_path_cardano(root, paths.BYRON_ROOT)
|
|
|
|
|
self.shelley_root = derive_path_cardano(root, paths.SHELLEY_ROOT)
|
|
|
|
|
self.multisig_root = derive_path_cardano(root, paths.MULTISIG_ROOT)
|
|
|
|
|
self.minting_root = derive_path_cardano(root, paths.MINTING_ROOT)
|
|
|
|
|
self.byron_root = self._derive_path(root, paths.BYRON_ROOT)
|
|
|
|
|
self.shelley_root = self._derive_path(root, paths.SHELLEY_ROOT)
|
|
|
|
|
self.multisig_root = self._derive_path(root, paths.MULTISIG_ROOT)
|
|
|
|
|
self.minting_root = self._derive_path(root, paths.MINTING_ROOT)
|
|
|
|
|
root.__del__()
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def _derive_path(root: bip32.HDNode, path: Bip32Path) -> bip32.HDNode:
|
|
|
|
|
"""Clone and derive path from the root."""
|
|
|
|
|
node = root.clone()
|
|
|
|
|
node.derive_path(path)
|
|
|
|
|
return node
|
|
|
|
|
|
|
|
|
|
def verify_path(self, path: Bip32Path) -> None:
|
|
|
|
|
if not self.is_in_keychain(path):
|
|
|
|
|
raise wire.DataError("Forbidden key path")
|
|
|
|
@ -67,7 +74,7 @@ class Keychain:
|
|
|
|
|
suffix = node_path[len(paths.SHELLEY_ROOT) :]
|
|
|
|
|
|
|
|
|
|
# derive child node from the root
|
|
|
|
|
return derive_path_cardano(path_root, suffix)
|
|
|
|
|
return self._derive_path(path_root, suffix)
|
|
|
|
|
|
|
|
|
|
# XXX the root node remains in session cache so we should not delete it
|
|
|
|
|
# def __del__(self) -> None:
|
|
|
|
@ -90,28 +97,35 @@ def is_minting_path(path: Bip32Path) -> bool:
|
|
|
|
|
return path[: len(paths.MINTING_ROOT)] == paths.MINTING_ROOT
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def derive_path_cardano(root: bip32.HDNode, path: Bip32Path) -> bip32.HDNode:
|
|
|
|
|
node = root.clone()
|
|
|
|
|
for i in path:
|
|
|
|
|
node.derive_cardano(i)
|
|
|
|
|
return node
|
|
|
|
|
def derive_and_store_secret(passphrase: str) -> None:
|
|
|
|
|
assert device.is_initialized()
|
|
|
|
|
assert cache.get(cache.APP_COMMON_DERIVE_CARDANO)
|
|
|
|
|
|
|
|
|
|
if not mnemonic.is_bip39():
|
|
|
|
|
# nothing to do for SLIP-39, where we can derive the root from the main seed
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
icarus_trezor_secret = mnemonic.derive_cardano_icarus_trezor(passphrase)
|
|
|
|
|
cache.set(cache.APP_CARDANO_SECRET, icarus_trezor_secret)
|
|
|
|
|
|
|
|
|
|
@cache.stored_async(cache.APP_CARDANO_PASSPHRASE)
|
|
|
|
|
async def _get_passphrase(ctx: wire.Context) -> bytes:
|
|
|
|
|
return (await get_passphrase(ctx)).encode()
|
|
|
|
|
|
|
|
|
|
@cache.stored_async(cache.APP_CARDANO_SECRET)
|
|
|
|
|
async def _get_secret(ctx: wire.Context) -> bytes:
|
|
|
|
|
await derive_and_store_roots(ctx)
|
|
|
|
|
secret = cache.get(cache.APP_CARDANO_SECRET)
|
|
|
|
|
assert secret is not None
|
|
|
|
|
return secret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def _get_keychain_bip39(ctx: wire.Context) -> Keychain:
|
|
|
|
|
if not device.is_initialized():
|
|
|
|
|
raise wire.NotInitialized("Device is not initialized")
|
|
|
|
|
|
|
|
|
|
# ask for passphrase, loading from cache if necessary
|
|
|
|
|
passphrase = await _get_passphrase(ctx)
|
|
|
|
|
# derive the root node from mnemonic and passphrase via Cardano Icarus algorithm
|
|
|
|
|
secret_bytes = mnemonic.get_secret()
|
|
|
|
|
assert secret_bytes is not None
|
|
|
|
|
root = bip32.from_mnemonic_cardano(secret_bytes.decode(), passphrase.decode())
|
|
|
|
|
if not cache.get(cache.APP_COMMON_DERIVE_CARDANO):
|
|
|
|
|
raise wire.ProcessError("Cardano derivation is not enabled for this session")
|
|
|
|
|
|
|
|
|
|
secret = await _get_secret(ctx)
|
|
|
|
|
root = cardano.from_secret(secret)
|
|
|
|
|
return Keychain(root)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -121,7 +135,7 @@ async def get_keychain(ctx: wire.Context) -> Keychain:
|
|
|
|
|
else:
|
|
|
|
|
# derive the root node via SLIP-0023 https://github.com/satoshilabs/slips/blob/master/slip-0022.md
|
|
|
|
|
seed = await get_seed(ctx)
|
|
|
|
|
return Keychain(bip32.from_seed(seed, "ed25519 cardano seed"))
|
|
|
|
|
return Keychain(cardano.from_seed_slip23(seed))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def with_keychain(func: HandlerWithKeychain[MsgIn, MsgOut]) -> Handler[MsgIn, MsgOut]:
|
|
|
|
|