jonesmarvin8 41f34f4ff4 fixes
2026-04-26 20:27:22 -04:00

81 lines
2.5 KiB
Python

from typing import Optional, Union
from .. import constants
from ..card_interface import CardInterface
from ..constants import DerivationOption, KeyExportOption, DerivationSource
from ..parsing import tlv
from ..parsing.exported_key import ExportedKey
from ..parsing.keypath import KeyPath
from ..preconditions import require_pin_verified
@require_pin_verified
def export_key(
card: CardInterface,
derivation_option: constants.DerivationOption,
public_only: bool,
keypath: Optional[Union[str, bytes, bytearray]] = None,
make_current: bool = False,
source: DerivationSource = DerivationSource.MASTER
) -> ExportedKey:
"""
Export a key (public or private) from the card using an optional keypath.
If derivation_option == CURRENT, keypath can be omitted or empty.
Args:
card: The card object
derivation_option: e.g. DERIVE, CURRENT, DERIVE_AND_MAKE_CURRENT
public_only: If True, export only public key
keypath: BIP32-style string or packed bytes, or None if CURRENT
make_current: Whether to update the card's current path
source: MASTER (0x00), PARENT (0x40), CURRENT (0x80)
Returns:
dict with optional 'public_key', 'private_key', 'chain_code'
"""
if keypath is None:
if derivation_option != constants.DerivationOption.CURRENT:
raise ValueError(
"Keypath required unless using CURRENT derivation")
data = b""
elif isinstance(keypath, str):
data = KeyPath(keypath).data
elif isinstance(keypath, (bytes, bytearray)):
if len(keypath) % 4 != 0:
raise ValueError("Byte keypath must be a multiple of 4")
data = bytes(keypath)
else:
raise TypeError("Keypath must be a string or bytes")
if make_current:
p1 = DerivationOption.DERIVE_AND_MAKE_CURRENT
else:
p1 = derivation_option
p1 |= source
if public_only:
p2 = KeyExportOption.PUBLIC_ONLY
else:
p2 = KeyExportOption.PRIVATE_AND_PUBLIC
response = card.send_secure_apdu(
ins=constants.INS_EXPORT_KEY,
p1=p1,
p2=p2,
data=data
)
outer = tlv.parse_tlv(response)
tpl = outer.get(0xA1)
if not tpl:
raise ValueError("Missing keypair template (tag 0xA1)")
inner = tlv.parse_tlv(tpl[0])
return ExportedKey(
public_key=inner.get(0x80, [None])[0],
private_key=inner.get(0x81, [None])[0],
chain_code=inner.get(0x82, [None])[0],
)