From 218b6a02a1da06b6bd0a9e4bb60c18751dd1b35e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Jas=CC=8Cko?= Date: Thu, 25 Jan 2024 20:04:36 +0100 Subject: [PATCH] Fixes public MAX_CHUNK_SIZE, total_size check, BASE_KEY_SCRIPT, Ledger link, remove multisig case --- core/src/apps/cardano/addresses.py | 6 +++-- core/src/apps/cardano/helpers/chunks.py | 16 +++++++++----- core/src/apps/cardano/helpers/utils.py | 18 +++++++++------ core/src/apps/cardano/layout.py | 29 +++++++++++++++---------- core/src/apps/cardano/sign_message.py | 8 +++---- core/src/apps/cardano/sign_tx/signer.py | 2 +- 6 files changed, 46 insertions(+), 33 deletions(-) diff --git a/core/src/apps/cardano/addresses.py b/core/src/apps/cardano/addresses.py index f43beb0ad8a..74d3870bc63 100644 --- a/core/src/apps/cardano/addresses.py +++ b/core/src/apps/cardano/addresses.py @@ -48,8 +48,9 @@ ADDRESS_TYPES_MESSAGE = ( CardanoAddressType.BASE, + CardanoAddressType.BASE_KEY_SCRIPT, CardanoAddressType.ENTERPRISE, - CardanoAddressType.REWARD + CardanoAddressType.REWARD, ) ADDRESS_TYPES_PAYMENT = ADDRESS_TYPES_PAYMENT_KEY + ADDRESS_TYPES_PAYMENT_SCRIPT @@ -247,8 +248,9 @@ def validate_cvote_payment_address_parameters( validate_address_parameters(parameters) assert_params_cond(parameters.address_type in ADDRESS_TYPES_SHELLEY) + def validate_message_address_parameters( - parameters: messages.CardanoAddressParametersType, + parameters: messages.CardanoAddressParametersType, ) -> None: validate_address_parameters(parameters) assert_params_cond(parameters.address_type in ADDRESS_TYPES_MESSAGE) diff --git a/core/src/apps/cardano/helpers/chunks.py b/core/src/apps/cardano/helpers/chunks.py index 09536175530..c702628d1b8 100644 --- a/core/src/apps/cardano/helpers/chunks.py +++ b/core/src/apps/cardano/helpers/chunks.py @@ -20,13 +20,13 @@ Generic = (object,) C = Chunk = 0 -_MAX_CHUNK_SIZE = const(1024) +MAX_CHUNK_SIZE = const(1024) def _get_chunks_count(size: int) -> int: - """Integer-only version of `ceil(size / _MAX_CHUNK_SIZE)`.""" + """Integer-only version of `ceil(size / MAX_CHUNK_SIZE)`.""" assert size >= 0 - return 0 if size == 0 else (size - 1) // _MAX_CHUNK_SIZE + 1 + return 0 if size == 0 else (size - 1) // MAX_CHUNK_SIZE + 1 def _validate_chunk( @@ -39,15 +39,19 @@ def _validate_chunk( chunk_name = chunk.__class__.__name__ - if len(chunk.data) > _MAX_CHUNK_SIZE: + if len(chunk.data) > MAX_CHUNK_SIZE: raise ProcessError(f"Invalid {chunk_name}: Too large") is_last_chunk = chunk_index == chunks_count - 1 - if not is_last_chunk and len(chunk.data) < _MAX_CHUNK_SIZE: + if not is_last_chunk and len(chunk.data) < MAX_CHUNK_SIZE: raise ProcessError(f"Invalid intermediate {chunk_name}: Too small") - if is_last_chunk and len(chunk.data) != total_size % _MAX_CHUNK_SIZE: + if ( + is_last_chunk + # check whether this chunk and preceding chunks add up to the supposed size + and len(chunk.data) + MAX_CHUNK_SIZE * (chunks_count - 1) != total_size + ): raise ProcessError( f"Invalid last {chunk_name}: Size inconsistent with total bytes" ) diff --git a/core/src/apps/cardano/helpers/utils.py b/core/src/apps/cardano/helpers/utils.py index a72ed5a46fa..8b7357c45ee 100644 --- a/core/src/apps/cardano/helpers/utils.py +++ b/core/src/apps/cardano/helpers/utils.py @@ -121,27 +121,31 @@ def is_printable_ascii(bytestring: bytes) -> bool: def is_unambiguous_ascii(bytestring: bytes) -> bool: - """Checks whether the bytestring can be printed as ASCII without confusion.""" - # empty string is unambiguous - # because text consisting only of spaces gets rejected later in this function + """ + Checks whether the bytestring can be printed as ASCII without confusion. + Based on https://github.com/vacuumlabs/ledger-app-cardano-shelley/blob/6ddc60e8fdff13e35bff5cdf108b84b81a79f10c/src/textUtils.c#L274 + """ + # no empty strings if len(bytestring) == 0: - return True + return False # no non-printable ascii except spaces if not is_printable_ascii(bytestring): return False + SPACE = ord(" ") + # no leading space - if bytestring[0] == ord(" "): + if bytestring[0] == SPACE: return False # no trailing space - if bytestring[-1] == ord(" "): + if bytestring[-1] == SPACE: return False # no consecutive spaces for a, b in zip(bytestring, bytestring[1:]): - if a == ord(" ") and b == ord(" "): + if a == SPACE and b == SPACE: return False return True diff --git a/core/src/apps/cardano/layout.py b/core/src/apps/cardano/layout.py index d1a94541c2d..fc68fb41071 100644 --- a/core/src/apps/cardano/layout.py +++ b/core/src/apps/cardano/layout.py @@ -274,6 +274,17 @@ async def confirm_inline_datum(first_chunk: bytes, inline_datum_size: int) -> No ) +async def confirm_reference_script( + first_chunk: bytes, reference_script_size: int +) -> None: + await _confirm_tx_data_chunk( + "confirm_reference_script", + "Reference script", + first_chunk, + reference_script_size, + ) + + async def confirm_message_payload( payload_first_chunk: bytes, payload_hash: bytes, @@ -283,7 +294,12 @@ async def confirm_message_payload( ) -> None: props: list[PropertyType] - if is_unambiguous_ascii(payload_first_chunk) and not prefer_hex_display: + if not payload_first_chunk: + assert payload_size == 0 + props = _get_data_chunk_props( + "Empty message", payload_first_chunk, payload_size + ) + elif not prefer_hex_display and is_unambiguous_ascii(payload_first_chunk): props = _get_data_chunk_props( "Message text", payload_first_chunk.decode("ascii"), payload_size ) @@ -300,17 +316,6 @@ async def confirm_message_payload( ) -async def confirm_reference_script( - first_chunk: bytes, reference_script_size: int -) -> None: - await _confirm_tx_data_chunk( - "confirm_reference_script", - "Reference script", - first_chunk, - reference_script_size, - ) - - def _get_data_chunk_props( title: str, first_chunk: bytes | str, data_size: int ) -> list[PropertyType]: diff --git a/core/src/apps/cardano/sign_message.py b/core/src/apps/cardano/sign_message.py index b5c51e17aa0..5d4a23439f0 100644 --- a/core/src/apps/cardano/sign_message.py +++ b/core/src/apps/cardano/sign_message.py @@ -4,7 +4,7 @@ from trezor.wire import ProcessError from trezor.wire.context import call as ctx_call -from apps.cardano.helpers.chunks import ChunkIterator +from apps.cardano.helpers.chunks import MAX_CHUNK_SIZE, ChunkIterator from apps.cardano.helpers.paths import SCHEMA_PUBKEY from apps.common import cbor @@ -23,11 +23,9 @@ _COSE_HEADER_ALGORITHM_KEY = const(1) _COSE_EDDSA_ALGORITHM_ID = const(-8) -_MAX_CHUNK_SIZE = const(1024) - def _validate_message_signing_path(path: list[int]) -> None: - if not (SCHEMA_PUBKEY.match(path) or seed.is_multisig_path(path)): + if not SCHEMA_PUBKEY.match(path): raise ProcessError("Invalid signing path") @@ -46,7 +44,7 @@ def _validate_message_init(msg: CardanoSignMessageInit) -> None: if msg.address_parameters: addresses.validate_message_address_parameters(msg.address_parameters) - if msg.payload_size > _MAX_CHUNK_SIZE and not msg.hash_payload: + if msg.payload_size > MAX_CHUNK_SIZE and not msg.hash_payload: raise ProcessError("Payload too long to sign without hashing") _validate_message_signing_path(msg.signing_path) diff --git a/core/src/apps/cardano/sign_tx/signer.py b/core/src/apps/cardano/sign_tx/signer.py index d399d324a8c..25ceb13318e 100644 --- a/core/src/apps/cardano/sign_tx/signer.py +++ b/core/src/apps/cardano/sign_tx/signer.py @@ -11,8 +11,8 @@ from trezor.wire import DataError, ProcessError from trezor.wire.context import call as ctx_call -from apps.common import safety_checks from apps.cardano.helpers.chunks import ChunkIterator +from apps.common import safety_checks from .. import addresses, certificates, layout, seed from ..helpers import INPUT_PREV_HASH_SIZE, LOVELACE_MAX_SUPPLY