You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/core/src/apps/bitcoin/get_public_key.py

139 lines
4.4 KiB

from typing import TYPE_CHECKING
if TYPE_CHECKING:
from trezor.enums import InputScriptType
from trezor.messages import GetPublicKey, PublicKey
from trezor.protobuf import MessageType
async def get_public_key(
msg: GetPublicKey, auth_msg: MessageType | None = None
) -> PublicKey:
from trezor import wire
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
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.
if not UnlockPath.is_type_of(auth_msg):
raise FORBIDDEN_KEY_PATH
# Verify that the desired path lies in the unlocked subtree.
if auth_msg.address_n != address_n[: len(auth_msg.address_n)]:
raise FORBIDDEN_KEY_PATH
keychain = await get_keychain(curve_name, [paths.AlwaysMatchingSchema])
node = keychain.derive(address_n)
if (
script_type
in (
InputScriptType.SPENDADDRESS,
InputScriptType.SPENDMULTISIG,
InputScriptType.SPENDTAPROOT,
)
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()
if pubkey[0] == 1:
pubkey = b"\x00" + pubkey[1:]
node_type = HDNodeType(
depth=node.depth(),
child_num=node.child_num(),
fingerprint=node.fingerprint(),
chain_code=node.chain_code(),
public_key=pubkey,
)
if msg.show_display:
from trezor.ui.layouts import confirm_path_warning, show_pubkey
from apps.common.paths import address_n_to_str
from .keychain import address_n_to_name
path = address_n_to_str(address_n)
account_name = address_n_to_name(
coin, address_n, script_type, account_level=True
)
if account_name is None:
account = None
await confirm_path_warning(path)
elif account_name == "":
account = coin.coin_shortcut
else:
account = f"{coin.coin_shortcut} {account_name}"
show_xpub = node_xpub
if script_type == InputScriptType.SPENDTAPROOT:
show_xpub = _xpub_descriptor(
node_xpub, address_n, script_type, node.fingerprint()
)
await show_pubkey(
show_xpub,
"XPUB",
account=account,
path=path,
mismatch_title="XPUB mismatch?",
br_type="show_xpub",
)
return PublicKey(
node=node_type,
xpub=node_xpub,
root_fingerprint=keychain.root_fingerprint(),
)
def _xpub_descriptor(
node_xpub: str,
address_n: list[int],
script_type: InputScriptType,
fingerprint: int,
) -> str:
from trezor.enums import InputScriptType
from apps.common import paths
from .common import descriptor_checksum
if script_type != InputScriptType.SPENDTAPROOT:
raise ValueError("Unsupported script type.")
path = paths.address_n_to_str(address_n)
descriptor = f"tr([{fingerprint:08x}{path[1:]}]{node_xpub}/<0;1>/*)"
checksum = descriptor_checksum(descriptor)
return f"{descriptor}#{checksum}"