Protocols (quantum_safe.protocols)

Higher-level protocol helpers built on top of the KEM and signature primitives.

Envelope

class quantum_safe.protocols.envelope.Envelope[source]

Bases: object

Authenticated encryption using hybrid KEM + AES-256-GCM.

This is a class with only class methods — you don’t instantiate it. Think of it as a namespace for seal() and open().

Example:

# Sender:
sealed = Envelope.seal(b"top secret", recipient_public_key)
wire   = sealed.to_bytes()        # send this over the network

# Recipient:
msg    = sealed.from_bytes(wire)
plain  = Envelope.open(msg, recipient_secret_key)
classmethod seal(plaintext, recipient_public_key, aad=b'', kem=None)[source]

Encrypt plaintext to the recipient’s public key.

Parameters:
  • plaintext (bytes) – The data to encrypt. No size limit.

  • recipient_public_key (PublicKey) – Recipient’s HybridKEM public key.

  • aad (bytes) – Additional authenticated data. Included unencrypted in the envelope but authenticated by GCM — any modification fails decryption. Use for metadata you want visible but protected (e.g. recipient ID, timestamp, content type).

  • kem (HybridKEM | None) – HybridKEM instance to use. If None, creates a default HybridKEM() matching the key’s algorithm.

Return type:

SealedMessage

Returns:

SealedMessage that can be decrypted with Envelope.open().

Raises:

UnsupportedAlgorithm – if the key’s algorithm isn’t a known hybrid KEM combination.

classmethod open(sealed, recipient_secret_key, kem=None)[source]

Decrypt a SealedMessage.

Parameters:
  • sealed (SealedMessage) – SealedMessage from Envelope.seal() or deserialized from the wire.

  • recipient_secret_key (SecretKey) – The recipient’s HybridKEM secret key.

  • kem (HybridKEM | None) – HybridKEM instance. If None, auto-created from the envelope’s algorithm field.

Return type:

bytes

Returns:

Original plaintext bytes.

Raises:
  • DecapsulationError – if KEM decapsulation fails.

  • cryptography.exceptions.InvalidTag – if the ciphertext is tampered or the wrong key is used (GCM authentication failure). We let this propagate from cryptography directly — don’t catch it.

class quantum_safe.protocols.envelope.SealedMessage(version, algorithm, kem_ct, nonce, ciphertext, aad=b'')[source]

Bases: object

A ciphertext envelope produced by Envelope.seal().

All fields are needed to decrypt. The aad field is authenticated but not encrypted — it’s safe to inspect without the decryption key.

version

Envelope format version.

algorithm

KEM algorithm used (e.g. “X25519+ML-KEM-768”).

kem_ct

KEM ciphertext bytes (HybridCipherText wire format).

nonce

12-byte AES-GCM nonce. Never reused.

ciphertext

AES-256-GCM encrypted payload including 16-byte tag.

aad

Additional authenticated data. Visible but authenticated.

Parameters:
to_bytes()[source]

Serialize to CBOR (or JSON-b64 fallback) bytes.

Return type:

bytes

classmethod from_bytes(data)[source]

Deserialize from bytes produced by to_bytes().

Parameters:

data (bytes)

Return type:

SealedMessage

to_hex()[source]

Convenience: serialize to a hex string.

Return type:

str

classmethod from_hex(hex_str)[source]

Deserialize from a hex string.

Parameters:

hex_str (str)

Return type:

SealedMessage

inspect()[source]

Return a dict of visible (non-secret) metadata for logging/debugging.

Never logs ciphertext or key material. Safe to include in structured logs.

Return type:

dict[str, Any]

JWT

class quantum_safe.protocols.jwt.JWTSigner(keypair, hedged=True, issuer=None, backend='auto')[source]

Bases: object

Signs JWTs using PQC or hybrid signature algorithms.

Parameters:
  • keypair (KeyPair) – The signing keypair. Algorithm is inferred from the key.

  • hedged (bool) – Use hedged signing mode (default True). See module docstring.

  • issuer (str | None) – Optional issuer string to embed in tokens as the iss claim.

  • backend (str) – Signature backend: “auto”, “liboqs”, “rustcrypto”.

Example:

signer = JWTSigner(keypair, issuer="auth.myapp.com")
token  = signer.sign({"sub": "user123", "role": "admin"})
# → "eyJhbGci..."

verifier = JWTVerifier(public_key)
claims   = verifier.verify(token)
# → {"sub": "user123", "role": "admin", "iss": "auth.myapp.com", ...}
sign(claims, expires_in=3600, context=b'jwt')[source]

Sign a claims dict and return a JWT string.

Parameters:
  • claims (dict[str, Any]) – Dict of JWT claims. Standard claims (iss, iat, exp) are added automatically.

  • expires_in (int) – Token lifetime in seconds. Default 1 hour. Pass 0 to omit exp (not recommended).

  • context (bytes) – Signing context for domain separation. Default b”jwt” — override to prevent cross-purpose reuse.

Return type:

str

Returns:

JWT string in the form “header.payload.signature”.

class quantum_safe.protocols.jwt.JWTVerifier(public_key, issuer=None, audience=None, backend='auto')[source]

Bases: object

Verifies PQC/hybrid JWTs.

