Skip to main content

A crypto-agile secure-transport library with pluggable cipher suites (classical, hybrid X25519+ML-KEM-768, pure-PQC)

Project description

pqchannel

A crypto-agile secure-transport library with pluggable cipher suites.

Python 3.10+ PyPI version License: MIT Status: Alpha

pqchannel gives an application a secure TCP connection object (connect() / send() / recv()) where the cryptography is provided by a pluggable cipher suite — classical, hybrid, or pure post-quantum — swappable with a single argument.

The one-line swap

from pqchannel import SecureConnection, HybridSuite, ClassicalSuite

# Quantum-safe today...
conn = SecureConnection.connect(("host", 9000), suite=HybridSuite())

# ...swap to classical with ONE line changed, no other app code touched:
conn = SecureConnection.connect(("host", 9000), suite=ClassicalSuite())

That's the whole pitch. The cryptography lives behind an interface — the app never hard-codes an algorithm.

Threat model

Harvest-now-decrypt-later (HNDL): an adversary recording today's classical-only traffic can decrypt it retroactively once a cryptographically-relevant quantum computer exists. The hybrid suite (X25519 + ML-KEM-768) defends against this: even if ML-KEM is broken by a future classical attack, X25519 still holds; even if X25519 is broken by a quantum computer, ML-KEM still holds. Either primitive alone is enough.

Authentication (Phase 4) adds MITM protection: the server signs its handshake material and the client verifies it against a pinned identity key.

Cipher suites

Suite Key exchange Authentication AEAD liboqs?
ClassicalSuite X25519 Ed25519 AES-256-GCM no
HybridSuite X25519 + ML-KEM-768 ML-DSA-65 AES-256-GCM yes
PQOnlySuite ML-KEM-768 ML-DSA-65 AES-256-GCM yes

Install

# From TestPyPI (v1.0.0)
pip install -i https://test.pypi.org/simple/ pqchannel

Post-quantum suites (HybridSuite / PQOnlySuite) require liboqs-python, which must be compiled against a native liboqs build. The easiest path is Docker — see SETUP.md for per-OS instructions.

The classical suite (ClassicalSuite) works on any host with only cryptography.

Running it (Docker)

The supported, reproducible environment is Docker — it compiles liboqs once inside the image so nobody needs a local C toolchain, and it builds identically on Windows (amd64) and Apple Silicon (arm64). Full setup for both machines is in SETUP.md.

docker compose build            # first time — compiles liboqs (a few minutes)
docker compose run --rm tests   # run the full test suite (89 tests)

The classical parts run natively too (they need only cryptography, not liboqs); the post-quantum tests skip off-Docker. See SETUP.md.

Quick start (library usage)

# server.py
from pqchannel import SecureListener, HybridSuite

with SecureListener(("0.0.0.0", 9000), suite=HybridSuite()) as listener:
    conn = listener.accept()
    print(conn.recv())
    conn.send(b"pong")

# client.py
from pqchannel import SecureConnection, HybridSuite

conn = SecureConnection.connect(("localhost", 9000), suite=HybridSuite())
conn.send(b"ping")
print(conn.recv())

Authenticated handshake

from pqchannel import SecureConnection, HybridSuite, AuthenticationError

# The client pins the server's identity public key (distributed out-of-band).
conn = SecureConnection.connect(
    ("host", 9000), suite=HybridSuite(), server_pubkey=pinned_pk
)
# AuthenticationError is raised — before any session key is derived — if a
# man-in-the-middle tampered with the server's handshake material.

Examples

Three runnable examples, each using only the public API. Exact two-terminal commands are in SETUP.md → Running the examples.

  • examples/chat.py — an encrypted chat. Swap the suite live with --suite classical|hybrid|pqonly — this is the crypto-agility story.
  • examples/filetransfer.py — send a file with an end-to-end SHA-256 integrity check.
  • examples/mitm.py — proves the authenticated handshake catches a man-in-the-middle (raises AuthenticationError).

Benchmarks

benchmarks/bench.py times each suite (keygen / kex / sign / verify / full authenticated handshake) and measures handshake bytes-on-the-wire:

