@noble/post-quantum 0.6.0 â 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +106 -80
- package/_crystals.d.ts +23 -16
- package/_crystals.js +50 -12
- package/falcon.d.ts +7 -8
- package/falcon.js +109 -74
- package/hybrid.d.ts +21 -32
- package/hybrid.js +157 -78
- package/index.d.ts +0 -1
- package/index.js +8 -1
- package/ml-dsa.d.ts +35 -9
- package/ml-dsa.js +116 -43
- package/ml-kem.d.ts +46 -5
- package/ml-kem.js +175 -67
- package/package.json +10 -18
- package/slh-dsa.d.ts +44 -24
- package/slh-dsa.js +150 -84
- package/src/_crystals.ts +86 -35
- package/src/falcon.ts +230 -169
- package/src/hybrid.ts +243 -129
- package/src/index.ts +8 -0
- package/src/ml-dsa.ts +194 -87
- package/src/ml-kem.ts +283 -122
- package/src/slh-dsa.ts +310 -182
- package/src/utils.ts +244 -48
- package/utils.d.ts +99 -24
- package/utils.js +92 -24
- package/_crystals.d.ts.map +0 -1
- package/_crystals.js.map +0 -1
- package/falcon.d.ts.map +0 -1
- package/falcon.js.map +0 -1
- package/hybrid.d.ts.map +0 -1
- package/hybrid.js.map +0 -1
- package/index.d.ts.map +0 -1
- package/index.js.map +0 -1
- package/ml-dsa.d.ts.map +0 -1
- package/ml-dsa.js.map +0 -1
- package/ml-kem.d.ts.map +0 -1
- package/ml-kem.js.map +0 -1
- package/slh-dsa.d.ts.map +0 -1
- package/slh-dsa.js.map +0 -1
- package/utils.d.ts.map +0 -1
- package/utils.js.map +0 -1
package/README.md
CHANGED
|
@@ -3,22 +3,20 @@
|
|
|
3
3
|
Auditable & minimal JS implementation of post-quantum public-key cryptography.
|
|
4
4
|
|
|
5
5
|
- ð Auditable
|
|
6
|
-
-
|
|
7
|
-
-
|
|
6
|
+
- ðŠķ Minimal: 7KB (gzipped) ML-KEM, unused code is excluded from your builds
|
|
7
|
+
- ð Fast: hand-optimized for caveats of JS engines
|
|
8
|
+
- ð Reliable: ACVP / wycheproof tests ensure correctness
|
|
8
9
|
- ðĶū ML-KEM & CRYSTALS-Kyber: lattice-based KEM from FIPS-203
|
|
9
10
|
- ð ML-DSA & CRYSTALS-Dilithium: lattice-based signatures from FIPS-204
|
|
10
11
|
- ð SLH-DSA & SPHINCS+: hash-based Winternitz signatures from FIPS-205
|
|
11
12
|
- ðĶ
Falcon: lattice-based signatures from Falcon Round 3
|
|
12
|
-
- ðĄ Hybrid algorithms
|
|
13
|
-
- ðŠķ 16KB (gzipped) for everything, including bundled hashes & curves
|
|
14
|
-
|
|
15
|
-
Take a glance at [GitHub Discussions](https://github.com/paulmillr/noble-post-quantum/discussions) for questions and support.
|
|
13
|
+
- ðĄ Hybrid algorithms (combining classic & post-quantum)
|
|
16
14
|
|
|
17
15
|
> [!IMPORTANT]
|
|
18
|
-
> NIST published [IR 8547](https://nvlpubs.nist.gov/nistpubs/ir/2024/NIST.IR.8547.ipd.pdf),
|
|
19
|
-
> prohibiting classical cryptography (RSA, DSA, ECDSA, ECDH) after 2035.
|
|
20
|
-
>
|
|
21
|
-
> Take
|
|
16
|
+
> NIST published draft [IR 8547](https://nvlpubs.nist.gov/nistpubs/ir/2024/NIST.IR.8547.ipd.pdf),
|
|
17
|
+
> which proposes prohibiting classical cryptography (RSA, DSA, ECDSA, ECDH) after 2035.
|
|
18
|
+
> Australia's ASD does the same [after 2030](https://www.cyber.gov.au/resources-business-and-government/essential-cyber-security/ism/cyber-security-guidelines/guidelines-cryptography).
|
|
19
|
+
> Take this into account when designing new cryptographic systems.
|
|
22
20
|
|
|
23
21
|
### This library belongs to _noble_ cryptography
|
|
24
22
|
|
|
@@ -34,6 +32,7 @@ Take a glance at [GitHub Discussions](https://github.com/paulmillr/noble-post-qu
|
|
|
34
32
|
[post-quantum](https://github.com/paulmillr/noble-post-quantum),
|
|
35
33
|
5kb [secp256k1](https://github.com/paulmillr/noble-secp256k1) /
|
|
36
34
|
[ed25519](https://github.com/paulmillr/noble-ed25519)
|
|
35
|
+
- WASM version: [awasm-noble](https://github.com/paulmillr/awasm-noble)
|
|
37
36
|
- [Check out the homepage](https://paulmillr.com/noble/)
|
|
38
37
|
for reading resources, documentation, and apps built with noble
|
|
39
38
|
|
|
@@ -72,8 +71,7 @@ import {
|
|
|
72
71
|
} from '@noble/post-quantum/falcon.js';
|
|
73
72
|
import {
|
|
74
73
|
ml_kem768_x25519, ml_kem768_p256, ml_kem1024_p384,
|
|
75
|
-
KitchenSink_ml_kem768_x25519,
|
|
76
|
-
QSF_ml_kem768_p256, QSF_ml_kem1024_p384,
|
|
74
|
+
KitchenSink_ml_kem768_x25519, QSF_ml_kem768_p256, QSF_ml_kem1024_p384,
|
|
77
75
|
} from '@noble/post-quantum/hybrid.js';
|
|
78
76
|
```
|
|
79
77
|
|
|
@@ -84,16 +82,15 @@ import {
|
|
|
84
82
|
- [hybrid: XWing, KitchenSink and others](#hybrid-xwing-kitchensink-and-others)
|
|
85
83
|
- [What should I use?](#what-should-i-use)
|
|
86
84
|
- [Security](#security)
|
|
87
|
-
- [Speed](#speed)
|
|
88
85
|
- [Contributing & testing](#contributing--testing)
|
|
86
|
+
- [Speed](#speed)
|
|
89
87
|
- [License](#license)
|
|
90
88
|
|
|
91
89
|
### ML-KEM / Kyber shared secrets
|
|
92
90
|
|
|
93
91
|
```ts
|
|
94
92
|
import { ml_kem512, ml_kem768, ml_kem1024 } from '@noble/post-quantum/ml-kem.js';
|
|
95
|
-
import { randomBytes } from '@noble/post-quantum/utils.js';
|
|
96
|
-
import { notDeepStrictEqual } from 'node:assert';
|
|
93
|
+
import { equalBytes, randomBytes } from '@noble/post-quantum/utils.js';
|
|
97
94
|
const seed = randomBytes(64); // seed is optional
|
|
98
95
|
const aliceKeys = ml_kem768.keygen(seed);
|
|
99
96
|
const { cipherText, sharedSecret: bobShared } = ml_kem768.encapsulate(aliceKeys.publicKey);
|
|
@@ -102,7 +99,7 @@ const aliceShared = ml_kem768.decapsulate(cipherText, aliceKeys.secretKey);
|
|
|
102
99
|
// Warning: Can be MITM-ed
|
|
103
100
|
const malloryKeys = ml_kem768.keygen();
|
|
104
101
|
const malloryShared = ml_kem768.decapsulate(cipherText, malloryKeys.secretKey); // No error!
|
|
105
|
-
|
|
102
|
+
console.log(equalBytes(aliceShared, malloryShared)); // false: different key!
|
|
106
103
|
```
|
|
107
104
|
|
|
108
105
|
Lattice-based key encapsulation mechanism, defined in [FIPS-203](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.pdf) ([website](https://www.pq-crystals.org/kyber/resources.shtml), [repo](https://github.com/pq-crystals/kyber)).
|
|
@@ -142,6 +139,27 @@ Lattice-based digital signature algorithm, defined in [FIPS-204](https://nvlpubs
|
|
|
142
139
|
[repo](https://github.com/pq-crystals/dilithium)).
|
|
143
140
|
The internals are similar to ML-KEM, but keys and params are different.
|
|
144
141
|
|
|
142
|
+
`sign` / `verify` accept optional parameters:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { ml_dsa65 } from '@noble/post-quantum/ml-dsa.js';
|
|
146
|
+
import { sha512 } from '@noble/hashes/sha2.js';
|
|
147
|
+
const keys = ml_dsa65.keygen();
|
|
148
|
+
const msg = new TextEncoder().encode('hello noble');
|
|
149
|
+
const ctx = new Uint8Array([1, 2, 3]);
|
|
150
|
+
const sigCtx = ml_dsa65.sign(msg, keys.secretKey, { context: ctx }); // verify needs same context
|
|
151
|
+
const sigDet = ml_dsa65.sign(msg, keys.secretKey, { extraEntropy: false }); // deterministic
|
|
152
|
+
const hml = ml_dsa65.prehash(sha512); // HashML-DSA
|
|
153
|
+
const sigPre = hml.sign(msg, keys.secretKey);
|
|
154
|
+
const isValidPre = hml.verify(sigPre, msg, keys.publicKey);
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
- `context`: domain-separation byte string, up to 255 bytes; must match between `sign` and `verify`
|
|
158
|
+
- `extraEntropy`: hedged-signing randomness. Default is 32 random bytes;
|
|
159
|
+
`false` produces deterministic signatures; custom 32-byte value is also allowed
|
|
160
|
+
- `externalMu`: treat `msg` as the precomputed 64-byte message representative Âĩ
|
|
161
|
+
- `prehash(hash)`: pre-hash variant (HashML-DSA) from FIPS-204
|
|
162
|
+
|
|
145
163
|
### SLH-DSA / SPHINCS+ signatures
|
|
146
164
|
|
|
147
165
|
```ts
|
|
@@ -172,6 +190,9 @@ Hash-based digital signature algorithm, defined in [FIPS-205](https://nvlpubs.ni
|
|
|
172
190
|
- 128 / 192 / 256: indicates security level in bits
|
|
173
191
|
- s / f: indicates small vs fast trade-off
|
|
174
192
|
|
|
193
|
+
`sign` / `verify` accept the same optional `context`, `extraEntropy` and `prehash(hash)`
|
|
194
|
+
(HashSLH-DSA) parameters as ML-DSA. With `extraEntropy: false`, signing is deterministic.
|
|
195
|
+
|
|
175
196
|
SLH-DSA is slow: see [benchmarks](#speed) for key size & speed.
|
|
176
197
|
|
|
177
198
|
### Falcon signatures
|
|
@@ -202,12 +223,12 @@ Lattice-based digital signature algorithm, submitted to NIST PQC Round 3 ([websi
|
|
|
202
223
|
```js
|
|
203
224
|
import {
|
|
204
225
|
ml_kem768_x25519, ml_kem768_p256, ml_kem1024_p384,
|
|
205
|
-
KitchenSink_ml_kem768_x25519,
|
|
226
|
+
KitchenSink_ml_kem768_x25519,
|
|
206
227
|
QSF_ml_kem768_p256, QSF_ml_kem1024_p384,
|
|
207
228
|
} from '@noble/post-quantum/hybrid.js';
|
|
208
229
|
```
|
|
209
230
|
|
|
210
|
-
|
|
231
|
+
The hybrid submodule combines post-quantum algorithms with elliptic curve cryptography:
|
|
211
232
|
|
|
212
233
|
- `ml_kem768_x25519`: ML-KEM-768 + X25519 (CG Framework, same as XWing)
|
|
213
234
|
- `ml_kem768_p256`: ML-KEM-768 + P-256 (CG Framework)
|
|
@@ -225,25 +246,26 @@ The following spec drafts are matched:
|
|
|
225
246
|
|
|
226
247
|
### What should I use?
|
|
227
248
|
|
|
228
|
-
| | Speed | Key size | Sig size
|
|
229
|
-
| ------- | ------ | ----------- |
|
|
230
|
-
| RSA | Normal | 256B - 2KB | 256B - 2KB
|
|
231
|
-
| ECC | Normal | 32 - 256B | 48 - 128B
|
|
232
|
-
| ML-KEM | Fast |
|
|
233
|
-
| ML-DSA | Normal | 1.3 - 2.5KB | 2.5 - 4.5KB
|
|
234
|
-
| SLH-DSA | Slow | 32 - 128B | 17 - 50KB
|
|
235
|
-
| FN-DSA | Slow | 0.9 - 1.8KB | 0.6 - 1.2KB
|
|
249
|
+
| | Speed | Key size | Sig / CT size | Created in | Popularized in | Post-quantum? |
|
|
250
|
+
| ------- | ------ | ----------- | ------------- | ---------- | -------------- | ------------- |
|
|
251
|
+
| RSA | Normal | 256B - 2KB | 256B - 2KB | 1970s | 1990s | No |
|
|
252
|
+
| ECC | Normal | 32 - 256B | 48 - 128B | 1980s | 2010s | No |
|
|
253
|
+
| ML-KEM | Fast | 0.8 - 1.6KB | 0.8 - 1.6KB | 1990s | 2020s | Yes |
|
|
254
|
+
| ML-DSA | Normal | 1.3 - 2.5KB | 2.5 - 4.5KB | 1990s | 2020s | Yes |
|
|
255
|
+
| SLH-DSA | Slow | 32 - 128B | 17 - 50KB | 1970s | 2020s | Yes |
|
|
256
|
+
| FN-DSA | Slow | 0.9 - 1.8KB | 0.6 - 1.2KB | 1990s | 2020s | Yes |
|
|
236
257
|
|
|
237
|
-
|
|
258
|
+
ML-KEM is a KEM, not a signature scheme: its last column is ciphertext (CT) size.
|
|
259
|
+
We suggest using ECC + ML-KEM for key agreement, ECC + SLH-DSA for signatures.
|
|
238
260
|
|
|
239
261
|
ML-KEM and ML-DSA are lattice-based. SLH-DSA is hash-based, which means it is built on top of older, more conservative primitives. NIST guidance for security levels:
|
|
240
262
|
|
|
241
263
|
- Category 3 (~AES-192): ML-KEM-768, ML-DSA-65, SLH-DSA-192
|
|
242
264
|
- Category 5 (~AES-256): ML-KEM-1024, ML-DSA-87, SLH-DSA-256
|
|
243
265
|
|
|
244
|
-
NIST recommends
|
|
266
|
+
NIST recommends cat-3+, while Australian [ASD only allows cat-5 after 2030](https://www.cyber.gov.au/resources-business-and-government/essential-cyber-security/ism/cyber-security-guidelines/guidelines-cryptography).
|
|
245
267
|
|
|
246
|
-
It's also useful to check out [NIST SP 800-131Ar3](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar3.ipd.pdf)
|
|
268
|
+
It's also useful to check out draft [NIST SP 800-131Ar3](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar3.ipd.pdf)
|
|
247
269
|
for "Transitioning the Use of Cryptographic Algorithms and Key Lengths".
|
|
248
270
|
|
|
249
271
|
For [hashes](https://github.com/paulmillr/noble-hashes), use SHA512 or SHA3-512 (not SHA256); and for [ciphers](https://github.com/paulmillr/noble-ciphers) ensure AES-256 or ChaCha.
|
|
@@ -252,7 +274,9 @@ For [hashes](https://github.com/paulmillr/noble-hashes), use SHA512 or SHA3-512
|
|
|
252
274
|
|
|
253
275
|
The library has not been independently audited yet.
|
|
254
276
|
|
|
255
|
-
|
|
277
|
+
- at version 0.6.1, in Apr 2026, it was audited by ourselves (self-audited)
|
|
278
|
+
- Scope: everything
|
|
279
|
+
- [Changes since audit](https://github.com/paulmillr/noble-post-quantum/compare/0.6.1..main)
|
|
256
280
|
|
|
257
281
|
If you see anything unusual: investigate and report.
|
|
258
282
|
|
|
@@ -272,10 +296,11 @@ Keep in mind that even hardware versions ML-KEM [are vulnerable](https://eprint.
|
|
|
272
296
|
- Version ranges are locked, and changes are checked with npm-diff.
|
|
273
297
|
- **Dev dependencies** are excluded from end-user installs; they're only used for development and build steps.
|
|
274
298
|
|
|
275
|
-
For this package, there are
|
|
299
|
+
For this package, there are 3 dependencies; and a few dev dependencies:
|
|
276
300
|
|
|
277
301
|
- [noble-hashes](https://github.com/paulmillr/noble-hashes) provides cryptographic hashing functionality, used internally in every algorithm
|
|
278
302
|
- [noble-curves](https://github.com/paulmillr/noble-curves) provides elliptic curve cryptography for hybrid algorithms
|
|
303
|
+
- [noble-ciphers](https://github.com/paulmillr/noble-ciphers) provides AES-CTR DRBG and ChaCha20, used internally in Falcon
|
|
279
304
|
- jsbt is used for benchmarking / testing / build tooling and developed by the same author
|
|
280
305
|
- prettier, fast-check and typescript are used for code quality / test generation / ts compilation
|
|
281
306
|
|
|
@@ -289,60 +314,61 @@ Browsers have had weaknesses in the past - and could again - but implementing a
|
|
|
289
314
|
|
|
290
315
|
## Speed
|
|
291
316
|
|
|
292
|
-
> `npm run
|
|
293
|
-
|
|
294
|
-
Noble is the fastest JS implementation of post-quantum algorithms. WASM libraries can be faster.
|
|
295
|
-
|
|
296
|
-
Benchmarks on Apple M4 (**higher is better**):
|
|
297
|
-
|
|
298
|
-
| OPs/sec | Keygen | Signing | Verification | Shared secret |
|
|
299
|
-
| ----------------- | ------ | ------- | ------------ | ------------- |
|
|
300
|
-
| ECC x/ed25519 | 14216 | 6849 | 1400 | 1981 |
|
|
301
|
-
| ML-KEM-768 | 3778 | | | 3750 |
|
|
302
|
-
| ML-DSA65 | 580 | 272 | 546 | |
|
|
303
|
-
| SLH-DSA-SHA2-192f | 245 | 8 | 169 | |
|
|
304
|
-
|
|
305
|
-
```
|
|
306
|
-
# ML-KEM768
|
|
307
|
-
keygen x 3,778 ops/sec @ 264Ξs/op
|
|
308
|
-
encapsulate x 3,220 ops/sec @ 310Ξs/op
|
|
309
|
-
decapsulate x 4,029 ops/sec @ 248Ξs/op
|
|
310
|
-
# ML-DSA65
|
|
311
|
-
keygen x 580 ops/sec @ 1ms/op
|
|
312
|
-
sign x 272 ops/sec @ 3ms/op
|
|
313
|
-
verify x 546 ops/sec @ 1ms/op
|
|
314
|
-
# SLH-DSA SHA2 192f
|
|
315
|
-
keygen x 245 ops/sec @ 4ms/op
|
|
316
|
-
sign x 8 ops/sec @ 114ms/op
|
|
317
|
-
verify x 169 ops/sec @ 5ms/op
|
|
318
|
-
```
|
|
317
|
+
> `npm run benchmark`
|
|
319
318
|
|
|
320
|
-
|
|
319
|
+
Noble is the fastest JS implementation of post-quantum algorithms.
|
|
321
320
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
| sha2_256f | 49856 | 15ms | 340ms | 9ms |
|
|
327
|
-
| sha2_128s | 7856 | 260ms | 2000ms | 2ms |
|
|
328
|
-
| sha2_192s | 16224 | 380ms | 3800ms | 3ms |
|
|
329
|
-
| sha2_256s | 29792 | 250ms | 3400ms | 4ms |
|
|
330
|
-
| shake_192f | 35664 | 21ms | 553ms | 29ms |
|
|
331
|
-
| shake_192s | 16224 | 260ms | 2635ms | 2ms |
|
|
321
|
+
There is experimental [git branch](https://github.com/paulmillr/noble-post-quantum/tree/awasm),
|
|
322
|
+
which uses WASM-based [awasm-noble](https://github.com/paulmillr/awasm-noble) for hashing.
|
|
323
|
+
It has 80% faster ML-KEM, 30% faster ML-DSA, 2.3x faster SLH-DSA-SHA256, 15x faster SLH-DSA-SHAKE.
|
|
324
|
+
Try it out.
|
|
332
325
|
|
|
333
|
-
|
|
326
|
+
Benchmarks on Apple M4 (operations/sec, **higher is better**):
|
|
334
327
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
-
|
|
338
|
-
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
328
|
+
| Primitive | Keygen | Signing | Verification | Shared secret |
|
|
329
|
+
| ----------------- | ------ | ------- | ------------ | ------------- |
|
|
330
|
+
| ML-KEM-768 | 4661 | | | 4089 |
|
|
331
|
+
| ML-DSA-65 | 719 | 294 | 610 | |
|
|
332
|
+
| Falcon512 | 14 | 749 | 2160 | |
|
|
333
|
+
| SLH-DSA-SHA2-192f | 321 | 11 | 198 | |
|
|
334
|
+
| Pre-quantum x/ed25519 | 12648 | 6157 | 1255 | 1981 |
|
|
335
|
+
|
|
336
|
+
SLH-DSA (`s` variants have 2x shorter signatures; SHAKE is very slow):
|
|
337
|
+
|
|
338
|
+
| | keygen | sign | verify |
|
|
339
|
+
| ---------- | ------ | ------ | ------ |
|
|
340
|
+
| sha2_128f | 2ms | 47ms | 3ms |
|
|
341
|
+
| shake_128f | 10ms | 237ms | 14ms |
|
|
342
|
+
| sha2_192f | 3.2ms | 93ms | 5.1ms |
|
|
343
|
+
| shake_192f | 15ms | 396ms | 21ms |
|
|
344
|
+
| sha2_256f | 8.5ms | 187ms | 5.2ms |
|
|
345
|
+
| shake_256f | 40ms | 813ms | 22ms |
|
|
346
|
+
| sha2_128s | 140ms | 1068ms | 1.1ms |
|
|
347
|
+
| shake_128s | 673ms | 5114ms | 5.2ms |
|
|
348
|
+
| sha2_192s | 209ms | 2114ms | 1.9ms |
|
|
349
|
+
| shake_192s | 974ms | 8779ms | 7.1ms |
|
|
350
|
+
| sha2_256s | 137ms | 1941ms | 2.7ms |
|
|
351
|
+
| shake_256s | 645ms | 7689ms | 11ms |
|
|
352
|
+
|
|
353
|
+
Key and signature sizes:
|
|
354
|
+
|
|
355
|
+
| Variant | Public key | Secret key | Signature / Ciphertext |
|
|
356
|
+
|---|---:|---:|---:|
|
|
357
|
+
| ML-KEM-512 | 800 | 1632 | 768 |
|
|
358
|
+
| ML-KEM-768 | 1184 | 2400 | 1088 |
|
|
359
|
+
| ML-KEM-1024 | 1568 | 3168 | 1568 |
|
|
360
|
+
| ML-DSA-44 | 1312 | 2560 | 2420 |
|
|
361
|
+
| ML-DSA-65 | 1952 | 4032 | 3309 |
|
|
362
|
+
| ML-DSA-87 | 2592 | 4896 | 4627 |
|
|
363
|
+
| Falcon512 | 897 | 1281 | 666 |
|
|
364
|
+
| Falcon1024 | 1793 | 2305 | 1280 |
|
|
365
|
+
| SLH-DSA-128f | 32 | 64 | 17088 |
|
|
366
|
+
| SLH-DSA-128s | 32 | 64 | 7856 |
|
|
367
|
+
| SLH-DSA-192f | 48 | 96 | 35664 |
|
|
368
|
+
| SLH-DSA-192s | 48 | 96 | 16224 |
|
|
369
|
+
| SLH-DSA-256f | 64 | 128 | 49856 |
|
|
370
|
+
| SLH-DSA-256s | 64 | 128 | 29792 |
|
|
342
371
|
|
|
343
|
-
See [paulmillr.com/noble](https://paulmillr.com/noble/)
|
|
344
|
-
for useful resources, articles, documentation and demos
|
|
345
|
-
related to the library.
|
|
346
372
|
|
|
347
373
|
## License
|
|
348
374
|
|
package/_crystals.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { TypedArray } from '@noble/hashes/utils.js';
|
|
2
|
-
import { type BytesCoderLen, type Coder } from './utils.ts';
|
|
2
|
+
import { type BytesCoderLen, type Coder, type TRet } from './utils.ts';
|
|
3
3
|
/** Extendable-output reader used by the CRYSTALS implementations. */
|
|
4
4
|
export type XOF = (seed: Uint8Array, blockLen?: number) => {
|
|
5
5
|
/**
|
|
@@ -50,6 +50,24 @@ export type CrystalOpts<T extends TypedArray> = {
|
|
|
50
50
|
};
|
|
51
51
|
/** Constructor function for typed polynomial containers. */
|
|
52
52
|
export type TypedCons<T extends TypedArray> = (n: number) => T;
|
|
53
|
+
type Crystals<T extends TypedArray> = {
|
|
54
|
+
mod: (a: number, modulo?: number) => number;
|
|
55
|
+
smod: (a: number, modulo?: number) => number;
|
|
56
|
+
nttZetas: T;
|
|
57
|
+
NTT: {
|
|
58
|
+
/**
|
|
59
|
+
* Forward transform in place. Mutates and returns `r`.
|
|
60
|
+
* Kyber-mode input coefficients must already use canonical representatives in `[0, Q)`.
|
|
61
|
+
*/
|
|
62
|
+
encode: (r: T) => T;
|
|
63
|
+
/**
|
|
64
|
+
* Inverse transform in place. Mutates and returns `r`.
|
|
65
|
+
* Kyber-mode input coefficients must already use canonical representatives in `[0, Q)`.
|
|
66
|
+
*/
|
|
67
|
+
decode: (r: T) => T;
|
|
68
|
+
};
|
|
69
|
+
bitsCoder: (d: number, c: Coder<number, number>) => BytesCoderLen<T>;
|
|
70
|
+
};
|
|
53
71
|
/**
|
|
54
72
|
* Creates shared modular arithmetic, NTT, and packing helpers for CRYSTALS schemes.
|
|
55
73
|
* @param opts - Polynomial and transform parameters. See {@link CrystalOpts}.
|
|
@@ -69,18 +87,7 @@ export type TypedCons<T extends TypedArray> = (n: number) => T;
|
|
|
69
87
|
* const reduced = crystals.mod(-1);
|
|
70
88
|
* ```
|
|
71
89
|
*/
|
|
72
|
-
export declare const genCrystals: <T extends TypedArray>(opts: CrystalOpts<T>) =>
|
|
73
|
-
mod: (a: number, modulo?: number) => number;
|
|
74
|
-
smod: (a: number, modulo?: number) => number;
|
|
75
|
-
nttZetas: T;
|
|
76
|
-
NTT: {
|
|
77
|
-
/** Forward transform in place. Mutates and returns `r`. */
|
|
78
|
-
encode: (r: T) => T;
|
|
79
|
-
/** Inverse transform in place. Mutates and returns `r`. */
|
|
80
|
-
decode: (r: T) => T;
|
|
81
|
-
};
|
|
82
|
-
bitsCoder: (d: number, c: Coder<number, number>) => BytesCoderLen<T>;
|
|
83
|
-
};
|
|
90
|
+
export declare const genCrystals: <T extends TypedArray>(opts: CrystalOpts<T>) => TRet<Crystals<T>>;
|
|
84
91
|
/**
|
|
85
92
|
* SHAKE128-based extendable-output reader factory used by ML-KEM.
|
|
86
93
|
* `get(x, y)` selects one coordinate pair at a time; calling it again invalidates previously
|
|
@@ -97,7 +104,7 @@ export declare const genCrystals: <T extends TypedArray>(opts: CrystalOpts<T>) =
|
|
|
97
104
|
* const block = reader.get(0, 0)();
|
|
98
105
|
* ```
|
|
99
106
|
*/
|
|
100
|
-
export declare const XOF128: XOF
|
|
107
|
+
export declare const XOF128: TRet<XOF>;
|
|
101
108
|
/**
|
|
102
109
|
* SHAKE256-based extendable-output reader factory used by ML-DSA.
|
|
103
110
|
* `get(x, y)` appends raw one-byte coordinates to the seed, invalidates previously returned
|
|
@@ -114,5 +121,5 @@ export declare const XOF128: XOF;
|
|
|
114
121
|
* const block = reader.get(0, 0)();
|
|
115
122
|
* ```
|
|
116
123
|
*/
|
|
117
|
-
export declare const XOF256: XOF
|
|
118
|
-
|
|
124
|
+
export declare const XOF256: TRet<XOF>;
|
|
125
|
+
export {};
|
package/_crystals.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
/*! noble-post-quantum - MIT License (c) 2024 Paul Miller (paulmillr.com) */
|
|
6
6
|
import { FFTCore, reverseBits } from '@noble/curves/abstract/fft.js';
|
|
7
7
|
import { shake128, shake256 } from '@noble/hashes/sha3.js';
|
|
8
|
-
import { cleanBytes, getMask } from "./utils.js";
|
|
8
|
+
import { cleanBytes, getMask, } from "./utils.js";
|
|
9
9
|
/**
|
|
10
10
|
* Creates shared modular arithmetic, NTT, and packing helpers for CRYSTALS schemes.
|
|
11
11
|
* @param opts - Polynomial and transform parameters. See {@link CrystalOpts}.
|
|
@@ -57,14 +57,34 @@ export const genCrystals = (opts) => {
|
|
|
57
57
|
// Explained: https://electricdusk.com/ntt.html
|
|
58
58
|
// Kyber has slightly different params, since there is no 512th primitive root of unity mod q,
|
|
59
59
|
// only 256th primitive root of unity mod. Which also complicates MultiplyNTT.
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
sub: (a, b) => mod((a | 0) - (b | 0)) | 0,
|
|
63
|
-
mul: (a, b) => mod((a | 0) * (b | 0)) | 0,
|
|
64
|
-
inv: (_a) => {
|
|
65
|
-
throw new Error('not implemented');
|
|
66
|
-
},
|
|
60
|
+
const inv = (_a) => {
|
|
61
|
+
throw new Error('not implemented');
|
|
67
62
|
};
|
|
63
|
+
// ML-KEM (Kyber) polynomials always enter the transform reduced to [0, Q), so add/sub only
|
|
64
|
+
// need one conditional correction instead of `%`; measured ~20% faster NTT there.
|
|
65
|
+
// ML-DSA keeps the generic mod() path on purpose: its first forward stage sees centered
|
|
66
|
+
// (negative) coefficients, and `sub(a, t)` can drop below -Q (t is a mul output in [0, Q)),
|
|
67
|
+
// so a single correction is not enough. A guarded fast path with mod() fallback was measured
|
|
68
|
+
// slower than plain `%` for the 23-bit Q (V8 int32 modulo is one div; the branches lose).
|
|
69
|
+
const field = isKyber
|
|
70
|
+
? {
|
|
71
|
+
add: (a, b) => {
|
|
72
|
+
const r = (a + b) | 0;
|
|
73
|
+
return r >= Q ? (r - Q) | 0 : r;
|
|
74
|
+
},
|
|
75
|
+
sub: (a, b) => {
|
|
76
|
+
const r = (a - b) | 0;
|
|
77
|
+
return r < 0 ? (r + Q) | 0 : r;
|
|
78
|
+
},
|
|
79
|
+
mul: (a, b) => mod((a | 0) * (b | 0)) | 0,
|
|
80
|
+
inv,
|
|
81
|
+
}
|
|
82
|
+
: {
|
|
83
|
+
add: (a, b) => mod((a | 0) + (b | 0)) | 0,
|
|
84
|
+
sub: (a, b) => mod((a | 0) - (b | 0)) | 0,
|
|
85
|
+
mul: (a, b) => mod((a | 0) * (b | 0)) | 0,
|
|
86
|
+
inv,
|
|
87
|
+
};
|
|
68
88
|
const nttOpts = {
|
|
69
89
|
N,
|
|
70
90
|
roots: nttZetas,
|
|
@@ -91,17 +111,27 @@ export const genCrystals = (opts) => {
|
|
|
91
111
|
// Pack one little-endian `d`-bit word per coefficient, matching FIPS 203 ByteEncode /
|
|
92
112
|
// ByteDecode and the FIPS 204 BitsToBytes-based polynomial packing helpers.
|
|
93
113
|
const bitsCoder = (d, c) => {
|
|
114
|
+
// Validate the carry shape once: JS bitwise operations silently truncate wider accumulators.
|
|
115
|
+
for (let i = 0, bufLen = 0; i < N; i++) {
|
|
116
|
+
bufLen += d;
|
|
117
|
+
if (bufLen > 32)
|
|
118
|
+
getMask(bufLen);
|
|
119
|
+
bufLen %= 8;
|
|
120
|
+
}
|
|
94
121
|
const mask = getMask(d);
|
|
95
122
|
const bytesLen = d * (N / 8);
|
|
96
123
|
return {
|
|
97
124
|
bytesLen,
|
|
98
|
-
encode: (
|
|
125
|
+
encode: (poly_) => {
|
|
126
|
+
const poly = poly_;
|
|
99
127
|
const r = new Uint8Array(bytesLen);
|
|
100
128
|
for (let i = 0, buf = 0, bufLen = 0, pos = 0; i < poly.length; i++) {
|
|
101
129
|
buf |= (c.encode(poly[i]) & mask) << bufLen;
|
|
102
130
|
bufLen += d;
|
|
131
|
+
// Take the low byte directly: `& 0xff` matches the previous getMask(bufLen) result
|
|
132
|
+
// after Uint8Array truncation, without a validated function call per output byte.
|
|
103
133
|
for (; bufLen >= 8; bufLen -= 8, buf >>= 8)
|
|
104
|
-
r[pos++] = buf &
|
|
134
|
+
r[pos++] = buf & 0xff;
|
|
105
135
|
}
|
|
106
136
|
return r;
|
|
107
137
|
},
|
|
@@ -117,7 +147,16 @@ export const genCrystals = (opts) => {
|
|
|
117
147
|
},
|
|
118
148
|
};
|
|
119
149
|
};
|
|
120
|
-
return {
|
|
150
|
+
return {
|
|
151
|
+
mod,
|
|
152
|
+
smod,
|
|
153
|
+
nttZetas: nttZetas,
|
|
154
|
+
NTT: {
|
|
155
|
+
encode: (r) => NTT.encode(r),
|
|
156
|
+
decode: (r) => NTT.decode(r),
|
|
157
|
+
},
|
|
158
|
+
bitsCoder: bitsCoder,
|
|
159
|
+
};
|
|
121
160
|
};
|
|
122
161
|
const createXofShake = (shake) => (seed, blockLen) => {
|
|
123
162
|
if (!blockLen)
|
|
@@ -188,4 +227,3 @@ export const XOF128 = /* @__PURE__ */ createXofShake(shake128);
|
|
|
188
227
|
* ```
|
|
189
228
|
*/
|
|
190
229
|
export const XOF256 = /* @__PURE__ */ createXofShake(shake256);
|
|
191
|
-
//# sourceMappingURL=_crystals.js.map
|
package/falcon.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { type CryptoKeys, type Signer, type SigOpts, type TRet, type VerOpts } from './utils.ts';
|
|
2
|
+
type FalconRandom = (bytesLength?: number) => TRet<Uint8Array>;
|
|
3
3
|
type FalconSigOpts = SigOpts & {
|
|
4
|
-
random?:
|
|
4
|
+
random?: FalconRandom;
|
|
5
5
|
};
|
|
6
6
|
/** Falcon attached-signature API. */
|
|
7
7
|
export type FalconAttached = CryptoKeys & {
|
|
@@ -42,7 +42,7 @@ export type Falcon = Signer & {
|
|
|
42
42
|
* falcon512.verify(sig, msg, publicKey);
|
|
43
43
|
* ```
|
|
44
44
|
*/
|
|
45
|
-
export declare const falcon512: Falcon
|
|
45
|
+
export declare const falcon512: TRet<Falcon>;
|
|
46
46
|
/**
|
|
47
47
|
* Falcon-512 padded detached-signature API with the attached helper exposed as `.attached`.
|
|
48
48
|
* @example
|
|
@@ -54,7 +54,7 @@ export declare const falcon512: Falcon;
|
|
|
54
54
|
* falcon512padded.verify(sig, msg, publicKey);
|
|
55
55
|
* ```
|
|
56
56
|
*/
|
|
57
|
-
export declare const falcon512padded: Falcon
|
|
57
|
+
export declare const falcon512padded: TRet<Falcon>;
|
|
58
58
|
/**
|
|
59
59
|
* Falcon-1024 detached-signature API with the attached helper exposed as `.attached`.
|
|
60
60
|
* @example
|
|
@@ -66,7 +66,7 @@ export declare const falcon512padded: Falcon;
|
|
|
66
66
|
* falcon1024.verify(sig, msg, publicKey);
|
|
67
67
|
* ```
|
|
68
68
|
*/
|
|
69
|
-
export declare const falcon1024: Falcon
|
|
69
|
+
export declare const falcon1024: TRet<Falcon>;
|
|
70
70
|
/**
|
|
71
71
|
* Falcon-1024 padded detached-signature API with the attached helper exposed as `.attached`.
|
|
72
72
|
* @example
|
|
@@ -78,7 +78,6 @@ export declare const falcon1024: Falcon;
|
|
|
78
78
|
* falcon1024padded.verify(sig, msg, publicKey);
|
|
79
79
|
* ```
|
|
80
80
|
*/
|
|
81
|
-
export declare const falcon1024padded: Falcon
|
|
81
|
+
export declare const falcon1024padded: TRet<Falcon>;
|
|
82
82
|
export declare const __tests: any;
|
|
83
83
|
export {};
|
|
84
|
-
//# sourceMappingURL=falcon.d.ts.map
|