1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-01-22 13:21:03 +00:00

apps: introduce Keychain API

This commit is contained in:
Jan Pochyla 2018-11-13 17:09:52 +01:00
parent 312c252bc1
commit 5bc47fc567
30 changed files with 202 additions and 128 deletions

View File

@ -1,8 +1,27 @@
from trezor.crypto import base58, crc, hashlib from trezor.crypto import base58, crc, hashlib
from . import cbor from apps.cardano import cbor
from apps.common import HARDENED
from apps.common.seed import remove_ed25519_prefix
from apps.common import HARDENED, seed
def derive_address_and_node(keychain, path: list):
node = keychain.derive(path)
address_payload = None
address_attributes = {}
address_root = _get_address_root(node, address_payload)
address_type = 0
address_data = [address_root, address_attributes, address_type]
address_data_encoded = cbor.encode(address_data)
address = base58.encode(
cbor.encode(
[cbor.Tagged(24, address_data_encoded), crc.crc32(address_data_encoded)]
)
)
return (address, node)
def validate_full_path(path: list) -> bool: def validate_full_path(path: list) -> bool:
@ -36,31 +55,9 @@ def _address_hash(data) -> bytes:
def _get_address_root(node, payload): def _get_address_root(node, payload):
extpubkey = seed.remove_ed25519_prefix(node.public_key()) + node.chain_code() extpubkey = remove_ed25519_prefix(node.public_key()) + node.chain_code()
if payload: if payload:
payload = {1: cbor.encode(payload)} payload = {1: cbor.encode(payload)}
else: else:
payload = {} payload = {}
return _address_hash([0, [0, extpubkey], payload]) return _address_hash([0, [0, extpubkey], payload])
def derive_address_and_node(root_node, path: list):
derived_node = root_node.clone()
address_payload = None
address_attributes = {}
for indice in path:
derived_node.derive_cardano(indice)
address_root = _get_address_root(derived_node, address_payload)
address_type = 0
address_data = [address_root, address_attributes, address_type]
address_data_encoded = cbor.encode(address_data)
address = base58.encode(
cbor.encode(
[cbor.Tagged(24, address_data_encoded), crc.crc32(address_data_encoded)]
)
)
return (address, derived_node)

View File

