mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-03-12 22:26:08 +00:00
199 lines
6.5 KiB
Python
199 lines
6.5 KiB
Python
from typing import TYPE_CHECKING
|
|
|
|
import storage.device as storage_device
|
|
from storage.cache_common import APP_COMMON_SEED, APP_COMMON_SEED_WITHOUT_PASSPHRASE
|
|
from trezor import utils
|
|
from trezor.crypto import hmac
|
|
from trezor.wire import context
|
|
from trezor.wire.context import get_context
|
|
from trezor.wire.errors import DataError
|
|
|
|
from apps.common import cache
|
|
|
|
from . import mnemonic
|
|
from .passphrase import get_passphrase as get_passphrase
|
|
|
|
if TYPE_CHECKING:
|
|
from trezor.crypto import bip32
|
|
from trezor.messages import ThpCreateNewSession
|
|
from trezor.wire.protocol_common import Context
|
|
|
|
from .paths import Bip32Path, Slip21Path
|
|
|
|
if not utils.BITCOIN_ONLY:
|
|
from storage.cache_common import (
|
|
APP_CARDANO_ICARUS_SECRET,
|
|
APP_COMMON_DERIVE_CARDANO,
|
|
)
|
|
|
|
if not utils.USE_THP:
|
|
from .passphrase import get as get_passphrase_legacy
|
|
|
|
|
|
class Slip21Node:
|
|
"""
|
|
This class implements the SLIP-0021 hierarchical derivation of symmetric keys, see
|
|
https://github.com/satoshilabs/slips/blob/master/slip-0021.md.
|
|
"""
|
|
|
|
def __init__(self, seed: bytes | None = None, data: bytes | None = None) -> None:
|
|
assert seed is None or data is None, "Specify exactly one of: seed, data"
|
|
if data is not None:
|
|
self.data = data
|
|
elif seed is not None:
|
|
self.data = hmac(hmac.SHA512, b"Symmetric key seed", seed).digest()
|
|
else:
|
|
raise ValueError # neither seed nor data specified
|
|
|
|
def __del__(self) -> None:
|
|
del self.data
|
|
|
|
def derive_path(self, path: Slip21Path) -> None:
|
|
for label in path:
|
|
h = hmac(hmac.SHA512, self.data[0:32], b"\x00")
|
|
h.update(label)
|
|
self.data = h.digest()
|
|
|
|
def key(self) -> bytes:
|
|
return self.data[32:64]
|
|
|
|
def clone(self) -> "Slip21Node":
|
|
return Slip21Node(data=self.data)
|
|
|
|
|
|
if utils.USE_THP:
|
|
|
|
async def get_seed() -> bytes: # type: ignore [Function declaration "get_seed" is obscured by a declaration of the same name]
|
|
common_seed = context.cache_get(APP_COMMON_SEED)
|
|
assert common_seed is not None
|
|
return common_seed
|
|
|
|
if utils.BITCOIN_ONLY:
|
|
# === Bitcoin_only variant ===
|
|
# We want to derive the normal seed ONLY
|
|
|
|
async def derive_and_store_roots(
|
|
ctx: Context, msg: ThpCreateNewSession
|
|
) -> None:
|
|
|
|
if msg.passphrase is not None and msg.on_device:
|
|
raise DataError("Passphrase provided when it shouldn't be!")
|
|
|
|
if ctx.cache.is_set(APP_COMMON_SEED):
|
|
raise Exception("Seed is already set!")
|
|
|
|
from trezor import wire
|
|
|
|
if not storage_device.is_initialized():
|
|
raise wire.NotInitialized("Device is not initialized")
|
|
|
|
passphrase = await get_passphrase(msg)
|
|
common_seed = mnemonic.get_seed(passphrase)
|
|
ctx.cache.set(APP_COMMON_SEED, common_seed)
|
|
|
|
else:
|
|
# === Cardano variant ===
|
|
# We want to derive both the normal seed and the Cardano seed together
|
|
async def derive_and_store_roots(
|
|
ctx: Context, msg: ThpCreateNewSession
|
|
) -> None:
|
|
|
|
if msg.passphrase is not None and msg.on_device:
|
|
raise DataError("Passphrase provided when it shouldn't be!")
|
|
|
|
from trezor import wire
|
|
|
|
if not storage_device.is_initialized():
|
|
raise wire.NotInitialized("Device is not initialized")
|
|
|
|
if ctx.cache.is_set(APP_CARDANO_ICARUS_SECRET):
|
|
raise Exception("Cardano icarus secret is already set!")
|
|
|
|
passphrase = await get_passphrase(msg)
|
|
common_seed = mnemonic.get_seed(passphrase)
|
|
ctx.cache.set(APP_COMMON_SEED, common_seed)
|
|
|
|
if msg.derive_cardano:
|
|
from apps.cardano.seed import derive_and_store_secrets
|
|
|
|
ctx.cache.set_bool(APP_COMMON_DERIVE_CARDANO, True)
|
|
derive_and_store_secrets(ctx, passphrase)
|
|
|
|
else:
|
|
if utils.BITCOIN_ONLY:
|
|
# === Bitcoin-only variant ===
|
|
# We use the simple version of `get_seed` that never needs to derive anything else.
|
|
|
|
@cache.stored_async(APP_COMMON_SEED)
|
|
async def get_seed() -> bytes:
|
|
passphrase = await get_passphrase_legacy()
|
|
return mnemonic.get_seed(passphrase=passphrase)
|
|
|
|
else:
|
|
# === Cardano variant ===
|
|
# We want to derive both the normal seed and the Cardano seed together, AND
|
|
# expose a method for Cardano to do the same
|
|
|
|
@cache.stored_async(APP_COMMON_SEED)
|
|
async def get_seed() -> bytes:
|
|
await derive_and_store_roots_legacy()
|
|
common_seed = context.cache_get(APP_COMMON_SEED)
|
|
assert common_seed is not None
|
|
return common_seed
|
|
|
|
async def derive_and_store_roots_legacy() -> None:
|
|
from trezor import wire
|
|
|
|
if not storage_device.is_initialized():
|
|
raise wire.NotInitialized("Device is not initialized")
|
|
|
|
ctx = get_context()
|
|
need_seed = not ctx.cache.is_set(APP_COMMON_SEED)
|
|
need_cardano_secret = ctx.cache.get_bool(
|
|
APP_COMMON_DERIVE_CARDANO
|
|
) and not ctx.cache.is_set(APP_CARDANO_ICARUS_SECRET)
|
|
|
|
if not need_seed and not need_cardano_secret:
|
|
return
|
|
|
|
passphrase = await get_passphrase_legacy()
|
|
|
|
if need_seed:
|
|
common_seed = mnemonic.get_seed(passphrase)
|
|
ctx.cache.set(APP_COMMON_SEED, common_seed)
|
|
|
|
if need_cardano_secret:
|
|
from apps.cardano.seed import derive_and_store_secrets
|
|
|
|
derive_and_store_secrets(ctx, passphrase)
|
|
|
|
|
|
@cache.stored(APP_COMMON_SEED_WITHOUT_PASSPHRASE)
|
|
def _get_seed_without_passphrase() -> bytes:
|
|
if not storage_device.is_initialized():
|
|
raise Exception("Device is not initialized")
|
|
return mnemonic.get_seed(progress_bar=False)
|
|
|
|
|
|
def derive_node_without_passphrase(
|
|
path: Bip32Path, curve_name: str = "secp256k1"
|
|
) -> bip32.HDNode:
|
|
from trezor.crypto import bip32
|
|
|
|
seed = _get_seed_without_passphrase()
|
|
node = bip32.from_seed(seed, curve_name)
|
|
node.derive_path(path)
|
|
return node
|
|
|
|
|
|
def derive_slip21_node_without_passphrase(path: Slip21Path) -> Slip21Node:
|
|
seed = _get_seed_without_passphrase()
|
|
node = Slip21Node(seed)
|
|
node.derive_path(path)
|
|
return node
|
|
|
|
|
|
def remove_ed25519_prefix(pubkey: bytes) -> bytes:
|
|
# 0x01 prefix is not part of the actual public key, hence removed
|
|
return pubkey[1:]
|