keycard.secure_channel package

Submodules

keycard.secure_channel.base module

class keycard.secure_channel.base.SecureChannel(*args, **kwargs)

Bases: Protocol

An open encrypted session with the card.

property is_open: bool
unwrap(response)

Verifies and decrypts a response.

Return type:

bytes

Returns:

The response data.

Return type:

bytes

Raises:

APDUError – If the card returned a failure status word.

property version: SecureChannelVersion
wrap(ins, p1, p2, data)

Protects a command for sending.

Return type:

bytes

Returns:

The complete command APDU.

Return type:

bytes

class keycard.secure_channel.base.SecureChannelVersion(*values)

Bases: IntEnum

V1 = 1
V2 = 2
keycard.secure_channel.base.channel_version(app_version)

Returns the secure channel version used by an applet version.

Return type:

SecureChannelVersion

Parameters:

app_version (tuple[int, int]) – Applet major and minor version.

Returns:

V2 from applet 4.0, V1 before.

Return type:

SecureChannelVersion

keycard.secure_channel.v1 module

class keycard.secure_channel.v1.SecureChannelV1(enc_key, mac_key, iv, authenticated=False, previous_iv=None)

Bases: object

Secure Channel V1 (applet 3.x): AES-CBC encryption with AES-CBC-MAC, keyed from a pairing.

enc_key

The AES encryption key for the session.

Type:

bytes

mac_key

The AES MAC key for message authentication.

Type:

bytes

iv

The initialization vector for AES operations.

Type:

bytes

authenticated

Indicates if the session is authenticated.

Type:

bool

previous_iv

The IV the last command was encrypted with.

Type:

Optional[bytes]

authenticated: bool = False
enc_key: bytes
classmethod from_handshake(shared_secret, pairing_key, salt, seed_iv)

Derives the session keys from the handshake.

Return type:

SecureChannelV1

Parameters:
  • shared_secret (bytes) – The shared secret used for key derivation.

  • pairing_key (bytes) – The pairing key used for key derivation.

  • salt (bytes) – The salt value used in the key derivation process.

  • seed_iv (bytes) – The initialization vector (IV) to seed the session.

Returns:

A channel initialized with derived encryption

and MAC keys, and the provided IV.

Return type:

SecureChannelV1

property is_open: bool
iv: bytes
mac_key: bytes
previous_iv: bytes | None = None
unwrap(response)

Verifies and decrypts a response.

Return type:

bytes

Returns:

The response data.

Return type:

bytes

Raises:

APDUError – If the card returned a failure status word.

unwrap_response(response)

Unwraps and verifies a secure channel response.

Return type:

tuple[bytes, int]

Parameters:

response (bytes) – The encrypted response bytes to unwrap.

Returns:

A tuple containing the decrypted plaintext

(excluding the status word) and the status word as an integer.

Return type:

tuple[bytes, int]

Raises:
  • ValueError – If the secure channel is not authenticated.

  • ValueError – If the response length is invalid.

  • ValueError – If the MAC verification fails.

  • ValueError – If the decrypted plaintext is too short to contain a status word.

property version: SecureChannelVersion
wrap(ins, p1, p2, data)

Protects a command for sending.

Return type:

bytes

Returns:

The complete command APDU.

Return type:

bytes

wrap_apdu(cla, ins, p1, p2, data)

Wraps an APDU command with secure channel encryption and MAC.

Return type:

bytes

Parameters:
  • cla (int) – The APDU class byte.

  • ins (int) – The APDU instruction byte.

  • p1 (int) – The APDU parameter 1 byte.

  • p2 (int) – The APDU parameter 2 byte.

  • data (bytes) – The APDU data field to be encrypted.

Returns:

The wrapped APDU as a tuple

containing the class, instruction, parameter 1, parameter 2, and the concatenated MAC and encrypted data.

Return type:

tuple[int, int, int, int, bytes]