@ -1,28 +1,23 @@
from trezor import log, ui, wire from trezor import log, ui, wire
from trezor.crypto import bip32
from trezor.messages.CardanoAddress import CardanoAddress from trezor.messages.CardanoAddress import CardanoAddress
from .address import derive_address_and_node, validate_full_path from apps.cardano import seed
from .layout import confirm_with_pagination from apps.cardano.address import derive_address_and_node, validate_full_path
from apps.cardano.layout import confirm_with_pagination
from apps.common import paths, seed, storage from apps.common import paths
async def get_address(ctx, msg): async def get_address(ctx, msg):
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, validate_full_path, path=msg.address_n) await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
mnemonic = storage.get_mnemonic()
passphrase = await seed._get_cached_passphrase(ctx)
root_node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
try: try:
address, _ = derive_address_and_node(root_node, msg.address_n) address, _ = derive_address_and_node(keychain, msg.address_n)
except ValueError as e: except ValueError as e:
if __debug__: if __debug__:
log.exception(__name__, e) log.exception(__name__, e)
raise wire.ProcessError("Deriving address failed") raise wire.ProcessError("Deriving address failed")
mnemonic = None
root_node = None
if msg.show_display: if msg.show_display:
if not await confirm_with_pagination( if not await confirm_with_pagination(

View File

@ -1,42 +1,38 @@
from ubinascii import hexlify from ubinascii import hexlify
from trezor import log, wire from trezor import log, wire
from trezor.crypto import bip32
from trezor.messages.CardanoPublicKey import CardanoPublicKey from trezor.messages.CardanoPublicKey import CardanoPublicKey
from trezor.messages.HDNodeType import HDNodeType from trezor.messages.HDNodeType import HDNodeType
from .address import derive_address_and_node from apps.cardano import seed
from apps.cardano.address import derive_address_and_node
from apps.common import layout, paths, seed, storage from apps.common import layout, paths
from apps.common.seed import remove_ed25519_prefix
async def get_public_key(ctx, msg): async def get_public_key(ctx, msg):
keychain = await seed.get_keychain(ctx)
await paths.validate_path( await paths.validate_path(
ctx, paths.validate_path_for_get_public_key, path=msg.address_n, slip44_id=1815 ctx, paths.validate_path_for_get_public_key, path=msg.address_n, slip44_id=1815
) )
mnemonic = storage.get_mnemonic()
passphrase = await seed._get_cached_passphrase(ctx)
root_node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
try: try:
key = _get_public_key(root_node, msg.address_n) key = _get_public_key(keychain, msg.address_n)
except ValueError as e: except ValueError as e:
if __debug__: if __debug__:
log.exception(__name__, e) log.exception(__name__, e)
raise wire.ProcessError("Deriving public key failed") raise wire.ProcessError("Deriving public key failed")
mnemonic = None
root_node = None
if msg.show_display: if msg.show_display:
await layout.show_pubkey(ctx, key.node.public_key) await layout.show_pubkey(ctx, key.node.public_key)
return key return key
def _get_public_key(root_node, derivation_path: list): def _get_public_key(keychain, derivation_path: list):
_, node = derive_address_and_node(root_node, derivation_path) _, node = derive_address_and_node(keychain, derivation_path)
public_key = hexlify(seed.remove_ed25519_prefix(node.public_key())).decode() public_key = hexlify(remove_ed25519_prefix(node.public_key())).decode()
chain_code = hexlify(node.chain_code()).decode() chain_code = hexlify(node.chain_code()).decode()
xpub_key = public_key + chain_code xpub_key = public_key + chain_code
@ -45,7 +41,7 @@ def _get_public_key(root_node, derivation_path: list):
child_num=node.child_num(), child_num=node.child_num(),
fingerprint=node.fingerprint(), fingerprint=node.fingerprint(),
chain_code=node.chain_code(), chain_code=node.chain_code(),
public_key=seed.remove_ed25519_prefix(node.public_key()), public_key=remove_ed25519_prefix(node.public_key()),
) )
return CardanoPublicKey(node=node_type, xpub=xpub_key) return CardanoPublicKey(node=node_type, xpub=xpub_key)

41
src/apps/cardano/seed.py Normal file
View File

@ -0,0 +1,41 @@
from trezor import wire
from trezor.crypto import bip32
from apps.common import HARDENED, cache, storage
from apps.common.request_passphrase import protect_by_passphrase
class Keychain:
def __init__(self, root: bip32.HDNode):
self.root = root
def derive(self, path: list) -> bip32.HDNode:
self.validate_path(path)
node = self.root.clone()
for i in path:
node.derive_cardano(i)
return node
def validate_path(self, path: list) -> None:
if len(path) < 2 or len(path) > 5:
raise wire.ProcessError("Derivation path must be composed from 2-5 indices")
if path[0] != HARDENED | 44 or path[1] != HARDENED | 1815:
raise wire.ProcessError("This is not cardano derivation path")
async def get_keychain(ctx: wire.Context) -> Keychain:
if not storage.is_initialized():
# device does not have any seed
raise wire.ProcessError("Device is not initialized")
# acquire passphrase
passphrase = cache.get_passphrase()
if passphrase is None:
passphrase = await protect_by_passphrase(ctx)
cache.set_passphrase(passphrase)
# compute the seed from mnemonic and passphrase
root = bip32.from_mnemonic_cardano(storage.get_mnemonic(), passphrase)
keychain = Keychain(root)
return keychain

View File

