From f5c8138df6e396dcf94d858357811d255370bcfa Mon Sep 17 00:00:00 2001 From: matejcik Date: Fri, 14 Aug 2020 15:39:42 +0200 Subject: [PATCH] feat(core): update most apps to use path schemas --- core/src/apps/binance/__init__.py | 3 + core/src/apps/binance/get_address.py | 12 ++- core/src/apps/binance/get_public_key.py | 10 +-- core/src/apps/binance/helpers.py | 24 ------ core/src/apps/binance/sign_tx.py | 10 +-- core/src/apps/eos/__init__.py | 3 + core/src/apps/eos/get_public_key.py | 9 +- core/src/apps/eos/helpers.py | 24 ------ core/src/apps/eos/sign_tx.py | 10 +-- core/src/apps/ethereum/address.py | 53 ------------ core/src/apps/ethereum/get_address.py | 10 +-- core/src/apps/ethereum/get_public_key.py | 9 +- core/src/apps/ethereum/keychain.py | 86 ++++++++++++------- core/src/apps/ethereum/sign_message.py | 10 +-- core/src/apps/ethereum/sign_tx.py | 5 +- core/src/apps/lisk/__init__.py | 3 + core/src/apps/lisk/get_address.py | 9 +- core/src/apps/lisk/get_public_key.py | 9 +- core/src/apps/lisk/helpers.py | 18 ---- core/src/apps/lisk/sign_message.py | 9 +- core/src/apps/lisk/sign_tx.py | 10 +-- core/src/apps/misc/cipher_key_value.py | 3 +- core/src/apps/misc/get_ecdh_session_key.py | 3 +- core/src/apps/misc/sign_identity.py | 3 +- core/src/apps/monero/__init__.py | 3 + core/src/apps/monero/get_address.py | 10 +-- core/src/apps/monero/get_tx_keys.py | 10 +-- core/src/apps/monero/get_watch_only.py | 10 +-- core/src/apps/monero/key_image_sync.py | 10 +-- core/src/apps/monero/live_refresh.py | 10 +-- core/src/apps/monero/misc.py | 18 ---- core/src/apps/monero/sign_tx.py | 5 +- .../signing/step_01_init_transaction.py | 6 +- core/src/apps/nem/__init__.py | 7 ++ core/src/apps/nem/get_address.py | 6 +- core/src/apps/nem/helpers.py | 40 ++++----- core/src/apps/nem/sign_tx.py | 8 +- core/src/apps/ripple/__init__.py | 3 + core/src/apps/ripple/get_address.py | 12 ++- core/src/apps/ripple/helpers.py | 24 ------ core/src/apps/ripple/sign_tx.py | 11 +-- core/src/apps/stellar/__init__.py | 3 + core/src/apps/stellar/get_address.py | 10 +-- core/src/apps/stellar/helpers.py | 18 ---- core/src/apps/stellar/sign_tx.py | 10 +-- core/src/apps/tezos/__init__.py | 6 ++ core/src/apps/tezos/get_address.py | 8 +- core/src/apps/tezos/get_public_key.py | 8 +- core/src/apps/tezos/helpers.py | 26 ------ core/src/apps/tezos/sign_tx.py | 8 +- 50 files changed, 217 insertions(+), 418 deletions(-) diff --git a/core/src/apps/binance/__init__.py b/core/src/apps/binance/__init__.py index 86e8814c38..d701b4b72f 100644 --- a/core/src/apps/binance/__init__.py +++ b/core/src/apps/binance/__init__.py @@ -1,8 +1,11 @@ from trezor import wire from trezor.messages import MessageType +from apps.common.paths import PATTERN_BIP44 + CURVE = "secp256k1" SLIP44_ID = 714 +PATTERN = PATTERN_BIP44 def boot() -> None: diff --git a/core/src/apps/binance/get_address.py b/core/src/apps/binance/get_address.py index 07ee96d018..4c91ea4734 100644 --- a/core/src/apps/binance/get_address.py +++ b/core/src/apps/binance/get_address.py @@ -2,23 +2,21 @@ from trezor.messages.BinanceAddress import BinanceAddress from trezor.messages.BinanceGetAddress import BinanceGetAddress from apps.common import paths -from apps.common.keychain import Keychain, with_slip44_keychain +from apps.common.keychain import Keychain, auto_keychain from apps.common.layout import address_n_to_str, show_address, show_qr -from . import CURVE, SLIP44_ID, helpers +from .helpers import address_from_public_key -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@auto_keychain(__name__) async def get_address(ctx, msg: BinanceGetAddress, keychain: Keychain): HRP = "bnb" - await paths.validate_path( - ctx, helpers.validate_full_path, keychain, msg.address_n, CURVE - ) + await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) pubkey = node.public_key() - address = helpers.address_from_public_key(pubkey, HRP) + address = address_from_public_key(pubkey, HRP) if msg.show_display: desc = address_n_to_str(msg.address_n) while True: diff --git a/core/src/apps/binance/get_public_key.py b/core/src/apps/binance/get_public_key.py index 8e8f155d2e..b523bae6e5 100644 --- a/core/src/apps/binance/get_public_key.py +++ b/core/src/apps/binance/get_public_key.py @@ -2,16 +2,12 @@ from trezor.messages.BinanceGetPublicKey import BinanceGetPublicKey from trezor.messages.BinancePublicKey import BinancePublicKey from apps.common import layout, paths -from apps.common.keychain import Keychain, with_slip44_keychain - -from . import CURVE, SLIP44_ID, helpers +from apps.common.keychain import Keychain, auto_keychain -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@auto_keychain(__name__) async def get_public_key(ctx, msg: BinanceGetPublicKey, keychain: Keychain): - await paths.validate_path( - ctx, helpers.validate_full_path, keychain, msg.address_n, CURVE - ) + await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) pubkey = node.public_key() diff --git a/core/src/apps/binance/helpers.py b/core/src/apps/binance/helpers.py index d4127e3aa3..bd0cdda730 100644 --- a/core/src/apps/binance/helpers.py +++ b/core/src/apps/binance/helpers.py @@ -8,8 +8,6 @@ from trezor.messages.BinanceOrderMsg import BinanceOrderMsg from trezor.messages.BinanceSignTx import BinanceSignTx from trezor.messages.BinanceTransferMsg import BinanceTransferMsg -from apps.common import HARDENED - ENVELOPE_BLUEPRINT = '{{"account_number":"{account_number}","chain_id":"{chain_id}","data":null,"memo":"{memo}","msgs":[{msgs}],"sequence":"{sequence}","source":"{source}"}}' MSG_TRANSFER_BLUEPRINT = '{{"inputs":[{inputs}],"outputs":[{outputs}]}}' MSG_NEWORDER_BLUEPRINT = '{{"id":"{id}","ordertype":{ordertype},"price":{price},"quantity":{quantity},"sender":"{sender}","side":{side},"symbol":"{symbol}","timeinforce":{timeinforce}}}' @@ -91,25 +89,3 @@ def address_from_public_key(pubkey: bytes, hrp: str) -> str: convertedbits = bech32.convertbits(h, 8, 5, False) return bech32.bech32_encode(hrp, convertedbits) - - -def validate_full_path(path: list) -> bool: - """ - Validates derivation path to equal 44'/714'/a'/0/0, - where `a` is an account index from 0 to 1 000 000. - Similar to Ethereum this should be 44'/714'/a', but for - compatibility with other HW vendors we use 44'/714'/a'/0/0. - """ - if len(path) != 5: - return False - if path[0] != 44 | HARDENED: - return False - if path[1] != 714 | HARDENED: - return False - if path[2] < HARDENED or path[2] > 1000000 | HARDENED: - return False - if path[3] != 0: - return False - if path[4] != 0: - return False - return True diff --git a/core/src/apps/binance/sign_tx.py b/core/src/apps/binance/sign_tx.py index e6386b4321..07f7ba298b 100644 --- a/core/src/apps/binance/sign_tx.py +++ b/core/src/apps/binance/sign_tx.py @@ -9,20 +9,18 @@ from trezor.messages.BinanceTransferMsg import BinanceTransferMsg from trezor.messages.BinanceTxRequest import BinanceTxRequest from apps.common import paths -from apps.common.keychain import Keychain, with_slip44_keychain +from apps.common.keychain import Keychain, auto_keychain -from . import CURVE, SLIP44_ID, helpers, layout +from . import helpers, layout -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@auto_keychain(__name__) async def sign_tx(ctx, envelope, keychain: Keychain): # create transaction message -> sign it -> create signature/pubkey message -> serialize all if envelope.msg_count > 1: raise wire.DataError("Multiple messages not supported.") - await paths.validate_path( - ctx, helpers.validate_full_path, keychain, envelope.address_n, CURVE - ) + await paths.validate_path(ctx, keychain, envelope.address_n) node = keychain.derive(envelope.address_n) tx_req = BinanceTxRequest() diff --git a/core/src/apps/eos/__init__.py b/core/src/apps/eos/__init__.py index 74183970af..6f92883238 100644 --- a/core/src/apps/eos/__init__.py +++ b/core/src/apps/eos/__init__.py @@ -1,8 +1,11 @@ from trezor import wire from trezor.messages import MessageType +from apps.common.paths import PATTERN_BIP44 + CURVE = "secp256k1" SLIP44_ID = 194 +PATTERN = PATTERN_BIP44 def boot() -> None: diff --git a/core/src/apps/eos/get_public_key.py b/core/src/apps/eos/get_public_key.py index 14f0f19f51..d49d1a7cb3 100644 --- a/core/src/apps/eos/get_public_key.py +++ b/core/src/apps/eos/get_public_key.py @@ -4,10 +4,9 @@ from trezor.messages.EosGetPublicKey import EosGetPublicKey from trezor.messages.EosPublicKey import EosPublicKey from apps.common import paths -from apps.common.keychain import Keychain, with_slip44_keychain +from apps.common.keychain import Keychain, auto_keychain -from . import CURVE, SLIP44_ID -from .helpers import public_key_to_wif, validate_full_path +from .helpers import public_key_to_wif from .layout import require_get_public_key if False: @@ -22,11 +21,11 @@ def _get_public_key(node: bip32.HDNode) -> Tuple[str, bytes]: return wif, public_key -@with_slip44_keychain(SLIP44_ID, CURVE) +@auto_keychain(__name__) async def get_public_key( ctx: wire.Context, msg: EosGetPublicKey, keychain: Keychain ) -> EosPublicKey: - await paths.validate_path(ctx, validate_full_path, keychain, msg.address_n, CURVE) + await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) wif, public_key = _get_public_key(node) diff --git a/core/src/apps/eos/helpers.py b/core/src/apps/eos/helpers.py index d5a8b0691d..01173889eb 100644 --- a/core/src/apps/eos/helpers.py +++ b/core/src/apps/eos/helpers.py @@ -2,8 +2,6 @@ from trezor import wire from trezor.crypto import base58 from trezor.messages.EosAsset import EosAsset -from apps.common import HARDENED - def base58_encode(prefix: str, sig_prefix: str, data: bytes) -> str: b58 = base58.encode(data + base58.ripemd160_32(data + sig_prefix.encode())) @@ -42,28 +40,6 @@ def eos_asset_to_string(asset: EosAsset) -> str: return "{} {}".format(amount_digits, symbol) -def validate_full_path(path: list) -> bool: - """ - Validates derivation path to equal 44'/194'/a'/0/0, - where `a` is an account index from 0 to 1 000 000. - Similar to Ethereum this should be 44'/194'/a', but for - compatibility with other HW vendors we use 44'/194'/a'/0/0. - """ - if len(path) != 5: - return False - if path[0] != 44 | HARDENED: - return False - if path[1] != 194 | HARDENED: - return False - if path[2] < HARDENED or path[2] > 1000000 | HARDENED: - return False - if path[3] != 0: - return False - if path[4] != 0: - return False - return True - - def public_key_to_wif(pub_key: bytes) -> str: if pub_key[0] == 0x04 and len(pub_key) == 65: head = b"\x03" if pub_key[64] & 0x01 else b"\x02" diff --git a/core/src/apps/eos/sign_tx.py b/core/src/apps/eos/sign_tx.py index e90fa4148d..d437e4c954 100644 --- a/core/src/apps/eos/sign_tx.py +++ b/core/src/apps/eos/sign_tx.py @@ -8,15 +8,15 @@ from trezor.messages.EosTxActionRequest import EosTxActionRequest from trezor.utils import HashWriter from apps.common import paths -from apps.common.keychain import Keychain, with_slip44_keychain +from apps.common.keychain import Keychain, auto_keychain -from . import CURVE, SLIP44_ID, writers +from . import writers from .actions import process_action -from .helpers import base58_encode, validate_full_path +from .helpers import base58_encode from .layout import require_sign_tx -@with_slip44_keychain(SLIP44_ID, CURVE) +@auto_keychain(__name__) async def sign_tx(ctx: wire.Context, msg: EosSignTx, keychain: Keychain) -> EosSignedTx: if msg.chain_id is None: raise wire.DataError("No chain id") @@ -25,7 +25,7 @@ async def sign_tx(ctx: wire.Context, msg: EosSignTx, keychain: Keychain) -> EosS if msg.num_actions is None or msg.num_actions == 0: raise wire.DataError("No actions") - await paths.validate_path(ctx, validate_full_path, keychain, msg.address_n, CURVE) + await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) sha = HashWriter(sha256()) diff --git a/core/src/apps/ethereum/address.py b/core/src/apps/ethereum/address.py index cfb2e70c16..e11fecd44d 100644 --- a/core/src/apps/ethereum/address.py +++ b/core/src/apps/ethereum/address.py @@ -2,59 +2,6 @@ from ubinascii import unhexlify from trezor import wire -from apps.common import HARDENED, paths - -from . import networks - - -""" -We believe Ethereum should use 44'/60'/a' for everything, because it is -account-based, rather than UTXO-based. Unfortunately, lot of Ethereum -tools (MEW, Metamask) do not use such scheme and set a = 0 and then -iterate the address index i. Therefore for compatibility reasons we use -the same scheme: 44'/60'/0'/0/i and only the i is being iterated. -""" - - -def validate_path_for_get_public_key(path: list) -> bool: - """ - This should be 44'/60'/0', but other non-hardened items are allowed. - """ - length = len(path) - if length < 3 or length > 5: - return False - if path[0] != 44 | HARDENED: - return False - if path[1] not in networks.all_slip44_ids_hardened(): - return False - if path[2] != 0 | HARDENED: - return False - if length > 3 and paths.is_hardened(path[3]): - return False - if length > 4 and paths.is_hardened(path[4]): - return False - return True - - -def validate_full_path(path: list) -> bool: - """ - Validates derivation path to equal 44'/60'/0'/0/i, - where `i` is an address index from 0 to 1 000 000. - """ - if len(path) != 5: - return False - if path[0] != 44 | HARDENED: - return False - if path[1] not in networks.all_slip44_ids_hardened(): - return False - if path[2] != 0 | HARDENED: - return False - if path[3] != 0: - return False - if path[4] > 1000000: - return False - return True - def address_from_bytes(address_bytes: bytes, network=None) -> str: """ diff --git a/core/src/apps/ethereum/get_address.py b/core/src/apps/ethereum/get_address.py index c553ae2766..562992e634 100644 --- a/core/src/apps/ethereum/get_address.py +++ b/core/src/apps/ethereum/get_address.py @@ -5,14 +5,14 @@ from trezor.messages.EthereumAddress import EthereumAddress from apps.common import paths from apps.common.layout import address_n_to_str, show_address, show_qr -from . import CURVE, networks -from .address import address_from_bytes, validate_full_path -from .keychain import with_keychain_from_path +from . import networks +from .address import address_from_bytes +from .keychain import PATTERN_ADDRESS, with_keychain_from_path -@with_keychain_from_path +@with_keychain_from_path(PATTERN_ADDRESS) async def get_address(ctx, msg, keychain): - await paths.validate_path(ctx, validate_full_path, keychain, msg.address_n, CURVE) + await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) seckey = node.private_key() diff --git a/core/src/apps/ethereum/get_public_key.py b/core/src/apps/ethereum/get_public_key.py index 9be1447f42..8d692bd3c8 100644 --- a/core/src/apps/ethereum/get_public_key.py +++ b/core/src/apps/ethereum/get_public_key.py @@ -3,15 +3,12 @@ from trezor.messages.HDNodeType import HDNodeType from apps.common import coins, layout, paths -from . import CURVE, address -from .keychain import with_keychain_from_path +from .keychain import PATTERN_PUBKEY, with_keychain_from_path -@with_keychain_from_path +@with_keychain_from_path(PATTERN_PUBKEY) async def get_public_key(ctx, msg, keychain): - await paths.validate_path( - ctx, address.validate_path_for_get_public_key, keychain, msg.address_n, CURVE - ) + await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) # we use the Bitcoin format for Ethereum xpubs diff --git a/core/src/apps/ethereum/keychain.py b/core/src/apps/ethereum/keychain.py index 503954d741..48f3e384ca 100644 --- a/core/src/apps/ethereum/keychain.py +++ b/core/src/apps/ethereum/keychain.py @@ -1,12 +1,12 @@ from trezor import wire -from apps.common import HARDENED, seed +from apps.common import HARDENED, paths from apps.common.keychain import get_keychain from . import CURVE, networks if False: - from typing import List + from typing import Callable from typing_extensions import Protocol from protobuf import MessageType @@ -16,48 +16,76 @@ if False: from apps.common.keychain import MsgOut, Handler, HandlerWithKeychain class MsgWithAddressN(MessageType, Protocol): - address_n = ... # type: List[int] + address_n = ... # type: paths.Bip32Path -async def from_address_n(ctx: wire.Context, address_n: List[int]) -> seed.Keychain: +# We believe Ethereum should use 44'/60'/a' for everything, because it is +# account-based, rather than UTXO-based. Unfortunately, lot of Ethereum +# tools (MEW, Metamask) do not use such scheme and set a = 0 and then +# iterate the address index i. Therefore for compatibility reasons we use +# the same scheme: 44'/60'/0'/0/i and only the i is being iterated. + +PATTERN_ADDRESS = "m/44'/coin_type'/0'/0/address_index" +PATTERN_PUBKEY = "m/44'/coin_type'/0'/*" + + +def _schema_from_address_n( + pattern: str, address_n: paths.Bip32Path +) -> paths.PathSchema: if len(address_n) < 2: - raise wire.DataError("Forbidden key path") + return paths.SCHEMA_NO_MATCH + slip44_hardened = address_n[1] if slip44_hardened not in networks.all_slip44_ids_hardened(): - raise wire.DataError("Forbidden key path") - namespace = [44 | HARDENED, slip44_hardened] - return await get_keychain(ctx, CURVE, [namespace]) + return paths.SCHEMA_NO_MATCH + + if not slip44_hardened & HARDENED: + return paths.SCHEMA_ANY_PATH + + slip44_id = slip44_hardened - HARDENED + return paths.PathSchema(pattern, slip44_id) def with_keychain_from_path( - func: HandlerWithKeychain[MsgWithAddressN, MsgOut] -) -> Handler[MsgWithAddressN, MsgOut]: - async def wrapper(ctx: wire.Context, msg: MsgWithAddressN) -> MsgOut: - keychain = await from_address_n(ctx, msg.address_n) - with keychain: - return await func(ctx, msg, keychain) + pattern: str, +) -> Callable[ + [HandlerWithKeychain[MsgWithAddressN, MsgOut]], Handler[MsgWithAddressN, MsgOut] +]: + def decorator( + func: HandlerWithKeychain[MsgWithAddressN, MsgOut] + ) -> Handler[MsgWithAddressN, MsgOut]: + async def wrapper(ctx: wire.Context, msg: MsgWithAddressN) -> MsgOut: + schema = _schema_from_address_n(pattern, msg.address_n) + keychain = await get_keychain(ctx, CURVE, [schema]) + with keychain: + return await func(ctx, msg, keychain) - return wrapper + return wrapper + + return decorator + + +def _schema_from_chain_id(msg: EthereumSignTx) -> paths.PathSchema: + if msg.chain_id is None: + return _schema_from_address_n(PATTERN_ADDRESS, msg.address_n) + + info = networks.by_chain_id(msg.chain_id) + if info is None: + return paths.SCHEMA_NO_MATCH + + slip44_id = info.slip44 + if networks.is_wanchain(msg.chain_id, msg.tx_type): + slip44_id = networks.SLIP44_WANCHAIN + return paths.PathSchema(PATTERN_ADDRESS, slip44_id) def with_keychain_from_chain_id( func: HandlerWithKeychain[EthereumSignTx, MsgOut] ) -> Handler[EthereumSignTx, MsgOut]: + # this is only for SignTx, and only PATTERN_ADDRESS is allowed async def wrapper(ctx: wire.Context, msg: EthereumSignTx) -> MsgOut: - if msg.chain_id is None: - keychain = await from_address_n(ctx, msg.address_n) - else: - info = networks.by_chain_id(msg.chain_id) - if info is None: - raise wire.DataError("Unsupported chain id") - - slip44 = info.slip44 - if networks.is_wanchain(msg.chain_id, msg.tx_type): - slip44 = networks.SLIP44_WANCHAIN - - namespace = [44 | HARDENED, slip44 | HARDENED] - keychain = await get_keychain(ctx, CURVE, [namespace]) - + schema = _schema_from_chain_id(msg) + keychain = await get_keychain(ctx, CURVE, [schema]) with keychain: return await func(ctx, msg, keychain) diff --git a/core/src/apps/ethereum/sign_message.py b/core/src/apps/ethereum/sign_message.py index e975dd0a3f..0052f365c8 100644 --- a/core/src/apps/ethereum/sign_message.py +++ b/core/src/apps/ethereum/sign_message.py @@ -6,8 +6,8 @@ from trezor.utils import HashWriter from apps.common import paths from apps.common.signverify import require_confirm_sign_message -from . import CURVE, address -from .keychain import with_keychain_from_path +from . import address +from .keychain import PATTERN_ADDRESS, with_keychain_from_path def message_digest(message): @@ -19,11 +19,9 @@ def message_digest(message): return h.get_digest() -@with_keychain_from_path +@with_keychain_from_path(PATTERN_ADDRESS) async def sign_message(ctx, msg, keychain): - await paths.validate_path( - ctx, address.validate_full_path, keychain, msg.address_n, CURVE - ) + await paths.validate_path(ctx, keychain, msg.address_n) await require_confirm_sign_message(ctx, "ETH", msg.message) node = keychain.derive(msg.address_n) diff --git a/core/src/apps/ethereum/sign_tx.py b/core/src/apps/ethereum/sign_tx.py index 7e2ab57512..7751f6587e 100644 --- a/core/src/apps/ethereum/sign_tx.py +++ b/core/src/apps/ethereum/sign_tx.py @@ -9,8 +9,7 @@ from trezor.utils import HashWriter from apps.common import paths -from . import CURVE, address, tokens -from .address import validate_full_path +from . import address, tokens from .keychain import with_keychain_from_chain_id from .layout import require_confirm_data, require_confirm_fee, require_confirm_tx @@ -22,7 +21,7 @@ MAX_CHAIN_ID = 2147483629 async def sign_tx(ctx, msg, keychain): msg = sanitize(msg) check(msg) - await paths.validate_path(ctx, validate_full_path, keychain, msg.address_n, CURVE) + await paths.validate_path(ctx, keychain, msg.address_n) data_total = msg.data_length diff --git a/core/src/apps/lisk/__init__.py b/core/src/apps/lisk/__init__.py index bb7ea974cd..dd7b2e7e27 100644 --- a/core/src/apps/lisk/__init__.py +++ b/core/src/apps/lisk/__init__.py @@ -1,8 +1,11 @@ from trezor import wire from trezor.messages import MessageType +from apps.common.paths import PATTERN_SEP5 + CURVE = "ed25519" SLIP44_ID = 134 +PATTERN = PATTERN_SEP5 def boot() -> None: diff --git a/core/src/apps/lisk/get_address.py b/core/src/apps/lisk/get_address.py index e5cba1543a..71288cd492 100644 --- a/core/src/apps/lisk/get_address.py +++ b/core/src/apps/lisk/get_address.py @@ -1,16 +1,15 @@ from trezor.messages.LiskAddress import LiskAddress from apps.common import paths -from apps.common.keychain import with_slip44_keychain +from apps.common.keychain import auto_keychain from apps.common.layout import address_n_to_str, show_address, show_qr -from . import CURVE, SLIP44_ID -from .helpers import get_address_from_public_key, validate_full_path +from .helpers import get_address_from_public_key -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@auto_keychain(__name__) async def get_address(ctx, msg, keychain): - await paths.validate_path(ctx, validate_full_path, keychain, msg.address_n, CURVE) + await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) pubkey = node.public_key() diff --git a/core/src/apps/lisk/get_public_key.py b/core/src/apps/lisk/get_public_key.py index 80d1f6bc93..7233f58a10 100644 --- a/core/src/apps/lisk/get_public_key.py +++ b/core/src/apps/lisk/get_public_key.py @@ -1,15 +1,12 @@ from trezor.messages.LiskPublicKey import LiskPublicKey from apps.common import layout, paths -from apps.common.keychain import with_slip44_keychain - -from . import CURVE, SLIP44_ID -from .helpers import validate_full_path +from apps.common.keychain import auto_keychain -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@auto_keychain(__name__) async def get_public_key(ctx, msg, keychain): - await paths.validate_path(ctx, validate_full_path, keychain, msg.address_n, CURVE) + await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) pubkey = node.public_key() diff --git a/core/src/apps/lisk/helpers.py b/core/src/apps/lisk/helpers.py index ee801f9424..edd0c2c78b 100644 --- a/core/src/apps/lisk/helpers.py +++ b/core/src/apps/lisk/helpers.py @@ -1,7 +1,5 @@ from trezor.crypto.hashlib import sha256 -from apps.common import HARDENED - def get_address_from_public_key(pubkey): pubkeyhash = sha256(pubkey).digest() @@ -31,19 +29,3 @@ def get_vote_tx_text(votes): def _text_with_plural(txt, value): return "%s %s %s" % (txt, value, ("votes" if value != 1 else "vote")) - - -def validate_full_path(path: list) -> bool: - """ - Validates derivation path to equal 44'/134'/a', - where `a` is an account index from 0 to 1 000 000. - """ - if len(path) != 3: - return False - if path[0] != 44 | HARDENED: - return False - if path[1] != 134 | HARDENED: - return False - if path[2] < HARDENED or path[2] > 1000000 | HARDENED: - return False - return True diff --git a/core/src/apps/lisk/sign_message.py b/core/src/apps/lisk/sign_message.py index 074ac2f9c0..fd747751f0 100644 --- a/core/src/apps/lisk/sign_message.py +++ b/core/src/apps/lisk/sign_message.py @@ -4,13 +4,10 @@ from trezor.messages.LiskMessageSignature import LiskMessageSignature from trezor.utils import HashWriter from apps.common import paths -from apps.common.keychain import with_slip44_keychain +from apps.common.keychain import auto_keychain from apps.common.signverify import require_confirm_sign_message from apps.common.writers import write_bitcoin_varint -from . import CURVE, SLIP44_ID -from .helpers import validate_full_path - def message_digest(message): h = HashWriter(sha256()) @@ -22,9 +19,9 @@ def message_digest(message): return sha256(h.get_digest()).digest() -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@auto_keychain(__name__) async def sign_message(ctx, msg, keychain): - await paths.validate_path(ctx, validate_full_path, keychain, msg.address_n, CURVE) + await paths.validate_path(ctx, keychain, msg.address_n) await require_confirm_sign_message(ctx, "Lisk", msg.message) node = keychain.derive(msg.address_n) diff --git a/core/src/apps/lisk/sign_tx.py b/core/src/apps/lisk/sign_tx.py index 7224017aec..b92278fac0 100644 --- a/core/src/apps/lisk/sign_tx.py +++ b/core/src/apps/lisk/sign_tx.py @@ -8,15 +8,15 @@ from trezor.messages.LiskSignedTx import LiskSignedTx from trezor.utils import HashWriter from apps.common import paths -from apps.common.keychain import with_slip44_keychain +from apps.common.keychain import auto_keychain -from . import CURVE, SLIP44_ID, layout -from .helpers import get_address_from_public_key, validate_full_path +from . import layout +from .helpers import get_address_from_public_key -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@auto_keychain(__name__) async def sign_tx(ctx, msg, keychain): - await paths.validate_path(ctx, validate_full_path, keychain, msg.address_n, CURVE) + await paths.validate_path(ctx, keychain, msg.address_n) pubkey, seckey = _get_keys(keychain, msg) transaction = _update_raw_tx(msg.transaction, pubkey) diff --git a/core/src/apps/misc/cipher_key_value.py b/core/src/apps/misc/cipher_key_value.py index cd564907b2..6111d9f259 100644 --- a/core/src/apps/misc/cipher_key_value.py +++ b/core/src/apps/misc/cipher_key_value.py @@ -5,10 +5,11 @@ from trezor.ui.text import Text from apps.common.confirm import require_confirm from apps.common.keychain import get_keychain +from apps.common.paths import AlwaysMatchingSchema async def cipher_key_value(ctx, msg): - keychain = await get_keychain(ctx, "secp256k1", [[]]) + keychain = await get_keychain(ctx, "secp256k1", [AlwaysMatchingSchema]) if len(msg.value) % 16 > 0: raise wire.DataError("Value length must be a multiple of 16") diff --git a/core/src/apps/misc/get_ecdh_session_key.py b/core/src/apps/misc/get_ecdh_session_key.py index d793759f1e..a789809c92 100644 --- a/core/src/apps/misc/get_ecdh_session_key.py +++ b/core/src/apps/misc/get_ecdh_session_key.py @@ -9,6 +9,7 @@ from trezor.utils import chunks from apps.common import HARDENED from apps.common.confirm import require_confirm from apps.common.keychain import get_keychain +from apps.common.paths import AlwaysMatchingSchema from .sign_identity import serialize_identity, serialize_identity_without_proto @@ -17,7 +18,7 @@ async def get_ecdh_session_key(ctx, msg): if msg.ecdsa_curve_name is None: msg.ecdsa_curve_name = "secp256k1" - keychain = await get_keychain(ctx, msg.ecdsa_curve_name, [[]]) + keychain = await get_keychain(ctx, msg.ecdsa_curve_name, [AlwaysMatchingSchema]) identity = serialize_identity(msg.identity) await require_confirm_ecdh_session_key(ctx, msg.identity) diff --git a/core/src/apps/misc/sign_identity.py b/core/src/apps/misc/sign_identity.py index 99a5268216..09f83f6a87 100644 --- a/core/src/apps/misc/sign_identity.py +++ b/core/src/apps/misc/sign_identity.py @@ -9,13 +9,14 @@ from trezor.utils import chunks from apps.common import HARDENED, coins from apps.common.confirm import require_confirm from apps.common.keychain import get_keychain +from apps.common.paths import AlwaysMatchingSchema async def sign_identity(ctx, msg): if msg.ecdsa_curve_name is None: msg.ecdsa_curve_name = "secp256k1" - keychain = await get_keychain(ctx, msg.ecdsa_curve_name, [[]]) + keychain = await get_keychain(ctx, msg.ecdsa_curve_name, [AlwaysMatchingSchema]) identity = serialize_identity(msg.identity) await require_confirm_sign_identity(ctx, msg.identity, msg.challenge_visual) diff --git a/core/src/apps/monero/__init__.py b/core/src/apps/monero/__init__.py index c4f8e87ac2..1cfff37bb9 100644 --- a/core/src/apps/monero/__init__.py +++ b/core/src/apps/monero/__init__.py @@ -1,8 +1,11 @@ from trezor import wire from trezor.messages import MessageType +from apps.common.paths import PATTERN_SEP5 + CURVE = "ed25519" SLIP44_ID = 128 +PATTERN = PATTERN_SEP5 def boot() -> None: diff --git a/core/src/apps/monero/get_address.py b/core/src/apps/monero/get_address.py index 17acbecaa1..1ee3daf15d 100644 --- a/core/src/apps/monero/get_address.py +++ b/core/src/apps/monero/get_address.py @@ -1,19 +1,17 @@ from trezor.messages.MoneroAddress import MoneroAddress from apps.common import paths -from apps.common.keychain import with_slip44_keychain +from apps.common.keychain import auto_keychain from apps.common.layout import address_n_to_str, show_qr -from apps.monero import CURVE, SLIP44_ID, misc +from apps.monero import misc from apps.monero.layout import confirms from apps.monero.xmr import addresses, crypto, monero from apps.monero.xmr.networks import net_version -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@auto_keychain(__name__) async def get_address(ctx, msg, keychain): - await paths.validate_path( - ctx, misc.validate_full_path, keychain, msg.address_n, CURVE - ) + await paths.validate_path(ctx, keychain, msg.address_n) creds = misc.get_creds(keychain, msg.address_n, msg.network_type) addr = creds.address diff --git a/core/src/apps/monero/get_tx_keys.py b/core/src/apps/monero/get_tx_keys.py index fef29d9320..3c960fd2b7 100644 --- a/core/src/apps/monero/get_tx_keys.py +++ b/core/src/apps/monero/get_tx_keys.py @@ -20,8 +20,8 @@ from trezor.messages.MoneroGetTxKeyAck import MoneroGetTxKeyAck from trezor.messages.MoneroGetTxKeyRequest import MoneroGetTxKeyRequest from apps.common import paths -from apps.common.keychain import with_slip44_keychain -from apps.monero import CURVE, SLIP44_ID, misc +from apps.common.keychain import auto_keychain +from apps.monero import misc from apps.monero.layout import confirms from apps.monero.xmr import crypto from apps.monero.xmr.crypto import chacha_poly @@ -30,11 +30,9 @@ _GET_TX_KEY_REASON_TX_KEY = 0 _GET_TX_KEY_REASON_TX_DERIVATION = 1 -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@auto_keychain(__name__) async def get_tx_keys(ctx, msg: MoneroGetTxKeyRequest, keychain): - await paths.validate_path( - ctx, misc.validate_full_path, keychain, msg.address_n, CURVE - ) + await paths.validate_path(ctx, keychain, msg.address_n) do_deriv = msg.reason == _GET_TX_KEY_REASON_TX_DERIVATION await confirms.require_confirm_tx_key(ctx, export_key=not do_deriv) diff --git a/core/src/apps/monero/get_watch_only.py b/core/src/apps/monero/get_watch_only.py index ce42dcfb94..ca621036c6 100644 --- a/core/src/apps/monero/get_watch_only.py +++ b/core/src/apps/monero/get_watch_only.py @@ -2,17 +2,15 @@ from trezor.messages.MoneroGetWatchKey import MoneroGetWatchKey from trezor.messages.MoneroWatchKey import MoneroWatchKey from apps.common import paths -from apps.common.keychain import with_slip44_keychain -from apps.monero import CURVE, SLIP44_ID, misc +from apps.common.keychain import auto_keychain +from apps.monero import misc from apps.monero.layout import confirms from apps.monero.xmr import crypto -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@auto_keychain(__name__) async def get_watch_only(ctx, msg: MoneroGetWatchKey, keychain): - await paths.validate_path( - ctx, misc.validate_full_path, keychain, msg.address_n, CURVE - ) + await paths.validate_path(ctx, keychain, msg.address_n) await confirms.require_confirm_watchkey(ctx) diff --git a/core/src/apps/monero/key_image_sync.py b/core/src/apps/monero/key_image_sync.py index 8428e3eb0a..526d0d0164 100644 --- a/core/src/apps/monero/key_image_sync.py +++ b/core/src/apps/monero/key_image_sync.py @@ -11,14 +11,14 @@ from trezor.messages.MoneroKeyImageSyncStepAck import MoneroKeyImageSyncStepAck from trezor.messages.MoneroKeyImageSyncStepRequest import MoneroKeyImageSyncStepRequest from apps.common import paths -from apps.common.keychain import with_slip44_keychain -from apps.monero import CURVE, SLIP44_ID, misc +from apps.common.keychain import auto_keychain +from apps.monero import misc from apps.monero.layout import confirms from apps.monero.xmr import crypto, key_image, monero from apps.monero.xmr.crypto import chacha_poly -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@auto_keychain(__name__) async def key_image_sync(ctx, msg, keychain): state = KeyImageSync() @@ -45,9 +45,7 @@ class KeyImageSync: async def _init_step(s, ctx, msg, keychain): - await paths.validate_path( - ctx, misc.validate_full_path, keychain, msg.address_n, CURVE - ) + await paths.validate_path(ctx, keychain, msg.address_n) s.creds = misc.get_creds(keychain, msg.address_n, msg.network_type) diff --git a/core/src/apps/monero/live_refresh.py b/core/src/apps/monero/live_refresh.py index b1904cbdc7..152a9374f1 100644 --- a/core/src/apps/monero/live_refresh.py +++ b/core/src/apps/monero/live_refresh.py @@ -10,14 +10,14 @@ from trezor.messages.MoneroLiveRefreshStepAck import MoneroLiveRefreshStepAck from trezor.messages.MoneroLiveRefreshStepRequest import MoneroLiveRefreshStepRequest from apps.common import paths -from apps.common.keychain import with_slip44_keychain -from apps.monero import CURVE, SLIP44_ID, misc +from apps.common.keychain import auto_keychain +from apps.monero import misc from apps.monero.layout import confirms from apps.monero.xmr import crypto, key_image, monero from apps.monero.xmr.crypto import chacha_poly -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@auto_keychain(__name__) async def live_refresh(ctx, msg: MoneroLiveRefreshStartRequest, keychain): state = LiveRefreshState() @@ -47,9 +47,7 @@ class LiveRefreshState: async def _init_step( s: LiveRefreshState, ctx, msg: MoneroLiveRefreshStartRequest, keychain ): - await paths.validate_path( - ctx, misc.validate_full_path, keychain, msg.address_n, CURVE - ) + await paths.validate_path(ctx, keychain, msg.address_n) if not storage.cache.get(storage.cache.APP_MONERO_LIVE_REFRESH): await confirms.require_confirm_live_refresh(ctx) diff --git a/core/src/apps/monero/misc.py b/core/src/apps/monero/misc.py index 880af3e25b..faf76d6b41 100644 --- a/core/src/apps/monero/misc.py +++ b/core/src/apps/monero/misc.py @@ -1,5 +1,3 @@ -from apps.common import HARDENED - if False: from typing import Tuple from apps.monero.xmr.types import Sc25519 @@ -18,22 +16,6 @@ def get_creds(keychain, address_n=None, network_type=None): return creds -def validate_full_path(path: list) -> bool: - """ - Validates derivation path to equal 44'/128'/a', - where `a` is an account index from 0 to 1 000 000. - """ - if len(path) != 3: - return False - if path[0] != 44 | HARDENED: - return False - if path[1] != 128 | HARDENED: - return False - if path[2] < HARDENED or path[2] > 1000000 | HARDENED: - return False - return True - - def compute_tx_key( spend_key_private: Sc25519, tx_prefix_hash: bytes, diff --git a/core/src/apps/monero/sign_tx.py b/core/src/apps/monero/sign_tx.py index 38e62febd1..a75242a273 100644 --- a/core/src/apps/monero/sign_tx.py +++ b/core/src/apps/monero/sign_tx.py @@ -3,12 +3,11 @@ import gc from trezor import log, utils, wire from trezor.messages import MessageType -from apps.common.keychain import with_slip44_keychain -from apps.monero import CURVE, SLIP44_ID +from apps.common.keychain import auto_keychain from apps.monero.signing.state import State -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@auto_keychain(__name__) async def sign_tx(ctx, received_msg, keychain): state = State(ctx) mods = utils.unimport_begin() diff --git a/core/src/apps/monero/signing/step_01_init_transaction.py b/core/src/apps/monero/signing/step_01_init_transaction.py index 902db09d3b..f2dedbae0a 100644 --- a/core/src/apps/monero/signing/step_01_init_transaction.py +++ b/core/src/apps/monero/signing/step_01_init_transaction.py @@ -4,7 +4,7 @@ Initializes a new transaction. import gc -from apps.monero import CURVE, misc, signing +from apps.monero import misc, signing from apps.monero.layout import confirms from apps.monero.signing.state import State from apps.monero.xmr import crypto, monero @@ -31,9 +31,7 @@ async def init_transaction( from apps.monero.signing import offloading_keys from apps.common import paths - await paths.validate_path( - state.ctx, misc.validate_full_path, keychain, address_n, CURVE - ) + await paths.validate_path(state.ctx, keychain, address_n) state.creds = misc.get_creds(keychain, address_n, network_type) state.client_version = tsx_data.client_version or 0 diff --git a/core/src/apps/nem/__init__.py b/core/src/apps/nem/__init__.py index 679a698c7b..880fd8b59c 100644 --- a/core/src/apps/nem/__init__.py +++ b/core/src/apps/nem/__init__.py @@ -1,9 +1,16 @@ from trezor import wire from trezor.messages import MessageType +from apps.common.paths import PATTERN_SEP5 + CURVE = "ed25519-keccak" SLIP44_ID = 43 +PATTERNS = ( + PATTERN_SEP5, + "m/44'/coin_type'/account'/0'/0'", # NanoWallet compatibility +) + def boot() -> None: wire.add(MessageType.NEMGetAddress, __name__, "get_address") diff --git a/core/src/apps/nem/get_address.py b/core/src/apps/nem/get_address.py index 99ae7d6c1e..5d52e2721c 100644 --- a/core/src/apps/nem/get_address.py +++ b/core/src/apps/nem/get_address.py @@ -4,16 +4,16 @@ from apps.common.keychain import with_slip44_keychain from apps.common.layout import address_n_to_str, show_address, show_qr from apps.common.paths import validate_path -from . import CURVE, SLIP44_ID +from . import CURVE, PATTERNS, SLIP44_ID from .helpers import check_path, get_network_str from .validators import validate_network -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@with_slip44_keychain(*PATTERNS, slip44_id=SLIP44_ID, curve=CURVE) async def get_address(ctx, msg, keychain): network = validate_network(msg.network) await validate_path( - ctx, check_path, keychain, msg.address_n, CURVE, network=network + ctx, keychain, msg.address_n, check_path(msg.address_n, msg.network) ) node = keychain.derive(msg.address_n) diff --git a/core/src/apps/nem/helpers.py b/core/src/apps/nem/helpers.py index 27327dbee6..948b7cbe66 100644 --- a/core/src/apps/nem/helpers.py +++ b/core/src/apps/nem/helpers.py @@ -1,6 +1,8 @@ from micropython import const -from apps.common import HARDENED +from apps.common import HARDENED, paths + +from . import SLIP44_ID NEM_NETWORK_MAINNET = const(0x68) NEM_NETWORK_TESTNET = const(0x98) @@ -38,26 +40,18 @@ def get_network_str(network: int) -> str: return "Mijin" -def check_path(path: list, network=None) -> bool: - """ - Validates derivation path to fit 44'/43'/a' or 44'/43'/a'/0'/0', - where `a` is an account number. We believe the path should be - 44'/43'/a', but for compatibility reasons with NEM's NanoWallet - we allow 44'/43'/a'/0'/0' as well. - Testnet is also allowed: 44'/1'/a'{/0'/0'} - """ - length = len(path) - if length != 3 and length != 5: +def check_path(path: paths.Bip32Path, network: int) -> bool: + """Validates that the appropriate coin_type is set for the given network.""" + if len(path) < 2: return False - if path[0] != 44 | HARDENED: - return False - if not ( - path[1] == 43 | HARDENED - or (network == NEM_NETWORK_TESTNET and path[1] == 1 | HARDENED) - ): - return False - if path[2] < HARDENED or path[2] > 1000000 | HARDENED: - return False - if length == 5 and (path[3] != 0 | HARDENED or path[4] != 0 | HARDENED): - return False - return True + + coin_type = path[1] - HARDENED + + if network == NEM_NETWORK_TESTNET: + return coin_type == 1 + + if network in (NEM_NETWORK_MAINNET, NEM_NETWORK_MIJIN): + return coin_type == SLIP44_ID + + # unknown network + return False diff --git a/core/src/apps/nem/sign_tx.py b/core/src/apps/nem/sign_tx.py index 73be9f9de7..33ba3533e4 100644 --- a/core/src/apps/nem/sign_tx.py +++ b/core/src/apps/nem/sign_tx.py @@ -7,22 +7,20 @@ from apps.common import seed from apps.common.keychain import with_slip44_keychain from apps.common.paths import validate_path -from . import CURVE, SLIP44_ID, mosaic, multisig, namespace, transfer +from . import CURVE, PATTERNS, SLIP44_ID, mosaic, multisig, namespace, transfer from .helpers import NEM_HASH_ALG, check_path from .validators import validate -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@with_slip44_keychain(*PATTERNS, slip44_id=SLIP44_ID, curve=CURVE) async def sign_tx(ctx, msg: NEMSignTx, keychain): validate(msg) await validate_path( ctx, - check_path, keychain, msg.transaction.address_n, - CURVE, - network=msg.transaction.network, + check_path(msg.transaction.address_n, msg.transaction.network), ) node = keychain.derive(msg.transaction.address_n) diff --git a/core/src/apps/ripple/__init__.py b/core/src/apps/ripple/__init__.py index 9b2c244d13..f2816366e6 100644 --- a/core/src/apps/ripple/__init__.py +++ b/core/src/apps/ripple/__init__.py @@ -1,8 +1,11 @@ from trezor import wire from trezor.messages import MessageType +from apps.common.paths import PATTERN_BIP44 + CURVE = "secp256k1" SLIP44_ID = 144 +PATTERN = PATTERN_BIP44 def boot() -> None: diff --git a/core/src/apps/ripple/get_address.py b/core/src/apps/ripple/get_address.py index 28fce0de4e..8b4b7643e3 100644 --- a/core/src/apps/ripple/get_address.py +++ b/core/src/apps/ripple/get_address.py @@ -2,21 +2,19 @@ from trezor.messages.RippleAddress import RippleAddress from trezor.messages.RippleGetAddress import RippleGetAddress from apps.common import paths -from apps.common.keychain import with_slip44_keychain +from apps.common.keychain import auto_keychain from apps.common.layout import address_n_to_str, show_address, show_qr -from . import CURVE, SLIP44_ID, helpers +from .helpers import address_from_public_key -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@auto_keychain(__name__) async def get_address(ctx, msg: RippleGetAddress, keychain): - await paths.validate_path( - ctx, helpers.validate_full_path, keychain, msg.address_n, CURVE - ) + await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) pubkey = node.public_key() - address = helpers.address_from_public_key(pubkey) + address = address_from_public_key(pubkey) if msg.show_display: desc = address_n_to_str(msg.address_n) diff --git a/core/src/apps/ripple/helpers.py b/core/src/apps/ripple/helpers.py index e817fa89f8..fc72c49404 100644 --- a/core/src/apps/ripple/helpers.py +++ b/core/src/apps/ripple/helpers.py @@ -2,8 +2,6 @@ from micropython import const from trezor.crypto.hashlib import ripemd160, sha256 -from apps.common import HARDENED - from . import base58_ripple # HASH_TX_ID = const(0x54584E00) # 'TXN' @@ -50,25 +48,3 @@ def decode_address(address: str): """Returns so called Account ID""" adr = base58_ripple.decode_check(address) return adr[1:] - - -def validate_full_path(path: list) -> bool: - """ - Validates derivation path to equal 44'/144'/a'/0/0, - where `a` is an account index from 0 to 1 000 000. - Similar to Ethereum this should be 44'/144'/a', but for - compatibility with other HW vendors we use 44'/144'/a'/0/0. - """ - if len(path) != 5: - return False - if path[0] != 44 | HARDENED: - return False - if path[1] != 144 | HARDENED: - return False - if path[2] < HARDENED or path[2] > 1000000 | HARDENED: - return False - if path[3] != 0: - return False - if path[4] != 0: - return False - return True diff --git a/core/src/apps/ripple/sign_tx.py b/core/src/apps/ripple/sign_tx.py index ad03953a03..ab108fc5bc 100644 --- a/core/src/apps/ripple/sign_tx.py +++ b/core/src/apps/ripple/sign_tx.py @@ -6,19 +6,16 @@ from trezor.messages.RippleSignTx import RippleSignTx from trezor.wire import ProcessError from apps.common import paths -from apps.common.keychain import with_slip44_keychain +from apps.common.keychain import auto_keychain -from . import CURVE, SLIP44_ID, helpers, layout +from . import helpers, layout from .serialize import serialize -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@auto_keychain(__name__) async def sign_tx(ctx, msg: RippleSignTx, keychain): validate(msg) - - await paths.validate_path( - ctx, helpers.validate_full_path, keychain, msg.address_n, CURVE - ) + await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) source_address = helpers.address_from_public_key(node.public_key()) diff --git a/core/src/apps/stellar/__init__.py b/core/src/apps/stellar/__init__.py index cd6edf2c4b..3677f87792 100644 --- a/core/src/apps/stellar/__init__.py +++ b/core/src/apps/stellar/__init__.py @@ -1,8 +1,11 @@ from trezor import wire from trezor.messages import MessageType +from apps.common.paths import PATTERN_SEP5 + CURVE = "ed25519" SLIP44_ID = 148 +PATTERN = PATTERN_SEP5 def boot() -> None: diff --git a/core/src/apps/stellar/get_address.py b/core/src/apps/stellar/get_address.py index 197609adcf..e7280ac8f5 100644 --- a/core/src/apps/stellar/get_address.py +++ b/core/src/apps/stellar/get_address.py @@ -2,17 +2,15 @@ from trezor.messages.StellarAddress import StellarAddress from trezor.messages.StellarGetAddress import StellarGetAddress from apps.common import paths, seed -from apps.common.keychain import with_slip44_keychain +from apps.common.keychain import auto_keychain from apps.common.layout import address_n_to_str, show_address, show_qr -from . import CURVE, SLIP44_ID, helpers +from . import helpers -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@auto_keychain(__name__) async def get_address(ctx, msg: StellarGetAddress, keychain): - await paths.validate_path( - ctx, helpers.validate_full_path, keychain, msg.address_n, CURVE - ) + await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) pubkey = seed.remove_ed25519_prefix(node.public_key()) diff --git a/core/src/apps/stellar/helpers.py b/core/src/apps/stellar/helpers.py index f145446902..6255154cba 100644 --- a/core/src/apps/stellar/helpers.py +++ b/core/src/apps/stellar/helpers.py @@ -3,8 +3,6 @@ import ustruct from trezor.crypto import base32 from trezor.wire import ProcessError -from apps.common import HARDENED - def public_key_from_address(address: str) -> bytes: """Extracts public key from an address @@ -26,22 +24,6 @@ def address_from_public_key(pubkey: bytes): return base32.encode(address) -def validate_full_path(path: list) -> bool: - """ - Validates derivation path to equal 44'/148'/a', - where `a` is an account index from 0 to 1 000 000. - """ - if len(path) != 3: - return False - if path[0] != 44 | HARDENED: - return False - if path[1] != 148 | HARDENED: - return False - if path[2] < HARDENED or path[2] > 1000000 | HARDENED: - return False - return True - - def _crc16_checksum_verify(data: bytes, checksum: bytes): if _crc16_checksum(data) != checksum: raise ProcessError("Invalid address checksum") diff --git a/core/src/apps/stellar/sign_tx.py b/core/src/apps/stellar/sign_tx.py index b4633d65e7..72ea2c6b73 100644 --- a/core/src/apps/stellar/sign_tx.py +++ b/core/src/apps/stellar/sign_tx.py @@ -8,17 +8,15 @@ from trezor.messages.StellarTxOpRequest import StellarTxOpRequest from trezor.wire import ProcessError from apps.common import paths, seed -from apps.common.keychain import with_slip44_keychain +from apps.common.keychain import auto_keychain -from . import CURVE, SLIP44_ID, consts, helpers, layout, writers +from . import consts, helpers, layout, writers from .operations import process_operation -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@auto_keychain(__name__) async def sign_tx(ctx, msg: StellarSignTx, keychain): - await paths.validate_path( - ctx, helpers.validate_full_path, keychain, msg.address_n, CURVE - ) + await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) pubkey = seed.remove_ed25519_prefix(node.public_key()) diff --git a/core/src/apps/tezos/__init__.py b/core/src/apps/tezos/__init__.py index e57a24b1bf..0ca63354f5 100644 --- a/core/src/apps/tezos/__init__.py +++ b/core/src/apps/tezos/__init__.py @@ -1,8 +1,14 @@ from trezor import wire from trezor.messages import MessageType +from apps.common.paths import PATTERN_SEP5 + CURVE = "ed25519" SLIP44_ID = 1729 +PATTERNS = ( + PATTERN_SEP5, + "m/44'/coin_type'/0'/account'", # Ledger compatibility +) def boot() -> None: diff --git a/core/src/apps/tezos/get_address.py b/core/src/apps/tezos/get_address.py index c8682b0e5d..b3c7399ccd 100644 --- a/core/src/apps/tezos/get_address.py +++ b/core/src/apps/tezos/get_address.py @@ -5,14 +5,12 @@ from apps.common import paths, seed from apps.common.keychain import with_slip44_keychain from apps.common.layout import address_n_to_str, show_address, show_qr -from . import CURVE, SLIP44_ID, helpers +from . import CURVE, PATTERNS, SLIP44_ID, helpers -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@with_slip44_keychain(*PATTERNS, slip44_id=SLIP44_ID, curve=CURVE) async def get_address(ctx, msg, keychain): - await paths.validate_path( - ctx, helpers.validate_full_path, keychain, msg.address_n, CURVE - ) + await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) diff --git a/core/src/apps/tezos/get_public_key.py b/core/src/apps/tezos/get_public_key.py index d2cffdf9b2..7b40b47a5c 100644 --- a/core/src/apps/tezos/get_public_key.py +++ b/core/src/apps/tezos/get_public_key.py @@ -8,14 +8,12 @@ from apps.common import paths, seed from apps.common.confirm import require_confirm from apps.common.keychain import with_slip44_keychain -from . import CURVE, SLIP44_ID, helpers +from . import CURVE, PATTERNS, SLIP44_ID, helpers -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@with_slip44_keychain(*PATTERNS, slip44_id=SLIP44_ID, curve=CURVE) async def get_public_key(ctx, msg, keychain): - await paths.validate_path( - ctx, helpers.validate_full_path, keychain, msg.address_n, CURVE - ) + await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) pk = seed.remove_ed25519_prefix(node.public_key()) diff --git a/core/src/apps/tezos/helpers.py b/core/src/apps/tezos/helpers.py index a2f0fec281..00ed87aa0e 100644 --- a/core/src/apps/tezos/helpers.py +++ b/core/src/apps/tezos/helpers.py @@ -2,7 +2,6 @@ from micropython import const from trezor.crypto import base58 -from apps.common import HARDENED from apps.common.writers import write_bytes_unchecked, write_uint8 TEZOS_AMOUNT_DECIMALS = const(6) @@ -66,31 +65,6 @@ def base58_decode_check(enc, prefix=None): return decoded -def validate_full_path(path: list) -> bool: - """ - Validates derivation path to equal 44'/1729'/a', - where `a` is an account index from 0 to 1 000 000. - Additional component added to allow ledger migration - 44'/1729'/0'/b' where `b` is an account index from 0 to 1 000 000 - """ - length = len(path) - if length < 3 or length > 4: - return False - if path[0] != 44 | HARDENED: - return False - if path[1] != 1729 | HARDENED: - return False - if length == 3: - if path[2] < HARDENED or path[2] > 1000000 | HARDENED: - return False - if length == 4: - if path[2] != 0 | HARDENED: - return False - if path[3] < HARDENED or path[3] > 1000000 | HARDENED: - return False - return True - - def write_bool(w: bytearray, boolean: bool): if boolean: write_uint8(w, 255) diff --git a/core/src/apps/tezos/sign_tx.py b/core/src/apps/tezos/sign_tx.py index ae9422559b..65088264aa 100644 --- a/core/src/apps/tezos/sign_tx.py +++ b/core/src/apps/tezos/sign_tx.py @@ -10,16 +10,14 @@ from apps.common import paths from apps.common.keychain import with_slip44_keychain from apps.common.writers import write_bytes_unchecked, write_uint8, write_uint32_be -from . import CURVE, SLIP44_ID, helpers, layout +from . import CURVE, PATTERNS, SLIP44_ID, helpers, layout PROPOSAL_LENGTH = const(32) -@with_slip44_keychain(SLIP44_ID, CURVE, allow_testnet=True) +@with_slip44_keychain(*PATTERNS, slip44_id=SLIP44_ID, curve=CURVE) async def sign_tx(ctx, msg, keychain): - await paths.validate_path( - ctx, helpers.validate_full_path, keychain, msg.address_n, CURVE - ) + await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n)