diff --git a/core/src/apps/cardano/helpers/paths.py b/core/src/apps/cardano/helpers/paths.py index e2b34ad95..6eb645acc 100644 --- a/core/src/apps/cardano/helpers/paths.py +++ b/core/src/apps/cardano/helpers/paths.py @@ -1,3 +1,5 @@ +from micropython import const + from apps.common import HARDENED from apps.common.paths import PathSchema @@ -12,3 +14,13 @@ SCHEMA_ADDRESS = PathSchema("m/[44,1852]'/coin_type'/account'/[0,1,2]/address_in # staking is only allowed on Shelley paths with suffix /2/0 SCHEMA_STAKING = PathSchema("m/1852'/coin_type'/account'/2/0", SLIP44_ID) # fmt: on + +# the maximum allowed change address. this should be large enough for normal +# use and still allow to quickly brute-force the correct bip32 path +MAX_CHANGE_ADDRESS_INDEX = const(1_000_000) +ACCOUNT_PATH_INDEX = const(2) +BIP_PATH_LENGTH = const(5) + + +def unharden(item: int) -> int: + return item ^ (item & HARDENED) diff --git a/core/src/apps/cardano/helpers/utils.py b/core/src/apps/cardano/helpers/utils.py index 04e84f776..297c6e306 100644 --- a/core/src/apps/cardano/helpers/utils.py +++ b/core/src/apps/cardano/helpers/utils.py @@ -1,4 +1,4 @@ -from micropython import const +from apps.cardano.helpers.paths import ACCOUNT_PATH_INDEX, unharden if False: from typing import List @@ -27,5 +27,11 @@ def variable_length_encode(number: int) -> bytes: def to_account_path(path: List[int]) -> List[int]: - ACCOUNT_PATH_LENGTH = const(3) - return path[:ACCOUNT_PATH_LENGTH] + return path[: ACCOUNT_PATH_INDEX + 1] + + +def format_account_number(path: List[int]) -> str: + if len(path) <= ACCOUNT_PATH_INDEX: + raise ValueError("Path is too short.") + + return "#%d" % (unharden(path[ACCOUNT_PATH_INDEX]) + 1) diff --git a/core/src/apps/cardano/layout.py b/core/src/apps/cardano/layout.py index 5933884e3..aecf81a70 100644 --- a/core/src/apps/cardano/layout.py +++ b/core/src/apps/cardano/layout.py @@ -16,7 +16,7 @@ from trezor.ui.text import Text from trezor.utils import chunks from apps.common.confirm import confirm, require_confirm, require_hold_to_confirm -from apps.common.layout import address_n_to_str, show_warning +from apps.common.layout import address_n_to_str from . import seed from .address import ( @@ -25,7 +25,7 @@ from .address import ( pack_reward_address_bytes, ) from .helpers import protocol_magics -from .helpers.utils import to_account_path +from .helpers.utils import format_account_number, to_account_path if False: from typing import List, Optional @@ -122,7 +122,7 @@ async def show_warning_tx_different_staking_account( page1.normal("the current account.") page2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) - page2.normal("Staking account:") + page2.normal("Staking account %s:" % format_account_number(staking_account_path)) page2.bold(address_n_to_str(staking_account_path)) page2.normal("Change amount:") page2.bold(format_coin_amount(amount)) @@ -198,7 +198,7 @@ async def confirm_certificate( page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page1.normal("Confirm:") page1.bold(CERTIFICATE_TYPE_NAMES[certificate.type]) - page1.normal("for account:") + page1.normal("for account %s:" % format_account_number(certificate.path)) page1.bold(address_n_to_str(to_account_path(certificate.path))) pages.append(page1) @@ -320,7 +320,7 @@ async def confirm_withdrawal( ) -> None: page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page1.normal("Confirm withdrawal") - page1.normal("for account:") + page1.normal("for account %s:" % format_account_number(withdrawal.path)) page1.bold(address_n_to_str(to_account_path(withdrawal.path))) page1.normal("Amount:") page1.bold(format_coin_amount(withdrawal.amount)) @@ -401,31 +401,24 @@ async def show_warning_address_foreign_staking_key( staking_account_path: List[int], staking_key_hash: bytes, ) -> None: - await show_warning( - ctx, - ( - "Stake rights associated", - "with this address do", - "not match your", - "account", - address_n_to_str(account_path), - ), - button="Ok", - ) - + page1 = Text("Warning", ui.ICON_WRONG, ui.RED) + page1.normal("Stake rights associated") + page1.normal("with this address do") + page1.normal("not match your") + page1.normal("account %s:" % format_account_number(account_path)) + page1.bold(address_n_to_str(account_path)) + + page2 = Text("Warning", ui.ICON_WRONG, ui.RED) if staking_account_path: - staking_key_message = ( - "Stake account path:", - address_n_to_str(staking_account_path), - ) + page2.normal("Stake account %s:" % format_account_number(staking_account_path)) + page2.bold(address_n_to_str(staking_account_path)) + page2.br_half() else: - staking_key_message = ("Staking key:", hexlify(staking_key_hash).decode()) + page2.normal("Staking key:") + page2.bold(hexlify(staking_key_hash).decode()) + page2.normal("Continue?") - await show_warning( - ctx, - staking_key_message, - button="Ok", - ) + await require_confirm(ctx, Paginated([page1, page2])) async def show_warning_tx_network_unverifiable(ctx: wire.Context) -> None: @@ -440,13 +433,10 @@ async def show_warning_tx_network_unverifiable(ctx: wire.Context) -> None: async def show_warning_address_pointer( ctx: wire.Context, pointer: CardanoBlockchainPointerType ) -> None: - await show_warning( - ctx, - ( - "Pointer address:", - "Block: %s" % pointer.block_index, - "Transaction: %s" % pointer.tx_index, - "Certificate: %s" % pointer.certificate_index, - ), - button="Ok", - ) + text = Text("Warning", ui.ICON_WRONG, ui.RED) + text.normal("Pointer address:") + text.normal("Block: %s" % pointer.block_index) + text.normal("Transaction: %s" % pointer.tx_index) + text.normal("Certificate: %s" % pointer.certificate_index) + text.normal("Continue?") + await require_confirm(ctx, text) diff --git a/core/src/apps/cardano/sign_tx.py b/core/src/apps/cardano/sign_tx.py index 1ec741fff..1716653d4 100644 --- a/core/src/apps/cardano/sign_tx.py +++ b/core/src/apps/cardano/sign_tx.py @@ -1,5 +1,3 @@ -from micropython import const - from trezor import log, wire from trezor.crypto import hashlib from trezor.crypto.curve import ed25519 @@ -30,7 +28,13 @@ from .helpers import ( protocol_magics, staking_use_cases, ) -from .helpers.paths import SCHEMA_ADDRESS, SCHEMA_STAKING +from .helpers.paths import ( + ACCOUNT_PATH_INDEX, + BIP_PATH_LENGTH, + MAX_CHANGE_ADDRESS_INDEX, + SCHEMA_ADDRESS, + SCHEMA_STAKING, +) from .helpers.utils import to_account_path from .layout import ( confirm_certificate, @@ -58,12 +62,6 @@ if False: from trezor.messages.CardanoTxWithdrawalType import CardanoTxWithdrawalType from typing import Dict, List, Tuple -# the maximum allowed change address. this should be large enough for normal -# use and still allow to quickly brute-force the correct bip32 path -MAX_CHANGE_ADDRESS_INDEX = const(1_000_000) -ACCOUNT_PATH_INDEX = const(2) -BIP_PATH_LENGTH = const(5) - METADATA_HASH_SIZE = 32 MAX_METADATA_LENGTH = 500 diff --git a/python/src/trezorlib/cli/cardano.py b/python/src/trezorlib/cli/cardano.py index 075b22f7b..4e7e77dac 100644 --- a/python/src/trezorlib/cli/cardano.py +++ b/python/src/trezorlib/cli/cardano.py @@ -47,7 +47,6 @@ def cli(): @click.option("-t", "--testnet", is_flag=True) @with_client def sign_tx(client, file, protocol_magic, network_id, testnet): - print("AAAAA") """Sign Cardano transaction.""" transaction = json.load(file) diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index 6b4c52a63..3ac1ae5aa 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -15,11 +15,11 @@ "cardano-test_sign_tx.py::test_cardano_sign_tx[testnet_transaction0]": "994900bb0b0d52d0872dae1254fc8d5ed337e51be0e2ddd85b9d613bc9b11687", "cardano-test_sign_tx.py::test_cardano_sign_tx[testnet_transaction1]": "120eb04eae3bbcd492e4e0ab19e25bd2e718e4fa9620d5a0496eaee5704f1951", "cardano-test_sign_tx.py::test_cardano_sign_tx[transaction_with_metadata]": "e0707009a202512da0e38ec536d24d0522f12453ca0bcf91ff074e7653b7958f", -"cardano-test_sign_tx.py::test_cardano_sign_tx[transaction_with_stake_deregistration]": "c2f15e879c71ba93d5e56e043db4e0f9d3d16b459a21106eb4a4e2bd26b0a964", -"cardano-test_sign_tx.py::test_cardano_sign_tx[transaction_with_stake_deregistration_and_withdrawal]": "02b11d9fae34da917ee80e4df75bdcd2e1a8bab7f273d2c0b49f0ba16d21ba85", -"cardano-test_sign_tx.py::test_cardano_sign_tx[transaction_with_stake_registration_and_stake-3fdfc583": "a1ad5b29757c996c5d58f92dbd99ace6ef94d67abbb4ca4e2020464acf7cb21c", -"cardano-test_sign_tx.py::test_cardano_sign_tx[transaction_with_stake_registration_certifica-e7bd462a": "6f79fb024254f9eab2be91f4fb15b24926ec3270130eee60f7fe185a5a7f21e4", -"cardano-test_sign_tx.py::test_cardano_sign_tx[transaction_with_stake_registration_certificate]": "985c5a87c7dc8f36f56aa339285deb5dfaf446b3ab1f3e15dfdbac6bf223b73d", +"cardano-test_sign_tx.py::test_cardano_sign_tx[transaction_with_stake_deregistration]": "3d2ae35075551db91aa24dedbc388a37f58f0b618e073a73aed6a094a352b306", +"cardano-test_sign_tx.py::test_cardano_sign_tx[transaction_with_stake_deregistration_and_withdrawal]": "60f510ca75fc1288c92b50ac38ce2d413b608cc008a5ac519772936a98e60253", +"cardano-test_sign_tx.py::test_cardano_sign_tx[transaction_with_stake_registration_and_stake-3fdfc583": "604b63443e2d554fd3173e748439c29a968597b4db82ad98f56176cda225128f", +"cardano-test_sign_tx.py::test_cardano_sign_tx[transaction_with_stake_registration_certifica-e7bd462a": "ab4eb02c8769a90c0c5a643826d6affbab9cda1a55b5bfa1cc1066ac97a86368", +"cardano-test_sign_tx.py::test_cardano_sign_tx[transaction_with_stake_registration_certificate]": "c804dd78f120d14ef2585f04a148df23ab6da539d35b2b20b5bfc6092a4a0da7", "cardano-test_sign_tx.py::test_cardano_sign_tx_failed[all_tx_inputs_must_be_external_(without_path)]": "612dad8ab8762162a186ec9279d7de0bdfc589c52b4e4f4eba0545a00f21c3f0", "cardano-test_sign_tx.py::test_cardano_sign_tx_failed[certificate_has_invalid_pool_size]": "612dad8ab8762162a186ec9279d7de0bdfc589c52b4e4f4eba0545a00f21c3f0", "cardano-test_sign_tx.py::test_cardano_sign_tx_failed[certificate_has_non_staking_path]": "612dad8ab8762162a186ec9279d7de0bdfc589c52b4e4f4eba0545a00f21c3f0",