diff --git a/core/src/apps/bitcoin/multisig.py b/core/src/apps/bitcoin/multisig.py index 563dbf9ec3..6c8c6f5551 100644 --- a/core/src/apps/bitcoin/multisig.py +++ b/core/src/apps/bitcoin/multisig.py @@ -59,14 +59,10 @@ def validate_multisig(multisig: MultisigRedeemScriptType) -> None: def multisig_pubkey_index(multisig: MultisigRedeemScriptType, pubkey: bytes) -> int: validate_multisig(multisig) - if multisig.nodes: - for i, hd_node in enumerate(multisig.nodes): - if multisig_get_pubkey(hd_node, multisig.address_n) == pubkey: - return i - else: - for i, hd in enumerate(multisig.pubkeys): - if multisig_get_pubkey(hd.node, hd.address_n) == pubkey: - return i + pubkeys = multisig_get_pubkeys(multisig) + for i, derived_pubkey in enumerate(pubkeys): + if derived_pubkey == pubkey: + return i raise DataError("Pubkey not found in multisig script") @@ -85,19 +81,44 @@ def multisig_get_pubkey(n: HDNodeType, p: paths.Bip32Path) -> bytes: return node.public_key() +def compute_taproot_dummy_chaincode(multisig: MultisigRedeemScriptType) -> bytes: + from trezor.crypto.hashlib import sha256 + from trezor.utils import HashWriter + + from .writers import write_bytes_fixed + + if len(multisig.address_n) != 2: + raise DataError("Taproot multisig must use xpub derivation depth of 2") + + if multisig.nodes: + pubkeys = [hd.public_key for hd in multisig.nodes] + else: + pubkeys = [hd.public_key for hd in multisig.pubkeys] + pubkeys.sort() + h = HashWriter(sha256()) + prev = None + for pubkey in pubkeys: + if prev == pubkey: + continue + prev = pubkey + write_bytes_fixed(h, pubkey, 33) + + return h.get_digest() + + def multisig_get_dummy_pubkey(multisig: MultisigRedeemScriptType) -> bytes: from trezor.crypto import bip32 # The following encodes this xpub into an HDNode. It is the NUMS point suggested - # in BIP341, with a chaincode of 32 0 bytes. Deriving a pubkey from this node - # results in a provably unspendable pubkey. + # in BIP341, with a chaincode derived from the sha256 of the sorted public keys with duplicates removed. + # Deriving a pubkey from this node results in a provably unspendable pubkey. # https://delvingbitcoin.org/t/unspendable-keys-in-descriptors/304 # xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6QgnecKFpJFPpdzxKrwoaZoV44qAJewsc4kX9vGaCaBExuvJH57 node = bip32.HDNode( depth=0, fingerprint=2084970077, child_num=0, - chain_code=b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + chain_code=compute_taproot_dummy_chaincode(multisig), public_key=b"\x02P\x92\x9bt\xc1\xa0IT\xb7\x8bK`5\xe9z^\x07\x8aZ\x0f(\xec\x96\xd5G\xbf\xee\x9a\xce\x80:\xc0", )