11---
22title : Post-Quantum Cryptography
3- description : Quantum-resistant algorithms (ML-DSA, ML-KEM)
3+ description : Quantum-resistant algorithms (ML-DSA, ML-KEM, SLH-DSA )
44---
55
66import { Callout } from ' fumadocs-ui/components/callout' ;
@@ -9,14 +9,17 @@ import { TypeTable } from 'fumadocs-ui/components/type-table';
99** Post-Quantum Cryptography (PQC)** provides cryptographic algorithms that are secure against both classical and quantum computers. RNQC implements the NIST standardized lattice-based algorithms via OpenSSL 3.6+.
1010
1111<Callout type = " info" title = " Why Post-Quantum?" >
12- Quantum computers threaten RSA, ECDSA, and ECDH. The PQC algorithms below are NIST-standardized replacements designed to resist quantum attacks while running efficiently on classical hardware.
12+ Quantum computers threaten RSA, ECDSA, and ECDH. The PQC algorithms below are
13+ NIST-standardized replacements designed to resist quantum attacks while
14+ running efficiently on classical hardware.
1315</Callout >
1416
1517## Table of Contents
1618
1719- [ Algorithms] ( #algorithms )
1820- [ ML-DSA (Digital Signatures)] ( #ml-dsa-digital-signatures )
1921- [ ML-KEM (Key Encapsulation)] ( #ml-kem-key-encapsulation )
22+ - [ SLH-DSA (Hash-Based Digital Signatures)] ( #slh-dsa-hash-based-digital-signatures )
2023- [ WebCrypto API] ( #webcrypto-api )
2124- [ Real-World Examples] ( #real-world-examples )
2225
@@ -26,21 +29,41 @@ import { TypeTable } from 'fumadocs-ui/components/type-table';
2629
2730Module Lattice Digital Signature Algorithm. Replacement for RSA and ECDSA signatures.
2831
29- | Parameter Set | Security Level | Public Key | Signature | Use Case |
30- | :------------- | :-------------- | :----------- | :---------- | :---------|
31- | ` ML-DSA-44 ` | NIST Level 2 | 1,312 B | 2,420 B | General purpose |
32- | ` ML-DSA-65 ` | NIST Level 3 | 1,952 B | 3,309 B | Recommended |
33- | ` ML-DSA-87 ` | NIST Level 5 | 2,592 B | 4,627 B | Maximum security |
32+ | Parameter Set | Security Level | Public Key | Signature | Use Case |
33+ | :------------ | :------------- | :--------- | :-------- | :--------------- |
34+ | ` ML-DSA-44 ` | NIST Level 2 | 1,312 B | 2,420 B | General purpose |
35+ | ` ML-DSA-65 ` | NIST Level 3 | 1,952 B | 3,309 B | Recommended |
36+ | ` ML-DSA-87 ` | NIST Level 5 | 2,592 B | 4,627 B | Maximum security |
3437
3538### ML-KEM (FIPS 203)
3639
3740Module Lattice Key Encapsulation Mechanism. Replacement for ECDH key exchange.
3841
3942| Parameter Set | Security Level | Public Key | Ciphertext | Shared Secret |
40- | :-------------| :--------------| :-----------| :-----------| :-------------|
41- | ` ML-KEM-512 ` | NIST Level 1 | 800 B | 768 B | 32 B |
42- | ` ML-KEM-768 ` | NIST Level 3 | 1,184 B | 1,088 B | 32 B |
43- | ` ML-KEM-1024 ` | NIST Level 5 | 1,568 B | 1,568 B | 32 B |
43+ | :------------ | :------------- | :--------- | :--------- | :------------ |
44+ | ` ML-KEM-512 ` | NIST Level 1 | 800 B | 768 B | 32 B |
45+ | ` ML-KEM-768 ` | NIST Level 3 | 1,184 B | 1,088 B | 32 B |
46+ | ` ML-KEM-1024 ` | NIST Level 5 | 1,568 B | 1,568 B | 32 B |
47+
48+ ### SLH-DSA (FIPS 205)
49+
50+ Stateless Hash-Based Digital Signature Algorithm (formerly SPHINCS+). A hash-based alternative to ML-DSA — security relies only on the underlying hash function, making it a conservative choice when lattice assumptions are a concern. Each parameter set comes in ` s ` (small signature, slow) and ` f ` (fast, larger signature) variants.
51+
52+ | Parameter Set | Security Level | Public Key | Signature | Notes |
53+ | :----------------------------------------- | :------------- | :--------- | :-------- | :-------- |
54+ | ` SLH-DSA-SHA2-128s ` / ` SLH-DSA-SHAKE-128s ` | NIST Level 1 | 32 B | 7,856 B | Small sig |
55+ | ` SLH-DSA-SHA2-128f ` / ` SLH-DSA-SHAKE-128f ` | NIST Level 1 | 32 B | 17,088 B | Fast sign |
56+ | ` SLH-DSA-SHA2-192s ` / ` SLH-DSA-SHAKE-192s ` | NIST Level 3 | 48 B | 16,224 B | Small sig |
57+ | ` SLH-DSA-SHA2-192f ` / ` SLH-DSA-SHAKE-192f ` | NIST Level 3 | 48 B | 35,664 B | Fast sign |
58+ | ` SLH-DSA-SHA2-256s ` / ` SLH-DSA-SHAKE-256s ` | NIST Level 5 | 64 B | 29,792 B | Small sig |
59+ | ` SLH-DSA-SHA2-256f ` / ` SLH-DSA-SHAKE-256f ` | NIST Level 5 | 64 B | 49,856 B | Fast sign |
60+
61+ <Callout type = " warn" title = " Performance Tradeoff" >
62+ The ` s ` variants produce smaller signatures but signing is markedly slower
63+ than ML-DSA. The ` f ` variants sign faster but emit signatures 4–6× larger. For
64+ most applications ML-DSA is preferable; reach for SLH-DSA when a hash-only
65+ security assumption is required.
66+ </Callout >
4467
4568---
4669
@@ -51,11 +74,7 @@ Module Lattice Key Encapsulation Mechanism. Replacement for ECDH key exchange.
5174Generate ML-DSA key pairs and sign/verify using the standard ` crypto ` API:
5275
5376``` ts
54- import {
55- generateKeyPairSync ,
56- sign ,
57- verify
58- } from ' react-native-quick-crypto' ;
77+ import { generateKeyPairSync , sign , verify } from ' react-native-quick-crypto' ;
5978
6079// Generate ML-DSA-65 key pair
6180const { publicKey, privateKey } = generateKeyPairSync (' ml-dsa-65' );
@@ -87,7 +106,7 @@ import { createPublicKey, createPrivateKey } from 'react-native-quick-crypto';
87106const imported = createPublicKey ({
88107 key: pubDer ,
89108 format: ' der' ,
90- type: ' spki'
109+ type: ' spki' ,
91110});
92111```
93112
@@ -103,7 +122,7 @@ ML-KEM uses **encapsulation** rather than key exchange. One party encapsulates a
103122import {
104123 generateKeyPairSync ,
105124 encapsulate ,
106- decapsulate
125+ decapsulate ,
107126} from ' react-native-quick-crypto' ;
108127
109128// Generate ML-KEM-768 key pair
@@ -120,6 +139,29 @@ console.log(sharedSecret.equals(recovered)); // true
120139
121140---
122141
142+ ## SLH-DSA (Hash-Based Digital Signatures)
143+
144+ ### Node.js API
145+
146+ Generate SLH-DSA key pairs and sign/verify using the standard ` crypto ` API. Twelve parameter sets are available: ` slh-dsa-{sha2,shake}-{128,192,256}{s,f} ` .
147+
148+ ``` ts
149+ import { generateKeyPairSync , sign , verify } from ' react-native-quick-crypto' ;
150+
151+ // Generate SLH-DSA-SHA2-128f key pair (fast variant)
152+ const { publicKey, privateKey } = generateKeyPairSync (' slh-dsa-sha2-128f' );
153+
154+ // Sign
155+ const message = Buffer .from (' hash-based, quantum-safe message' );
156+ const signature = sign (null , message , privateKey );
157+
158+ // Verify
159+ const isValid = verify (null , message , publicKey , signature );
160+ console .log (' Valid:' , isValid ); // true
161+ ```
162+
163+ ---
164+
123165## WebCrypto API
124166
125167PQC algorithms are fully supported through the SubtleCrypto interface.
@@ -130,26 +172,25 @@ PQC algorithms are fully supported through the SubtleCrypto interface.
130172import { subtle } from ' react-native-quick-crypto' ;
131173
132174// Generate key pair
133- const keyPair = await subtle .generateKey (
134- { name: ' ML-DSA-65' },
135- true ,
136- [' sign' , ' verify' ]
137- );
175+ const keyPair = await subtle .generateKey ({ name: ' ML-DSA-65' }, true , [
176+ ' sign' ,
177+ ' verify' ,
178+ ]);
138179
139180// Sign
140181const data = new TextEncoder ().encode (' quantum-safe data' );
141182const signature = await subtle .sign (
142183 { name: ' ML-DSA-65' },
143184 keyPair .privateKey ,
144- data
185+ data ,
145186);
146187
147188// Verify
148189const isValid = await subtle .verify (
149190 { name: ' ML-DSA-65' },
150191 keyPair .publicKey ,
151192 signature ,
152- data
193+ data ,
153194);
154195```
155196
@@ -159,23 +200,22 @@ const isValid = await subtle.verify(
159200import { subtle } from ' react-native-quick-crypto' ;
160201
161202// Generate encapsulation key pair
162- const keyPair = await subtle .generateKey (
163- { name: ' ML-KEM-768' },
164- true ,
165- [' deriveBits' , ' deriveKey' ]
166- );
203+ const keyPair = await subtle .generateKey ({ name: ' ML-KEM-768' }, true , [
204+ ' deriveBits' ,
205+ ' deriveKey' ,
206+ ]);
167207
168208// Encapsulate: get shared secret bits + ciphertext
169209const { sharedSecret, ciphertext } = await subtle .encapsulateBits (
170210 { name: ' ML-KEM-768' },
171- keyPair .publicKey
211+ keyPair .publicKey ,
172212);
173213
174214// Decapsulate: recover shared secret
175215const recovered = await subtle .decapsulateBits (
176216 { name: ' ML-KEM-768' },
177217 keyPair .privateKey ,
178- ciphertext
218+ ciphertext ,
179219);
180220
181221// Or derive a key directly from encapsulation
@@ -184,7 +224,7 @@ const { key: aesKey, ciphertext: ct } = await subtle.encapsulateKey(
184224 keyPair .publicKey ,
185225 { name: ' AES-GCM' , length: 256 },
186226 true ,
187- [' encrypt' , ' decrypt' ]
227+ [' encrypt' , ' decrypt' ],
188228);
189229
190230// Decapsulate to get the same AES key
@@ -194,16 +234,43 @@ const recoveredKey = await subtle.decapsulateKey(
194234 ct ,
195235 { name: ' AES-GCM' , length: 256 },
196236 true ,
197- [' encrypt' , ' decrypt' ]
237+ [' encrypt' , ' decrypt' ],
238+ );
239+ ```
240+
241+ ### SLH-DSA via SubtleCrypto
242+
243+ ``` ts
244+ import { subtle } from ' react-native-quick-crypto' ;
245+
246+ // Generate key pair (use the canonical FIPS 205 name)
247+ const keyPair = await subtle .generateKey ({ name: ' SLH-DSA-SHA2-128f' }, true , [
248+ ' sign' ,
249+ ' verify' ,
250+ ]);
251+
252+ const data = new TextEncoder ().encode (' signed with hash-based PQC' );
253+ const signature = await subtle .sign (
254+ { name: ' SLH-DSA-SHA2-128f' },
255+ keyPair .privateKey ,
256+ data ,
257+ );
258+
259+ const isValid = await subtle .verify (
260+ { name: ' SLH-DSA-SHA2-128f' },
261+ keyPair .publicKey ,
262+ signature ,
263+ data ,
198264);
199265```
200266
201267### Key Export Formats
202268
203- | Algorithm | ` spki ` | ` pkcs8 ` | ` jwk ` | ` raw-public ` | ` raw-seed ` |
204- | :----------| :------:| :-------:| :-----:| :------------:| :----------:|
205- | ML-DSA-44/65/87 | ✅ | ✅ | ✅ | ✅ | ✅ |
206- | ML-KEM-512/768/1024 | ✅ | ✅ | ✅ | ✅ | ✅ |
269+ | Algorithm | ` spki ` | ` pkcs8 ` | ` jwk ` | ` raw-public ` | ` raw-seed ` |
270+ | :---------------------------------------- | :----: | :-----: | :---: | :----------: | :--------: |
271+ | ML-DSA-44/65/87 | ✅ | ✅ | ✅ | ✅ | ✅ |
272+ | ML-KEM-512/768/1024 | ✅ | ✅ | ✅ | ✅ | ✅ |
273+ | ` SLH-DSA-{SHA2,SHAKE}-{128,192,256}{s,f} ` | ✅ | ✅ | ✅ | ✅ | ✅ |
207274
208275``` ts
209276// Export ML-DSA public key as JWK
@@ -221,7 +288,7 @@ const imported = await subtle.importKey(
221288 rawPub ,
222289 { name: ' ML-DSA-65' },
223290 true ,
224- [' verify' ]
291+ [' verify' ],
225292);
226293```
227294
@@ -234,11 +301,7 @@ const imported = await subtle.importKey(
234301Combine Ed25519 with ML-DSA for defense-in-depth during the quantum transition:
235302
236303``` ts
237- import {
238- generateKeyPairSync ,
239- sign ,
240- verify
241- } from ' react-native-quick-crypto' ;
304+ import { generateKeyPairSync , sign , verify } from ' react-native-quick-crypto' ;
242305
243306function hybridSign(message : Buffer ) {
244307 const ed = generateKeyPairSync (' ed25519' );
@@ -250,18 +313,22 @@ function hybridSign(message: Buffer) {
250313 return {
251314 message ,
252315 signatures: { ed25519: edSig , mlDsa65: pqcSig },
253- publicKeys: { ed25519: ed .publicKey , mlDsa65: pqc .publicKey }
316+ publicKeys: { ed25519: ed .publicKey , mlDsa65: pqc .publicKey },
254317 };
255318}
256319
257320function hybridVerify(signed : ReturnType <typeof hybridSign >): boolean {
258321 const edValid = verify (
259- null , signed .message ,
260- signed .publicKeys .ed25519 , signed .signatures .ed25519
322+ null ,
323+ signed .message ,
324+ signed .publicKeys .ed25519 ,
325+ signed .signatures .ed25519 ,
261326 );
262327 const pqcValid = verify (
263- null , signed .message ,
264- signed .publicKeys .mlDsa65 , signed .signatures .mlDsa65
328+ null ,
329+ signed .message ,
330+ signed .publicKeys .mlDsa65 ,
331+ signed .signatures .mlDsa65 ,
265332 );
266333
267334 // Both must pass
@@ -281,7 +348,7 @@ import {
281348 createCipheriv ,
282349 createDecipheriv ,
283350 createHash ,
284- randomBytes
351+ randomBytes ,
285352} from ' react-native-quick-crypto' ;
286353
287354// Server publishes its ML-KEM public key
0 commit comments