keycard.commands package

Submodules

keycard.commands.change_secret module

keycard.commands.change_secret.change_secret(card, new_value, pin_type)

Changes the specified secret (PIN, PUK, PAIRING) or secret on the card.

Return type:

None

Preconditions:
  • Secure Channel must be opened

  • User PIN must be verified

Parameters:
  • card – The card session object.

  • new_value (bytes | str) – The new PIN/PUK/secret.

  • pin_type (PinType) – Type of PIN (USER, PUK, or PAIRING)

Raises:
  • ValueError – If input format is invalid.

  • APDUError – If the card returns an error status word.

keycard.commands.derive_key module

keycard.commands.derive_key.derive_key(card, path='')

Set the derivation path for subsequent SIGN and EXPORT KEY commands.

Return type:

None

Parameters:
  • card (CardInterface) – The card interface.

  • path (str) – BIP-32-style path (e.g., “m/44’/60’/0’/0/0”) or “../0/1” (parent) or “./0” (current).

Raises:

APDUError – if the derivation fails or the format is invalid.

keycard.commands.ecdh module

keycard.commands.ecdh.ecdh(card, public_key, path)

Computes an ECDH shared secret with the key at a path (applet 4.0).

The result is the raw X coordinate of the shared point, not an encryption key; run it through the protocol’s own KDF.

Return type:

bytes

Parameters:
  • card – The card interface.

  • public_key (bytes) – The peer’s uncompressed public key, 65 bytes.

  • path (str) – Derivation path under “m/44’/1237’” (NIP-44) or “m/43’/60’/1581’” (EIP-1581), at least 5 levels deep. The card rejects any other path.

Returns:

The 32-byte X coordinate of the shared point.

Return type:

bytes

Raises:
  • ValueError – If the public key is not 65 bytes starting with 0x04, or the path does not start at the master key.

  • InvalidResponseError – If the card returns a different length.

  • APDUError – If the card returns a failure status word.

keycard.commands.export_bip85 module

keycard.commands.export_bip85.export_bip85(card, path, length)

Exports BIP-85 entropy derived from the loaded key (applet 4.0).

Return type:

bytes

Parameters:
  • card – The card interface.

  • path (str) – Derivation path starting with “m/83696968’”, every component hardened, e.g. “m/83696968’/0’/0’”. The card rejects any other path.

  • length (int) – Number of entropy bytes, 1 to 64.

Returns:

The first length bytes of the BIP-85 entropy.

Return type:

bytes

Raises:
  • ValueError – If the length is out of range or the path does not start at the master key.

  • InvalidResponseError – If the card returns a different length.

  • APDUError – If the card returns a failure status word.

keycard.commands.export_key module

keycard.commands.export_key.export_key(card, derivation_option, public_only, keypath=None, make_current=False, source=DerivationSource.MASTER)

Export a key (public or private) from the card using an optional keypath.

If derivation_option == CURRENT, keypath can be omitted or empty.

Return type:

ExportedKey

Parameters:
  • 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’

keycard.commands.export_lee module

keycard.commands.export_lee.export_lee(card, path='m')

Exports the LEE keys at a path (applet 4.0).

The card must be in LEE mode, entered by loading a seed with LoadKeyType.LEE_SEED.

Return type:

LeeKeys

Parameters:
  • card – The card interface.

  • path (str) – Derivation path from the master key, e.g. “m/44’/60’”. “m” exports the master keys.

Returns:

ASK, NSK, VSK_D and VSK_Z, 32 bytes each.

Return type:

LeeKeys

Raises:
  • ValueError – If the path does not start at the master key.

  • InvalidResponseError – If a key is missing or not 32 bytes.

  • APDUError – If the card returns a failure status word.

keycard.commands.factory_reset module

keycard.commands.factory_reset.factory_reset(card)

Sends the FACTORY_RESET command to the card.

Return type:

None

keycard.commands.generate_key module

keycard.commands.generate_key.generate_key(card)

Generates a new key on the card and returns the key UID.

Return type:

bytes

Preconditions:
  • Secure Channel must be opened

  • PIN must be verified

Parameters:
  • transport – Transport instance for APDU communication

  • session – SecureChannel instance for wrapping/unwrapping