Parameters:
  • public_key (PublicKey) – The signer’s public key. Algorithm is inferred from it.

  • issuer (str | None) – If set, the iss claim must match this value.

  • audience (str | None) – If set, the aud claim must include this value.

  • backend (str) – Signature backend.

Example:

verifier = JWTVerifier(public_key, issuer="auth.myapp.com")
claims   = verifier.verify(token)
user_id  = claims["sub"]
verify(token, context=b'jwt', validate_exp=True, validate_nbf=True)[source]

Verify a JWT and return its claims.

Parameters:
  • token (str) – JWT string from JWTSigner.sign().

  • context (bytes) – Signing context. Must match what was used to sign.

  • validate_exp (bool) – Check expiration (default True).

  • validate_nbf (bool) – Check not-before (default True).

Return type:

dict[str, Any]

Returns:

Verified claims dict.

Raises:

TLS

class quantum_safe.protocols.tls.HybridTLSConfig(kem_algorithm='X25519+ML-KEM-768', fallback_classical=True, require_hybrid=False, min_tls_version=TLSVersion.TLSv1_3, oqs_provider_path=None)[source]

Bases: object

Configuration for hybrid TLS key exchange.

kem_algorithm

The hybrid KEM algorithm to request. Default: “X25519+ML-KEM-768”.

fallback_classical

If True (default), include classical X25519 as a fallback group. This allows handshaking with peers that don’t support hybrid.

require_hybrid

If True, reject connections from peers that don’t support hybrid key exchange. Default False — too disruptive for most deployments today.

min_tls_version

Minimum TLS version. Default TLS 1.3 — never negotiate lower.

oqs_provider_path

Path to OQS OpenSSL provider .so/.dylib. If None, we search standard provider locations.

Parameters:
property group_preference: list[str]

Ordered list of TLS group names to pass to set_groups().

Hybrid groups are listed first. Classical groups follow as fallbacks if fallback_classical is True.

quantum_safe.protocols.tls.configure_hybrid_context(ctx, config=None)[source]

Configure an ssl.SSLContext for hybrid key exchange.

This modifies the context in-place and also returns it for chaining.

Parameters:
  • ctx (SSLContext) – An ssl.SSLContext to configure. You create this with ssl.create_default_context() or ssl.SSLContext().

  • config (HybridTLSConfig | None) – HybridTLSConfig. If None, uses default (X25519+ML-KEM-768 with X25519 fallback).

Return type:

SSLContext

Returns:

The modified ssl.SSLContext.

Raises:

ssl.SSLError – if the requested groups aren’t supported by the installed OpenSSL.

Example:

import ssl
from quantum_safe.protocols.tls import configure_hybrid_context

ctx = ssl.create_default_context()
configure_hybrid_context(ctx)
# ctx now prefers X25519MLKEM768 with X25519 fallback

X.509

class quantum_safe.protocols.x509.HybridCertificateBuilder(subject_cn, classical_private_key, pqc_keypair, validity_days=365, is_ca=False, dns_names=<factory>, ip_addresses=<factory>, organization='', country='', issuer_cert=None, issuer_key=None, extended_key_usage=<factory>)[source]

Bases: object

Builder for hybrid X.509 certificates.

Usage:

from quantum_safe.protocols.x509 import HybridCertificateBuilder
from quantum_safe.signatures.hybrid import HybridSign

# Generate key material
signer    = HybridSign()
hybrid_kp = signer.generate_keypair()
classical_priv = Ed25519PrivateKey.generate()

builder = HybridCertificateBuilder(
    subject_cn="service.internal",
    classical_private_key=classical_priv,
    pqc_keypair=hybrid_kp,
    validity_days=365,
)
cert_pem, cosig_bundle = builder.build()
Parameters:
  • subject_cn (str)

  • classical_private_key (Any)

  • pqc_keypair (KeyPair)

  • validity_days (int)

  • is_ca (bool)

  • dns_names (list[str])

  • ip_addresses (list[str])

  • organization (str)

  • country (str)

  • issuer_cert (Any)

  • issuer_key (Any)

  • extended_key_usage (list[ObjectIdentifier])

build(signer=None)[source]

Build the hybrid certificate.

Return type:

tuple[bytes, bytes]

Returns:

(cert_pem, cosig_bundle)

cert_pem: PEM-encoded X.509 certificate with embedded

PQC public key extension. Valid to classical verifiers.

cosig_bundle: CBOR-encoded co-signature bundle:

{pqc_sig: bytes, pqc_algo: str, cert_fp: str} Verifiers that support hybrid validation check this alongside the certificate.

Parameters:

signer (object)

static verify_cosig(cert_pem, cosig_bundle, pqc_public_key)[source]

Verify a hybrid certificate’s PQC co-signature.

Parameters:
  • cert_pem (bytes) – PEM-encoded certificate.

  • cosig_bundle (bytes) – Co-signature bundle from build().

  • pqc_public_key (PublicKey) – The signer’s PQC public key.

Raises:
Return type:

None

quantum_safe.protocols.x509.generate_classical_keypair_for_cert(algorithm='Ed25519')[source]

Generate a classical keypair suitable for certificate signing.

Parameters:

algorithm (str) – “Ed25519”, “P-256”, or “P-384”.

Return type:

Any

Returns:

A private key object from the cryptography library.