KEM (quantum_safe.kem)

Key encapsulation mechanisms. The high-level entry point is HybridKEM.

class quantum_safe.kem.hybrid.HybridKEM(classical='X25519', pqc='ML-KEM-768', backend='auto', validate=True)[source]

Bases: object

Hybrid KEM: classical Diffie-Hellman + post-quantum KEM.

Default configuration: X25519 + ML-KEM-768. This matches the TLS 1.3 hybrid group X25519MLKEM768 and is recommended by all major standards bodies for the current transition period.

Parameters:
  • classical (str) – Classical KEM algorithm. Currently “X25519” or “P-256”. Default: “X25519”.

  • pqc (str) – PQC KEM algorithm. Default: “ML-KEM-768”.

  • backend (str) – Backend for PQC operations: “auto”, “liboqs”, “rustcrypto”. Default: “auto”.

  • validate (bool) – If True (default), validate that the classical+pqc combination is an approved hybrid. Set False only if you’re testing a non-standard combination.

Example:

from quantum_safe import HybridKEM

kem = HybridKEM()                      # X25519 + ML-KEM-768
kp  = kem.generate_keypair()
ct, ss = kem.encapsulate(kp.public)
ss2    = kem.decapsulate(kp.secret, ct)
assert ss == ss2
property algorithm: str

Full hybrid algorithm string, e.g. ‘X25519+ML-KEM-768’.

generate_keypair()[source]

Generate a hybrid key pair.

The public key contains both the X25519 public key and the ML-KEM public key, packed with a length prefix. The secret key is similarly structured.

The migration_state is set to HYBRID_TRANSITION by default — this key participates in the current hybrid deployment.

Return type:

KeyPair

Returns:

KeyPair where both .public and .secret contain hybrid key material.

encapsulate(public_key)[source]

Encapsulate a shared secret under the recipient’s hybrid public key.

Parameters:

public_key (PublicKey) – The recipient’s HybridKEM public key.

Return type:

tuple[HybridCipherText, SharedSecret]

Returns:

(ct, ss)

HybridCipherText to send to the recipient, SharedSecret

for the sender’s use.

Note

The HybridCipherText.to_bytes() gives you the wire-format bytes to transmit. The SharedSecret is 32 bytes of combined key material derived from both the classical and PQC exchanges.

decapsulate(secret_key, ciphertext)[source]

Decapsulate: recover the shared secret from a hybrid ciphertext.

Parameters:
  • secret_key (SecretKey) – The recipient’s HybridKEM secret key.

  • ciphertext (HybridCipherText) – HybridCipherText from the sender.

Return type:

SharedSecret

Returns:

SharedSecret matching the sender’s.

Raises:

DecapsulationError – on structural failures. Note that ML-KEM uses implicit rejection, so a bad ML-KEM ciphertext returns a pseudorandom value rather than failing — this is by design.

class quantum_safe.kem.core.KEM(algorithm='ML-KEM-768', backend='auto', allow_low_security=False, strict=False)[source]

Bases: object

Single-algorithm KEM.

Wraps a backend and presents a typed, safe interface. Key decisions:

  • Key generation always returns a KeyPair (not raw bytes).

  • Encapsulate takes a PublicKey, returns (CipherText, SharedSecret).

  • Decapsulate takes (SecretKey, CipherText), returns SharedSecret.

  • All inputs/outputs are typed — you can’t accidentally pass a shared secret where a ciphertext is expected.

Parameters:
  • algorithm (str) – PQC algorithm name. Defaults to ML-KEM-768.

  • backend (str) – Backend name: “auto”, “liboqs”, “rustcrypto”. “auto” tries rustcrypto first, then liboqs.

  • allow_low_security (bool) – If True, allows NIST level 1 algorithms without warning. Default False (warns on L1).

  • strict (bool) – If True, raises instead of warning for non-standard configurations. Default False.

property algorithm: str

The PQC algorithm name.

property backend_name: str

Name of the backend being used.

generate_keypair()[source]

Generate a fresh key pair for this algorithm.

Return type:

KeyPair

Returns:

KeyPair with .public (PublicKey) and .secret (SecretKey).

Example:

kp = kem.generate_keypair()
print(kp.public.fingerprint())
encapsulate(public_key)[source]

Encapsulate a shared secret under the recipient’s public key.

Parameters:

public_key (PublicKey) – The recipient’s public key. Must be for the same algorithm as this KEM instance.

Return type:

tuple[CipherText, SharedSecret]

Returns:

(ct, ss)

CipherText to send to the recipient, SharedSecret

for the sender to use.

Raises:

UnsupportedAlgorithm – if the key’s algorithm doesn’t match.

decapsulate(secret_key, ciphertext)[source]

Decapsulate: recover the shared secret from a ciphertext.

Parameters:
  • secret_key (SecretKey) – The recipient’s secret key.

  • ciphertext (CipherText) – The CipherText from the sender.

Return type:

SharedSecret

Returns:

SharedSecret matching the one the sender derived.

Raises:

DecapsulationError – if decapsulation fails. Note that ML-KEM implements implicit rejection (FIPS 203 §6.3), so malformed ciphertexts produce a pseudorandom value rather than raising. The error is raised only for structural failures.

Algorithm registry

quantum_safe.kem.algorithms.get_algorithm_spec(name)[source]

Return the spec for a PQC algorithm, raising UnsupportedAlgorithm if unknown.

Parameters:

name (str)

Return type:

KEMAlgorithmSpec

quantum_safe.kem.algorithms.canonical_hybrid_name(classical, pqc)[source]

Return the canonical algorithm string for a hybrid combination.

Example: canonical_hybrid_name(“X25519”, “ML-KEM-768”) -> “X25519+ML-KEM-768”

Parameters:
Return type:

str

quantum_safe.kem.algorithms.parse_hybrid_name(name)[source]

Parse a hybrid algorithm name into (classical, pqc) components.

Raises ValueError if the name doesn’t look like a hybrid name.

Parameters:

name (str)

Return type:

tuple[str, str]

quantum_safe.kem.algorithms.validate_hybrid_combination(classical, pqc)[source]

Raise ValueError if the combination is not approved.

We don’t block unapproved combinations entirely (a researcher might have a legitimate reason), but we raise by default so callers have to be explicit about using a non-standard combination.

Parameters:
Return type:

None