Returns:

Key UID (SHA-256 of the public key)

Return type:

bytes

Raises:

APDUError – If the response status word is not 0x9000

keycard.commands.generate_mnemonic module

keycard.commands.generate_mnemonic.generate_mnemonic(card, checksum_size=6)

Generate a BIP39 mnemonic using the card’s RNG.

Return type:

list[int]

Parameters:
  • card (CardInterface) – The card interface.

  • checksum_size (int) – Number of checksum bits (between 4 and 8 inclusive).

Returns:

List of integers (0-2047) corresponding to wordlist

indexes.

Return type:

List[int]

Raises:
  • ValueError – If checksum size is outside the allowed range.

  • APDUError – If the card rejects the request.

keycard.commands.get_challenge module

keycard.commands.get_challenge.get_challenge(card, length)

Gets random bytes generated by the card.

Applet 4.0 answers only inside the secure channel, also before INIT. Earlier applets that support the command answer with or without one; without a channel the command is sent in plain.

Return type:

bytes

Parameters:
  • card – The card interface.

  • length (int) – Number of bytes, 1 to 255. The card limits it further: 245 bytes over Secure Channel V2, 221 over V1.

Returns:

The random bytes.

Return type:

bytes

Raises:
  • ValueError – If the length does not fit in P1.

  • InvalidStateError – If a Secure Channel V2 card has no open channel.

  • InvalidResponseError – If the card returns a different length.

  • APDUError – If the card returns a failure status word.

keycard.commands.get_data module

keycard.commands.get_data.get_data(card, slot=StorageSlot.PUBLIC)

Gets the data on the card previously stored with the store data command in the specified slot.

If the secure channel is open, it uses the secure APDU command. Otherwise, it uses the proprietary APDU command.

NDEF data is the complete NDEF file, starting with its 2-byte length. From applet 4.0 it is read in segments until the whole file is received; earlier applets return at most the first 223 bytes.

Return type:

bytes

Parameters:
  • card – The card session object.

  • slot (StorageSlot) – Where to retrieve the data from (PUBLIC, NDEF, CASH)

Raises:

InvalidResponseError – If segmented NDEF data is malformed.

keycard.commands.get_status module

keycard.commands.get_status.get_status(card, key_path=False)

Query the application status or key path from the Keycard.

Requires an open Secure Channel.

Return type:

dict[str, int | bool] | list[int]

Parameters:
  • transport – Transport instance used to send APDU bytes.

  • session – An established SecureChannel instance.

  • key_path (bool) – If True, returns the current key path. If False (default), returns application status.

Returns:

dict with keys:
  • pin_retry_count (int)

  • puk_retry_count (int)

  • initialized (bool)

If key_path is True:

List of 32-bit integers representing the current key path.

Return type:

If key_path is False

Raises:
  • APDUError – If the response status word is not 0x9000.

  • ValueError – If the application status template (tag 0xA3) is missing.

keycard.commands.ident module

keycard.commands.ident.ident(card, challenge)

Sends a challenge to the card to receive a signed identity response.

Return type:

bytes

Parameters:
  • transport – An instance of the Transport class to communicate with the card.

  • challenge (bytes) – A challenge (nonce or data) to send to the card. If None, a random 32-byte challenge is generated.

Returns:

The public key extracted from the card’s identity response.

Return type:

bytes

Raises:

APDUError – If the response status word is not successful (0x9000).

keycard.commands.init module

keycard.commands.init.init(card, pin, puk, pairing_secret=None, pin_retries=None, puk_retries=None, alt_pin=None)

Initializes the card the way its secure channel version requires.

Secure Channel V1 (3.x) cards need the pairing secret and receive the credentials encrypted once to the card’s public key. Secure Channel V2 (4.x) cards take no pairing secret and receive INIT inside the secure channel, which is opened if needed and stays open.

Return type:

None

Parameters:
  • card – The card interface.

  • pin – The 6-digit PIN.

  • puk – The 12-digit PUK.

  • pairing_secret – A 32-byte secret or a passphrase (V1 only).

  • pin_retries – PIN attempts, 2 to 10 (default 3).

  • puk_retries – PUK attempts, 3 to 12 (default 5).

  • alt_pin – Optional 6-digit Alternate PIN.

