From 982e67b711e7a30c7075d4ae4dbc1b5ef2e59ca5 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Tue, 15 Apr 2025 09:56:19 +0200 Subject: [PATCH] feat(core): Include address_n in address MAC. --- core/src/apps/bitcoin/get_address.py | 2 +- core/src/apps/common/address_mac.py | 12 +++++++++--- core/src/apps/common/payment_request.py | 8 ++++++-- core/src/apps/ethereum/get_address.py | 2 +- tests/device_tests/payment_req.py | 5 ++++- 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/core/src/apps/bitcoin/get_address.py b/core/src/apps/bitcoin/get_address.py index e3a053cf80..5d842d17e6 100644 --- a/core/src/apps/bitcoin/get_address.py +++ b/core/src/apps/bitcoin/get_address.py @@ -101,7 +101,7 @@ async def get_address(msg: GetAddress, keychain: Keychain, coin: CoinInfo) -> Ad keychain.is_in_keychain(address_n) and validate_path_against_script_type(coin, msg) ): - mac = get_address_mac(address, coin.slip44, keychain) + mac = get_address_mac(address, coin.slip44, address_n, keychain) if msg.show_display: path = address_n_to_str(address_n) diff --git a/core/src/apps/common/address_mac.py b/core/src/apps/common/address_mac.py index f4eccf38f6..9b9645b7da 100644 --- a/core/src/apps/common/address_mac.py +++ b/core/src/apps/common/address_mac.py @@ -4,22 +4,25 @@ from trezor import utils if TYPE_CHECKING: from apps.common.keychain import Keychain + from apps.common.paths import Bip32Path _ADDRESS_MAC_KEY_PATH = [b"SLIP-0024", b"Address MAC key"] def check_address_mac( - address: str, mac: bytes, slip44: int, keychain: Keychain + address: str, mac: bytes, slip44: int, address_n: Bip32Path, keychain: Keychain ) -> None: from trezor import wire from trezor.crypto import hashlib - expected_mac = get_address_mac(address, slip44, keychain) + expected_mac = get_address_mac(address, slip44, address_n, keychain) if len(mac) != hashlib.sha256.digest_size or not utils.consteq(expected_mac, mac): raise wire.DataError("Invalid address MAC.") -def get_address_mac(address: str, slip44: int, keychain: Keychain) -> bytes: +def get_address_mac( + address: str, slip44: int, address_n: Bip32Path, keychain: Keychain +) -> bytes: from trezor.crypto import hmac from .writers import write_bytes_unchecked, write_compact_size, write_uint32_le @@ -31,6 +34,9 @@ def get_address_mac(address: str, slip44: int, keychain: Keychain) -> bytes: mac = utils.HashWriter(hmac(hmac.SHA256, node.key())) address_bytes = address.encode() write_uint32_le(mac, slip44) + write_compact_size(mac, len(address_n)) + for n in address_n: + write_uint32_le(mac, n) write_compact_size(mac, len(address_bytes)) write_bytes_unchecked(mac, address_bytes) return mac.get_digest() diff --git a/core/src/apps/common/payment_request.py b/core/src/apps/common/payment_request.py index a11c92ace4..8336b00c76 100644 --- a/core/src/apps/common/payment_request.py +++ b/core/src/apps/common/payment_request.py @@ -59,12 +59,16 @@ class PaymentRequestVerifier: elif m.refund_memo is not None: memo = m.refund_memo # Unlike in a coin purchase memo, the coin type is implied by the payment request. - check_address_mac(memo.address, memo.mac, slip44_id, keychain) + check_address_mac( + memo.address, memo.mac, slip44_id, memo.address_n, keychain + ) writers.write_uint32_le(self.h_pr, _MEMO_TYPE_REFUND) writers.write_bytes_prefixed(self.h_pr, memo.address.encode()) elif m.coin_purchase_memo is not None: memo = m.coin_purchase_memo - check_address_mac(memo.address, memo.mac, memo.coin_type, keychain) + check_address_mac( + memo.address, memo.mac, memo.coin_type, memo.address_n, keychain + ) writers.write_uint32_le(self.h_pr, _MEMO_TYPE_COIN_PURCHASE) writers.write_uint32_le(self.h_pr, memo.coin_type) writers.write_bytes_prefixed(self.h_pr, memo.amount.encode()) diff --git a/core/src/apps/ethereum/get_address.py b/core/src/apps/ethereum/get_address.py index 75eac57102..9367bc14ae 100644 --- a/core/src/apps/ethereum/get_address.py +++ b/core/src/apps/ethereum/get_address.py @@ -33,7 +33,7 @@ async def get_address( address = address_from_bytes(node.ethereum_pubkeyhash(), defs.network) slip44_id = address_n[1] # it depends on the network (ETH vs ETC...) - mac = get_address_mac(address, paths.unharden(slip44_id), keychain) + mac = get_address_mac(address, paths.unharden(slip44_id), address_n, keychain) if msg.show_display: await show_address( diff --git a/tests/device_tests/payment_req.py b/tests/device_tests/payment_req.py index 62c1f44047..f83114f0c3 100644 --- a/tests/device_tests/payment_req.py +++ b/tests/device_tests/payment_req.py @@ -71,7 +71,9 @@ def make_payment_request( hash_bytes_prefixed(h_pr, memo.text.encode()) elif isinstance(memo, RefundMemo): msg_memo = messages.RefundMemo( - address=memo.address_resp.address, mac=memo.address_resp.mac + address=memo.address_resp.address, + address_n=memo.address_n, + mac=memo.address_resp.mac, ) msg_memos.append(messages.PaymentRequestMemo(refund_memo=msg_memo)) memo_type = 2 @@ -82,6 +84,7 @@ def make_payment_request( coin_type=memo.slip44, amount=memo.amount, address=memo.address_resp.address, + address_n=memo.address_n, mac=memo.address_resp.mac, ) msg_memos.append(messages.PaymentRequestMemo(coin_purchase_memo=msg_memo))