@ -1,18 +1,17 @@
from trezor import log, ui, wire from trezor import log, ui, wire
from trezor.crypto import base58, bip32, hashlib from trezor.crypto import base58, hashlib
from trezor.crypto.curve import ed25519 from trezor.crypto.curve import ed25519
from trezor.messages.CardanoSignedTx import CardanoSignedTx from trezor.messages.CardanoSignedTx import CardanoSignedTx
from trezor.messages.CardanoTxRequest import CardanoTxRequest from trezor.messages.CardanoTxRequest import CardanoTxRequest
from trezor.messages.MessageType import CardanoTxAck from trezor.messages.MessageType import CardanoTxAck
from trezor.ui.text import BR from trezor.ui.text import BR
from .address import derive_address_and_node, validate_full_path from apps.cardano import cbor, seed
from .layout import confirm_with_pagination, progress from apps.cardano.address import derive_address_and_node, validate_full_path
from apps.cardano.layout import confirm_with_pagination, progress
from apps.cardano import cbor
from apps.common import seed, storage
from apps.common.layout import address_n_to_str, split_address from apps.common.layout import address_n_to_str, split_address
from apps.common.paths import validate_path from apps.common.paths import validate_path
from apps.common.seed import remove_ed25519_prefix
from apps.homescreen.homescreen import display_homescreen from apps.homescreen.homescreen import display_homescreen
@ -80,9 +79,7 @@ async def request_transaction(ctx, tx_req: CardanoTxRequest, index: int):
async def sign_tx(ctx, msg): async def sign_tx(ctx, msg):
mnemonic = storage.get_mnemonic() keychain = await seed.get_keychain(ctx)
passphrase = await seed._get_cached_passphrase(ctx)
root_node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
progress.init(msg.transactions_count, "Loading data") progress.init(msg.transactions_count, "Loading data")
@ -103,7 +100,7 @@ async def sign_tx(ctx, msg):
# sign the transaction bundle and prepare the result # sign the transaction bundle and prepare the result
transaction = Transaction( transaction = Transaction(
msg.inputs, msg.outputs, transactions, root_node, msg.network msg.inputs, msg.outputs, transactions, keychain, msg.network
) )
tx_body, tx_hash = transaction.serialise_tx() tx_body, tx_hash = transaction.serialise_tx()
tx = CardanoSignedTx(tx_body=tx_body, tx_hash=tx_hash) tx = CardanoSignedTx(tx_body=tx_body, tx_hash=tx_hash)
@ -135,12 +132,12 @@ def _micro_ada_to_ada(amount: float) -> float:
class Transaction: class Transaction:
def __init__( def __init__(
self, inputs: list, outputs: list, transactions: list, root_node, network: int self, inputs: list, outputs: list, transactions: list, keychain, network: int
): ):
self.inputs = inputs self.inputs = inputs
self.outputs = outputs self.outputs = outputs
self.transactions = transactions self.transactions = transactions
self.root_node = root_node self.keychain = keychain
# attributes have to be always empty in current Cardano # attributes have to be always empty in current Cardano
self.attributes = {} self.attributes = {}
if network == 1: if network == 1:
@ -170,7 +167,7 @@ class Transaction:
nodes = [] nodes = []
for input in self.inputs: for input in self.inputs:
_, node = derive_address_and_node(self.root_node, input.address_n) _, node = derive_address_and_node(self.keychain, input.address_n)
nodes.append(node) nodes.append(node)
for index, output_index in enumerate(output_indexes): for index, output_index in enumerate(output_indexes):
@ -198,7 +195,7 @@ class Transaction:
for output in self.outputs: for output in self.outputs:
if output.address_n: if output.address_n:
address, _ = derive_address_and_node(self.root_node, output.address_n) address, _ = derive_address_and_node(self.keychain, output.address_n)
change_addresses.append(address) change_addresses.append(address)
change_derivation_paths.append(output.address_n) change_derivation_paths.append(output.address_n)
change_coins.append(output.amount) change_coins.append(output.amount)
@ -225,7 +222,7 @@ class Transaction:
node.private_key(), node.private_key_ext(), message node.private_key(), node.private_key_ext(), message
) )
extended_public_key = ( extended_public_key = (
seed.remove_ed25519_prefix(node.public_key()) + node.chain_code() remove_ed25519_prefix(node.public_key()) + node.chain_code()
) )
witnesses.append( witnesses.append(
[ [

View File

@ -7,38 +7,43 @@ from apps.common.request_passphrase import protect_by_passphrase
_DEFAULT_CURVE = "secp256k1" _DEFAULT_CURVE = "secp256k1"
async def derive_node( class Keychain:
ctx: wire.Context, path: list, curve_name: str = _DEFAULT_CURVE def __init__(self, seed: bytes):
) -> bip32.HDNode: self.seed = seed
seed = await _get_cached_seed(ctx)
node = bip32.from_seed(seed, curve_name) def derive(self, path: list, curve_name: str = _DEFAULT_CURVE) -> bip32.HDNode:
node = bip32.from_seed(self.seed, curve_name)
node.derive_path(path) node.derive_path(path)
return node return node
async def _get_cached_seed(ctx: wire.Context) -> bytes: async def get_keychain(ctx: wire.Context) -> Keychain:
if not storage.is_initialized(): if not storage.is_initialized():
# device does not have any seed
raise wire.ProcessError("Device is not initialized") raise wire.ProcessError("Device is not initialized")
if cache.get_seed() is None:
passphrase = await _get_cached_passphrase(ctx)
seed = bip39.seed(storage.get_mnemonic(), passphrase)
cache.set_seed(seed)
return cache.get_seed()
seed = cache.get_seed()
async def _get_cached_passphrase(ctx: wire.Context) -> str: if seed is None:
if cache.get_passphrase() is None: # acquire passphrase
passphrase = cache.get_passphrase()
if passphrase is None:
passphrase = await protect_by_passphrase(ctx) passphrase = await protect_by_passphrase(ctx)
cache.set_passphrase(passphrase) cache.set_passphrase(passphrase)
return cache.get_passphrase()
# compute the seed from mnemonic and passphrase
seed = bip39.seed(storage.get_mnemonic(), passphrase)
cache.set_seed(seed)
keychain = Keychain(seed)
return keychain
def derive_node_without_passphrase( def derive_node_without_passphrase(
path: list, curve_name: str = _DEFAULT_CURVE path: list, curve_name: str = _DEFAULT_CURVE
) -> bip32.HDNode: ) -> bip32.HDNode:
if not storage.is_initialized(): if not storage.is_initialized():
# device does not have any seed
raise Exception("Device is not initialized") raise Exception("Device is not initialized")
seed = bip39.seed(storage.get_mnemonic(), "") seed = bip39.seed(storage.get_mnemonic(), "")
node = bip32.from_seed(seed, curve_name) node = bip32.from_seed(seed, curve_name)
node.derive_path(path) node.derive_path(path)

View File

@ -1,20 +1,19 @@
from .address import ethereum_address_hex, validate_full_path from trezor.crypto.curve import secp256k1
from trezor.crypto.hashlib import sha3_256
from trezor.messages.EthereumAddress import EthereumAddress
from apps.common import paths from apps.common import paths, seed
from apps.common.layout import address_n_to_str, show_address, show_qr from apps.common.layout import address_n_to_str, show_address, show_qr
from apps.ethereum import networks from apps.ethereum import networks
from apps.ethereum.address import ethereum_address_hex, validate_full_path
async def get_address(ctx, msg): async def get_address(ctx, msg):
from trezor.messages.EthereumAddress import EthereumAddress keychain = await seed.get_keychain(ctx)
from trezor.crypto.curve import secp256k1
from trezor.crypto.hashlib import sha3_256
from apps.common import seed
await paths.validate_path(ctx, validate_full_path, path=msg.address_n) await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
node = await seed.derive_node(ctx, msg.address_n) node = keychain.derive(msg.address_n)
seckey = node.private_key() seckey = node.private_key()
public_key = secp256k1.publickey(seckey, False) # uncompressed public_key = secp256k1.publickey(seckey, False) # uncompressed
address = sha3_256(public_key[1:], keccak=True).digest()[12:] address = sha3_256(public_key[1:], keccak=True).digest()[12:]

View File

@ -21,11 +21,12 @@ def message_digest(message):
async def sign_message(ctx, msg): async def sign_message(ctx, msg):
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, validate_full_path, path=msg.address_n) await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
await require_confirm_sign_message(ctx, msg.message) await require_confirm_sign_message(ctx, msg.message)
node = await seed.derive_node(ctx, msg.address_n) node = keychain.derive(msg.address_n)
signature = secp256k1.sign( signature = secp256k1.sign(
node.private_key(), node.private_key(),
message_digest(msg.message), message_digest(msg.message),

View File

@ -131,8 +131,9 @@ async def send_request_chunk(ctx, data_left: int):
async def send_signature(ctx, msg: EthereumSignTx, digest): async def send_signature(ctx, msg: EthereumSignTx, digest):
node = await seed.derive_node(ctx, msg.address_n) keychain = await seed.get_keychain(ctx)
node = keychain.derive(msg.address_n)
signature = secp256k1.sign( signature = secp256k1.sign(
node.private_key(), digest, False, secp256k1.CANONICAL_SIG_ETHEREUM node.private_key(), digest, False, secp256k1.CANONICAL_SIG_ETHEREUM
) )

View File

@ -7,9 +7,11 @@ from apps.common.layout import address_n_to_str, show_address, show_qr
async def get_address(ctx, msg): async def get_address(ctx, msg):
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, validate_full_path, path=msg.address_n) await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
node = await seed.derive_node(ctx, msg.address_n, LISK_CURVE) node = keychain.derive(msg.address_n, LISK_CURVE)
pubkey = node.public_key() pubkey = node.public_key()
pubkey = pubkey[1:] # skip ed25519 pubkey marker pubkey = pubkey[1:] # skip ed25519 pubkey marker
address = get_address_from_public_key(pubkey) address = get_address_from_public_key(pubkey)

View File

@ -6,9 +6,11 @@ from apps.common import layout, paths, seed
async def get_public_key(ctx, msg): async def get_public_key(ctx, msg):
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, validate_full_path, path=msg.address_n) await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
node = await seed.derive_node(ctx, msg.address_n, LISK_CURVE) node = keychain.derive(msg.address_n, LISK_CURVE)
pubkey = node.public_key() pubkey = node.public_key()
pubkey = pubkey[1:] # skip ed25519 pubkey marker pubkey = pubkey[1:] # skip ed25519 pubkey marker

View File

@ -23,10 +23,12 @@ def message_digest(message):
async def sign_message(ctx, msg): async def sign_message(ctx, msg):
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, validate_full_path, path=msg.address_n) await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
await require_confirm_sign_message(ctx, msg.message) await require_confirm_sign_message(ctx, msg.message)
node = await seed.derive_node(ctx, msg.address_n, LISK_CURVE) node = keychain.derive(msg.address_n, LISK_CURVE)
seckey = node.private_key() seckey = node.private_key()
pubkey = node.public_key() pubkey = node.public_key()
pubkey = pubkey[1:] # skip ed25519 pubkey marker pubkey = pubkey[1:] # skip ed25519 pubkey marker

View File

@ -14,9 +14,11 @@ from apps.common import paths, seed
async def sign_tx(ctx, msg): async def sign_tx(ctx, msg):
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, validate_full_path, path=msg.address_n) await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
pubkey, seckey = await _get_keys(ctx, msg) pubkey, seckey = _get_keys(keychain, msg)
transaction = _update_raw_tx(msg.transaction, pubkey) transaction = _update_raw_tx(msg.transaction, pubkey)
try: try:
@ -37,8 +39,8 @@ async def sign_tx(ctx, msg):
return LiskSignedTx(signature=signature) return LiskSignedTx(signature=signature)
async def _get_keys(ctx, msg): def _get_keys(keychain, msg):
node = await seed.derive_node(ctx, msg.address_n, LISK_CURVE) node = keychain.derive(msg.address_n, LISK_CURVE)
seckey = node.private_key() seckey = node.private_key()
pubkey = node.public_key() pubkey = node.public_key()

View File

@ -12,7 +12,9 @@ async def get_creds(ctx, address_n=None, network_type=None):
curve = "ed25519" curve = "ed25519"
else: else:
curve = "secp256k1" curve = "secp256k1"
node = await seed.derive_node(ctx, address_n, curve)
keychain = await seed.get_keychain(ctx)
node = keychain.derive(address_n, curve)
if use_slip0010: if use_slip0010:
key_seed = node.private_key() key_seed = node.private_key()

View File

@ -9,10 +9,12 @@ from apps.common.paths import validate_path
async def get_address(ctx, msg): async def get_address(ctx, msg):
network = validate_network(msg.network) keychain = await seed.get_keychain(ctx)
await validate_path(ctx, check_path, path=msg.address_n, network=msg.network)
node = await seed.derive_node(ctx, msg.address_n, NEM_CURVE) network = validate_network(msg.network)
await validate_path(ctx, check_path, path=msg.address_n, network=network)
node = keychain.derive(msg.address_n, NEM_CURVE)
address = node.nem_address(network) address = node.nem_address(network)
if msg.show_display: if msg.show_display:

View File

@ -11,12 +11,15 @@ from apps.common.paths import validate_path
async def sign_tx(ctx, msg: NEMSignTx): async def sign_tx(ctx, msg: NEMSignTx):
keychain = await seed.get_keychain(ctx)
validate(msg) validate(msg)
await validate_path( await validate_path(
ctx, check_path, path=msg.transaction.address_n, network=msg.transaction.network ctx, check_path, path=msg.transaction.address_n, network=msg.transaction.network
) )
node = await seed.derive_node(ctx, msg.transaction.address_n, NEM_CURVE) node = keychain.derive(msg.transaction.address_n, NEM_CURVE)
if msg.multisig: if msg.multisig:
public_key = msg.multisig.signer public_key = msg.multisig.signer

View File

@ -8,9 +8,11 @@ from apps.common.layout import address_n_to_str, show_address, show_qr
async def get_address(ctx, msg: RippleGetAddress): async def get_address(ctx, msg: RippleGetAddress):
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n) await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
node = await seed.derive_node(ctx, msg.address_n) node = keychain.derive(msg.address_n)
pubkey = node.public_key() pubkey = node.public_key()
address = helpers.address_from_public_key(pubkey) address = helpers.address_from_public_key(pubkey)

