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

[no changelog]
matejcik/one-of
Andrew Kozlik 3 years ago committed by Andrew Kozlik
parent 2ac3d0acb4
commit d0c3a6a2fa

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

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

@ -5,6 +5,7 @@ from trezor.enums import InputScriptType
from trezor.messages import 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 . import addresses
@ -64,6 +65,7 @@ async def get_address(
else:
address_qr = address # base58 address
mac: bytes | None = None
multisig_xpub_magic = coin.xpub_magic
if msg.multisig:
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
):
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.multisig:
@ -101,4 +111,4 @@ async def get_address(
ctx, address=address_short, address_qr=address_qr, title=title
)
return Address(address=address)
return Address(address=address, mac=mac)

@ -238,7 +238,7 @@ async def get_keychain_for_coin(
) -> tuple[Keychain, coininfo.CoinInfo]:
coin = get_coin_by_name(coin_name)
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)
return keychain, coin

@ -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()

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

@ -226,6 +226,27 @@ class TestAddress(unittest.TestCase):
for path, input_type in incorrect_derivation_paths:
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__':
unittest.main()

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

@ -128,7 +128,12 @@ def get_public_node(
@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",
coin_name: str,
n: "Address",

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

Loading…
Cancel
Save