docker compose run --rm dev python benchmarks/bench.py
Suite handshake latency* handshake bytes on the wire
Classical ~0.21 ms 176 B
Hybrid ~0.22 ms 7,617 B
PQOnly ~0.14 ms 7,553 B

* Crypto only (no TCP), measured in Docker — hardware-specific and noisy, so compare suites relative to each other, not as absolute figures. The wire sizes are exact.

Takeaway: post-quantum security here is fast but large — the PQC handshakes are ~43× the size of the classical one on the wire, at comparable CPU cost (the cost of ML-KEM/ML-DSA is bandwidth, not compute). The table, a raw CSV, and charts are written to benchmarks/results/.

Handshake size by suite

Architecture

Application code
      │  suite=HybridSuite()
      ▼
SecureConnection / SecureListener   ← channel.py  (public API + AEAD records)
      │
      ▼
Handshake (suite-agnostic)          ← handshake.py (KEX + auth, no algo names)
      │
      ├─▶ CipherSuite interface     ← suites.py   (abstract contract)
      │       │
      │       ├─ ClassicalSuite     (X25519 + Ed25519)
      │       ├─ HybridSuite        (X25519 + ML-KEM-768 + ML-DSA-65)
      │       └─ PQOnlySuite        (ML-KEM-768  + ML-DSA-65)
      │
      └─▶ Framing layer             ← framing.py  (4-byte length prefix)

Limitations ⚠️

This is a prototyping-grade library for learning and demonstration, not a production security product.

  • liboqs is not production-vetted — it is for experimentation.
  • Demo-grade authentication. The server's identity key is generated in-process and pinned by the client from that same process — there is no real PKI or out-of-band key distribution. The server signs only its own KEX material (not the full transcript), and there is no mutual (client) auth.
  • Counter-based nonces. A direction-partitioned counter drives the AES-GCM nonce — safe for demo scale; a production library would harden this.
  • Example-scale. The listener handles one blocking connection at a time (no concurrency model, no data-phase timeouts) — fine for the demos, not a server.
  • Benchmark latencies are indicative only (see the note above); the wire sizes are exact.

Building on vetted primitives

This library never implements cryptographic primitives. All crypto comes from:

Development

docker compose run --rm tests                          # full test suite (89)
docker compose run --rm dev mypy src/                  # type check (strict)
docker compose run --rm dev python benchmarks/bench.py # benchmarks + charts

See SETUP.md for the native (non-Docker) path and per-OS notes.

References

Changelog

v1.0.0 (2026-07-04)

  • Full crypto-agility story complete: ClassicalSuite, HybridSuite, PQOnlySuite all implement the same CipherSuite interface; swap with one argument.
  • Authenticated handshake with ML-DSA-65 / Ed25519; MITM example (mitm.py).
  • examples/chat.py and examples/filetransfer.py use only the public API.
  • benchmarks/bench.py quantifies handshake size and latency across all three suites.
  • Published to TestPyPI: pip install -i https://test.pypi.org/simple/ pqchannel
  • 91+ pytest tests; mypy --strict src/ clean.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pqchannel-1.0.0.tar.gz (167.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pqchannel-1.0.0-py3-none-any.whl (18.6 kB view details)

Uploaded Python 3

File details

Details for the file pqchannel-1.0.0.tar.gz.

File metadata

  • Download URL: pqchannel-1.0.0.tar.gz
  • Upload date:
  • Size: 167.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.1

File hashes

Hashes for pqchannel-1.0.0.tar.gz
Algorithm Hash digest
SHA256 be203b5512e450da6f177cdbe3336c32739656afc202937184262a454269aef4
MD5 51fc1e95f28f73d808dfedd3c3ef5e9b
BLAKE2b-256 9dc381173fa7d1e677c07f08ac57d3fa4b2e142c4de7eccd514eaee3502faa3f

See more details on using hashes here.

File details

Details for the file pqchannel-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: pqchannel-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 18.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.1

File hashes

Hashes for pqchannel-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 053c82ae1fb8de896e19850bace662bbb1d092c426690cd9ed1ce5f628e34480
MD5 30bfaf1abaff791d789cfce1df87056d
BLAKE2b-256 a9440bb331b8276104fb4d45922f81ffa717d2745e1ed23c31cd641430e7dbd2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page