View File

@ -12,10 +12,13 @@ from apps.common import paths, seed
async def sign_tx(ctx, msg: RippleSignTx): async def sign_tx(ctx, msg: RippleSignTx):
keychain = await seed.get_keychain(ctx)
validate(msg) validate(msg)
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n) await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
node = await seed.derive_node(ctx, msg.address_n) node = keychain.derive(msg.address_n)
source_address = helpers.address_from_public_key(node.public_key()) source_address = helpers.address_from_public_key(node.public_key())
set_canonical_flag(msg) set_canonical_flag(msg)

View File

@ -7,9 +7,11 @@ from apps.stellar import helpers
async def get_address(ctx, msg: StellarGetAddress): async def get_address(ctx, msg: StellarGetAddress):
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n) await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
node = await seed.derive_node(ctx, msg.address_n, helpers.STELLAR_CURVE) node = keychain.derive(msg.address_n, helpers.STELLAR_CURVE)
pubkey = seed.remove_ed25519_prefix(node.public_key()) pubkey = seed.remove_ed25519_prefix(node.public_key())
address = helpers.address_from_public_key(pubkey) address = helpers.address_from_public_key(pubkey)

View File

@ -13,14 +13,16 @@ from apps.stellar.operations import process_operation
async def sign_tx(ctx, msg: StellarSignTx): async def sign_tx(ctx, msg: StellarSignTx):
if msg.num_operations == 0: keychain = await seed.get_keychain(ctx)
raise ProcessError("Stellar: At least one operation is required")
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n) await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
node = await seed.derive_node(ctx, msg.address_n, consts.STELLAR_CURVE) node = keychain.derive(msg.address_n, consts.STELLAR_CURVE)
pubkey = seed.remove_ed25519_prefix(node.public_key()) pubkey = seed.remove_ed25519_prefix(node.public_key())
if msg.num_operations == 0:
raise ProcessError("Stellar: At least one operation is required")
w = bytearray() w = bytearray()
await _init(ctx, w, pubkey, msg) await _init(ctx, w, pubkey, msg)
_timebounds(w, msg.timebounds_start, msg.timebounds_end) _timebounds(w, msg.timebounds_start, msg.timebounds_end)

