@noble/post-quantum 0.1.0 → 0.2.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 +154 -179
- package/_crystals.d.ts +0 -1
- package/_crystals.d.ts.map +1 -1
- package/_crystals.js +1 -31
- package/_crystals.js.map +1 -1
- package/esm/_crystals.d.ts +33 -0
- package/esm/_crystals.d.ts.map +1 -0
- package/esm/_crystals.js +0 -30
- package/esm/_crystals.js.map +1 -1
- package/esm/index.d.ts +2 -0
- package/esm/index.d.ts.map +1 -0
- package/esm/ml-dsa.d.ts +17 -0
- package/esm/ml-dsa.d.ts.map +1 -0
- package/esm/ml-dsa.js +35 -84
- package/esm/ml-dsa.js.map +1 -1
- package/esm/ml-kem.d.ts +55 -0
- package/esm/ml-kem.d.ts.map +1 -0
- package/esm/ml-kem.js +26 -83
- package/esm/ml-kem.js.map +1 -1
- package/esm/slh-dsa.d.ts +46 -0
- package/esm/slh-dsa.d.ts.map +1 -0
- package/esm/slh-dsa.js +26 -109
- package/esm/slh-dsa.js.map +1 -1
- package/esm/utils.d.ts +38 -0
- package/esm/utils.d.ts.map +1 -0
- package/ml-dsa.d.ts +0 -20
- package/ml-dsa.d.ts.map +1 -1
- package/ml-dsa.js +35 -84
- package/ml-dsa.js.map +1 -1
- package/ml-kem.d.ts +1 -80
- package/ml-kem.d.ts.map +1 -1
- package/ml-kem.js +26 -83
- package/ml-kem.js.map +1 -1
- package/package.json +13 -21
- package/slh-dsa.d.ts +0 -24
- package/slh-dsa.d.ts.map +1 -1
- package/slh-dsa.js +26 -109
- package/slh-dsa.js.map +1 -1
- package/src/_crystals.ts +0 -33
- package/src/ml-dsa.ts +36 -88
- package/src/ml-kem.ts +28 -87
- package/src/slh-dsa.ts +26 -119
- package/utils.js +6 -6
- package/utils.js.map +1 -1
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
|
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 {
|
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
|
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 =
|
313
|
-
const seedCoder = splitCoder(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
|
-
|
320
|
-
const
|
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
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
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
|
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
|
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
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
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,
|
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
|
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
|
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(
|
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(
|
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
|
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
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
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
|
-
|
301
|
-
|
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
|
-
|
319
|
-
|
320
|
-
|
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
|
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
|
});
|