Paramant SDK for Python — real post-quantum file encryption
Python client for the PARAMANT post-quantum relay.
v3.0.0 swaps the v2.x kyber-py dependency (whose own README explicitly
warns against production use: not constant-time, timing-attack vulnerable)
and the silent ECDH fallback for pqcrypto
— a thin CFFI wrapper around the audited PQClean C implementations. The SDK
now produces wire-format v1 blobs (PQHB magic) and negotiates algorithm
support with the relay via GET /v2/capabilities before the first send.
Default algorithms: ML-KEM-768 (NIST FIPS 203) for key encapsulation and ML-DSA-65 (NIST FIPS 204) for signatures.
Install
pip install 'paramant-sdk>=3'
pqcrypto ships pre-built wheels for Linux, macOS and Windows — no compiler
needed. If the install fails because wheels are missing for your platform, the
SDK will raise a clear ImportError at module load rather than silently
degrading.
Quick start
from paramant_sdk import GhostPipe
# Defaults: ML-KEM-768 + ML-DSA-65, wire format v1
gp = GhostPipe(api_key='pgp_your_key', device='sender-001')
gp.receive_setup()
# Send
blob_hash, inclusion_proof = gp.send(b'encrypt me', recipient='receiver-001')
# Receive (burn-on-read)
data, receipt = gp.receive(blob_hash)
Overriding algorithms
# Anonymous (unsigned) blob: still ML-KEM-768, but no sender identity attached
gp = GhostPipe('pgp_...', 'device', sig_id=0x0000)
# Future: when the relay loads Falcon-512 for signatures
gp = GhostPipe('pgp_...', 'device', sig_id=0x0100)
The client calls /v2/capabilities before the first blob and validates that
the relay supports the requested kem_id / sig_id. Unsupported combination
→ CapabilityMismatch. No silent fallback.
Capabilities introspection
caps = gp.capabilities()
print(caps.wire_version) # 1
print(caps.kem_names()) # ['ML-KEM-512', 'ML-KEM-768', 'ML-KEM-1024']
print(caps.sig_names()) # ['none', 'ML-DSA-44', 'ML-DSA-65', 'Falcon-512', ...]
Breaking changes from 2.x
- Dependency swap:
kyber-pyremoved,pqcrypto>=0.4,<1.0required. kyber-py was author-declared not production-safe (see its own README). - ECDH silent fallback removed. v2.x degraded to ECDH-P256 + AES-GCM when
kyber-pywas absent while still advertising post-quantum protection. v3 raisesImportErrorat module load ifpqcryptois missing. - Wire format: legacy 0x02 format is gone. v3 produces and consumes wire
format v1 (
PQHBmagic, 10-byte header, algorithm IDs, length-prefixed fields). Seedocs/wire-format-v1.md. - Capability negotiation: the client calls
/v2/capabilitieson construction. Disable withnegotiate_on_init=Falsefor offline testing only. - Keypair file (
~/.paramant/<device>.keypair.json): v2.x files withoutml_kem_pub/ml_dsa_pubare renamed to*.v2.bakon first use and a fresh v3 keypair is generated. v2 blobs cannot be decrypted by v3. - Python: minimum 3.10 (unchanged).
Migration
# 2.x: silent ECDH fallback, empty ML-KEM hex, wire format 0x02
from paramant_sdk import GhostPipe
gp = GhostPipe('pgp_...', 'device') # may have silently used ECDH
gp.send(data) # blob starts with 0x00 00 00 ?? length prefix
# 3.x: real ML-KEM-768 + ML-DSA-65, wire format v1 (PQHB)
from paramant_sdk import GhostPipe
gp = GhostPipe('pgp_...', 'device') # ImportError if pqcrypto missing
gp.send(data) # blob starts with 50 51 48 42 (PQHB)
In-flight 2.x blobs will not decrypt under v3 — plan a drain window or re-upload.
Algorithm matrix
| Component | Algorithm | ID (wire) | Source |
|---|---|---|---|
| KEM | ML-KEM-768 | 0x0002 |
pqcrypto / PQClean |
| Signature | ML-DSA-65 | 0x0002 |
pqcrypto / PQClean |
| Signature | (none, anon) | 0x0000 |
skips signature section |
| AEAD | AES-256-GCM | — | cryptography |
| KDF | HKDF-SHA-256 | — | cryptography |
The relay may load additional algorithms (ML-KEM-512/1024, ML-DSA-44/87,
Falcon-512/1024, SLH-DSA). Check gp.capabilities() at runtime.
Security properties
- Post-quantum KEM: ML-KEM-768 meets NIST FIPS 203 category 3.
- Post-quantum signatures: ML-DSA-65 meets NIST FIPS 204 category 3.
- Header integrity: the 10-byte wire header (magic, version, KEM_ID, SIG_ID, FLAGS) is bound to the payload via GCM AAD. A bit-flip in the algorithm IDs aborts decryption.
- Key zeroization: best-effort
ctypes.memsetof shared secrets and derived AES keys on CPython. Non-CPython runtimes emit a warning.
Version
Current: 3.0.0 — matches relay v3 wire format, interoperable with
sdk-js >= 3 and the Chromium / Outlook clients.
Release files for paramant-sdk 3.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| paramant_sdk-3.0.0.tar.gz | 26.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| paramant_sdk-3.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 47.8 kB
Release files / paramant_sdk-3.0.0.tar.gz
| Download URL | paramant_sdk-3.0.0.tar.gz |
|---|---|
| Size | 26.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
234bc1f9b5202c3c1bbf6a45ba459ac900038b9a8565a1d55d25aa45c6b5eace
|
|
BLAKE2b-256 checksum How to use checksums |
1e21dfac826e181ea8ad212c23b9834dc8e391358ab67fec53fb249455417c50
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.12
|
Release files / paramant_sdk-3.0.0-py3-none-any.whl
| Download URL | paramant_sdk-3.0.0-py3-none-any.whl |
|---|---|
| Size | 21.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
658bff6e978ccfe6edeb14d705b748aaf403176b68bcd5848493a3cc2ac4c24a
|
|
BLAKE2b-256 checksum How to use checksums |
115cdbcf1361165532ee2c6586a0c17b4ed7adb3bb0b1052d256fe7909d355ee
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.12
|