View File

@ -7,8 +7,11 @@ from apps.tezos import helpers
async def get_address(ctx, msg): async def get_address(ctx, msg):
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n) await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
node = await seed.derive_node(ctx, msg.address_n, helpers.TEZOS_CURVE)
node = keychain.derive(msg.address_n, helpers.TEZOS_CURVE)
pk = seed.remove_ed25519_prefix(node.public_key()) pk = seed.remove_ed25519_prefix(node.public_key())
pkh = hashlib.blake2b(pk, outlen=20).digest() pkh = hashlib.blake2b(pk, outlen=20).digest()

View File

@ -10,9 +10,11 @@ from apps.tezos import helpers
async def get_public_key(ctx, msg): async def get_public_key(ctx, msg):
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n) keychain = await seed.get_keychain(ctx)
node = await seed.derive_node(ctx, msg.address_n, helpers.TEZOS_CURVE)
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
node = keychain.derive(msg.address_n, helpers.TEZOS_CURVE)
pk = seed.remove_ed25519_prefix(node.public_key()) pk = seed.remove_ed25519_prefix(node.public_key())
pk_prefixed = helpers.base58_encode_check(pk, prefix=helpers.TEZOS_PUBLICKEY_PREFIX) pk_prefixed = helpers.base58_encode_check(pk, prefix=helpers.TEZOS_PUBLICKEY_PREFIX)

