@noble/post-quantum 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
package/esm/utils.js ADDED
@@ -0,0 +1,86 @@
1
+ /*! noble-post-quantum - MIT License (c) 2024 Paul Miller (paulmillr.com) */
2
+ import { bytes as abytes } from '@noble/hashes/_assert';
3
+ import { randomBytes as randb } from '@noble/hashes/utils';
4
+ export const ensureBytes = abytes;
5
+ export const randomBytes = randb;
6
+ // Compares 2 u8a-s in kinda constant time
7
+ export function equalBytes(a, b) {
8
+ if (a.length !== b.length)
9
+ return false;
10
+ let diff = 0;
11
+ for (let i = 0; i < a.length; i++)
12
+ diff |= a[i] ^ b[i];
13
+ return diff === 0;
14
+ }
15
+ export function splitCoder(...lengths) {
16
+ const getLength = (c) => (typeof c === 'number' ? c : c.bytesLen);
17
+ const bytesLen = lengths.reduce((sum, a) => sum + getLength(a), 0);
18
+ return {
19
+ bytesLen,
20
+ encode: (bufs) => {
21
+ const res = new Uint8Array(bytesLen);
22
+ for (let i = 0, pos = 0; i < lengths.length; i++) {
23
+ const c = lengths[i];
24
+ const l = getLength(c);
25
+ const b = typeof c === 'number' ? bufs[i] : c.encode(bufs[i]);
26
+ ensureBytes(b, l);
27
+ res.set(b, pos);
28
+ if (typeof c !== 'number')
29
+ b.fill(0); // clean
30
+ pos += l;
31
+ }
32
+ return res;
33
+ },
34
+ decode: (buf) => {
35
+ ensureBytes(buf, bytesLen);
36
+ const res = [];
37
+ for (const c of lengths) {
38
+ const l = getLength(c);
39
+ const b = buf.subarray(0, l);
40
+ res.push(typeof c === 'number' ? b : c.decode(b));
41
+ buf = buf.subarray(l);
42
+ }
43
+ return res;
44
+ },
45
+ };
46
+ }
47
+ // nano-packed.array (fixed size)
48
+ export function vecCoder(c, vecLen) {
49
+ const bytesLen = vecLen * c.bytesLen;
50
+ return {
51
+ bytesLen,
52
+ encode: (u) => {
53
+ if (u.length !== vecLen)
54
+ throw new Error(`vecCoder.encode: wrong length=${u.length}. Expected: ${vecLen}`);
55
+ const res = new Uint8Array(bytesLen);
56
+ for (let i = 0, pos = 0; i < u.length; i++) {
57
+ const b = c.encode(u[i]);
58
+ res.set(b, pos);
59
+ b.fill(0); // clean
60
+ pos += b.length;
61
+ }
62
+ return res;
63
+ },
64
+ decode: (a) => {
65
+ ensureBytes(a, bytesLen);
66
+ const r = [];
67
+ for (let i = 0; i < a.length; i += c.bytesLen)
68
+ r.push(c.decode(a.subarray(i, i + c.bytesLen)));
69
+ return r;
70
+ },
71
+ };
72
+ }
73
+ // cleanBytes(new Uint8Array(), [new Uint16Array(), new Uint32Array()])
74
+ export function cleanBytes(...list) {
75
+ for (const t of list) {
76
+ if (Array.isArray(t))
77
+ for (const b of t)
78
+ b.fill(0);
79
+ else
80
+ t.fill(0);
81
+ }
82
+ }
83
+ export function getMask(bits) {
84
+ return (1 << bits) - 1; // 4 -> 0b1111
85
+ }
86
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=index.d.ts.map
package/index.d.ts.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":""}
package/index.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ throw new Error('noble-post-quantum has no entry-point: consult README for usage');
3
+ //# sourceMappingURL=index.js.map
package/index.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":";AAAA,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC"}
package/ml-dsa.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { Signer } from './utils.js';
2
+ type Param = {
3
+ K: number;
4
+ L: number;
5
+ D: number;
6
+ GAMMA1: number;
7
+ GAMMA2: number;
8
+ TAU: number;
9
+ ETA: number;
10
+ OMEGA: number;
11
+ };
12
+ export declare const PARAMS: Record<string, Param>;
13
+ export declare const dilithium_v30: {
14
+ dilithium2: Signer;
15
+ dilithium3: Signer;
16
+ dilithium5: Signer;
17
+ };
18
+ export declare const dilithium_v31: {
19
+ dilithium2: Signer;
20
+ dilithium3: Signer;
21
+ dilithium5: Signer;
22
+ };
23
+ export declare const dilithium_v30_aes: {
24
+ dilithium2: Signer;
25
+ dilithium3: Signer;
26
+ dilithium5: Signer;
27
+ };
28
+ export declare const dilithium_v31_aes: {
29
+ dilithium2: Signer;
30
+ dilithium3: Signer;
31
+ dilithium5: Signer;
32
+ };
33
+ export declare const ml_dsa44: Signer;
34
+ export declare const ml_dsa65: Signer;
35
+ export declare const ml_dsa87: Signer;
36
+ export {};
37
+ //# sourceMappingURL=ml-dsa.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ml-dsa.d.ts","sourceRoot":"","sources":["src/ml-dsa.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,MAAM,EAOP,MAAM,YAAY,CAAC;AA6BpB,KAAK,KAAK,GAAG;IACX,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAI/B,CAAC;AA8bX,eAAO,MAAM,aAAa;;;;CAMxB,CAAC;AAEH,eAAO,MAAM,aAAa;;;;CAOxB,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;CAM5B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;CAO5B,CAAC;AAGH,eAAO,MAAM,QAAQ,QASnB,CAAC;AAEH,eAAO,MAAM,QAAQ,QASnB,CAAC;AAEH,eAAO,MAAM,QAAQ,QASnB,CAAC"}
package/ml-dsa.js ADDED
@@ -0,0 +1,532 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ml_dsa87 = exports.ml_dsa65 = exports.ml_dsa44 = exports.dilithium_v31_aes = exports.dilithium_v30_aes = exports.dilithium_v31 = exports.dilithium_v30 = exports.PARAMS = void 0;
4
+ /*! noble-post-quantum - MIT License (c) 2024 Paul Miller (paulmillr.com) */
5
+ const sha3_1 = require("@noble/hashes/sha3");
6
+ const _crystals_js_1 = require("./_crystals.js");
7
+ const utils_js_1 = require("./utils.js");
8
+ /*
9
+ Lattice-based digital signature algorithm. See
10
+ [official site](https://www.pq-crystals.org/dilithium/index.shtml),
11
+ [repo](https://github.com/pq-crystals/dilithium).
12
+ Dilithium has similar internals to Kyber, but their keys and params are different.
13
+
14
+ Three versions are provided:
15
+
16
+ 1. Dilithium v3.0, v3.0 AES
17
+ 2. Dilithium v3.1, v3.1 AES
18
+ 3. ML-DSA aka [FIPS-204](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.ipd.pdf)
19
+ */
20
+ // Constants
21
+ const N = 256;
22
+ // 2**23 − 2**13 + 1, 23 bits: multiply will be 46. We have enough precision in JS to avoid bigints
23
+ const Q = 8380417;
24
+ const ROOT_OF_UNITY = 1753;
25
+ // f = 256**−1 mod q, pow(256, -1, q) = 8347681 (python3)
26
+ const F = 8347681;
27
+ const D = 13;
28
+ // Dilithium is kinda parametrized over GAMMA2, but everything will break with any other value.
29
+ const GAMMA2_1 = Math.floor((Q - 1) / 88) | 0;
30
+ const GAMMA2_2 = Math.floor((Q - 1) / 32) | 0;
31
+ // prettier-ignore
32
+ exports.PARAMS = {
33
+ 2: { K: 4, L: 4, D, GAMMA1: 2 ** 17, GAMMA2: GAMMA2_1, TAU: 39, ETA: 2, OMEGA: 80 },
34
+ 3: { K: 6, L: 5, D, GAMMA1: 2 ** 19, GAMMA2: GAMMA2_2, TAU: 49, ETA: 4, OMEGA: 55 },
35
+ 5: { K: 8, L: 7, D, GAMMA1: 2 ** 19, GAMMA2: GAMMA2_2, TAU: 60, ETA: 2, OMEGA: 75 },
36
+ };
37
+ const newPoly = (n) => new Int32Array(n);
38
+ const { mod, smod, NTT, bitsCoder } = (0, _crystals_js_1.genCrystals)({
39
+ N,
40
+ Q,
41
+ F,
42
+ ROOT_OF_UNITY,
43
+ newPoly,
44
+ isKyber: false,
45
+ brvBits: 8,
46
+ });
47
+ const polyCoder = (d, compress) => bitsCoder(d, {
48
+ encode: (i) => (compress ? compress(i) : i),
49
+ decode: (i) => (compress ? compress(i) : i),
50
+ });
51
+ const polyAdd = (a, b) => {
52
+ for (let i = 0; i < a.length; i++)
53
+ a[i] = mod(a[i] + b[i]);
54
+ return a;
55
+ };
56
+ const polySub = (a, b) => {
57
+ for (let i = 0; i < a.length; i++)
58
+ a[i] = mod(a[i] - b[i]);
59
+ return a;
60
+ };
61
+ const polyShiftl = (p) => {
62
+ for (let i = 0; i < N; i++)
63
+ p[i] <<= D;
64
+ return p;
65
+ };
66
+ const polyChknorm = (p, B) => {
67
+ // Not very sure about this, but FIPS204 doesn't provide any function for that :(
68
+ for (let i = 0; i < N; i++)
69
+ if (Math.abs(smod(p[i])) >= B)
70
+ return true;
71
+ return false;
72
+ };
73
+ const MultiplyNTTs = (a, b) => {
74
+ // NOTE: we don't use montgomery reduction in code, since it requires 64 bit ints,
75
+ // which is not available in JS. mod(a[i] * b[i]) is ok, since Q is 23 bit,
76
+ // which means a[i] * b[i] is 46 bit, which is safe to use in JS. (number is 53 bits).
77
+ // Barrett reduction is slower than mod :(
78
+ const c = newPoly(N);
79
+ for (let i = 0; i < a.length; i++)
80
+ c[i] = mod(a[i] * b[i]);
81
+ return c;
82
+ };
83
+ // Return poly in NTT representation
84
+ function RejNTTPoly(xof) {
85
+ // Samples a polynomial ∈ Tq.
86
+ const r = newPoly(N);
87
+ // NOTE: we can represent 3xu24 as 4xu32, but it doesn't improve perf :(
88
+ for (let j = 0; j < N;) {
89
+ const b = xof();
90
+ if (b.length % 3)
91
+ throw new Error('RejNTTPoly: unaligned block');
92
+ for (let i = 0; j < N && i <= b.length - 3; i += 3) {
93
+ const t = (b[i + 0] | (b[i + 1] << 8) | (b[i + 2] << 16)) & 0x7fffff; // 3 bytes
94
+ if (t < Q)
95
+ r[j++] = t;
96
+ }
97
+ }
98
+ return r;
99
+ }
100
+ function getDilithium(opts) {
101
+ const { K, L, GAMMA1, GAMMA2, TAU, ETA, OMEGA } = opts;
102
+ const { FIPS204, V31, CRH_BYTES, TR_BYTES, C_TILDE_BYTES, XOF128, XOF256 } = opts;
103
+ if (![2, 4].includes(ETA))
104
+ throw new Error('Wrong ETA');
105
+ if (![1 << 17, 1 << 19].includes(GAMMA1))
106
+ throw new Error('Wrong GAMMA1');
107
+ if (![GAMMA2_1, GAMMA2_2].includes(GAMMA2))
108
+ throw new Error('Wrong GAMMA2');
109
+ const BETA = TAU * ETA;
110
+ const decompose = (r) => {
111
+ // Decomposes r into (r1, r0) such that r ≡ r1(2γ2) + r0 mod q.
112
+ const rPlus = mod(r);
113
+ const r0 = smod(rPlus, 2 * GAMMA2) | 0;
114
+ if (rPlus - r0 === Q - 1)
115
+ return { r1: 0 | 0, r0: (r0 - 1) | 0 };
116
+ const r1 = Math.floor((rPlus - r0) / (2 * GAMMA2)) | 0;
117
+ return { r1, r0 }; // r1 = HighBits, r0 = LowBits
118
+ };
119
+ const HighBits = (r) => decompose(r).r1;
120
+ const LowBits = (r) => decompose(r).r0;
121
+ const MakeHint = (z, r) => {
122
+ // Compute hint bit indicating whether adding z to r alters the high bits of r.
123
+ // From dilithium code
124
+ const res0 = z <= GAMMA2 || z > Q - GAMMA2 || (z === Q - GAMMA2 && r === 0) ? 0 : 1;
125
+ // from FIPS204:
126
+ // // const r1 = HighBits(r);
127
+ // // const v1 = HighBits(r + z);
128
+ // // const res1 = +(r1 !== v1);
129
+ // But they return different results! However, decompose is same.
130
+ // So, either there is a bug in Dilithium ref implementation or in FIPS204.
131
+ // For now, lets use dilithium one, so test vectors can be passed.
132
+ return res0;
133
+ };
134
+ const UseHint = (h, r) => {
135
+ // Returns the high bits of r adjusted according to hint h
136
+ const m = Math.floor((Q - 1) / (2 * GAMMA2));
137
+ const { r1, r0 } = decompose(r);
138
+ // 3: if h = 1 and r0 > 0 return (r1 + 1) mod m
139
+ // 4: if h = 1 and r0 ≤ 0 return (r1 − 1) mod m
140
+ if (h === 1)
141
+ return r0 > 0 ? mod(r1 + 1, m) | 0 : mod(r1 - 1, m) | 0;
142
+ return r1 | 0;
143
+ };
144
+ const Power2Round = (r) => {
145
+ // Decomposes r into (r1, r0) such that r ≡ r1*(2**d) + r0 mod q.
146
+ const rPlus = mod(r);
147
+ const r0 = smod(rPlus, 2 ** D) | 0;
148
+ return { r1: Math.floor((rPlus - r0) / 2 ** D) | 0, r0 };
149
+ };
150
+ const hintCoder = {
151
+ bytesLen: OMEGA + K,
152
+ encode: (h) => {
153
+ if (h === false)
154
+ throw new Error('hint.encode: hint is false'); // should never happen
155
+ const res = new Uint8Array(OMEGA + K);
156
+ for (let i = 0, k = 0; i < K; i++) {
157
+ for (let j = 0; j < N; j++)
158
+ if (h[i][j] !== 0)
159
+ res[k++] = j;
160
+ res[OMEGA + i] = k;
161
+ }
162
+ return res;
163
+ },
164
+ decode: (buf) => {
165
+ const h = [];
166
+ let k = 0;
167
+ for (let i = 0; i < K; i++) {
168
+ const hi = newPoly(N);
169
+ if (buf[OMEGA + i] < k || buf[OMEGA + i] > OMEGA)
170
+ return false;
171
+ for (let j = k; j < buf[OMEGA + i]; j++) {
172
+ if (j > k && buf[j] <= buf[j - 1])
173
+ return false;
174
+ hi[buf[j]] = 1;
175
+ }
176
+ k = buf[OMEGA + i];
177
+ h.push(hi);
178
+ }
179
+ for (let j = k; j < OMEGA; j++)
180
+ if (buf[j] !== 0)
181
+ return false;
182
+ return h;
183
+ },
184
+ };
185
+ const ETACoder = polyCoder(ETA === 2 ? 3 : 4, (i) => ETA - i);
186
+ const T0Coder = polyCoder(13, (i) => (1 << (D - 1)) - i);
187
+ const T1Coder = polyCoder(10);
188
+ // Requires smod. Need to fix!
189
+ const ZCoder = polyCoder(GAMMA1 === 1 << 17 ? 18 : 20, (i) => smod(GAMMA1 - i));
190
+ const W1Coder = polyCoder(GAMMA2 === GAMMA2_1 ? 6 : 4);
191
+ const W1Vec = (0, utils_js_1.vecCoder)(W1Coder, K);
192
+ // Main structures
193
+ const publicCoder = (0, utils_js_1.splitCoder)(32, (0, utils_js_1.vecCoder)(T1Coder, K));
194
+ const secretCoder = (0, utils_js_1.splitCoder)(32, 32, TR_BYTES, (0, utils_js_1.vecCoder)(ETACoder, L), (0, utils_js_1.vecCoder)(ETACoder, K), (0, utils_js_1.vecCoder)(T0Coder, K));
195
+ const sigCoder = (0, utils_js_1.splitCoder)(C_TILDE_BYTES, (0, utils_js_1.vecCoder)(ZCoder, L), hintCoder);
196
+ const CoefFromHalfByte = ETA === 2
197
+ ? (n) => (n < 15 ? 2 - (n % 5) : false)
198
+ : (n) => (n < 9 ? 4 - n : false);
199
+ // Return poly in NTT representation
200
+ function RejBoundedPoly(xof) {
201
+ // Samples an element a ∈ Rq with coeffcients in [−η, η] computed via rejection sampling from ρ.
202
+ const r = newPoly(N);
203
+ for (let j = 0; j < N;) {
204
+ const b = xof();
205
+ for (let i = 0; j < N && i < b.length; i += 1) {
206
+ // half byte. Should be superfast with vector instructions. But very slow with js :(
207
+ const d1 = CoefFromHalfByte(b[i] & 0x0f);
208
+ const d2 = CoefFromHalfByte((b[i] >> 4) & 0x0f);
209
+ if (d1 !== false)
210
+ r[j++] = d1;
211
+ if (j < N && d2 !== false)
212
+ r[j++] = d2;
213
+ }
214
+ }
215
+ return r;
216
+ }
217
+ const SampleInBall = (seed) => {
218
+ // Samples a polynomial c ∈ Rq with coeffcients from {−1, 0, 1} and Hamming weight τ
219
+ const pre = newPoly(N);
220
+ const s = sha3_1.shake256.create({}).update(seed.slice(0, 32));
221
+ const buf = new Uint8Array(sha3_1.shake256.blockLen);
222
+ s.xofInto(buf);
223
+ const masks = buf.slice(0, 8);
224
+ for (let i = N - TAU, pos = 8, maskPos = 0, maskBit = 0; i < N; i++) {
225
+ let b = i + 1;
226
+ for (; b > i;) {
227
+ b = buf[pos++];
228
+ if (pos < sha3_1.shake256.blockLen)
229
+ continue;
230
+ s.xofInto(buf);
231
+ pos = 0;
232
+ }
233
+ pre[i] = pre[b];
234
+ pre[b] = 1 - (((masks[maskPos] >> maskBit++) & 1) << 1);
235
+ if (maskBit >= 8) {
236
+ maskPos++;
237
+ maskBit = 0;
238
+ }
239
+ }
240
+ return pre;
241
+ };
242
+ const polyPowerRound = (p) => {
243
+ const res0 = newPoly(N);
244
+ const res1 = newPoly(N);
245
+ for (let i = 0; i < p.length; i++) {
246
+ const { r0, r1 } = Power2Round(p[i]);
247
+ res0[i] = r0;
248
+ res1[i] = r1;
249
+ }
250
+ return { r0: res0, r1: res1 };
251
+ };
252
+ const polyUseHint = (u, h) => {
253
+ for (let i = 0; i < N; i++)
254
+ u[i] = UseHint(h[i], u[i]);
255
+ return u;
256
+ };
257
+ const polyMakeHint = (a, b) => {
258
+ const v = newPoly(N);
259
+ let cnt = 0;
260
+ for (let i = 0; i < N; i++) {
261
+ const h = MakeHint(a[i], b[i]);
262
+ v[i] = h;
263
+ cnt += h;
264
+ }
265
+ return { v, cnt };
266
+ };
267
+ const signRandBytes = FIPS204 ? 32 : CRH_BYTES;
268
+ const seedCoder = (0, utils_js_1.splitCoder)(32, V31 ? 64 : 32, 32);
269
+ const seedXOF = V31 ? XOF256 : XOF128;
270
+ // API & argument positions are exactly as in FIPS204.
271
+ return {
272
+ signRandBytes,
273
+ keygen: (seed = (0, utils_js_1.randomBytes)(32)) => {
274
+ const [rho, rhoPrime, K_] = seedCoder.decode((0, sha3_1.shake256)(seed, { dkLen: seedCoder.bytesLen }));
275
+ const xofPrime = seedXOF(rhoPrime);
276
+ const s1 = [];
277
+ for (let i = 0; i < L; i++)
278
+ s1.push(RejBoundedPoly(xofPrime.get(i & 0xff, (i >> 8) & 0xff)));
279
+ const s2 = [];
280
+ for (let i = L; i < L + K; i++)
281
+ s2.push(RejBoundedPoly(xofPrime.get(i & 0xff, (i >> 8) & 0xff)));
282
+ const s1Hat = s1.map((i) => NTT.encode(i.slice()));
283
+ const t0 = [];
284
+ const t1 = [];
285
+ const xof = XOF128(rho);
286
+ const t = newPoly(N);
287
+ for (let i = 0; i < K; i++) {
288
+ // t ← NTT−1(A*NTT(s1)) + s2
289
+ t.fill(0); // don't-reallocate
290
+ for (let j = 0; j < L; j++) {
291
+ const aij = RejNTTPoly(xof.get(j, i)); // super slow!
292
+ polyAdd(t, MultiplyNTTs(aij, s1Hat[j]));
293
+ }
294
+ NTT.decode(t);
295
+ const { r0, r1 } = polyPowerRound(polyAdd(t, s2[i])); // (t1, t0) ← Power2Round(t, d)
296
+ t0.push(r0);
297
+ t1.push(r1);
298
+ }
299
+ const publicKey = publicCoder.encode([rho, t1]); // pk ← pkEncode(ρ, t1)
300
+ const tr = (0, sha3_1.shake256)(publicKey, { dkLen: TR_BYTES }); // tr ← H(BytesToBits(pk), 512)
301
+ const secretKey = secretCoder.encode([rho, K_, tr, s1, s2, t0]); // sk ← skEncode(ρ, K,tr, s1, s2, t0)
302
+ xof.clean();
303
+ xofPrime.clean();
304
+ // STATS
305
+ // Kyber512: { calls: 4, xofs: 12 }, Kyber768: { calls: 9, xofs: 27 }, Kyber1024: { calls: 16, xofs: 48 }
306
+ // DSA44: { calls: 24, xofs: 24 }, DSA65: { calls: 41, xofs: 41 }, DSA87: { calls: 71, xofs: 71 }
307
+ (0, utils_js_1.cleanBytes)(rho, rhoPrime, K_, s1, s2, s1Hat, t, t0, t1, tr);
308
+ return { publicKey, secretKey };
309
+ },
310
+ // NOTE: random is optional.
311
+ sign: (secretKey, msg, random) => {
312
+ // This part can be pre-cached per secretKey, but there is only minor performance improvement,
313
+ // since we re-use a lot of variables to computation.
314
+ const [rho, _K, tr, s1, s2, t0] = secretCoder.decode(secretKey); // (ρ, K,tr, s1, s2, t0) ← skDecode(sk)
315
+ // Cache matrix to avoid re-compute later
316
+ const A = []; // A ← ExpandA(ρ)
317
+ const xof = XOF128(rho);
318
+ for (let i = 0; i < K; i++) {
319
+ const pv = [];
320
+ for (let j = 0; j < L; j++)
321
+ pv.push(RejNTTPoly(xof.get(j, i)));
322
+ A.push(pv);
323
+ }
324
+ xof.clean();
325
+ for (let i = 0; i < L; i++)
326
+ NTT.encode(s1[i]); // sˆ1 ← NTT(s1)
327
+ for (let i = 0; i < K; i++) {
328
+ NTT.encode(s2[i]); // sˆ2 ← NTT(s2)
329
+ NTT.encode(t0[i]); // tˆ0 ← NTT(t0)
330
+ }
331
+ // This part is per msg
332
+ const mu = sha3_1.shake256.create({ dkLen: CRH_BYTES }).update(tr).update(msg).digest(); // 6: µ ← H(tr||M, 512) ▷ Compute message representative µ
333
+ let rhoprime; // Compute private random seed
334
+ if (FIPS204) {
335
+ const rnd = random ? random : new Uint8Array(32);
336
+ (0, utils_js_1.ensureBytes)(rnd);
337
+ rhoprime = sha3_1.shake256.create({ dkLen: CRH_BYTES }).update(_K).update(rnd).update(mu).digest(); // ρ′← H(K||rnd||µ, 512)
338
+ }
339
+ else {
340
+ rhoprime = random
341
+ ? random
342
+ : sha3_1.shake256.create({ dkLen: CRH_BYTES }).update(_K).update(mu).digest();
343
+ }
344
+ (0, utils_js_1.ensureBytes)(rhoprime, CRH_BYTES);
345
+ const x256 = XOF256(rhoprime, ZCoder.bytesLen);
346
+ // Rejection sampling loop
347
+ main_loop: for (let kappa = 0;;) {
348
+ const y = [];
349
+ // y ← ExpandMask(ρ , κ)
350
+ for (let i = 0; i < L; i++, kappa++)
351
+ y.push(ZCoder.decode(x256.get(kappa & 0xff, kappa >> 8)()));
352
+ const z = y.map((i) => NTT.encode(i.slice()));
353
+ const w = [];
354
+ for (let i = 0; i < K; i++) {
355
+ // w ← NTT−1(A ◦ NTT(y))
356
+ const wi = newPoly(N);
357
+ for (let j = 0; j < L; j++)
358
+ polyAdd(wi, MultiplyNTTs(A[i][j], z[j]));
359
+ NTT.decode(wi);
360
+ w.push(wi);
361
+ }
362
+ const w1 = w.map((j) => j.map(HighBits)); // w1 ← HighBits(w)
363
+ // Commitment hash: c˜ ∈{0, 1 2λ } ← H(µ||w1Encode(w1), 2λ)
364
+ const cTilde = sha3_1.shake256
365
+ .create({ dkLen: C_TILDE_BYTES })
366
+ .update(mu)
367
+ .update(W1Vec.encode(w1))
368
+ .digest();
369
+ // Verifer’s challenge
370
+ const cHat = NTT.encode(SampleInBall(cTilde.subarray(0, 32))); // c ← SampleInBall(c˜1); cˆ ← NTT(c)
371
+ // ⟨⟨cs1⟩⟩ ← NTT−1(cˆ◦ sˆ1)
372
+ const cs1 = s1.map((i) => MultiplyNTTs(i, cHat));
373
+ for (let i = 0; i < L; i++) {
374
+ polyAdd(NTT.decode(cs1[i]), y[i]); // z ← y + ⟨⟨cs1⟩⟩
375
+ if (polyChknorm(cs1[i], GAMMA1 - BETA))
376
+ continue main_loop; // ||z||∞ ≥ γ1 − β
377
+ }
378
+ // cs1 is now z (▷ Signer’s response)
379
+ let cnt = 0;
380
+ const h = [];
381
+ for (let i = 0; i < K; i++) {
382
+ const cs2 = NTT.decode(MultiplyNTTs(s2[i], cHat)); // ⟨⟨cs2⟩⟩ ← NTT−1(cˆ◦ sˆ2)
383
+ const r0 = polySub(w[i], cs2).map(LowBits); // r0 ← LowBits(w − ⟨⟨cs2⟩⟩)
384
+ if (polyChknorm(r0, GAMMA2 - BETA))
385
+ continue main_loop; // ||r0||∞ ≥ γ2 − β
386
+ const ct0 = NTT.decode(MultiplyNTTs(t0[i], cHat)); // ⟨⟨ct0⟩⟩ ← NTT−1(cˆ◦ tˆ0)
387
+ if (polyChknorm(ct0, GAMMA2))
388
+ continue main_loop;
389
+ polyAdd(r0, ct0);
390
+ // ▷ Signer’s hint
391
+ const hint = polyMakeHint(r0, w1[i]); // h ← MakeHint(−⟨⟨ct0⟩⟩, w− ⟨⟨cs2⟩⟩ + ⟨⟨ct0⟩⟩)
392
+ h.push(hint.v);
393
+ cnt += hint.cnt;
394
+ }
395
+ if (cnt > OMEGA)
396
+ continue; // the number of 1’s in h is greater than ω
397
+ x256.clean();
398
+ const res = sigCoder.encode([cTilde, cs1, h]); // σ ← sigEncode(c˜, z mod±q, h)
399
+ // rho, _K, tr is subarray of secretKey, cannot clean.
400
+ (0, utils_js_1.cleanBytes)(cTilde, cs1, h, cHat, w1, w, z, y, rhoprime, mu, s1, s2, t0, ...A);
401
+ return res;
402
+ }
403
+ // @ts-ignore
404
+ throw new Error('Unreachable code path reached, report this error');
405
+ },
406
+ verify: (publicKey, msg, sig) => {
407
+ // ML-DSA.Verify(pk, M, σ): Verifes a signature σ for a message M.
408
+ const [rho, t1] = publicCoder.decode(publicKey); // (ρ, t1) ← pkDecode(pk)
409
+ const tr = (0, sha3_1.shake256)(publicKey, { dkLen: TR_BYTES }); // 6: tr ← H(BytesToBits(pk), 512)
410
+ if (sig.length !== sigCoder.bytesLen)
411
+ return false; // return false instead of exception
412
+ const [cTilde, z, h] = sigCoder.decode(sig); // (c˜, z, h) ← sigDecode(σ), ▷ Signer’s commitment hash c ˜, response z and hint
413
+ if (h === false)
414
+ return false; // if h = ⊥ then return false
415
+ for (let i = 0; i < L; i++)
416
+ if (polyChknorm(z[i], GAMMA1 - BETA))
417
+ return false;
418
+ const mu = sha3_1.shake256.create({ dkLen: CRH_BYTES }).update(tr).update(msg).digest(); // 7: µ ← H(tr||M, 512)
419
+ // Compute verifer’s challenge from c˜
420
+ const c = NTT.encode(SampleInBall(cTilde.subarray(0, 32))); // c ← SampleInBall(c˜1)
421
+ const zNtt = z.map((i) => i.slice()); // zNtt = NTT(z)
422
+ for (let i = 0; i < L; i++)
423
+ NTT.encode(zNtt[i]);
424
+ const wTick1 = [];
425
+ const xof = XOF128(rho);
426
+ for (let i = 0; i < K; i++) {
427
+ const ct12d = MultiplyNTTs(NTT.encode(polyShiftl(t1[i])), c); //c * t1 * (2**d)
428
+ const Az = newPoly(N); // // A * z
429
+ for (let j = 0; j < L; j++) {
430
+ const aij = RejNTTPoly(xof.get(j, i)); // A[i][j] inplace
431
+ polyAdd(Az, MultiplyNTTs(aij, zNtt[j]));
432
+ }
433
+ // wApprox = A*z - c*t1 * (2**d)
434
+ const wApprox = NTT.decode(polySub(Az, ct12d));
435
+ // Reconstruction of signer’s commitment
436
+ wTick1.push(polyUseHint(wApprox, h[i])); // w ′ ← UseHint(h, w'approx )
437
+ }
438
+ xof.clean();
439
+ // c˜′← H (µ||w1Encode(w′1), 2λ), Hash it; this should match c˜
440
+ const c2 = sha3_1.shake256
441
+ .create({ dkLen: C_TILDE_BYTES })
442
+ .update(mu)
443
+ .update(W1Vec.encode(wTick1))
444
+ .digest();
445
+ if (FIPS204) {
446
+ // Additional checks in FIPS-204:
447
+ // [[ ||z||∞ < γ1 − β ]] and [[c ˜ = c˜′]] and [[number of 1’s in h is ≤ ω]]
448
+ for (const t of h) {
449
+ const sum = t.reduce((acc, i) => acc + i, 0);
450
+ if (!(sum <= OMEGA))
451
+ return false;
452
+ }
453
+ for (const t of z)
454
+ if (polyChknorm(t, GAMMA1 - BETA))
455
+ return false;
456
+ }
457
+ return (0, utils_js_1.equalBytes)(cTilde, c2);
458
+ },
459
+ };
460
+ }
461
+ function getDilithiumVersions(cfg) {
462
+ return {
463
+ dilithium2: getDilithium({ ...exports.PARAMS[2], ...cfg }),
464
+ dilithium3: getDilithium({ ...exports.PARAMS[3], ...cfg }),
465
+ dilithium5: getDilithium({ ...exports.PARAMS[5], ...cfg }),
466
+ };
467
+ }
468
+ // v30 is NIST round 3 submission, for original vectors and benchmarking.
469
+ // v31 is kyber: more secure than v30.
470
+ // ml-dsa is NIST FIPS 204, but it is still a draft and may change.
471
+ exports.dilithium_v30 = getDilithiumVersions({
472
+ CRH_BYTES: 48,
473
+ TR_BYTES: 48,
474
+ C_TILDE_BYTES: 32,
475
+ XOF128: _crystals_js_1.XOF128,
476
+ XOF256: _crystals_js_1.XOF256,
477
+ });
478
+ exports.dilithium_v31 = getDilithiumVersions({
479
+ CRH_BYTES: 64,
480
+ TR_BYTES: 32,
481
+ C_TILDE_BYTES: 32,
482
+ XOF128: _crystals_js_1.XOF128,
483
+ XOF256: _crystals_js_1.XOF256,
484
+ V31: true,
485
+ });
486
+ exports.dilithium_v30_aes = getDilithiumVersions({
487
+ CRH_BYTES: 48,
488
+ TR_BYTES: 48,
489
+ C_TILDE_BYTES: 32,
490
+ XOF128: _crystals_js_1.XOF_AES,
491
+ XOF256: _crystals_js_1.XOF_AES,
492
+ });
493
+ exports.dilithium_v31_aes = getDilithiumVersions({
494
+ CRH_BYTES: 64,
495
+ TR_BYTES: 32,
496
+ C_TILDE_BYTES: 32,
497
+ XOF128: _crystals_js_1.XOF_AES,
498
+ XOF256: _crystals_js_1.XOF_AES,
499
+ V31: true,
500
+ });
501
+ // ML-DSA
502
+ exports.ml_dsa44 = getDilithium({
503
+ ...exports.PARAMS[2],
504
+ CRH_BYTES: 64,
505
+ TR_BYTES: 64,
506
+ C_TILDE_BYTES: 32,
507
+ XOF128: _crystals_js_1.XOF128,
508
+ XOF256: _crystals_js_1.XOF256,
509
+ V31: true,
510
+ FIPS204: true,
511
+ });
512
+ exports.ml_dsa65 = getDilithium({
513
+ ...exports.PARAMS[3],
514
+ CRH_BYTES: 64,
515
+ TR_BYTES: 64,
516
+ C_TILDE_BYTES: 48,
517
+ XOF128: _crystals_js_1.XOF128,
518
+ XOF256: _crystals_js_1.XOF256,
519
+ V31: true,
520
+ FIPS204: true,
521
+ });
522
+ exports.ml_dsa87 = getDilithium({
523
+ ...exports.PARAMS[5],
524
+ CRH_BYTES: 64,
525
+ TR_BYTES: 64,
526
+ C_TILDE_BYTES: 64,
527
+ XOF128: _crystals_js_1.XOF128,
528
+ XOF256: _crystals_js_1.XOF256,
529
+ V31: true,
530
+ FIPS204: true,
531
+ });
532
+ //# sourceMappingURL=ml-dsa.js.map