Raises:
  • ValueError – If the pairing secret is missing on V1, given on V2, or a parameter is out of range.

  • APDUError – If the card returns a failure status word.

keycard.commands.load_key module

keycard.commands.load_key.load_key(card, key_type, public_key=None, private_key=None, chain_code=None, bip39_seed=None)

Load a key into the card for signing purposes.

Return type:

bytes

Parameters:
  • card – The card interface.

  • key_type – Key type

  • public_key – Optional ECC public key (tag 0x80).

  • private_key – ECC private key (tag 0x81).

  • chain_code – Optional chain code (tag 0x82, only for extended key).

  • bip39_seed – 16 to 64-byte binary seed (only for key_type BIP39_SEED, or LEE_SEED to also derive the LEE keys on applet 4.0).

Returns:

UID of the loaded key (SHA-256 of public key).

keycard.commands.mutually_authenticate module

keycard.commands.mutually_authenticate.mutually_authenticate(card, client_challenge=None)

Performs mutual authentication between the client and the Keycard.

Return type:

None

Preconditions:
  • Secure Channel must be opened

The card will respond with a cryptographic challenge. The secure session will verify the response. If the response is not exactly 32 bytes, or if the response has an unexpected status word, the function raises an error.

Parameters:
  • transport – A Transport instance for sending APDUs.

  • session – A SecureChannel instance used for wrapping/unwrapping.

  • client_challenge (bytes, optional) – Optional challenge bytes. If not provided, a random 32-byte value will be generated.

Raises:
  • APDUError – If the response status word is not 0x9000.

  • ValueError – If the decrypted response is not exactly 32 bytes.

keycard.commands.open_secure_channel module

keycard.commands.open_secure_channel.open_secure_channel(card)

Opens a secure channel with the selected card, using the channel version its applet supports.

Secure Channel V1 (3.x) uses the card’s pairing and runs MUTUALLY AUTHENTICATE. Secure Channel V2 (4.x) first checks the card’s Identity Certificate against the trusted Keycard CAs.

Return type:

SecureChannel

Parameters:

card – The card interface.

Returns:

The open channel.

Return type:

SecureChannel

Raises:

keycard.commands.pair module

keycard.commands.pair.pair(card, shared_secret, pairing_mode=PairingMode.ANY)

Performs an ECDH-based pairing handshake with the card.

Return type:

tuple[int, bytes]

Parameters:
  • card – The keycard interface.

  • shared_secret – A 32-byte secret or a passphrase convertible to one.

  • pairing_mode – Mode for pairing: ANY, EPHEMERAL, PERSISTENT

Returns:

Pairing index and derived 32-byte pairing key.

Return type:

tuple[int, bytes]

Raises:
  • ValueError – If the shared secret is not 32 bytes.

  • APDUError – If the card returns a non-success status word.

  • InvalidResponseError – If response lengths or values are unexpected.

keycard.commands.remove_key module

keycard.commands.remove_key.remove_key(card)

Removes the key from the card, returning it to an uninitialized state.

Return type:

None

keycard.commands.select module

keycard.commands.select.select(card)

Selects the Keycard application on the smart card and retrieves application information.

Sends a SELECT APDU command using the Keycard AID, checks for a successful response, parses the returned application information, and returns it.

Return type:

ApplicationInfo

Parameters:

transport – The transport instance used to send the APDU command.

Returns:

Parsed information about the selected Keycard

application.

Return type:

ApplicationInfo

Raises:

APDUError – If the card returns a status word indicating failure.

keycard.commands.set_pinless_path module

keycard.commands.set_pinless_path.set_pinless_path(card, path)

Set a PIN-less path on the card. Allows signing without PIN/auth if the current derived key matches this path.

Return type:

None

Parameters:
  • card (CardInterface) – The card interface.

  • path (str) – BIP-32-style path (e.g., “m/44’/60’/0’/0/0”). An empty string disables the pinless path.

Raises:

APDUError – if the card rejects the input (invalid path)

keycard.commands.sign module

keycard.commands.sign.sign(card, digest, p1=DerivationOption.CURRENT, p2=SigningAlgorithm.ECDSA_SECP256K1, derivation_path=None, tweak=None)

