pq_crypto
pq_crypto is a primitive-first Ruby gem for post-quantum cryptography.
It currently exposes three public building blocks:
PQCrypto::KEM— pureML-KEM-768PQCrypto::Signature—ML-DSA-65PQCrypto::HybridKEM— an optional custom hybrid KEM that combinesML-KEM-768andX25519with transcript-boundHKDF-SHA256
The gem is backed by vendored PQClean sources for ML-KEM-768 / ML-DSA-65 and OpenSSL for conventional primitives such as X25519 and HKDF-SHA256.
Status
- first public release
- primitive-first API only
- no protocol/session helpers in the public surface
- serialization uses pq_crypto-specific
pqc_container_*wrappers - not audited
- not yet positioned as production-ready
Installation
Add the gem to your project and compile the extension:
# Gemfile
gem "pq_crypto"
bundle install
bundle exec rake compile
Native dependencies
- Ruby 3.1+
- a C toolchain
- OpenSSL 3.0 or later
Primitive API
ML-KEM-768
keypair = PQCrypto::KEM.generate(:ml_kem_768)
result = keypair.public_key.encapsulate
shared_secret = keypair.secret_key.decapsulate(result.ciphertext)
ML-DSA-65
keypair = PQCrypto::Signature.generate(:ml_dsa_65)
signature = keypair.secret_key.sign("hello")
keypair.public_key.verify!("hello", signature)
Hybrid ML-KEM-768 + X25519
keypair = PQCrypto::HybridKEM.generate(:ml_kem_768_x25519_hkdf_sha256)
result = keypair.public_key.encapsulate
shared_secret = keypair.secret_key.decapsulate(result.ciphertext)
PQCrypto::HybridKEM is a custom pq_crypto construction. It is not advertised as compatible with HPKE, TLS hybrid drafts, X-Wing, OpenSSL native PQ APIs, or any other external wire format.
Serialization
Key import/export is available through pq_crypto-specific containers:
to_pqc_container_derto_pqc_container_pem*_from_pqc_container_der*_from_pqc_container_pem
Example:
keypair = PQCrypto::KEM.generate(:ml_kem_768)
der = keypair.public_key.to_pqc_container_der
imported = PQCrypto::KEM.public_key_from_pqc_container_der(der)
These containers are not real ASN.1 SPKI or PKCS#8. They are intended for stable import/export inside pq_crypto itself and are not advertised as interoperable with external PKI tooling.
Introspection
PQCrypto.version
PQCrypto.backend
PQCrypto.supported_kems
PQCrypto.supported_hybrid_kems
PQCrypto.supported_signatures
PQCrypto::KEM.details(:ml_kem_768)
PQCrypto::HybridKEM.details(:ml_kem_768_x25519_hkdf_sha256)
PQCrypto::Signature.details(:ml_dsa_65)
Testing helpers
Deterministic test hooks are exposed under PQCrypto::Testing for regression coverage:
ml_kem_keypair_from_seedml_kem_encapsulate_from_seedml_dsa_keypair_from_seedml_dsa_sign_from_seed
These helpers are intended for tests only.
Development
Run the test suite with:
bundle exec rake test
Refresh vendored PQClean sources manually only when you intentionally update the vendor snapshot. The refresh script now has a safe pinned default and records the exact vendored snapshot in ext/pqcrypto/vendor/.vendored:
bundle exec ruby script/vendor_libs.rb
To intentionally change the upstream snapshot, override all four pinning inputs together:
PQCLEAN_VERSION=<full-git-commit> \
PQCLEAN_URL=https://github.com/PQClean/PQClean/archive/<full-git-commit>.tar.gz \
PQCLEAN_SHA256=<archive-sha256> \
PQCLEAN_STRIP=PQClean-<full-git-commit> \
bundle exec ruby script/vendor_libs.rb