Raises:

ValueError – If the secure channel is not authenticated and the instruction is not 0x11.

keycard.secure_channel.v1.cbc_decrypt(key, iv, data)

Decrypts AES-CBC data and removes the padding.

Return type:

bytes

Raises:

ValueError – If the padding is invalid.

keycard.secure_channel.v1.cbc_encrypt(key, iv, data)

Pads and encrypts data with AES-CBC.

Return type:

bytes

Returns:

The ciphertext.

Return type:

bytes

keycard.secure_channel.v1.cbc_mac(key, data)

AES-CBC-MAC with a zero IV over block-aligned data.

Return type:

bytes

Returns:

The 16-byte MAC.

Return type:

bytes

Raises:

ValueError – If the data is not a multiple of the block size.

keycard.secure_channel.v1.one_shot_encrypt(card_public_key, plaintext)

Encrypts data once to the card’s public key, as Secure Channel V1 INIT requires: an ephemeral ECDH key, then AES-CBC under the shared secret.

Return type:

bytes

Returns:

Ephemeral public key length and key, IV, then ciphertext.

Return type:

bytes

keycard.secure_channel.v2 module

class keycard.secure_channel.v2.SecureChannelV2(key_h2c, key_c2h)

Bases: object

Secure Channel V2 (applet 4.x): AES-128-CCM with keys from an ECDHE handshake authenticated by the card’s Identity Key.

close()
Return type:

None

classmethod from_handshake(salt, shared_x)

Derives the session keys from the handshake.

Return type:

SecureChannelV2

Parameters:
  • salt (bytes) – The 32-byte HKDF salt sent by the client.

  • shared_x (bytes) – X coordinate of the ECDH shared point.

Returns:

An open channel.

Return type:

SecureChannelV2

property is_open: bool
unwrap(response)

Verifies and decrypts a SECURED APDU response.

Return type:

bytes

Returns:

The response data.

Return type:

bytes

Raises:
  • APDUError – If the card returned a failure status word. A failure of the channel itself also closes it.

  • InvalidResponseError – If the response does not verify; the channel is closed.

property version: SecureChannelVersion
wrap(ins, p1, p2, data)

Encrypts a command as a SECURED APDU.

Return type:

bytes

Returns:

The complete SECURED APDU.

Return type:

bytes

Raises:
  • InvalidStateError – If the channel is closed.

  • ValueError – If the command is too long for the channel.

keycard.secure_channel.v2.transcript_digest(salt, client_public_key, card_public_key)

SHA-256 of the handshake transcript the card signs.

Return type:

bytes

Returns:

SHA-256(“sc_v2_ccm” || salt || client key || card key).

Return type:

bytes

Module contents

class keycard.secure_channel.SecureChannel(*args, **kwargs)

Bases: Protocol

An open encrypted session with the card.

property is_open: bool
unwrap(response)

Verifies and decrypts a response.

Return type:

bytes

Returns:

The response data.

Return type:

bytes

Raises:

APDUError – If the card returned a failure status word.

property version: SecureChannelVersion
wrap(ins, p1, p2, data)

Protects a command for sending.

Return type:

bytes

Returns:

The complete command APDU.

Return type:

bytes

class keycard.secure_channel.SecureChannelV1(enc_key, mac_key, iv, authenticated=False, previous_iv=None)

Bases: object

Secure Channel V1 (applet 3.x): AES-CBC encryption with AES-CBC-MAC, keyed from a pairing.

enc_key

The AES encryption key for the session.

Type:

bytes

mac_key

The AES MAC key for message authentication.

Type:

bytes

iv

The initialization vector for AES operations.

Type:

bytes

authenticated

Indicates if the session is authenticated.

Type:

bool

previous_iv

The IV the last command was encrypted with.

Type:

Optional[bytes]

authenticated: bool = False
enc_key: bytes
classmethod from_handshake(shared_secret, pairing_key, salt, seed_iv)