Sign a 32-byte digest using the specified key and signing algorithm.

This command sends the SIGN APDU to the Keycard and parses the response, returning a structured SignatureResult object. The signature may be returned as a DER-encoded structure, a raw 65-byte format including the recovery ID, or an ECDSA template depending on card behavior. Schnorr signatures are 64 bytes, R.x || s, with no recovery ID.

Return type:

SignatureResult

Preconditions:
  • Secure Channel must be opened (unless using PINLESS)

  • PIN must be verified (unless using PINLESS)

  • A valid keypair must be loaded on the card

  • If P1=PINLESS, a PIN-less path must be configured

Parameters:
  • card (CardInterface) – Active Keycard transport session.

  • digest (bytes) – 32-byte hash to be signed.

  • p1 (DerivationOption) –

    Key derivation option. One of: - CURRENT: Sign with the currently loaded key; on applet 4.0,

    which has no current key, with the master key

    • DERIVE: Derive key for signing without changing current

    • DERIVE_AND_MAKE_CURRENT: Derive and load for future use

    • PINLESS: Use pre-defined PIN-less key without SC/PIN

    Applet 4.0 accepts only CURRENT and DERIVE from the master key.

  • p2 (SigningAlgorithm) – Signing algorithm. Defaults to ECDSA_SECP256K1. Applet 4.0 also supports SCHNORR_BIP340.

  • derivation_path (Optional[str]) – String-formatted BIP32 path (e.g. “m/44’/60’/0’/0/0”). Required if p1 uses derivation; “m” is the master key. The source (master/parent/current) is inferred from the path prefix.

  • tweak (Optional[bytes]) – 32 bytes the card adds to the private key before a Schnorr signature, e.g. a Taproot tweak. Defaults to zeros, an untweaked BIP-340 signature. The returned public key is the untweaked one.

Returns:

Parsed signature result, including the signature (DER or raw), algorithm, and optional recovery ID or public key.

Return type:

SignatureResult

Raises:
  • ValueError – If the digest or tweak is not 32 bytes, a tweak is given for ECDSA, or the path is invalid.

  • InvalidStateError – If preconditions (PIN, SC) are not met.

  • APDUError – If the card returns an error (e.g., SW=0x6985).

keycard.commands.store_data module

keycard.commands.store_data.store_data(card, data, slot=StorageSlot.PUBLIC)

Stores data on the card in the specified slot.

NDEF data is the complete NDEF file, starting with its 2-byte length. From applet 3.2 it can be up to 512 bytes and is sent in chunks.

Return type:

None

Parameters:
  • card – The card session object.

  • data (bytes) – The data to store (max 127 bytes, or 512 bytes for NDEF from applet 3.2).

  • slot (StorageSlot) – Where to store the data (PUBLIC, NDEF, CASH)

Raises:

ValueError – If the data is too long.

keycard.commands.unblock_pin module

keycard.commands.unblock_pin.unblock_pin(card, puk_and_pin)

Unblocks the user PIN using the provided PUK and sets a new PIN.

Return type:

None

Parameters:
  • card – The card session object.

  • puk_and_pin (bytes | str) – Concatenation of PUK (12 digits) + new PIN (6 digits)

Raises:
  • ValueError – If the format is invalid.

  • APDUError – If the card returns an error.

keycard.commands.unpair module

keycard.commands.unpair.unpair(card, index)

Sends the UNPAIR command to remove a pairing index from the card.

Return type:

None

Preconditions:
  • Secure Channel must be opened

  • PIN must be verified

This function securely communicates with the card using the established session to instruct it to forget a specific pairing index.

Parameters:
  • transport – The transport interface used to send APDUs.

  • secure_session – The active SecureChannel object used to wrap APDUs.

  • index (int) – The pairing index (0–15) to unpair from the card.

Raises:
  • ValueError – If transport or secure_session is not provided, or if the session is not authenticated.

  • APDUError – If the response status word indicates an error.

keycard.commands.verify_pin module

keycard.commands.verify_pin.verify_pin(card, pin)

Verifies the user PIN with the card using a secure session.

Return type:

bool

Preconditions:
  • Secure Channel must be opened

  • PIN must be verified

