jonesmarvin8 24f6f1f8ca fixes
2026-04-26 21:29:54 -04:00

69 lines
2.0 KiB
Python

from ecdsa import SigningKey, VerifyingKey, SECP256k1, ECDH
from .. import constants
from ..card_interface import CardInterface
from ..exceptions import NotSelectedError
from ..secure_channel import SecureChannel
from ..preconditions import require_initialized
@require_initialized
def open_secure_channel(
card: CardInterface,
pairing_index: int,
pairing_key: bytes
) -> SecureChannel:
'''
Opens a secure session with the Keycard using ECDH and a pairing key.
This function performs an ephemeral ECDH key exchange with the card,
sends the ephemeral public key, and receives cryptographic material
from the card to derive a secure session.
Args:
transport: The transport used to communicate with the card.
card_public_key (bytes): The ECC public key of the card, retrieved
via select().
pairing_index (int): The index of the previously established
pairing slot.
pairing_key (bytes): The shared 32-byte pairing key.
Returns:
SecureChannel: A newly established secure session with the card.
Raises:
NotSelectedError: If no card public key is provided.
APDUError: If the card returns a failure status word.
'''
if not card.card_public_key:
raise NotSelectedError('Card not selected or missing public key')
ephemeral_key = SigningKey.generate(curve=SECP256k1)
eph_pub_bytes = ephemeral_key.verifying_key.to_string('uncompressed')
response: bytes = card.send_apdu(
ins=constants.INS_OPEN_SECURE_CHANNEL,
p1=pairing_index,
data=eph_pub_bytes
)
salt = bytes(response[:32])
seed_iv = bytes(response[32:])
public_key = VerifyingKey.from_string(
card.card_public_key,
curve=SECP256k1
)
ecdh = ECDH(
curve=SECP256k1,
private_key=ephemeral_key,
public_key=public_key
)
shared_secret = ecdh.generate_sharedsecret_bytes()
return SecureChannel.open(
shared_secret,
pairing_key,
salt,
seed_iv,
)