Derives the session keys from the handshake.

Return type:

SecureChannelV1

Parameters:
  • shared_secret (bytes) – The shared secret used for key derivation.

  • pairing_key (bytes) – The pairing key used for key derivation.

  • salt (bytes) – The salt value used in the key derivation process.

  • seed_iv (bytes) – The initialization vector (IV) to seed the session.

Returns:

A channel initialized with derived encryption

and MAC keys, and the provided IV.

Return type:

SecureChannelV1

property is_open: bool
iv: bytes
mac_key: bytes
previous_iv: bytes | None = None
unwrap(response)

Verifies and decrypts a response.

Return type:

bytes

Returns:

The response data.

Return type:

bytes

Raises:

APDUError – If the card returned a failure status word.

unwrap_response(response)

Unwraps and verifies a secure channel response.

Return type:

tuple[bytes, int]

Parameters:

response (bytes) – The encrypted response bytes to unwrap.

Returns:

A tuple containing the decrypted plaintext

(excluding the status word) and the status word as an integer.

Return type:

tuple[bytes, int]

Raises:
  • ValueError – If the secure channel is not authenticated.

  • ValueError – If the response length is invalid.

  • ValueError – If the MAC verification fails.

  • ValueError – If the decrypted plaintext is too short to contain a status word.

property version: SecureChannelVersion
wrap(ins, p1, p2, data)

Protects a command for sending.

Return type:

bytes

Returns:

The complete command APDU.

Return type:

bytes

wrap_apdu(cla, ins, p1, p2, data)

Wraps an APDU command with secure channel encryption and MAC.

Return type:

bytes

Parameters:
  • cla (int) – The APDU class byte.

  • ins (int) – The APDU instruction byte.

  • p1 (int) – The APDU parameter 1 byte.

  • p2 (int) – The APDU parameter 2 byte.

  • data (bytes) – The APDU data field to be encrypted.

Returns:

The wrapped APDU as a tuple

containing the class, instruction, parameter 1, parameter 2, and the concatenated MAC and encrypted data.

Return type:

tuple[int, int, int, int, bytes]

Raises:

ValueError – If the secure channel is not authenticated and the instruction is not 0x11.

class keycard.secure_channel.SecureChannelV2(key_h2c, key_c2h)

Bases: object

Secure Channel V2 (applet 4.x): AES-128-CCM with keys from an ECDHE handshake authenticated by the card’s Identity Key.

close()
Return type:

None

classmethod from_handshake(salt, shared_x)

Derives the session keys from the handshake.

Return type:

SecureChannelV2

Parameters:
  • salt (bytes) – The 32-byte HKDF salt sent by the client.

  • shared_x (bytes) – X coordinate of the ECDH shared point.

Returns:

An open channel.

Return type:

SecureChannelV2

property is_open: bool
unwrap(response)

Verifies and decrypts a SECURED APDU response.

Return type:

bytes

Returns:

The response data.

Return type:

bytes

Raises:
  • APDUError – If the card returned a failure status word. A failure of the channel itself also closes it.

  • InvalidResponseError – If the response does not verify; the channel is closed.

property version: SecureChannelVersion
wrap(ins, p1, p2, data)

Encrypts a command as a SECURED APDU.

Return type:

bytes

Returns:

The complete SECURED APDU.

Return type:

bytes

Raises:
  • InvalidStateError – If the channel is closed.

  • ValueError – If the command is too long for the channel.

class keycard.secure_channel.SecureChannelVersion(*values)

Bases: IntEnum

V1 = 1
V2 = 2
keycard.secure_channel.channel_version(app_version)

Returns the secure channel version used by an applet version.

Return type:

SecureChannelVersion

Parameters:

app_version (tuple[int, int]) – Applet major and minor version.

Returns:

V2 from applet 4.0, V1 before.

Return type:

SecureChannelVersion