Sends the VERIFY PIN APDU command through the secure session. Returns True if the PIN is correct, False if incorrect with remaining attempts, and raises an error if blocked or another APDU error occurs.

Parameters:
  • transport – The transport instance used to send the command.

  • session – An established SecureChannel object.

  • pin (str) – The PIN string to be verified.

Returns:

True if the PIN is correct, False if incorrect but still allowed.

Return type:

bool

Raises:
  • ValueError – If no secure session is provided.

  • RuntimeError – If the PIN is blocked (no attempts remaining).

  • APDUError – For other status word errors returned by the card.

Module contents

keycard.commands.change_secret(card, new_value, pin_type)

Changes the specified secret (PIN, PUK, PAIRING) or secret on the card.

Return type:

None

Preconditions:
  • Secure Channel must be opened

  • User PIN must be verified

Parameters:
  • card – The card session object.

  • new_value (bytes | str) – The new PIN/PUK/secret.

  • pin_type (PinType) – Type of PIN (USER, PUK, or PAIRING)

Raises:
  • ValueError – If input format is invalid.

  • APDUError – If the card returns an error status word.

keycard.commands.derive_key(card, path='')

Set the derivation path for subsequent SIGN and EXPORT KEY commands.

Return type:

None

Parameters:
  • card (CardInterface) – The card interface.

  • path (str) – BIP-32-style path (e.g., “m/44’/60’/0’/0/0”) or “../0/1” (parent) or “./0” (current).

Raises:

APDUError – if the derivation fails or the format is invalid.

keycard.commands.ecdh(card, public_key, path)

Computes an ECDH shared secret with the key at a path (applet 4.0).

The result is the raw X coordinate of the shared point, not an encryption key; run it through the protocol’s own KDF.

Return type:

bytes

Parameters:
  • card – The card interface.

  • public_key (bytes) – The peer’s uncompressed public key, 65 bytes.

  • path (str) – Derivation path under “m/44’/1237’” (NIP-44) or “m/43’/60’/1581’” (EIP-1581), at least 5 levels deep. The card rejects any other path.

Returns:

The 32-byte X coordinate of the shared point.

Return type:

bytes

Raises:
  • ValueError – If the public key is not 65 bytes starting with 0x04, or the path does not start at the master key.

  • InvalidResponseError – If the card returns a different length.

  • APDUError – If the card returns a failure status word.

keycard.commands.export_bip85(card, path, length)

Exports BIP-85 entropy derived from the loaded key (applet 4.0).

Return type:

bytes

Parameters:
  • card – The card interface.

  • path (str) – Derivation path starting with “m/83696968’”, every component hardened, e.g. “m/83696968’/0’/0’”. The card rejects any other path.

  • length (int) – Number of entropy bytes, 1 to 64.

Returns:

The first length bytes of the BIP-85 entropy.

Return type:

bytes

Raises:
  • ValueError – If the length is out of range or the path does not start at the master key.

  • InvalidResponseError – If the card returns a different length.

  • APDUError – If the card returns a failure status word.

keycard.commands.export_key(card, derivation_option, public_only, keypath=None, make_current=False, source=DerivationSource.MASTER)

Export a key (public or private) from the card using an optional keypath.

If derivation_option == CURRENT, keypath can be omitted or empty.

Return type:

ExportedKey

Parameters:
  • 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’

keycard.commands.export_lee(card, path='m')

Exports the LEE keys at a path (applet 4.0).

The card must be in LEE mode, entered by loading a seed with LoadKeyType.LEE_SEED.

Return type:

LeeKeys

Parameters:
  • card – The card interface.

  • path (str) – Derivation path from the master key, e.g. “m/44’/60’”. “m” exports the master keys.

Returns:

ASK, NSK, VSK_D and VSK_Z, 32 bytes each.

Return type:

LeeKeys

Raises:
  • ValueError – If the path does not start at the master key.

  • InvalidResponseError – If a key is missing or not 32 bytes.

  • APDUError – If the card returns a failure status word.

keycard.commands.factory_reset(card)

Sends the FACTORY_RESET command to the card.

Return type:

None

keycard.commands.generate_key(card)

Generates a new key on the card and returns the key UID.

