1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-29 02:48:18 +00:00

core: rework cache and fix cardano caching

This commit is contained in:
Tomas Susanka 2020-01-27 13:58:32 +00:00 committed by Pavol Rusnak
parent aa6988a556
commit b96d7cafbb
No known key found for this signature in database
GPG Key ID: 91F3B339B9A02A3D
4 changed files with 91 additions and 44 deletions

View File

@ -1,4 +1,5 @@
import storage import storage
from storage import cache
from trezor import wire from trezor import wire
from trezor.crypto import bip32 from trezor.crypto import bip32
@ -6,11 +7,6 @@ from apps.cardano import CURVE, SEED_NAMESPACE
from apps.common import mnemonic from apps.common import mnemonic
from apps.common.passphrase import get as get_passphrase from apps.common.passphrase import get as get_passphrase
if False:
from typing import Optional
_cached_root = None # type: Optional[bytes]
class Keychain: class Keychain:
def __init__(self, path: list, root: bip32.HDNode): def __init__(self, path: list, root: bip32.HDNode):
@ -35,25 +31,30 @@ class Keychain:
async def get_keychain(ctx: wire.Context) -> Keychain: async def get_keychain(ctx: wire.Context) -> Keychain:
global _cached_root root = cache.get(cache.APP_CARDANO_ROOT)
if not storage.is_initialized(): if not storage.is_initialized():
raise wire.NotInitialized("Device is not initialized") raise wire.NotInitialized("Device is not initialized")
if _cached_root is None: if root is None:
passphrase = await get_passphrase(ctx) passphrase = await get_passphrase(ctx)
if mnemonic.is_bip39(): if mnemonic.is_bip39():
# derive the root node from mnemonic and passphrase # derive the root node from mnemonic and passphrase
_cached_root = bip32.from_mnemonic_cardano( root = bip32.from_mnemonic_cardano(
mnemonic.get_secret().decode(), passphrase mnemonic.get_secret().decode(), passphrase
) )
else: else:
seed = mnemonic.get_seed(passphrase) seed = mnemonic.get_seed(passphrase)
_cached_root = bip32.from_seed(seed, "ed25519 cardano seed") root = bip32.from_seed(seed, "ed25519 cardano seed")
storage.cache.set(cache.APP_CARDANO_ROOT, root)
# let's not modify the one in the cache
root = root.clone()
# derive the namespaced root node # derive the namespaced root node
for i in SEED_NAMESPACE: for i in SEED_NAMESPACE:
_cached_root.derive_cardano(i) root.derive_cardano(i)
keychain = Keychain(SEED_NAMESPACE, _cached_root) keychain = Keychain(SEED_NAMESPACE, root)
return keychain return keychain

View File

