1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-18 12:28:09 +00:00

chore(core): Add mac field to Address message.

[no changelog]
This commit is contained in:
Andrew Kozlik 2021-02-03 15:58:00 +01:00 committed by Andrew Kozlik
parent 2ac3d0acb4
commit d0c3a6a2fa
10 changed files with 80 additions and 3 deletions

View File

@ -118,6 +118,7 @@ message GetAddress {
*/ */
message Address { message Address {
required string address = 1; // Coin address in Base58 encoding required string address = 1; // Coin address in Base58 encoding
optional bytes mac = 2; // Address authentication code
} }
/** /**

View File

@ -292,6 +292,8 @@ apps.bitcoin.writers
import apps.bitcoin.writers import apps.bitcoin.writers
apps.common apps.common
import apps.common import apps.common
apps.common.address_mac
import apps.common.address_mac
apps.common.address_type apps.common.address_type
import apps.common.address_type import apps.common.address_type
apps.common.authorization apps.common.authorization

View File

@ -5,6 +5,7 @@ from trezor.enums import InputScriptType
from trezor.messages import Address from trezor.messages import Address
from trezor.ui.layouts import show_address from trezor.ui.layouts import show_address
from apps.common.address_mac import get_address_mac
from apps.common.paths import address_n_to_str, validate_path from apps.common.paths import address_n_to_str, validate_path
from . import addresses from . import addresses
@ -64,6 +65,7 @@ async def get_address(
else: else:
address_qr = address # base58 address address_qr = address # base58 address
mac: bytes | None = None
multisig_xpub_magic = coin.xpub_magic multisig_xpub_magic = coin.xpub_magic
if msg.multisig: if msg.multisig:
if coin.segwit and not msg.ignore_xpub_magic: if coin.segwit and not msg.ignore_xpub_magic:
@ -77,6 +79,14 @@ async def get_address(
and coin.xpub_magic_multisig_segwit_p2sh is not None and coin.xpub_magic_multisig_segwit_p2sh is not None
): ):
multisig_xpub_magic = coin.xpub_magic_multisig_segwit_p2sh multisig_xpub_magic = coin.xpub_magic_multisig_segwit_p2sh
else:
# Attach a MAC for single-sig addresses, but only if the path is standard
# or if the user explicitly confirms a non-standard path.
if msg.show_display or (
keychain.is_in_keychain(msg.address_n)
and validate_path_against_script_type(coin, msg)
):
mac = get_address_mac(address, coin.slip44, keychain)
if msg.show_display: if msg.show_display:
if msg.multisig: if msg.multisig:
@ -101,4 +111,4 @@ async def get_address(
ctx, address=address_short, address_qr=address_qr, title=title ctx, address=address_short, address_qr=address_qr, title=title
) )
return Address(address=address) return Address(address=address, mac=mac)

View File

@ -238,7 +238,7 @@ async def get_keychain_for_coin(
) -> tuple[Keychain, coininfo.CoinInfo]: ) -> tuple[Keychain, coininfo.CoinInfo]:
coin = get_coin_by_name(coin_name) coin = get_coin_by_name(coin_name)
schemas = get_schemas_for_coin(coin) schemas = get_schemas_for_coin(coin)
slip21_namespaces = [[b"SLIP-0019"]] slip21_namespaces = [[b"SLIP-0019"], [b"SLIP-0024"]]
keychain = await get_keychain(ctx, coin.curve_name, schemas, slip21_namespaces) keychain = await get_keychain(ctx, coin.curve_name, schemas, slip21_namespaces)
return keychain, coin return keychain, coin

View File

@ -0,0 +1,32 @@
from typing import TYPE_CHECKING
from trezor import utils, wire
from trezor.crypto import hashlib, hmac
from .writers import write_bitcoin_varint, write_bytes_unchecked, write_uint32_le
if TYPE_CHECKING:
from apps.common.keychain import Keychain
_ADDRESS_MAC_KEY_PATH = [b"SLIP-0024", b"Address MAC key"]
def check_address_mac(
address: str, mac: bytes, slip44: int, keychain: Keychain
) -> None:
expected_mac = get_address_mac(address, slip44, 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:
# k = Key(m/"SLIP-0024"/"Address MAC key")
node = keychain.derive_slip21(_ADDRESS_MAC_KEY_PATH)
# mac = HMAC-SHA256(key = k, msg = slip44 || address)
mac = utils.HashWriter(hmac(hmac.SHA256, node.key()))
address_bytes = address.encode()
write_uint32_le(mac, slip44)
write_bitcoin_varint(mac, len(address_bytes))
write_bytes_unchecked(mac, address_bytes)
return mac.get_digest()

View File

@ -474,11 +474,13 @@ if TYPE_CHECKING:
class Address(protobuf.MessageType): class Address(protobuf.MessageType):
address: "str" address: "str"
mac: "bytes | None"
def __init__( def __init__(
self, self,
*, *,
address: "str", address: "str",
mac: "bytes | None" = None,
) -> None: ) -> None:
pass pass

View File

@ -226,6 +226,27 @@ class TestAddress(unittest.TestCase):
for path, input_type in incorrect_derivation_paths: for path, input_type in incorrect_derivation_paths:
self.assertFalse(self.validate(path, coin, input_type)) self.assertFalse(self.validate(path, coin, input_type))
def test_address_mac(self):
from apps.common.address_mac import check_address_mac, get_address_mac
from apps.common.keychain import Keychain
from apps.common.paths import AlwaysMatchingSchema
VECTORS = (
('Bitcoin', '1DyHzbQUoQEsLxJn6M7fMD8Xdt1XvNiwNE', '9cf7c230041d6ed95b8273bd32e023d3f227ec8c44257f6463c743a4b4add028'),
('Testnet', 'mm6kLYbGEL1tGe4ZA8xacfgRPdW1NLjCbZ', '4375089e50423505dc3480e6e85b0ba37a52bd1e009db5d260b6329f22c950d9')
)
seed = bip39.seed(' '.join(['all'] * 12), '')
for coin_name, address, mac in VECTORS:
coin = coins.by_name(coin_name)
mac = unhexlify(mac)
keychain = Keychain(seed, coin.curve_name, [AlwaysMatchingSchema], slip21_namespaces=[[b"SLIP-0024"]])
self.assertEqual(get_address_mac(address, coin.slip44, keychain), mac)
check_address_mac(address, mac, coin.slip44, keychain)
with self.assertRaises(wire.DataError):
mac = bytes([mac[0]^1]) + mac[1:]
check_address_mac(address, mac, coin.slip44, keychain)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -8,6 +8,7 @@ GetAddress.address_n max_count:8
GetAddress.coin_name max_size:21 GetAddress.coin_name max_size:21
Address.address max_size:130 Address.address max_size:130
Address.mac type:FT_IGNORE
SignTx.coin_name max_size:21 SignTx.coin_name max_size:21

View File

@ -128,7 +128,12 @@ def get_public_node(
@expect(messages.Address, field="address", ret_type=str) @expect(messages.Address, field="address", ret_type=str)
def get_address( def get_address(*args: Any, **kwargs: Any):
return get_authenticated_address(*args, **kwargs)
@expect(messages.Address)
def get_authenticated_address(
client: "TrezorClient", client: "TrezorClient",
coin_name: str, coin_name: str,
n: "Address", n: "Address",

View File

@ -1024,14 +1024,17 @@ class Address(protobuf.MessageType):
MESSAGE_WIRE_TYPE = 30 MESSAGE_WIRE_TYPE = 30
FIELDS = { FIELDS = {
1: protobuf.Field("address", "string", repeated=False, required=True), 1: protobuf.Field("address", "string", repeated=False, required=True),
2: protobuf.Field("mac", "bytes", repeated=False, required=False),
} }
def __init__( def __init__(
self, self,
*, *,
address: "str", address: "str",
mac: Optional["bytes"] = None,
) -> None: ) -> None:
self.address = address self.address = address
self.mac = mac
class GetOwnershipId(protobuf.MessageType): class GetOwnershipId(protobuf.MessageType):