Sovereign, app-agnostic hybrid post-quantum crypto primitives: hybrid X25519+ML-KEM-768 KEM (FIPS 203), PQXDH-style seal, metadata-sealing routing envelope, epoch ratchets, crypto-agility registry. Python sibling of the Dart sk_pqc.
Project description
sk-pqc (Python)
⚠️ Experimental · pre-1.0 · NOT independently security-audited. This is a clean-room reference implementation — tested and cross-impl-parity-verified against our Rust (
sk-core) and Dart (sk_pqc) builds, but it has had no third-party security audit, fuzzing, or formal review. Primitives bind vetted libraries (liboqs/ML-KEM,cryptography); the original code is the wiring. Review it yourself before production use. We apply our own honest-claims discipline to the library itself: don't trust it beyond the evidence.
sk-pqc is a small, app-agnostic Python library of vetted hybrid post-quantum cryptographic primitives.
Use it to add hybrid X25519 + ML-KEM-768 (FIPS 203) confidentiality to any app — without dragging in a messaging framework.
It is the Python sibling of the public Dart sk_pqc package and is byte-for-byte interoperable with it (shared cross-impl KAT vector). Import name sk_pqc; PyPI name sk-pqc.
- Maturity tier: T2 — Hybrid KEM (key exchange/wrap is
HKDF(X25519 ‖ ML-KEM-768); signatures stay classical/optional-hybrid). Per sk-standards CRYPTOGRAPHY_STANDARD. - License: Apache-2.0 · Python: ≥ 3.10 · Version: 0.1.0
Honest claim. "Hybrid" means a derived secret is confidential if EITHER the classical X25519 leg OR the ML-KEM-768 leg holds — it survives a future cryptographically-relevant quantum computer breaking X25519, and it survives a classical break of ML-KEM. It is not "quantum-proof", "quantum-safe", or "unbreakable". AES-256-GCM (the bulk cipher) is symmetric / Grover-only and already quantum-acceptable. Citations: FIPS 203 (ML-KEM), FIPS 204 (ML-DSA, registry only), RFC 7748 (X25519), RFC 5869 (HKDF), SP 800-38D (AES-GCM).
What's in the box
| Module | Primitive | What it gives you |
|---|---|---|
sk_pqc.pqkem |
Hybrid KEM x25519-mlkem768 |
hybrid_keypair / hybrid_encap / hybrid_decap. ML-KEM-768 leg = liboqs (oqs), X25519 leg + HKDF combiner = pyca cryptography. The combiner is the only original crypto: HKDF-SHA256(X25519_ss ‖ MLKEM768_ss) — concat-then-KDF, never XOR, never pure-PQ. |
sk_pqc.pqdm |
PQXDH-style seal | seal / open_sealed a body to a recipient's published hybrid prekey (PrekeyBundle), AES-256-GCM under a KEM-derived key, with a downgrade-lock AAD that makes silent classical downgrade detectable. |
sk_pqc.pqroute |
Metadata-sealing routing envelope pqroute1 |
seal_routed / open_routed / read_route_header. Splits a plaintext next-hop header (a relay reads it, but it is AEAD-bound / tamper-evident) from a hybrid-sealed inner (final destination + content) a relay cannot read. |
sk_pqc.group_ratchet |
Group epoch ratchet | Per-epoch secret distributed once via the hybrid KEM; per-message keys derived symmetrically + index-addressable (loss/reorder tolerant). Forward secrecy across epochs, post-compromise security from independent epoch secrets. |
sk_pqc.dm_ratchet |
1:1 DM epoch ratchet | The pairwise analogue of the group ratchet (distinct HKDF domain labels — a DM key can never collide with a group key). |
sk_pqc.anon_queue |
Anon-queue addressing + deniable auth | new_queue_pair (uncorrelated recipient/sender ids), the aqid: address codec, and a repudiable HMAC-SHA256 authenticator. Addressing + deniable-auth only — not a transport. |
sk_pqc.crypto_suites |
Crypto-agility registry | Machine-readable suite-ids → primitives + quantum-resistance status + FIPS refs. The honest predicate is_quantum_resistant(suite_id) no caller should hand-roll. |
Never silently downgrades. If the liboqs backend is missing, hybrid operations raise PqKemUnavailable (a hard error). The pure-pyca pieces — combiner KAT, suite registry, anon-queue codec/MAC, key derivation — work with no PQ backend at all.
Architecture
flowchart TD
subgraph backends["Vetted backends — no hand-rolled math"]
OQS["liboqs (oqs)<br/>ML-KEM-768 · FIPS 203"]
PYCA["pyca/cryptography<br/>X25519 · HKDF-SHA256 · AES-256-GCM"]
end
OQS --> KEM
PYCA --> KEM
KEM["pqkem<br/>hybrid X25519 ‖ ML-KEM-768 KEM<br/>HKDF(X25519_ss ‖ MLKEM_ss)"]
KEM --> DM["pqdm<br/>PQXDH-style seal<br/>(downgrade-lock AAD)"]
KEM --> ROUTE["pqroute1<br/>metadata-sealing<br/>routing envelope"]
KEM --> GR["group_ratchet<br/>per-epoch group keys"]
KEM --> DMR["dm_ratchet<br/>per-epoch 1:1 DM keys"]
REG["crypto_suites<br/>agility registry<br/>+ honest self-report"]
AQ["anon_queue<br/>aqid: addressing<br/>+ deniable HMAC auth"]
REG -. "describes / status" .-> KEM
REG -. "describes / status" .-> DM
KEM --> VEC{{"cross-impl KAT vector<br/>↔ Dart sk_pqc"}}
classDef prim fill:#e6f0ff,stroke:#369;
classDef be fill:#eee,stroke:#999;
class KEM,DM,ROUTE,GR,DMR,REG,AQ prim;
class OQS,PYCA be;
Install
# Core (pure-pyca pieces work; hybrid KEM needs the pq extra)
pip install sk-pqc
# With the post-quantum (ML-KEM-768) leg via liboqs
pip install "sk-pqc[pq]"
The ML-KEM leg uses
liboqs-python(import nameoqs), which binds the native liboqs. Pointoqsat a prebuiltliboqs.sowithOQS_INSTALL_PATH(orSK_PQC_LIBOQS) to avoid a source build —sk_pqc.pqkem.ensure_liboqs_path()applies this best-effort on import.
Quickstart
from sk_pqc import hybrid_keypair, hybrid_encap, hybrid_decap
kp = hybrid_keypair() # 1216 B pub, 2432 B priv
ct, ss_sender = hybrid_encap(kp.public_key) # 1120 B ciphertext + 32 B secret
ss_recipient = hybrid_decap(ct, kp.private_key)
assert ss_sender == ss_recipient # secure if EITHER leg holds
from sk_pqc import PrekeyBundle, seal, open_sealed, SUITE_ID
bundle = PrekeyBundle(suite=SUITE_ID, hybrid_public_hex=kp.public_key.hex())
blob = seal(b"top secret", bundle, sender="alice", recipient="bob")
assert open_sealed(blob, kp.private_key, sender="alice", recipient="bob") == b"top secret"
Test
# From a checkout (run from HOME to avoid local-namespace collisions)
cd ~ && python -m pytest /path/to/sk-pqc-py/tests -q
The cross-implementation interop gate (test_pqkem.py::test_cross_impl_vector_matches_sk_pqc) decapsulates the shared Dart/Python KAT vector and asserts the recorded shared secret — this is what proves the two implementations agree byte-for-byte. PQ tests skip cleanly if liboqs is unavailable; the pure-pyca combiner KAT + registry tests always run.
Self-report (claim evidence)
from sk_pqc import get_suite, is_quantum_resistant
s = get_suite("x25519-mlkem768")
print(s.status.value, s.fips_refs, is_quantum_resistant("x25519-mlkem768"))
# hybrid-pq ('FIPS 203', 'RFC 7748', 'RFC 5869') True
Provenance / clean-room
The pqroute1 routing split and the anon_queue addressing are inspired by the shape of mix/relay designs (incl. SimpleX, AGPL-3.0) — clean-room: no third-party code was copied or translated, only the protocol idea. The wire formats, codecs, and MAC constructions are original and built solely on the vetted backends above.
Related projects / See also
- ↔️ Sibling: sk_pqc — the Dart hybrid-KEM companion this package interoperates with (shared KAT vector).
- ⬇️ Used by: skcomms — sovereign multi-transport comms (envelope payload + routing seal).
- ⬇️ Used by: skchat — AI-native encrypted chat (group + 1:1 DM ratchets).
- ↔️ Sibling: sk_pgp — sovereign OpenPGP-PQC signing library (the signature counterpart).
- 📐 Standards: sk-standards — crypto, data-flow, version, and doc/SOP standards this repo conforms to.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sk_pqc-0.1.0.tar.gz.
File metadata
- Download URL: sk_pqc-0.1.0.tar.gz
- Upload date:
- Size: 49.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6edfb4d53672b273c7657676bff179c5052d5174c14f817001b0557057d25e18
|
|
| MD5 |
7709ad67cb4d55783fc80c1442454900
|
|
| BLAKE2b-256 |
95ad6d5cd4837f28df75bec838f6ace13d0c5fa9a3c2fd3b42ba1e8269b94bfb
|
File details
Details for the file sk_pqc-0.1.0-py3-none-any.whl.
File metadata
- Download URL: sk_pqc-0.1.0-py3-none-any.whl
- Upload date:
- Size: 41.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ffdeaab25b98cc0905e0b6cbcc4e10d1ef0f4390d556bda40c07ffb094290b39
|
|
| MD5 |
b6d2324e6ca7ae608895b314bd08065d
|
|
| BLAKE2b-256 |
dc3336caed730dd2125e6d124001e7d3e25fc42a902fb8d507ef99f33baa987a
|