Return type:

bytes

Preconditions:
  • Secure Channel must be opened

  • PIN must be verified

Parameters:
  • transport – Transport instance for APDU communication

  • session – SecureChannel instance for wrapping/unwrapping

Returns:

Key UID (SHA-256 of the public key)

Return type:

bytes

Raises:

APDUError – If the response status word is not 0x9000

keycard.commands.generate_mnemonic(card, checksum_size=6)

Generate a BIP39 mnemonic using the card’s RNG.

Return type:

list[int]

Parameters:
  • card (CardInterface) – The card interface.

  • checksum_size (int) – Number of checksum bits (between 4 and 8 inclusive).

Returns:

List of integers (0-2047) corresponding to wordlist

indexes.

Return type:

List[int]

Raises:
  • ValueError – If checksum size is outside the allowed range.

  • APDUError – If the card rejects the request.

keycard.commands.get_challenge(card, length)

Gets random bytes generated by the card.

Applet 4.0 answers only inside the secure channel, also before INIT. Earlier applets that support the command answer with or without one; without a channel the command is sent in plain.

Return type:

bytes

Parameters:
  • card – The card interface.

  • length (int) – Number of bytes, 1 to 255. The card limits it further: 245 bytes over Secure Channel V2, 221 over V1.

Returns:

The random bytes.

Return type:

bytes

Raises:
  • ValueError – If the length does not fit in P1.

  • InvalidStateError – If a Secure Channel V2 card has no open channel.

  • InvalidResponseError – If the card returns a different length.

  • APDUError – If the card returns a failure status word.

keycard.commands.get_data(card, slot=StorageSlot.PUBLIC)

Gets the data on the card previously stored with the store data command in the specified slot.

If the secure channel is open, it uses the secure APDU command. Otherwise, it uses the proprietary APDU command.

NDEF data is the complete NDEF file, starting with its 2-byte length. From applet 4.0 it is read in segments until the whole file is received; earlier applets return at most the first 223 bytes.

Return type:

bytes

Parameters:
  • card – The card session object.

  • slot (StorageSlot) – Where to retrieve the data from (PUBLIC, NDEF, CASH)

Raises:

InvalidResponseError – If segmented NDEF data is malformed.

keycard.commands.get_status(card, key_path=False)

Query the application status or key path from the Keycard.

Requires an open Secure Channel.

Return type:

dict[str, int | bool] | list[int]

Parameters:
  • transport – Transport instance used to send APDU bytes.

  • session – An established SecureChannel instance.

  • key_path (bool) – If True, returns the current key path. If False (default), returns application status.

Returns:

dict with keys:
  • pin_retry_count (int)

  • puk_retry_count (int)

  • initialized (bool)

If key_path is True:

List of 32-bit integers representing the current key path.

Return type:

If key_path is False

Raises:
  • APDUError – If the response status word is not 0x9000.

  • ValueError – If the application status template (tag 0xA3) is missing.

keycard.commands.ident(card, challenge)

Sends a challenge to the card to receive a signed identity response.

Return type:

bytes

Parameters:
  • transport – An instance of the Transport class to communicate with the card.

  • challenge (bytes) – A challenge (nonce or data) to send to the card. If None, a random 32-byte challenge is generated.

Returns:

The public key extracted from the card’s identity response.

Return type:

bytes

Raises:

APDUError – If the response status word is not successful (0x9000).

keycard.commands.init(card, pin, puk, pairing_secret=None, pin_retries=None, puk_retries=None, alt_pin=None)

Initializes the card the way its secure channel version requires.

Secure Channel V1 (3.x) cards need the pairing secret and receive the credentials encrypted once to the card’s public key. Secure Channel V2 (4.x) cards take no pairing secret and receive INIT inside the secure channel, which is opened if needed and stays open.

Return type:

None

Parameters:
  • card – The card interface.

  • pin – The 6-digit PIN.

  • puk – The 12-digit PUK.

  • pairing_secret – A 32-byte secret or a passphrase (V1 only).

  • pin_retries – PIN attempts, 2 to 10 (default 3).

  • puk_retries – PUK attempts, 3 to 12 (default 5).

  • alt_pin – Optional 6-digit Alternate PIN.