View File

@ -10,8 +10,11 @@ from apps.tezos import helpers, layout
async def sign_tx(ctx, msg): async def sign_tx(ctx, msg):
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n) await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
node = await seed.derive_node(ctx, msg.address_n, helpers.TEZOS_CURVE)
node = keychain.derive(msg.address_n, helpers.TEZOS_CURVE)
if msg.transaction is not None: if msg.transaction is not None:
to = _get_address_from_contract(msg.transaction.destination) to = _get_address_from_contract(msg.transaction.destination)

View File

@ -9,6 +9,8 @@ from apps.common.confirm import require_confirm
async def cipher_key_value(ctx, msg): async def cipher_key_value(ctx, msg):
keychain = await seed.get_keychain(ctx)
if len(msg.value) % 16 > 0: if len(msg.value) % 16 > 0:
raise wire.DataError("Value length must be a multiple of 16") raise wire.DataError("Value length must be a multiple of 16")
@ -23,7 +25,7 @@ async def cipher_key_value(ctx, msg):
text.normal(msg.key) text.normal(msg.key)
await require_confirm(ctx, text) await require_confirm(ctx, text)
node = await seed.derive_node(ctx, msg.address_n) node = keychain.derive(msg.address_n)
value = compute_cipher_key_value(msg, node.private_key()) value = compute_cipher_key_value(msg, node.private_key())
return CipheredKeyValue(value=value) return CipheredKeyValue(value=value)

