@noble/post-quantum 0.6.1 → 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 CHANGED
@@ -3,22 +3,20 @@
3
3
  Auditable & minimal JS implementation of post-quantum public-key cryptography.
4
4
 
5
5
  - 🔒 Auditable
6
- - ðŸ”ŧ Tree-shakeable: unused code is excluded from your builds
7
- - 🔍 Reliable: tests ensure correctness
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, combining classic & post-quantum: Concrete, XWing, KitchenSink
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
- > Australian ASD does same thing [after 2030](https://www.cyber.gov.au/resources-business-and-government/essential-cyber-security/ism/cyber-security-guidelines/guidelines-cryptography).
21
- > Take it into an account while designing a new cryptographic system.
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, XWing,
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
- notDeepStrictEqual(aliceShared, malloryShared); // Different key!
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, XWing,
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
- Hybrid submodule combine post-quantum algorithms with elliptic curve cryptography:
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 | Created in | Popularized in | Post-quantum? |
229
- | ------- | ------ | ----------- | ----------- | ---------- | -------------- | ------------- |
230
- | RSA | Normal | 256B - 2KB | 256B - 2KB | 1970s | 1990s | No |
231
- | ECC | Normal | 32 - 256B | 48 - 128B | 1980s | 2010s | No |
232
- | ML-KEM | Fast | 1.6 - 31KB | 1KB | 1990s | 2020s | Yes |
233
- | ML-DSA | Normal | 1.3 - 2.5KB | 2.5 - 4.5KB | 1990s | 2020s | Yes |
234
- | SLH-DSA | Slow | 32 - 128B | 17 - 50KB | 1970s | 2020s | Yes |
235
- | FN-DSA | Slow | 0.9 - 1.8KB | 0.6 - 1.2KB | 1990s | 2020s | Yes |
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
- We suggest to use ECC + ML-KEM for key agreement, ECC + SLH-DSA for signatures.
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 to use 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).
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.
@@ -274,10 +296,11 @@ Keep in mind that even hardware versions ML-KEM [are vulnerable](https://eprint.
274
296
  - Version ranges are locked, and changes are checked with npm-diff.
275
297
  - **Dev dependencies** are excluded from end-user installs; they're only used for development and build steps.
276
298
 
277
- For this package, there are 2 dependencies; and a few dev dependencies:
299
+ For this package, there are 3 dependencies; and a few dev dependencies:
278
300
 
279
301
  - [noble-hashes](https://github.com/paulmillr/noble-hashes) provides cryptographic hashing functionality, used internally in every algorithm
280
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
281
304
  - jsbt is used for benchmarking / testing / build tooling and developed by the same author
282
305
  - prettier, fast-check and typescript are used for code quality / test generation / ts compilation
283
306
 
@@ -289,73 +312,63 @@ which is considered a cryptographically secure PRNG.
289
312
 
290
313
  Browsers have had weaknesses in the past - and could again - but implementing a userspace CSPRNG is even worse, as there’s no reliable userspace source of high-quality entropy.
291
314
 
292
- ## Contributing & testing
293
-
294
- - `npm install && npm run build && npm test` will build the code and run tests.
295
- - `npm run lint` / `npm run format` will run linter / fix linter issues.
296
- - `npm run bench` will run benchmarks
297
- - `npm run build:release` will build single file
298
-
299
- Check out [github.com/paulmillr/guidelines](https://github.com/paulmillr/guidelines)
300
- for general coding practices and rules.
301
-
302
- See [paulmillr.com/noble](https://paulmillr.com/noble/)
303
- for useful resources, articles, documentation and demos
304
- related to the library.
305
-
306
315
  ## Speed
307
316
 
308
- > `npm run bench`
317
+ > `npm run benchmark`
309
318
 
310
319
  Noble is the fastest JS implementation of post-quantum algorithms.
311
320
 
312
- Benchmarks on Apple M4 (**higher is better**):
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.
313
325
 
314
- ```
315
- # ML-KEM768
316
- keygen x 4,277 ops/sec @ 233Ξs/op
317
- encapsulate x 3,470 ops/sec @ 288Ξs/op
318
- decapsulate x 3,757 ops/sec @ 266Ξs/op
319
- # ML-DSA65
320
- keygen x 669 ops/sec @ 1ms/op
321
- sign x 271 ops/sec @ 3ms/op
322
- verify x 565 ops/sec @ 1ms/op
323
- # SLH-DSA SHA2 192f
324
- keygen x 235 ops/sec @ 4ms/op
325
- sign x 8 ops/sec @ 117ms/op
326
- verify x 159 ops/sec @ 6ms/op
327
- # Falcon512
328
- keygen x 14 ops/sec @ 66ms/op Âą 11.01% (56ms..96ms)
329
- sign x 749 ops/sec @ 1ms/op
330
- verify x 2,160 ops/sec @ 462Ξs/op
331
- # Falcon1024
332
- keygen x 4 ops/sec @ 247ms/op Âą 5.22% (234ms..266ms)
333
- sign x 343 ops/sec @ 2ms/op
334
- verify x 950 ops/sec @ 1ms/op
335
- ```
326
+ Benchmarks on Apple M4 (operations/sec, **higher is better**):
336
327
 
337
- Compared with pre-quantum:
338
-
339
- | OPs/sec | Keygen | Signing | Verification | Shared secret |
328
+ | Primitive | Keygen | Signing | Verification | Shared secret |
340
329
  | ----------------- | ------ | ------- | ------------ | ------------- |
341
- | ECC x/ed25519 | 12648 | 6157 | 1255 | 1981 |
342
- | ML-KEM-768 | 4277 | | | 3757 |
343
- | ML-DSA65 | 669 | 271 | 565 | |
344
- | SLH-DSA-SHA2-192f | 235 | 8 | 159 | |
345
- | Falcon512 | 14 | 749 | 950 | |
346
-
347
- SLH-DSA:
348
-
349
- | | sig size | keygen | sign | verify |
350
- | --------- | -------- | ------ | ------ | ------ |
351
- | sha2_128f | 18088 | 4ms | 90ms | 6ms |
352
- | sha2_192f | 35664 | 6ms | 160ms | 9ms |
353
- | sha2_256f | 49856 | 15ms | 340ms | 9ms |
354
- | sha2_128s | 7856 | 260ms | 2000ms | 2ms |
355
- | sha2_192s | 16224 | 380ms | 3800ms | 3ms |
356
- | sha2_256s | 29792 | 250ms | 3400ms | 4ms |
357
- | shake_192f | 35664 | 21ms | 553ms | 29ms |
358
- | shake_192s | 16224 | 260ms | 2635ms | 2ms |
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 |
371
+
359
372
 
360
373
  ## License
361
374
 
package/_crystals.d.ts CHANGED
@@ -55,9 +55,15 @@ type Crystals<T extends TypedArray> = {
55
55
  smod: (a: number, modulo?: number) => number;
56
56
  nttZetas: T;
57
57
  NTT: {
58
- /** Forward transform in place. Mutates and returns `r`. */
58
+ /**
59
+ * Forward transform in place. Mutates and returns `r`.
60
+ * Kyber-mode input coefficients must already use canonical representatives in `[0, Q)`.
61
+ */
59
62
  encode: (r: T) => T;
60
- /** Inverse transform in place. Mutates and returns `r`. */
63
+ /**
64
+ * Inverse transform in place. Mutates and returns `r`.
65
+ * Kyber-mode input coefficients must already use canonical representatives in `[0, Q)`.
66
+ */
61
67
  decode: (r: T) => T;
62
68
  };
63
69
  bitsCoder: (d: number, c: Coder<number, number>) => BytesCoderLen<T>;
@@ -117,4 +123,3 @@ export declare const XOF128: TRet<XOF>;
117
123
  */
118
124
  export declare const XOF256: TRet<XOF>;
119
125
  export {};
120
- //# sourceMappingURL=_crystals.d.ts.map
package/_crystals.js CHANGED
@@ -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 field = {
61
- add: (a, b) => mod((a | 0) + (b | 0)) | 0,
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,6 +111,13 @@ 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 {
@@ -101,8 +128,10 @@ export const genCrystals = (opts) => {
101
128
  for (let i = 0, buf = 0, bufLen = 0, pos = 0; i < poly.length; i++) {
102
129
  buf |= (c.encode(poly[i]) & mask) << bufLen;
103
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.
104
133
  for (; bufLen >= 8; bufLen -= 8, buf >>= 8)
105
- r[pos++] = buf & getMask(bufLen);
134
+ r[pos++] = buf & 0xff;
106
135
  }
107
136
  return r;
108
137
  },
@@ -198,4 +227,3 @@ export const XOF128 = /* @__PURE__ */ createXofShake(shake128);
198
227
  * ```
199
228
  */
200
229
  export const XOF256 = /* @__PURE__ */ createXofShake(shake256);
201
- //# sourceMappingURL=_crystals.js.map
package/falcon.d.ts CHANGED
@@ -81,4 +81,3 @@ export declare const falcon1024: TRet<Falcon>;
81
81
  export declare const falcon1024padded: TRet<Falcon>;
82
82
  export declare const __tests: any;
83
83
  export {};
84
- //# sourceMappingURL=falcon.d.ts.map