Raises:
  • ValueError – If the pairing secret is missing on V1, given on V2, or a parameter is out of range.

  • APDUError – If the card returns a failure status word.

keycard.commands.load_key(card, key_type, public_key=None, private_key=None, chain_code=None, bip39_seed=None)

Load a key into the card for signing purposes.

Return type:

bytes

Parameters:
  • card – The card interface.

  • key_type – Key type

  • public_key – Optional ECC public key (tag 0x80).

  • private_key – ECC private key (tag 0x81).

  • chain_code – Optional chain code (tag 0x82, only for extended key).

  • bip39_seed – 16 to 64-byte binary seed (only for key_type BIP39_SEED, or LEE_SEED to also derive the LEE keys on applet 4.0).

Returns:

UID of the loaded key (SHA-256 of public key).

keycard.commands.mutually_authenticate(card, client_challenge=None)

Performs mutual authentication between the client and the Keycard.

Return type:

None

Preconditions:
  • Secure Channel must be opened

The card will respond with a cryptographic challenge. The secure session will verify the response. If the response is not exactly 32 bytes, or if the response has an unexpected status word, the function raises an error.

Parameters:
  • transport – A Transport instance for sending APDUs.

  • session – A SecureChannel instance used for wrapping/unwrapping.

  • client_challenge (bytes, optional) – Optional challenge bytes. If not provided, a random 32-byte value will be generated.

Raises:
  • APDUError – If the response status word is not 0x9000.

  • ValueError – If the decrypted response is not exactly 32 bytes.

keycard.commands.open_secure_channel(card)

Opens a secure channel with the selected card, using the channel version its applet supports.

Secure Channel V1 (3.x) uses the card’s pairing and runs MUTUALLY AUTHENTICATE. Secure Channel V2 (4.x) first checks the card’s Identity Certificate against the trusted Keycard CAs.

Return type:

SecureChannel

Parameters:

card – The card interface.

Returns:

The open channel.

Return type:

SecureChannel

Raises:
keycard.commands.pair(card, shared_secret, pairing_mode=PairingMode.ANY)

Performs an ECDH-based pairing handshake with the card.

Return type:

tuple[int, bytes]

Parameters:
  • card – The keycard interface.

  • shared_secret – A 32-byte secret or a passphrase convertible to one.

  • pairing_mode – Mode for pairing: ANY, EPHEMERAL, PERSISTENT

Returns:

Pairing index and derived 32-byte pairing key.

Return type:

tuple[int, bytes]

Raises:
  • ValueError – If the shared secret is not 32 bytes.

  • APDUError – If the card returns a non-success status word.

  • InvalidResponseError – If response lengths or values are unexpected.

keycard.commands.remove_key(card)

Removes the key from the card, returning it to an uninitialized state.

Return type:

None

keycard.commands.select(card)

Selects the Keycard application on the smart card and retrieves application information.

Sends a SELECT APDU command using the Keycard AID, checks for a successful response, parses the returned application information, and returns it.

Return type:

ApplicationInfo

Parameters:

transport – The transport instance used to send the APDU command.

Returns:

Parsed information about the selected Keycard

application.

Return type:

ApplicationInfo

Raises:

APDUError – If the card returns a status word indicating failure.

keycard.commands.set_pinless_path(card, path)

Set a PIN-less path on the card. Allows signing without PIN/auth if the current derived key matches this path.

Return type:

None

Parameters:
  • card (CardInterface) – The card interface.

  • path (str) – BIP-32-style path (e.g., “m/44’/60’/0’/0/0”). An empty string disables the pinless path.

Raises:

APDUError – if the card rejects the input (invalid path)

keycard.commands.sign(card, digest, p1=DerivationOption.CURRENT, p2=SigningAlgorithm.ECDSA_SECP256K1, derivation_path=None, tweak=None)

Sign a 32-byte digest using the specified key and signing algorithm.

This command sends the SIGN APDU to the Keycard and parses the response, returning a structured SignatureResult object. The signature may be returned as a DER-encoded structure, a raw 65-byte format including the recovery ID, or an ECDSA template depending on card behavior. Schnorr signatures are 64 bytes, R.x || s, with no recovery ID.

Return type:

