diff --git a/src/apps/cardano/address.py b/src/apps/cardano/address.py index 078d30903..d870aa73b 100644 --- a/src/apps/cardano/address.py +++ b/src/apps/cardano/address.py @@ -1,3 +1,4 @@ +from trezor import log from trezor.crypto import base58, crc, hashlib from apps.cardano import cbor @@ -5,6 +6,14 @@ from apps.common import HARDENED from apps.common.seed import remove_ed25519_prefix +def _encode_address_raw(address_data_encoded): + return base58.encode( + cbor.encode( + [cbor.Tagged(24, address_data_encoded), crc.crc32(address_data_encoded)] + ) + ) + + def derive_address_and_node(keychain, path: list): node = keychain.derive(path) @@ -16,12 +25,31 @@ def derive_address_and_node(keychain, path: list): 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) + return (_encode_address_raw(address_data_encoded), node) + + +def is_safe_output_address(address) -> bool: + """ + Determines whether it is safe to include the address as-is as + a tx output, preventing unintended side effects (e.g. CBOR injection) + """ + try: + address_hex = base58.decode(address) + address_unpacked = cbor.decode(address_hex) + except ValueError as e: + if __debug__: + log.exception(__name__, e) + return False + + if not isinstance(address_unpacked, list) or len(address_unpacked) != 2: + return False + + address_data_encoded = address_unpacked[0] + + if not isinstance(address_data_encoded, bytes): + return False + + return _encode_address_raw(address_data_encoded) == address def validate_full_path(path: list) -> bool: diff --git a/src/apps/cardano/sign_tx.py b/src/apps/cardano/sign_tx.py index fb85a628a..12cfa5b39 100644 --- a/src/apps/cardano/sign_tx.py +++ b/src/apps/cardano/sign_tx.py @@ -8,7 +8,11 @@ from trezor.messages.CardanoTxRequest import CardanoTxRequest from trezor.messages.MessageType import CardanoTxAck from apps.cardano import cbor, seed -from apps.cardano.address import derive_address_and_node, validate_full_path +from apps.cardano.address import ( + derive_address_and_node, + is_safe_output_address, + validate_full_path, +) from apps.cardano.layout import confirm_sending, confirm_transaction, progress from apps.common.paths import validate_path from apps.common.seed import remove_ed25519_prefix @@ -184,6 +188,8 @@ class Transaction: raise wire.ProcessError( "Each output must have address or address_n field!" ) + if not is_safe_output_address(output.address): + raise wire.ProcessError("Invalid output address!") outgoing_coins.append(output.amount) output_addresses.append(output.address)