From 6568c36a83ef015047a2868455b20ed08a20b184 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Sun, 3 Nov 2024 10:59:30 +0100 Subject: [PATCH] feat(core): Support multisig_xpub_magic in GetPublicKey. --- core/.changelog.d/2658.added | 1 + core/src/apps/bitcoin/common.py | 27 ++++++++++++++++ core/src/apps/bitcoin/get_address.py | 20 ++++-------- core/src/apps/bitcoin/get_public_key.py | 43 ++++++------------------- 4 files changed, 44 insertions(+), 47 deletions(-) create mode 100644 core/.changelog.d/2658.added diff --git a/core/.changelog.d/2658.added b/core/.changelog.d/2658.added new file mode 100644 index 0000000000..1d290b5c18 --- /dev/null +++ b/core/.changelog.d/2658.added @@ -0,0 +1 @@ +Add multisig_xpub_magic option to GetPublicKey. diff --git a/core/src/apps/bitcoin/common.py b/core/src/apps/bitcoin/common.py index 3fb2054a16..206a0eb34c 100644 --- a/core/src/apps/bitcoin/common.py +++ b/core/src/apps/bitcoin/common.py @@ -271,3 +271,30 @@ def descriptor_checksum(desc: str) -> str: for j in range(0, 8): ret[j] = CHECKSUM_CHARSET[(c >> (5 * (7 - j))) & 31] return "".join(ret) + + +def get_xpub_magic( + coin: CoinInfo, + script_type: InputScriptType, + ignore_xpub_magic: bool | None, + is_multisig: bool | None, +) -> int: + from trezor.enums import InputScriptType + + xpub_magic = None + if not ignore_xpub_magic: + # Handle SegWit v0 script types. + # When coin.segwit is true, cointool.py guarantees that the corresponding xpub_magic_* attributes not None. + if not is_multisig: + if script_type == InputScriptType.SPENDP2SHWITNESS: + xpub_magic = coin.xpub_magic_segwit_p2sh + elif script_type == InputScriptType.SPENDWITNESS: + xpub_magic = coin.xpub_magic_segwit_native + else: + if script_type == InputScriptType.SPENDWITNESS: + xpub_magic = coin.xpub_magic_multisig_segwit_native + elif script_type == InputScriptType.SPENDP2SHWITNESS: + xpub_magic = coin.xpub_magic_multisig_segwit_p2sh + + # SPENDADDRESS, SPENDMULTISIG, SPENDTAPROOT, ignore_xpub_magic or fallback. + return xpub_magic or coin.xpub_magic diff --git a/core/src/apps/bitcoin/get_address.py b/core/src/apps/bitcoin/get_address.py index 9bbd8e845e..8468eb378f 100644 --- a/core/src/apps/bitcoin/get_address.py +++ b/core/src/apps/bitcoin/get_address.py @@ -39,6 +39,7 @@ async def get_address(msg: GetAddress, keychain: Keychain, coin: CoinInfo) -> Ad from apps.common.paths import address_n_to_str, validate_path from . import addresses + from .common import get_xpub_magic from .keychain import ( address_n_to_name_or_unknown, validate_path_against_script_type, @@ -72,20 +73,7 @@ async def get_address(msg: GetAddress, keychain: Keychain, coin: CoinInfo) -> Ad address_case_sensitive = False # cashaddr address mac: bytes | None = None - multisig_xpub_magic = coin.xpub_magic - if multisig: - if coin.segwit and not msg.ignore_xpub_magic: - if ( - script_type == InputScriptType.SPENDWITNESS - and coin.xpub_magic_multisig_segwit_native is not None - ): - multisig_xpub_magic = coin.xpub_magic_multisig_segwit_native - elif ( - script_type == InputScriptType.SPENDP2SHWITNESS - and coin.xpub_magic_multisig_segwit_p2sh is not None - ): - multisig_xpub_magic = coin.xpub_magic_multisig_segwit_p2sh - else: + if not multisig: # 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 ( @@ -97,6 +85,10 @@ async def get_address(msg: GetAddress, keychain: Keychain, coin: CoinInfo) -> Ad if msg.show_display: path = address_n_to_str(address_n) if multisig: + multisig_xpub_magic = get_xpub_magic( + coin, script_type, msg.ignore_xpub_magic, is_multisig=True + ) + if multisig.nodes: pubnodes = multisig.nodes else: diff --git a/core/src/apps/bitcoin/get_public_key.py b/core/src/apps/bitcoin/get_public_key.py index f852b1d134..3d0637b1ec 100644 --- a/core/src/apps/bitcoin/get_public_key.py +++ b/core/src/apps/bitcoin/get_public_key.py @@ -10,20 +10,20 @@ if TYPE_CHECKING: async def get_public_key( msg: GetPublicKey, auth_msg: MessageType | None = None ) -> PublicKey: - from trezor import TR, wire + from trezor import TR from trezor.enums import InputScriptType from trezor.messages import HDNodeType, PublicKey, UnlockPath from apps.common import coininfo, paths from apps.common.keychain import FORBIDDEN_KEY_PATH, get_keychain + from .common import get_xpub_magic, sanitize_input_script_type + coin_name = msg.coin_name or "Bitcoin" script_type = msg.script_type or InputScriptType.SPENDADDRESS coin = coininfo.by_name(coin_name) curve_name = msg.ecdsa_curve_name or coin.curve_name address_n = msg.address_n # local_cache_attribute - ignore_xpub_magic = msg.ignore_xpub_magic # local_cache_attribute - xpub_magic = coin.xpub_magic # local_cache_attribute if address_n and address_n[0] == paths.SLIP25_PURPOSE: # UnlockPath is required to access SLIP25 paths. @@ -38,36 +38,13 @@ async def get_public_key( node = keychain.derive(address_n) - if ( - script_type - in ( - InputScriptType.SPENDADDRESS, - InputScriptType.SPENDMULTISIG, - InputScriptType.SPENDTAPROOT, + sanitize_input_script_type(coin, script_type) + + node_xpub = node.serialize_public( + get_xpub_magic( + coin, script_type, msg.ignore_xpub_magic, msg.multisig_xpub_magic ) - and xpub_magic is not None - ): - node_xpub = node.serialize_public(xpub_magic) - elif ( - coin.segwit - and script_type == InputScriptType.SPENDP2SHWITNESS - and (ignore_xpub_magic or coin.xpub_magic_segwit_p2sh is not None) - ): - assert coin.xpub_magic_segwit_p2sh is not None - node_xpub = node.serialize_public( - xpub_magic if ignore_xpub_magic else coin.xpub_magic_segwit_p2sh - ) - elif ( - coin.segwit - and script_type == InputScriptType.SPENDWITNESS - and (ignore_xpub_magic or coin.xpub_magic_segwit_native is not None) - ): - assert coin.xpub_magic_segwit_native is not None - node_xpub = node.serialize_public( - xpub_magic if ignore_xpub_magic else coin.xpub_magic_segwit_native - ) - else: - raise wire.DataError("Invalid combination of coin and script_type") + ) pubkey = node.public_key() # For curve25519 and ed25519, the public key has the prefix 0x00, as specified by SLIP-10. However, since this prefix is non-standard, it may be removed in the future. @@ -79,7 +56,7 @@ async def get_public_key( public_key=pubkey, ) descriptor = _xpub_descriptor( - node, xpub_magic, address_n, script_type, keychain.root_fingerprint() + node, coin.xpub_magic, address_n, script_type, keychain.root_fingerprint() ) if msg.show_display: