@noble/post-quantum 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
package/src/ml-dsa.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /*! noble-post-quantum - MIT License (c) 2024 Paul Miller (paulmillr.com) */
2
2
  import { shake256 } from '@noble/hashes/sha3';
3
- import { genCrystals, XOF, XOF128, XOF256, XOF_AES } from './_crystals.js';
3
+ import { genCrystals, XOF, XOF128, XOF256 } from './_crystals.js';
4
4
  import {
5
5
  BytesCoderLen,
6
6
  Signer,
@@ -18,11 +18,6 @@ Lattice-based digital signature algorithm. See
18
18
  [repo](https://github.com/pq-crystals/dilithium).
19
19
  Dilithium has similar internals to Kyber, but their keys and params are different.
20
20
 
21
- Three versions are provided:
22
-
23
- 1. Dilithium v3.0, v3.0 AES
24
- 2. Dilithium v3.1, v3.1 AES
25
- 3. ML-DSA aka [FIPS-204](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.ipd.pdf)
26
21
  */
27
22
 
28
23
  // Constants
@@ -135,13 +130,11 @@ type DilithiumOpts = {
135
130
  TR_BYTES: number;
136
131
  XOF128: XOF;
137
132
  XOF256: XOF;
138
- FIPS204?: boolean;
139
- V31?: boolean;
140
133
  };
141
134
 
142
135
  function getDilithium(opts: DilithiumOpts): Signer {
143
136
  const { K, L, GAMMA1, GAMMA2, TAU, ETA, OMEGA } = opts;
144
- const { FIPS204, V31, CRH_BYTES, TR_BYTES, C_TILDE_BYTES, XOF128, XOF256 } = opts;
137
+ const { CRH_BYTES, TR_BYTES, C_TILDE_BYTES, XOF128, XOF256 } = opts;
145
138
 
146
139
  if (![2, 4].includes(ETA)) throw new Error('Wrong ETA');
147
140
  if (![1 << 17, 1 << 19].includes(GAMMA1)) throw new Error('Wrong GAMMA1');
@@ -171,6 +164,8 @@ function getDilithium(opts: DilithiumOpts): Signer {
171
164
  // But they return different results! However, decompose is same.
172
165
  // So, either there is a bug in Dilithium ref implementation or in FIPS204.
173
166
  // For now, lets use dilithium one, so test vectors can be passed.
167
+ // See
168
+ // https://github.com/GiacomoPope/dilithium-py?tab=readme-ov-file#optimising-decomposition-and-making-hints
174
169
  return res0;
175
170
  };
176
171
 
@@ -262,7 +257,7 @@ function getDilithium(opts: DilithiumOpts): Signer {
262
257
  const SampleInBall = (seed: Uint8Array) => {
263
258
  // Samples a polynomial c ∈ Rq with coeffcients from {−1, 0, 1} and Hamming weight τ
264
259
  const pre = newPoly(N);
265
- const s = shake256.create({}).update(seed.slice(0, 32));
260
+ const s = shake256.create({}).update(seed);
266
261
  const buf = new Uint8Array(shake256.blockLen);
267
262
  s.xofInto(buf);
268
263
  const masks = buf.slice(0, 8);
@@ -309,15 +304,21 @@ function getDilithium(opts: DilithiumOpts): Signer {
309
304
  return { v, cnt };
310
305
  };
311
306
 
312
- const signRandBytes = FIPS204 ? 32 : CRH_BYTES;
313
- const seedCoder = splitCoder(32, V31 ? 64 : 32, 32);
314
- const seedXOF = V31 ? XOF256 : XOF128;
307
+ const signRandBytes = 32;
308
+ const seedCoder = splitCoder(32, 64, 32);
315
309
  // API & argument positions are exactly as in FIPS204.
316
310
  return {
317
311
  signRandBytes,
318
312
  keygen: (seed = randomBytes(32)) => {
319
- const [rho, rhoPrime, K_] = seedCoder.decode(shake256(seed, { dkLen: seedCoder.bytesLen }));
320
- const xofPrime = seedXOF(rhoPrime);
313
+ // H(𝜉||IntegerToBytes(𝑘, 1)||IntegerToBytes(ℓ, 1), 128) 2: expand seed
314
+ const seedDst = new Uint8Array(32 + 2);
315
+ seedDst.set(seed);
316
+ seedDst[32] = K;
317
+ seedDst[33] = L;
318
+ const [rho, rhoPrime, K_] = seedCoder.decode(
319
+ shake256(seedDst, { dkLen: seedCoder.bytesLen })
320
+ );
321
+ const xofPrime = XOF256(rhoPrime);
321
322
  const s1 = [];
322
323
  for (let i = 0; i < L; i++) s1.push(RejBoundedPoly(xofPrime.get(i & 0xff, (i >> 8) & 0xff)));
323
324
  const s2 = [];
@@ -348,7 +349,7 @@ function getDilithium(opts: DilithiumOpts): Signer {
348
349
  // STATS
349
350
  // Kyber512: { calls: 4, xofs: 12 }, Kyber768: { calls: 9, xofs: 27 }, Kyber1024: { calls: 16, xofs: 48 }
350
351
  // DSA44: { calls: 24, xofs: 24 }, DSA65: { calls: 41, xofs: 41 }, DSA87: { calls: 71, xofs: 71 }
351
- cleanBytes(rho, rhoPrime, K_, s1, s2, s1Hat, t, t0, t1, tr);
352
+ cleanBytes(rho, rhoPrime, K_, s1, s2, s1Hat, t, t0, t1, tr, seedDst);
352
353
  return { publicKey, secretKey };
353
354
  },
354
355
  // NOTE: random is optional.
@@ -372,16 +373,17 @@ function getDilithium(opts: DilithiumOpts): Signer {
372
373
  }
373
374
  // This part is per msg
374
375
  const mu = shake256.create({ dkLen: CRH_BYTES }).update(tr).update(msg).digest(); // 6: µ ← H(tr||M, 512) ▷ Compute message representative µ
375
- let rhoprime; // Compute private random seed
376
- if (FIPS204) {
377
- const rnd = random ? random : new Uint8Array(32);
378
- ensureBytes(rnd);
379
- rhoprime = shake256.create({ dkLen: CRH_BYTES }).update(_K).update(rnd).update(mu).digest(); // ρ′← H(K||rnd||µ, 512)
380
- } else {
381
- rhoprime = random
382
- ? random
383
- : shake256.create({ dkLen: CRH_BYTES }).update(_K).update(mu).digest();
384
- }
376
+
377
+ // Compute private random seed
378
+ const rnd = random ? random : new Uint8Array(32);
379
+ ensureBytes(rnd);
380
+ const rhoprime = shake256
381
+ .create({ dkLen: CRH_BYTES })
382
+ .update(_K)
383
+ .update(rnd)
384
+ .update(mu)
385
+ .digest(); // ρ′← H(K||rnd||µ, 512)
386
+
385
387
  ensureBytes(rhoprime, CRH_BYTES);
386
388
  const x256 = XOF256(rhoprime, ZCoder.bytesLen);
387
389
  // Rejection sampling loop
@@ -407,7 +409,7 @@ function getDilithium(opts: DilithiumOpts): Signer {
407
409
  .update(W1Vec.encode(w1))
408
410
  .digest();
409
411
  // Verifer’s challenge
410
- const cHat = NTT.encode(SampleInBall(cTilde.subarray(0, 32))); // c ← SampleInBall(c˜1); cˆ ← NTT(c)
412
+ const cHat = NTT.encode(SampleInBall(cTilde)); // c ← SampleInBall(c˜1); cˆ ← NTT(c)
411
413
  // ⟨⟨cs1⟩⟩ ← NTT−1(cˆ◦ sˆ1)
412
414
  const cs1 = s1.map((i) => MultiplyNTTs(i, cHat));
413
415
  for (let i = 0; i < L; i++) {
@@ -450,7 +452,7 @@ function getDilithium(opts: DilithiumOpts): Signer {
450
452
  for (let i = 0; i < L; i++) if (polyChknorm(z[i], GAMMA1 - BETA)) return false;
451
453
  const mu = shake256.create({ dkLen: CRH_BYTES }).update(tr).update(msg).digest(); // 7: µ ← H(tr||M, 512)
452
454
  // Compute verifer’s challenge from c˜
453
- const c = NTT.encode(SampleInBall(cTilde.subarray(0, 32))); // c ← SampleInBall(c˜1)
455
+ const c = NTT.encode(SampleInBall(cTilde)); // c ← SampleInBall(c˜1)
454
456
  const zNtt = z.map((i) => i.slice()); // zNtt = NTT(z)
455
457
  for (let i = 0; i < L; i++) NTT.encode(zNtt[i]);
456
458
  const wTick1 = [];
@@ -474,66 +476,18 @@ function getDilithium(opts: DilithiumOpts): Signer {
474
476
  .update(mu)
475
477
  .update(W1Vec.encode(wTick1))
476
478
  .digest();
477
- if (FIPS204) {
478
- // Additional checks in FIPS-204:
479
- // [[ ||z||∞ < γ1 − β ]] and [[c ˜ = c˜′]] and [[number of 1’s in h is ≤ ω]]
480
- for (const t of h) {
481
- const sum = t.reduce((acc, i) => acc + i, 0);
482
- if (!(sum <= OMEGA)) return false;
483
- }
484
- for (const t of z) if (polyChknorm(t, GAMMA1 - BETA)) return false;
479
+ // Additional checks in FIPS-204:
480
+ // [[ ||z||∞ < γ1 − β ]] and [[c ˜ = c˜′]] and [[number of 1’s in h is ≤ ω]]
481
+ for (const t of h) {
482
+ const sum = t.reduce((acc, i) => acc + i, 0);
483
+ if (!(sum <= OMEGA)) return false;
485
484
  }
485
+ for (const t of z) if (polyChknorm(t, GAMMA1 - BETA)) return false;
486
486
  return equalBytes(cTilde, c2);
487
487
  },
488
488
  };
489
489
  }
490
490
 
491
- function getDilithiumVersions(cfg: Partial<DilithiumOpts>) {
492
- return {
493
- dilithium2: getDilithium({ ...PARAMS[2], ...cfg } as DilithiumOpts),
494
- dilithium3: getDilithium({ ...PARAMS[3], ...cfg } as DilithiumOpts),
495
- dilithium5: getDilithium({ ...PARAMS[5], ...cfg } as DilithiumOpts),
496
- };
497
- }
498
-
499
- // v30 is NIST round 3 submission, for original vectors and benchmarking.
500
- // v31 is kyber: more secure than v30.
501
- // ml-dsa is NIST FIPS 204, but it is still a draft and may change.
502
-
503
- export const dilithium_v30 = /* @__PURE__ */ getDilithiumVersions({
504
- CRH_BYTES: 48,
505
- TR_BYTES: 48,
506
- C_TILDE_BYTES: 32,
507
- XOF128,
508
- XOF256,
509
- });
510
-
511
- export const dilithium_v31 = /* @__PURE__ */ getDilithiumVersions({
512
- CRH_BYTES: 64,
513
- TR_BYTES: 32,
514
- C_TILDE_BYTES: 32,
515
- XOF128,
516
- XOF256,
517
- V31: true,
518
- });
519
-
520
- export const dilithium_v30_aes = /* @__PURE__ */ getDilithiumVersions({
521
- CRH_BYTES: 48,
522
- TR_BYTES: 48,
523
- C_TILDE_BYTES: 32,
524
- XOF128: XOF_AES,
525
- XOF256: XOF_AES,
526
- });
527
-
528
- export const dilithium_v31_aes = /* @__PURE__ */ getDilithiumVersions({
529
- CRH_BYTES: 64,
530
- TR_BYTES: 32,
531
- C_TILDE_BYTES: 32,
532
- XOF128: XOF_AES,
533
- XOF256: XOF_AES,
534
- V31: true,
535
- });
536
-
537
491
  // ML-DSA
538
492
  export const ml_dsa44 = /* @__PURE__ */ getDilithium({
539
493
  ...PARAMS[2],
@@ -542,8 +496,6 @@ export const ml_dsa44 = /* @__PURE__ */ getDilithium({
542
496
  C_TILDE_BYTES: 32,
543
497
  XOF128,
544
498
  XOF256,
545
- V31: true,
546
- FIPS204: true,
547
499
  });
548
500
 
549
501
  export const ml_dsa65 = /* @__PURE__ */ getDilithium({
@@ -553,8 +505,6 @@ export const ml_dsa65 = /* @__PURE__ */ getDilithium({
553
505
  C_TILDE_BYTES: 48,
554
506
  XOF128,
555
507
  XOF256,
556
- V31: true,
557
- FIPS204: true,
558
508
  });
559
509
 
560
510
  export const ml_dsa87 = /* @__PURE__ */ getDilithium({
@@ -564,6 +514,4 @@ export const ml_dsa87 = /* @__PURE__ */ getDilithium({
564
514
  C_TILDE_BYTES: 64,
565
515
  XOF128,
566
516
  XOF256,
567
- V31: true,
568
- FIPS204: true,
569
517
  });
package/src/ml-kem.ts CHANGED
@@ -1,9 +1,7 @@
1
1
  /*! noble-post-quantum - MIT License (c) 2024 Paul Miller (paulmillr.com) */
2
- import { ctr } from '@noble/ciphers/aes';
3
- import { sha256, sha512 } from '@noble/hashes/sha2';
4
2
  import { sha3_256, sha3_512, shake256 } from '@noble/hashes/sha3';
5
3
  import { u32, wrapConstructor, wrapConstructorWithOpts } from '@noble/hashes/utils';
6
- import { genCrystals, XOF, XOF_AES, XOF128 } from './_crystals.js';
4
+ import { genCrystals, XOF, XOF128 } from './_crystals.js';
7
5
  import {
8
6
  Coder,
9
7
  cleanBytes,
@@ -34,16 +32,11 @@ There are some concerns with regards to security: see
34
32
  [djb blog](https://blog.cr.yp.to/20231003-countcorrectly.html) and
35
33
  [mailing list](https://groups.google.com/a/list.nist.gov/g/pqc-forum/c/W2VOzy0wz_E).
36
34
 
37
- Three versions are provided:
38
-
39
- 1. Kyber
40
- 2. Kyber-90s, using algorithms from 1990s
41
- 3. ML-KEM aka [FIPS-203](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.ipd.pdf)
42
35
  */
43
36
 
44
37
  const N = 256; // Kyber (not FIPS-203) supports different lengths, but all std modes were using 256
45
38
  const Q = 3329; // 13*(2**8)+1, modulo prime
46
- const F = 3303; // 3303 ≡ 128−1 mod q (FIPS-203)
39
+ const F = 3303; // 3303 ≡ 128**(−1) mod q (FIPS-203)
47
40
  const ROOT_OF_UNITY = 17; // ζ = 17 ∈ Zq is a primitive 256-th root of unity modulo Q. ζ**128 ≡−1
48
41
  const { mod, nttZetas, NTT, bitsCoder } = genCrystals({
49
42
  N,
@@ -136,7 +129,6 @@ type KyberOpts = ParameterSet & {
136
129
  KDF: Hash | HashWOpts;
137
130
  XOF: XOF; // (seed: Uint8Array, len: number, x: number, y: number) => Uint8Array;
138
131
  PRF: PRF;
139
- FIPS203?: boolean;
140
132
  };
141
133
 
142
134
  // Return poly in NTT representation
@@ -185,7 +177,7 @@ function sampleCBD(PRF: PRF, seed: Uint8Array, nonce: number, eta: number): Poly
185
177
  // K-PKE
186
178
  // As per FIPS-203, it doesn't perform any input validation and can't be used in standalone fashion.
187
179
  const genKPKE = (opts: KyberOpts) => {
188
- const { K, PRF, XOF, HASH512, ETA1, ETA2, du, dv, FIPS203 } = opts;
180
+ const { K, PRF, XOF, HASH512, ETA1, ETA2, du, dv } = opts;
189
181
  const poly1 = polyCoder(1);
190
182
  const polyV = polyCoder(dv);
191
183
  const polyU = polyCoder(du);
@@ -199,7 +191,12 @@ const genKPKE = (opts: KyberOpts) => {
199
191
  publicKeyLen: publicCoder.bytesLen,
200
192
  cipherTextLen: cipherCoder.bytesLen,
201
193
  keygen: (seed: Uint8Array) => {
202
- const [rho, sigma] = seedCoder.decode(HASH512(seed));
194
+ const seedDst = new Uint8Array(33);
195
+ seedDst.set(seed);
196
+ seedDst[32] = K;
197
+ const seedHash = HASH512(seedDst);
198
+
199
+ const [rho, sigma] = seedCoder.decode(seedHash);
203
200
  const sHat: Poly[] = [];
204
201
  const tHat: Poly[] = [];
205
202
  for (let i = 0; i < K; i++) sHat.push(NTT.encode(sampleCBD(PRF, sigma, i, ETA1)));
@@ -207,7 +204,7 @@ const genKPKE = (opts: KyberOpts) => {
207
204
  for (let i = 0; i < K; i++) {
208
205
  const e = NTT.encode(sampleCBD(PRF, sigma, K + i, ETA1));
209
206
  for (let j = 0; j < K; j++) {
210
- const aji = SampleNTT(FIPS203 ? x.get(i, j) : x.get(j, i)); // A[j][i], inplace
207
+ const aji = SampleNTT(x.get(j, i)); // A[j][i], inplace
211
208
  polyAdd(e, MultiplyNTTs(aji, sHat[j]));
212
209
  }
213
210
  tHat.push(e); // t ← A ◦ s + e
@@ -217,7 +214,7 @@ const genKPKE = (opts: KyberOpts) => {
217
214
  publicKey: publicCoder.encode([tHat, rho]),
218
215
  secretKey: secretCoder.encode(sHat),
219
216
  };
220
- cleanBytes(rho, sigma, sHat, tHat);
217
+ cleanBytes(rho, sigma, sHat, tHat, seedDst, seedHash);
221
218
  return res;
222
219
  },
223
220
  encrypt: (publicKey: Uint8Array, msg: Uint8Array, seed: Uint8Array) => {
@@ -231,7 +228,7 @@ const genKPKE = (opts: KyberOpts) => {
231
228
  const e1 = sampleCBD(PRF, seed, K + i, ETA2);
232
229
  const tmp = new Uint16Array(N);
233
230
  for (let j = 0; j < K; j++) {
234
- const aij = SampleNTT(FIPS203 ? x.get(j, i) : x.get(i, j)); // A[i][j], inplace
231
+ const aij = SampleNTT(x.get(i, j)); // A[i][j], inplace
235
232
  polyAdd(tmp, MultiplyNTTs(aij, rHat[j])); // t += aij * rHat[j]
236
233
  }
237
234
  polyAdd(e1, NTT.decode(tmp)); // e1 += tmp
@@ -261,7 +258,7 @@ const genKPKE = (opts: KyberOpts) => {
261
258
 
262
259
  function createKyber(opts: KyberOpts) {
263
260
  const KPKE = genKPKE(opts);
264
- const { HASH256, HASH512, KDF, FIPS203 } = opts;
261
+ const { HASH256, HASH512, KDF } = opts;
265
262
  const { secretCoder: KPKESecretCoder, cipherTextLen } = KPKE;
266
263
  const publicKeyLen = KPKE.publicKeyLen; // 384*K+32
267
264
  const secretCoder = splitCoder(KPKE.secretKeyLen, KPKE.publicKeyLen, 32, 32);
@@ -282,29 +279,21 @@ function createKyber(opts: KyberOpts) {
282
279
  encapsulate: (publicKey: Uint8Array, msg = randomBytes(32)) => {
283
280
  ensureBytes(publicKey, publicKeyLen);
284
281
  ensureBytes(msg, msgLen);
285
- if (!FIPS203) msg = HASH256(msg); // NOTE: ML-KEM doesn't have this step!
286
- else {
287
- // FIPS-203 includes additional verification check for modulus
288
- const eke = publicKey.subarray(0, 384 * opts.K);
289
- const ek = KPKESecretCoder.encode(KPKESecretCoder.decode(eke.slice())); // Copy because of inplace encoding
290
- // (Modulus check.) Perform the computation ek ByteEncode12(ByteDecode12(eke)).
291
- // If ek = ̸ eke, the input is invalid. (See Section 4.2.1.)
292
- if (!equalBytes(ek, eke)) {
293
- cleanBytes(ek);
294
- throw new Error('ML-KEM.encapsulate: wrong publicKey modulus');
295
- }
282
+
283
+ // FIPS-203 includes additional verification check for modulus
284
+ const eke = publicKey.subarray(0, 384 * opts.K);
285
+ const ek = KPKESecretCoder.encode(KPKESecretCoder.decode(eke.slice())); // Copy because of inplace encoding
286
+ // (Modulus check.) Perform the computation ek ByteEncode12(ByteDecode12(eke)).
287
+ // If ek = ̸ eke, the input is invalid. (See Section 4.2.1.)
288
+ if (!equalBytes(ek, eke)) {
296
289
  cleanBytes(ek);
290
+ throw new Error('ML-KEM.encapsulate: wrong publicKey modulus');
297
291
  }
292
+ cleanBytes(ek);
298
293
  const kr = HASH512.create().update(msg).update(HASH256(publicKey)).digest(); // derive randomness
299
294
  const cipherText = KPKE.encrypt(publicKey, msg, kr.subarray(32, 64));
300
- if (FIPS203) return { cipherText, sharedSecret: kr.subarray(0, 32) };
301
- const cipherTextHash = HASH256(cipherText);
302
- const sharedSecret = KDF.create({})
303
- .update(kr.subarray(0, 32))
304
- .update(cipherTextHash)
305
- .digest();
306
- cleanBytes(kr, cipherTextHash);
307
- return { cipherText, sharedSecret };
295
+ kr.subarray(32).fill(0);
296
+ return { cipherText, sharedSecret: kr.subarray(0, 32) };
308
297
  },
309
298
  decapsulate: (cipherText: Uint8Array, secretKey: Uint8Array) => {
310
299
  ensureBytes(secretKey, secretKeyLen); // 768*k + 96
@@ -315,43 +304,13 @@ function createKyber(opts: KyberOpts) {
315
304
  const Khat = kr.subarray(0, 32);
316
305
  const cipherText2 = KPKE.encrypt(publicKey, msg, kr.subarray(32, 64)); // re-encrypt using the derived randomness
317
306
  const isValid = equalBytes(cipherText, cipherText2); // if ciphertexts do not match, “implicitly reject”
318
- if (FIPS203) {
319
- const Kbar = KDF.create({ dkLen: 32 }).update(z).update(cipherText).digest();
320
- cleanBytes(msg, cipherText2, !isValid ? Khat : Kbar);
321
- return isValid ? Khat : Kbar;
322
- }
323
- const cipherTextHash = HASH256(cipherText);
324
- const sharedSecret = KDF.create({ dkLen: 32 })
325
- .update(isValid ? Khat : z)
326
- .update(cipherTextHash)
327
- .digest();
328
- cleanBytes(msg, cipherTextHash, cipherText2, Khat, z);
329
- return sharedSecret;
307
+ const Kbar = KDF.create({ dkLen: 32 }).update(z).update(cipherText).digest();
308
+ cleanBytes(msg, cipherText2, !isValid ? Khat : Kbar);
309
+ return isValid ? Khat : Kbar;
330
310
  },
331
311
  };
332
312
  }
333
313
 
334
- function PRF(l: number, key: Uint8Array, nonce: number) {
335
- const _nonce = new Uint8Array(16);
336
- _nonce[0] = nonce;
337
- return ctr(key, _nonce).encrypt(new Uint8Array(l));
338
- }
339
-
340
- const opts90s = { HASH256: sha256, HASH512: sha512, KDF: sha256, XOF: XOF_AES, PRF };
341
-
342
- export const kyber512_90s = /* @__PURE__ */ createKyber({
343
- ...opts90s,
344
- ...PARAMS[512],
345
- });
346
- export const kyber768_90s = /* @__PURE__ */ createKyber({
347
- ...opts90s,
348
- ...PARAMS[768],
349
- });
350
- export const kyber1024_90s = /* @__PURE__ */ createKyber({
351
- ...opts90s,
352
- ...PARAMS[1024],
353
- });
354
-
355
314
  function shakePRF(dkLen: number, key: Uint8Array, nonce: number) {
356
315
  return shake256
357
316
  .create({ dkLen })
@@ -368,36 +327,18 @@ const opts = {
368
327
  PRF: shakePRF,
369
328
  };
370
329
 
371
- export const kyber512 = /* @__PURE__ */ createKyber({
372
- ...opts,
373
- ...PARAMS[512],
374
- });
375
- export const kyber768 = /* @__PURE__ */ createKyber({
376
- ...opts,
377
- ...PARAMS[768],
378
- });
379
- export const kyber1024 = /* @__PURE__ */ createKyber({
380
- ...opts,
381
- ...PARAMS[1024],
382
- });
383
-
384
330
  /**
385
- * FIPS-203 (draft) ML-KEM.
386
- * Unsafe: we can't cross-verify, because there are no test vectors or other implementations.
331
+ * FIPS-203 ML-KEM.
387
332
  */
388
-
389
333
  export const ml_kem512 = /* @__PURE__ */ createKyber({
390
334
  ...opts,
391
335
  ...PARAMS[512],
392
- FIPS203: true,
393
336
  });
394
337
  export const ml_kem768 = /* @__PURE__ */ createKyber({
395
338
  ...opts,
396
339
  ...PARAMS[768],
397
- FIPS203: true,
398
340
  });
399
341
  export const ml_kem1024 = /* @__PURE__ */ createKyber({
400
342
  ...opts,
401
343
  ...PARAMS[1024],
402
- FIPS203: true,
403
344
  });