View File

@ -8,6 +8,8 @@ from apps.wallet.sign_tx import addresses
async def get_address(ctx, msg): async def get_address(ctx, msg):
keychain = await seed.get_keychain(ctx)
coin_name = msg.coin_name or "Bitcoin" coin_name = msg.coin_name or "Bitcoin"
coin = coins.by_name(coin_name) coin = coins.by_name(coin_name)
@ -19,7 +21,7 @@ async def get_address(ctx, msg):
script_type=msg.script_type, script_type=msg.script_type,
) )
node = await seed.derive_node(ctx, msg.address_n, curve_name=coin.curve_name) node = keychain.derive(msg.address_n, coin.curve_name)
address = addresses.get_address(msg.script_type, coin, node, msg.multisig) address = addresses.get_address(msg.script_type, coin, node, msg.multisig)
address_short = addresses.address_short(coin, address) address_short = addresses.address_short(coin, address)

View File

@ -14,6 +14,8 @@ from apps.wallet.sign_identity import (
async def get_ecdh_session_key(ctx, msg): async def get_ecdh_session_key(ctx, msg):
keychain = await seed.get_keychain(ctx)
if msg.ecdsa_curve_name is None: if msg.ecdsa_curve_name is None:
msg.ecdsa_curve_name = "secp256k1" msg.ecdsa_curve_name = "secp256k1"
@ -22,7 +24,7 @@ async def get_ecdh_session_key(ctx, msg):
await require_confirm_ecdh_session_key(ctx, msg.identity) await require_confirm_ecdh_session_key(ctx, msg.identity)
address_n = get_ecdh_path(identity, msg.identity.index or 0) address_n = get_ecdh_path(identity, msg.identity.index or 0)
node = await seed.derive_node(ctx, address_n, msg.ecdsa_curve_name) node = keychain.derive(address_n, msg.ecdsa_curve_name)
session_key = ecdh( session_key = ecdh(
seckey=node.private_key(), seckey=node.private_key(),

View File

@ -7,14 +7,14 @@ from apps.common import coins, layout, seed
async def get_public_key(ctx, msg): async def get_public_key(ctx, msg):
keychain = await seed.get_keychain(ctx)
coin_name = msg.coin_name or "Bitcoin" coin_name = msg.coin_name or "Bitcoin"
coin = coins.by_name(coin_name) coin = coins.by_name(coin_name)
curve_name = msg.ecdsa_curve_name or coin.curve_name
script_type = msg.script_type or InputScriptType.SPENDADDRESS script_type = msg.script_type or InputScriptType.SPENDADDRESS
curve_name = msg.ecdsa_curve_name node = keychain.derive(msg.address_n, curve_name=curve_name)
if not curve_name:
curve_name = coin.curve_name
node = await seed.derive_node(ctx, msg.address_n, curve_name=curve_name)
if ( if (
script_type in [InputScriptType.SPENDADDRESS, InputScriptType.SPENDMULTISIG] script_type in [InputScriptType.SPENDADDRESS, InputScriptType.SPENDMULTISIG]

View File

@ -11,6 +11,8 @@ from apps.common.confirm import require_confirm
async def sign_identity(ctx, msg): async def sign_identity(ctx, msg):
keychain = await seed.get_keychain(ctx)
if msg.ecdsa_curve_name is None: if msg.ecdsa_curve_name is None:
msg.ecdsa_curve_name = "secp256k1" msg.ecdsa_curve_name = "secp256k1"
@ -19,7 +21,7 @@ async def sign_identity(ctx, msg):
await require_confirm_sign_identity(ctx, msg.identity, msg.challenge_visual) await require_confirm_sign_identity(ctx, msg.identity, msg.challenge_visual)
address_n = get_identity_path(identity, msg.identity.index or 0) address_n = get_identity_path(identity, msg.identity.index or 0)
node = await seed.derive_node(ctx, address_n, msg.ecdsa_curve_name) node = keychain.derive(address_n, msg.ecdsa_curve_name)
coin = coins.by_name("Bitcoin") coin = coins.by_name("Bitcoin")
if msg.ecdsa_curve_name == "secp256k1": if msg.ecdsa_curve_name == "secp256k1":

View File

@ -12,6 +12,8 @@ from apps.wallet.sign_tx.addresses import get_address, validate_full_path
async def sign_message(ctx, msg): async def sign_message(ctx, msg):
keychain = await seed.get_keychain(ctx)
message = msg.message message = msg.message
address_n = msg.address_n address_n = msg.address_n
coin_name = msg.coin_name or "Bitcoin" coin_name = msg.coin_name or "Bitcoin"
@ -19,7 +21,6 @@ async def sign_message(ctx, msg):
coin = coins.by_name(coin_name) coin = coins.by_name(coin_name)
await require_confirm_sign_message(ctx, message) await require_confirm_sign_message(ctx, message)
await validate_path( await validate_path(
ctx, ctx,
validate_full_path, validate_full_path,
@ -29,7 +30,7 @@ async def sign_message(ctx, msg):
validate_script_type=False, validate_script_type=False,
) )
node = await seed.derive_node(ctx, address_n, curve_name=coin.curve_name) node = keychain.derive(address_n, coin.curve_name)
seckey = node.private_key() seckey = node.private_key()
address = get_address(script_type, coin, node) address = get_address(script_type, coin, node)

View File

@ -19,7 +19,8 @@ async def sign_tx(ctx, msg):
coin_name = msg.coin_name or "Bitcoin" coin_name = msg.coin_name or "Bitcoin"
coin = coins.by_name(coin_name) coin = coins.by_name(coin_name)
# TODO: rework this so we don't have to pass root to signing.sign_tx # TODO: rework this so we don't have to pass root to signing.sign_tx
root = await seed.derive_node(ctx, [], curve_name=coin.curve_name) keychain = await seed.get_keychain(ctx)
root = keychain.derive([], coin.curve_name)
signer = signing.sign_tx(msg, root) signer = signing.sign_tx(msg, root)
res = None res = None