paragonie / ext-pqcrypto
PHP extension for post-quantum cryptography (ML-KEM, X-Wing, ML-DSA, SLH-DSA)
Package info
github.com/paragonie/ext-pqcrypto
Language:Rust
Type:php-ext
Ext name:ext-pqcrypto
pkg:composer/paragonie/ext-pqcrypto
Requires
- php: >=8.1
Requires (Dev)
- phpunit/phpunit: ^10|^11|^12|^13
Suggests
- ext-pqcrypto: This package provides the native extension. Install via 'make install'.
README
A PHP extension (written in Rust) that exposes post-quantum cryptography algorithms from the RustCrypto project.
Implements FIPS 203 (ML-KEM), FIPS 204 (ML-DSA), FIPS 205 (SLH-DSA), and X-Wing (hybrid X25519 + ML-KEM-768).
Requirements
- PHP >= 8.1
- Rust toolchain (rustc >= 1.85, nightly)
php-configin PATH (for ext-php-rs build)
Building
make build # cargo build --release make test # run PHP test suite make install # copy to PHP extension dir
After installing, add extension=pqcrypto to your php.ini, then run:
var_dump(extension_loaded('pqcrypto')); // bool(true)
Usage
X-Wing (Hybrid KEM: X25519 + ML-KEM-768)
Tip
X-Wing is the recommend hybrid post-quantum KEM.
[$sk, $pk] = PQCrypto\XWing::generateKeypair(); [$sharedSecret, $ciphertext] = $pk->encapsulate(); $recipientSecret = $sk->decapsulate($ciphertext); assert(hash_equals($recipientSecret, $sharedSecret));
ML-KEM (Key Encapsulation)
Tip
We do not recommend ML-KEM-512, but include it for completeness. ML-KEM-768 or ML-KEM-1024 should be used if X-Wing is not acceptable.
// ML-KEM-768 (also: MLKem512, MLKem1024) [$sk, $pk] = PQCrypto\MLKem768::generateKeypair(); [$sharedSecret, $ciphertext] = $pk->encapsulate(); $recipientSecret = $sk->decapsulate($ciphertext); assert(hash_equals($recipientSecret, $sharedSecret)); // Serialize / restore $skBytes = $sk->bytes(); // 64-byte seed $sk2 = PQCrypto\MLKem768\DecapsulationKey::fromBytes($skBytes);
ML-DSA (Digital Signatures)
Tip
ML-DSA-44 is fine. The larger parameter sets should only be used if you specifically need them for compliance reasons (i.e., CNSA 2.0).
// ML-DSA-44 (also: MLDSA65, MLDSA87) [$signingKey, $verifyingKey] = PQCrypto\MLDSA44::generateKeypair(); $signature = $signingKey->sign('message'); $valid = $verifyingKey->verify($signature, 'message'); // true $invalid = $verifyingKey->verify($signature, 'wrong'); // false // Serialize / restore $seed = $signingKey->bytes(); // 32-byte seed $sk2 = PQCrypto\MLDSA44\SigningKey::fromBytes($seed);
SLH-DSA (Stateless Hash-Based Signatures)
// Parameters: hash function + speed // Hash: 'shake128', 'shake192', 'shake256', // 'sha2-128', 'sha2-192', 'sha2-256' // Speed: 'fast' (larger sigs) or 'small' (smaller sigs) $slh = new PQCrypto\SLHDSA('shake256', 'fast'); [$signingKey, $verifyingKey] = $slh->generateKeypair(); $signature = $signingKey->sign('message'); $valid = $verifyingKey->verify($signature, 'message'); // Import keys $sk2 = $slh->importSigningKey($signingKey->bytes()); $vk2 = $slh->importVerifyingKey($verifyingKey->bytes());
Key Sizes
KEM
| Algorithm | Seed | Public Key | Ciphertext | Shared Secret |
|---|---|---|---|---|
| ML-KEM-512 | 64 | 800 | 768 | 32 |
| ML-KEM-768 | 64 | 1184 | 1088 | 32 |
| ML-KEM-1024 | 64 | 1568 | 1568 | 32 |
| X-Wing | 32 | 1216 | 1120 | 32 |
Signatures
| Algorithm | Seed | Public Key | Signature |
|---|---|---|---|
| ML-DSA-44 | 32 | 1312 | 2420 |
| ML-DSA-65 | 32 | 1952 | 3309 |
| ML-DSA-87 | 32 | 2592 | 4627 |
| SLH-DSA-*-128s | 64 | 32 | 7856 |
| SLH-DSA-*-128f | 64 | 32 | 17088 |
| SLH-DSA-*-192s | 96 | 48 | 16224 |
| SLH-DSA-*-192f | 96 | 48 | 35664 |
| SLH-DSA-*-256s | 128 | 64 | 29792 |
| SLH-DSA-*-256f | 128 | 64 | 49856 |
All sizes above are measured in bytes.
Secret keys are stored as seeds, never semi-expanded secrets. This is a deliberate design choice.