@ -1,5 +1,5 @@
import storage import storage
import storage.cache from storage import cache
from trezor import wire from trezor import wire
from trezor.crypto import bip32, hashlib, hmac from trezor.crypto import bip32, hashlib, hmac
from trezor.crypto.curve import secp256k1 from trezor.crypto.curve import secp256k1
@ -112,11 +112,11 @@ class Keychain:
async def get_keychain(ctx: wire.Context, namespaces: list) -> Keychain: async def get_keychain(ctx: wire.Context, namespaces: list) -> Keychain:
if not storage.is_initialized(): if not storage.is_initialized():
raise wire.NotInitialized("Device is not initialized") raise wire.NotInitialized("Device is not initialized")
seed = storage.cache.get_seed() seed = cache.get(cache.APP_COMMON_SEED)
if seed is None: if seed is None:
passphrase = await get_passphrase(ctx) passphrase = await get_passphrase(ctx)
seed = mnemonic.get_seed(passphrase) seed = mnemonic.get_seed(passphrase)
storage.cache.set_seed(seed) cache.set(cache.APP_COMMON_SEED, seed)
keychain = Keychain(seed, namespaces) keychain = Keychain(seed, namespaces)
return keychain return keychain
@ -126,10 +126,10 @@ def derive_node_without_passphrase(
) -> bip32.HDNode: ) -> bip32.HDNode:
if not storage.is_initialized(): if not storage.is_initialized():
raise Exception("Device is not initialized") raise Exception("Device is not initialized")
seed = storage.cache.get_seed_without_passphrase() seed = cache.get(cache.APP_COMMON_SEED_WITHOUT_PASSPHRASE)
if seed is None: if seed is None:
seed = mnemonic.get_seed(progress_bar=False) seed = mnemonic.get_seed(progress_bar=False)
storage.cache.set_seed_without_passphrase(seed) cache.set(cache.APP_COMMON_SEED_WITHOUT_PASSPHRASE, seed)
node = bip32.from_seed(seed, curve_name) node = bip32.from_seed(seed, curve_name)
node.derive_path(path) node.derive_path(path)
return node return node
@ -138,10 +138,10 @@ def derive_node_without_passphrase(
def derive_slip21_node_without_passphrase(path: list) -> Slip21Node: def derive_slip21_node_without_passphrase(path: list) -> Slip21Node:
if not storage.is_initialized(): if not storage.is_initialized():
raise Exception("Device is not initialized") raise Exception("Device is not initialized")
seed = storage.cache.get_seed_without_passphrase() seed = cache.get(cache.APP_COMMON_SEED_WITHOUT_PASSPHRASE)
if seed is None: if seed is None:
seed = mnemonic.get_seed(progress_bar=False) seed = mnemonic.get_seed(progress_bar=False)
storage.cache.set_seed_without_passphrase(seed) cache.set(cache.APP_COMMON_SEED_WITHOUT_PASSPHRASE, seed)
node = Slip21Node(seed) node = Slip21Node(seed)
node.derive_path(path) node.derive_path(path)
return node return node

View File

@ -3,39 +3,35 @@ from trezor.crypto import random
if False: if False:
from typing import Optional from typing import Optional
_cached_seed = None # type: Optional[bytes] APP_COMMON_SEED = 0
_cached_seed_without_passphrase = None # type: Optional[bytes] # Needed for SLIP-21 APP_COMMON_SEED_WITHOUT_PASSPHRASE = 1
_cached_session_id = None # type: Optional[bytes] APP_CARDANO_ROOT = 2
_cache_session_id = None # type: Optional[bytes]
_cache = {}
if False:
from typing import Any
def get_session_id() -> bytes: def get_session_id() -> bytes:
global _cached_session_id global _cache_session_id
if not _cached_session_id: if not _cache_session_id:
_cached_session_id = random.bytes(32) _cache_session_id = random.bytes(32)
return _cached_session_id return _cache_session_id
def set_seed(seed: Optional[bytes]) -> None: def set(key: int, value: Any) -> None:
global _cached_seed global _cache
_cached_seed = seed _cache[key] = value
def get_seed() -> Optional[bytes]: def get(key: int) -> Any:
return _cached_seed return _cache.get(key)
def set_seed_without_passphrase(seed: Optional[bytes]) -> None:
global _cached_seed_without_passphrase
_cached_seed_without_passphrase = seed
def get_seed_without_passphrase() -> Optional[bytes]:
return _cached_seed_without_passphrase
def clear() -> None: def clear() -> None:
global _cached_session_id global _cache_session_id
_cached_session_id = None global _cache
_cache_session_id = None
set_seed(None) _cache.clear()
set_seed_without_passphrase(None)

View File

@ -22,6 +22,7 @@ from trezorlib.tools import parse_path
XPUB_PASSPHRASE_A = "xpub6CekxGcnqnJ6osfY4Rrq7W5ogFtR54KUvz4H16XzaQuukMFZCGebEpVznfq4yFcKEmYyShwj2UKjL7CazuNSuhdkofF4mHabHkLxCMVvsqG" XPUB_PASSPHRASE_A = "xpub6CekxGcnqnJ6osfY4Rrq7W5ogFtR54KUvz4H16XzaQuukMFZCGebEpVznfq4yFcKEmYyShwj2UKjL7CazuNSuhdkofF4mHabHkLxCMVvsqG"
XPUB_PASSPHRASE_NONE = "xpub6BiVtCpG9fQPxnPmHXG8PhtzQdWC2Su4qWu6XW9tpWFYhxydCLJGrWBJZ5H6qTAHdPQ7pQhtpjiYZVZARo14qHiay2fvrX996oEP42u8wZy" XPUB_PASSPHRASE_NONE = "xpub6BiVtCpG9fQPxnPmHXG8PhtzQdWC2Su4qWu6XW9tpWFYhxydCLJGrWBJZ5H6qTAHdPQ7pQhtpjiYZVZARo14qHiay2fvrX996oEP42u8wZy"
XPUB_CARDANO_PASSPHRASE_B = "d80e770f6dfc3edb58eaab68aa091b2c27b08a47583471e93437ac5f8baa61880c7af4938a941c084c19731e6e57a5710e6ad1196263291aea297ce0eec0f177"
def _get_xpub(client, passphrase): def _get_xpub(client, passphrase):
@ -222,3 +223,52 @@ def test_passphrase_missing(client):
response = client.call_raw(messages.PassphraseAck(passphrase=None, on_device=False)) response = client.call_raw(messages.PassphraseAck(passphrase=None, on_device=False))
assert isinstance(response, messages.Failure) assert isinstance(response, messages.Failure)
assert response.code == FailureType.DataError assert response.code == FailureType.DataError
@pytest.mark.skip_ui
@pytest.mark.skip_t1
@pytest.mark.altcoin
@pytest.mark.setup_client(passphrase=True)
def test_cardano_passphrase(client):
# Cardano uses a variation of BIP-39 so we need to ask for the passphrase again.
response = client.call_raw(messages.Initialize())
assert isinstance(response, messages.Features)
session_id = response.session_id
assert len(session_id) == 32
# GetPublicKey requires passphrase and since it is not cached,
# Trezor will prompt for it.
xpub = _get_xpub(client, passphrase="A")
assert xpub == XPUB_PASSPHRASE_A
# The passphrase is now cached for non-Cardano coins.
xpub = _get_xpub(client, passphrase=None)
assert xpub == XPUB_PASSPHRASE_A
# Cardano will prompt for it again.
response = client.call_raw(
messages.CardanoGetPublicKey(address_n=parse_path("44'/1815'/0'/0/0"))
)
assert isinstance(response, messages.PassphraseRequest)
response = client.call_raw(messages.PassphraseAck(passphrase="B"))
assert response.xpub == XPUB_CARDANO_PASSPHRASE_B
# But now also Cardano has it cached.
response = client.call_raw(
messages.CardanoGetPublicKey(address_n=parse_path("44'/1815'/0'/0/0"))
)
assert response.xpub == XPUB_CARDANO_PASSPHRASE_B
# And others behaviour did not change.
xpub = _get_xpub(client, passphrase=None)
assert xpub == XPUB_PASSPHRASE_A
# Initialize with the session id does not destroy the state
client.call_raw(messages.Initialize(session_id=session_id))
xpub = _get_xpub(client, passphrase=None)
assert xpub == XPUB_PASSPHRASE_A
response = client.call_raw(
messages.CardanoGetPublicKey(address_n=parse_path("44'/1815'/0'/0/0"))
)
assert response.xpub == XPUB_CARDANO_PASSPHRASE_B