SignatureResult

Preconditions:
  • Secure Channel must be opened (unless using PINLESS)

  • PIN must be verified (unless using PINLESS)

  • A valid keypair must be loaded on the card

  • If P1=PINLESS, a PIN-less path must be configured

Parameters:
  • card (CardInterface) – Active Keycard transport session.

  • digest (bytes) – 32-byte hash to be signed.

  • p1 (DerivationOption) –

    Key derivation option. One of: - CURRENT: Sign with the currently loaded key; on applet 4.0,

    which has no current key, with the master key

    • DERIVE: Derive key for signing without changing current

    • DERIVE_AND_MAKE_CURRENT: Derive and load for future use

    • PINLESS: Use pre-defined PIN-less key without SC/PIN

    Applet 4.0 accepts only CURRENT and DERIVE from the master key.

  • p2 (SigningAlgorithm) – Signing algorithm. Defaults to ECDSA_SECP256K1. Applet 4.0 also supports SCHNORR_BIP340.

  • derivation_path (Optional[str]) – String-formatted BIP32 path (e.g. “m/44’/60’/0’/0/0”). Required if p1 uses derivation; “m” is the master key. The source (master/parent/current) is inferred from the path prefix.

  • tweak (Optional[bytes]) – 32 bytes the card adds to the private key before a Schnorr signature, e.g. a Taproot tweak. Defaults to zeros, an untweaked BIP-340 signature. The returned public key is the untweaked one.

Returns:

Parsed signature result, including the signature (DER or raw), algorithm, and optional recovery ID or public key.

Return type:

SignatureResult

Raises:
  • ValueError – If the digest or tweak is not 32 bytes, a tweak is given for ECDSA, or the path is invalid.

  • InvalidStateError – If preconditions (PIN, SC) are not met.

  • APDUError – If the card returns an error (e.g., SW=0x6985).

keycard.commands.store_data(card, data, slot=StorageSlot.PUBLIC)

Stores data on the card in the specified slot.

NDEF data is the complete NDEF file, starting with its 2-byte length. From applet 3.2 it can be up to 512 bytes and is sent in chunks.

Return type:

None

Parameters:
  • card – The card session object.

  • data (bytes) – The data to store (max 127 bytes, or 512 bytes for NDEF from applet 3.2).

  • slot (StorageSlot) – Where to store the data (PUBLIC, NDEF, CASH)

Raises:

ValueError – If the data is too long.

keycard.commands.unblock_pin(card, puk_and_pin)

Unblocks the user PIN using the provided PUK and sets a new PIN.

Return type:

None

Parameters:
  • card – The card session object.

  • puk_and_pin (bytes | str) – Concatenation of PUK (12 digits) + new PIN (6 digits)

Raises:
  • ValueError – If the format is invalid.

  • APDUError – If the card returns an error.

keycard.commands.unpair(card, index)

Sends the UNPAIR command to remove a pairing index from the card.

Return type:

None

Preconditions:
  • Secure Channel must be opened

  • PIN must be verified

This function securely communicates with the card using the established session to instruct it to forget a specific pairing index.

Parameters:
  • transport – The transport interface used to send APDUs.

  • secure_session – The active SecureChannel object used to wrap APDUs.

  • index (int) – The pairing index (0–15) to unpair from the card.

Raises:
  • ValueError – If transport or secure_session is not provided, or if the session is not authenticated.

  • APDUError – If the response status word indicates an error.

keycard.commands.verify_pin(card, pin)

Verifies the user PIN with the card using a secure session.

Return type:

bool

Preconditions:
  • Secure Channel must be opened

  • PIN must be verified

Sends the VERIFY PIN APDU command through the secure session. Returns True if the PIN is correct, False if incorrect with remaining attempts, and raises an error if blocked or another APDU error occurs.

Parameters:
  • transport – The transport instance used to send the command.

  • session – An established SecureChannel object.

  • pin (str) – The PIN string to be verified.

Returns:

True if the PIN is correct, False if incorrect but still allowed.

Return type:

bool

Raises:
  • ValueError – If no secure session is provided.

  • RuntimeError – If the PIN is blocked (no attempts remaining).

  • APDUError – For other status word errors returned by the card.