@noble/post-quantum 0.5.4 → 0.6.1
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 +86 -42
- package/_crystals.d.ts +90 -4
- package/_crystals.d.ts.map +1 -1
- package/_crystals.js +77 -6
- package/_crystals.js.map +1 -1
- package/falcon.d.ts +84 -0
- package/falcon.d.ts.map +1 -0
- package/falcon.js +2385 -0
- package/falcon.js.map +1 -0
- package/hybrid.d.ts +194 -24
- package/hybrid.d.ts.map +1 -1
- package/hybrid.js +401 -77
- package/hybrid.js.map +1 -1
- package/index.js +8 -0
- package/index.js.map +1 -1
- package/ml-dsa.d.ts +29 -8
- package/ml-dsa.d.ts.map +1 -1
- package/ml-dsa.js +154 -78
- package/ml-dsa.js.map +1 -1
- package/ml-kem.d.ts +31 -7
- package/ml-kem.d.ts.map +1 -1
- package/ml-kem.js +194 -75
- package/ml-kem.js.map +1 -1
- package/package.json +15 -8
- package/slh-dsa.d.ts +137 -34
- package/slh-dsa.d.ts.map +1 -1
- package/slh-dsa.js +189 -68
- package/slh-dsa.js.map +1 -1
- package/src/_crystals.ts +135 -24
- package/src/falcon.ts +2503 -0
- package/src/hybrid.ts +515 -144
- package/src/index.ts +8 -0
- package/src/ml-dsa.ts +263 -138
- package/src/ml-kem.ts +240 -97
- package/src/slh-dsa.ts +391 -153
- package/src/utils.ts +491 -46
- package/utils.d.ts +362 -24
- package/utils.d.ts.map +1 -1
- package/utils.js +273 -20
- package/utils.js.map +1 -1
package/src/ml-dsa.ts
CHANGED
|
@@ -25,6 +25,8 @@ import {
|
|
|
25
25
|
type Signer,
|
|
26
26
|
type SigOpts,
|
|
27
27
|
splitCoder,
|
|
28
|
+
type TArg,
|
|
29
|
+
type TRet,
|
|
28
30
|
validateOpts,
|
|
29
31
|
validateSigOpts,
|
|
30
32
|
validateVerOpts,
|
|
@@ -32,33 +34,51 @@ import {
|
|
|
32
34
|
type VerOpts,
|
|
33
35
|
} from './utils.ts';
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
/** Internal ML-DSA options. */
|
|
38
|
+
export type DSAInternalOpts = {
|
|
39
|
+
/**
|
|
40
|
+
* Whether `internal.sign` / `internal.verify` receive a caller-supplied 64-byte `mu`
|
|
41
|
+
* instead of the usual FIPS 204 formatted message `M'` / prehash-formatted message.
|
|
42
|
+
* validateInternalOpts() only checks this flag; callers still must supply the right input length.
|
|
43
|
+
*/
|
|
44
|
+
externalMu?: boolean;
|
|
45
|
+
};
|
|
46
|
+
function validateInternalOpts(opts: TArg<DSAInternalOpts>) {
|
|
37
47
|
validateOpts(opts);
|
|
38
48
|
if (opts.externalMu !== undefined) abool(opts.externalMu, 'opts.externalMu');
|
|
39
49
|
}
|
|
40
50
|
|
|
41
|
-
/**
|
|
51
|
+
/** ML-DSA signer surface with access to the internal message formatting mode. */
|
|
42
52
|
export type DSAInternal = CryptoKeys & {
|
|
43
53
|
lengths: Signer['lengths'];
|
|
44
|
-
sign: (
|
|
54
|
+
sign: (
|
|
55
|
+
msg: TArg<Uint8Array>,
|
|
56
|
+
secretKey: TArg<Uint8Array>,
|
|
57
|
+
opts?: TArg<SigOpts & DSAInternalOpts>
|
|
58
|
+
) => TRet<Uint8Array>;
|
|
45
59
|
verify: (
|
|
46
|
-
sig: Uint8Array
|
|
47
|
-
msg: Uint8Array
|
|
48
|
-
pubKey: Uint8Array
|
|
49
|
-
opts?: VerOpts & DSAInternalOpts
|
|
60
|
+
sig: TArg<Uint8Array>,
|
|
61
|
+
msg: TArg<Uint8Array>,
|
|
62
|
+
pubKey: TArg<Uint8Array>,
|
|
63
|
+
opts?: TArg<VerOpts & DSAInternalOpts>
|
|
50
64
|
) => boolean;
|
|
51
65
|
};
|
|
52
|
-
|
|
66
|
+
/** Public ML-DSA signer surface. */
|
|
67
|
+
export type DSA = Signer & { internal: TRet<DSAInternal> };
|
|
53
68
|
|
|
54
69
|
// Constants
|
|
70
|
+
// FIPS 204 fixes ML-DSA over R = Z[X]/(X^256 + 1), so every polynomial has 256 coefficients.
|
|
55
71
|
const N = 256;
|
|
56
72
|
// 2**23 − 2**13 + 1, 23 bits: multiply will be 46. We have enough precision in JS to avoid bigints
|
|
57
73
|
const Q = 8380417;
|
|
74
|
+
// FIPS 204 §2.5 / Table 1 fixes zeta = 1753 as the 512th root of unity used by ML-DSA's NTT.
|
|
58
75
|
const ROOT_OF_UNITY = 1753;
|
|
59
76
|
// f = 256**−1 mod q, pow(256, -1, q) = 8347681 (python3)
|
|
60
77
|
const F = 8347681;
|
|
78
|
+
// FIPS 204 Table 1 / §7.4 fixes d = 13 dropped low bits for Power2Round on t.
|
|
61
79
|
const D = 13;
|
|
80
|
+
// FIPS 204 Table 1 fixes gamma2 to (q-1)/88 for ML-DSA-44 and (q-1)/32 for ML-DSA-65/87;
|
|
81
|
+
// §7.4 then uses alpha = 2*gamma2 for Decompose / MakeHint / UseHint.
|
|
62
82
|
// Dilithium is kinda parametrized over GAMMA2, but everything will break with any other value.
|
|
63
83
|
const GAMMA2_1 = Math.floor((Q - 1) / 88) | 0;
|
|
64
84
|
const GAMMA2_2 = Math.floor((Q - 1) / 32) | 0;
|
|
@@ -66,29 +86,52 @@ const GAMMA2_2 = Math.floor((Q - 1) / 32) | 0;
|
|
|
66
86
|
type XofGet = ReturnType<ReturnType<XOF>['get']>;
|
|
67
87
|
|
|
68
88
|
/** Various lattice params. */
|
|
89
|
+
/** Public ML-DSA parameter-set description. */
|
|
69
90
|
export type DSAParam = {
|
|
91
|
+
/** Matrix row count. */
|
|
70
92
|
K: number;
|
|
93
|
+
/** Matrix column count. */
|
|
71
94
|
L: number;
|
|
95
|
+
/** Bit width used when rounding `t`. */
|
|
72
96
|
D: number;
|
|
97
|
+
/** Bound used for the `y` sampling range. */
|
|
73
98
|
GAMMA1: number;
|
|
99
|
+
/** Bound used during decomposition and hints. */
|
|
74
100
|
GAMMA2: number;
|
|
101
|
+
/** Number of non-zero challenge coefficients. */
|
|
75
102
|
TAU: number;
|
|
103
|
+
/** Centered-binomial noise parameter. */
|
|
76
104
|
ETA: number;
|
|
105
|
+
/** Maximum number of hint bits in a signature. */
|
|
77
106
|
OMEGA: number;
|
|
78
107
|
};
|
|
79
108
|
/** Internal params for different versions of ML-DSA */
|
|
80
109
|
// prettier-ignore
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
110
|
+
/** Built-in ML-DSA parameter presets keyed by security categories `2/3/5`
|
|
111
|
+
* for `ml_dsa44` / `ml_dsa65` / `ml_dsa87`.
|
|
112
|
+
* This is only the Table 1 subset used directly here: `BETA = TAU * ETA` is derived later,
|
|
113
|
+
* while `C_TILDE_BYTES`, `TR_BYTES`, `CRH_BYTES`, and `securityLevel` live in the preset wrappers.
|
|
114
|
+
*/
|
|
115
|
+
export const PARAMS: Record<string, DSAParam> = /* @__PURE__ */ (() =>
|
|
116
|
+
Object.freeze({
|
|
117
|
+
2: Object.freeze({
|
|
118
|
+
K: 4, L: 4, D, GAMMA1: 2 ** 17, GAMMA2: GAMMA2_1, TAU: 39, ETA: 2, OMEGA: 80
|
|
119
|
+
}),
|
|
120
|
+
3: Object.freeze({
|
|
121
|
+
K: 6, L: 5, D, GAMMA1: 2 ** 19, GAMMA2: GAMMA2_2, TAU: 49, ETA: 4, OMEGA: 55
|
|
122
|
+
}),
|
|
123
|
+
5: Object.freeze({
|
|
124
|
+
K: 8, L: 7, D, GAMMA1: 2 ** 19, GAMMA2: GAMMA2_2, TAU: 60, ETA: 2, OMEGA: 75
|
|
125
|
+
}),
|
|
126
|
+
} as const))();
|
|
86
127
|
|
|
87
128
|
// NOTE: there is a lot cases where negative numbers used (with smod instead of mod).
|
|
88
129
|
type Poly = Int32Array;
|
|
89
|
-
const newPoly = (n: number): Int32Array => new Int32Array(n)
|
|
130
|
+
const newPoly = (n: number): TRet<Int32Array> => new Int32Array(n) as TRet<Int32Array>;
|
|
90
131
|
|
|
91
|
-
|
|
132
|
+
// Shared CRYSTALS helper in the ML-DSA branch: non-Kyber mode, 8-bit bit-reversal,
|
|
133
|
+
// and Int32Array polys because ordinary-form coefficients can be negative / centered.
|
|
134
|
+
const crystals = /* @__PURE__ */ genCrystals({
|
|
92
135
|
N,
|
|
93
136
|
Q,
|
|
94
137
|
F,
|
|
@@ -101,56 +144,73 @@ const { mod, smod, NTT, bitsCoder } = genCrystals({
|
|
|
101
144
|
const id = <T>(n: T): T => n;
|
|
102
145
|
type IdNum = (n: number) => number;
|
|
103
146
|
|
|
147
|
+
// compress()/verify() must be compatible in both directions:
|
|
148
|
+
// wrap the shared d-bit packer with the FIPS 204 SimpleBitPack / BitPack coefficient maps.
|
|
149
|
+
// malformed-input rejection only happens through the optional verify hook.
|
|
104
150
|
const polyCoder = (d: number, compress: IdNum = id, verify: IdNum = id) =>
|
|
105
|
-
bitsCoder(d, {
|
|
151
|
+
crystals.bitsCoder(d, {
|
|
106
152
|
encode: (i: number) => compress(verify(i)),
|
|
107
153
|
decode: (i: number) => verify(compress(i)),
|
|
108
154
|
});
|
|
109
155
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
156
|
+
// Mutates `a` in place; callers must pass same-length polynomials.
|
|
157
|
+
const polyAdd = (a_: TArg<Poly>, b_: TArg<Poly>): TRet<Poly> => {
|
|
158
|
+
const a = a_ as Poly;
|
|
159
|
+
const b = b_ as Poly;
|
|
160
|
+
for (let i = 0; i < a.length; i++) a[i] = crystals.mod(a[i] + b[i]);
|
|
161
|
+
return a as TRet<Poly>;
|
|
113
162
|
};
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
163
|
+
// Mutates `a` in place; callers must pass same-length polynomials.
|
|
164
|
+
const polySub = (a_: TArg<Poly>, b_: TArg<Poly>): TRet<Poly> => {
|
|
165
|
+
const a = a_ as Poly;
|
|
166
|
+
const b = b_ as Poly;
|
|
167
|
+
for (let i = 0; i < a.length; i++) a[i] = crystals.mod(a[i] - b[i]);
|
|
168
|
+
return a as TRet<Poly>;
|
|
117
169
|
};
|
|
118
170
|
|
|
119
|
-
|
|
171
|
+
// Mutates `p` in place and assumes it is a decoded `t1`-range polynomial.
|
|
172
|
+
const polyShiftl = (p_: TArg<Poly>): TRet<Poly> => {
|
|
173
|
+
const p = p_ as Poly;
|
|
120
174
|
for (let i = 0; i < N; i++) p[i] <<= D;
|
|
121
|
-
return p
|
|
175
|
+
return p as TRet<Poly>;
|
|
122
176
|
};
|
|
123
177
|
|
|
124
|
-
const polyChknorm = (
|
|
125
|
-
|
|
126
|
-
|
|
178
|
+
const polyChknorm = (p_: TArg<Poly>, B: number): boolean => {
|
|
179
|
+
const p = p_ as Poly;
|
|
180
|
+
// FIPS 204 Algorithms 7 and 8 express the same centered-norm check with explicit inequalities.
|
|
181
|
+
for (let i = 0; i < N; i++) if (Math.abs(crystals.smod(p[i])) >= B) return true;
|
|
127
182
|
return false;
|
|
128
183
|
};
|
|
129
184
|
|
|
130
|
-
|
|
185
|
+
// Both inputs must already be in NTT / `T_q` form.
|
|
186
|
+
const MultiplyNTTs = (a_: TArg<Poly>, b_: TArg<Poly>): TRet<Poly> => {
|
|
187
|
+
const a = a_ as Poly;
|
|
188
|
+
const b = b_ as Poly;
|
|
131
189
|
// NOTE: we don't use montgomery reduction in code, since it requires 64 bit ints,
|
|
132
190
|
// which is not available in JS. mod(a[i] * b[i]) is ok, since Q is 23 bit,
|
|
133
191
|
// which means a[i] * b[i] is 46 bit, which is safe to use in JS. (number is 53 bits).
|
|
134
192
|
// Barrett reduction is slower than mod :(
|
|
135
193
|
const c = newPoly(N);
|
|
136
|
-
for (let i = 0; i < a.length; i++) c[i] = mod(a[i] * b[i]);
|
|
137
|
-
return c
|
|
194
|
+
for (let i = 0; i < a.length; i++) c[i] = crystals.mod(a[i] * b[i]);
|
|
195
|
+
return c as TRet<Poly>;
|
|
138
196
|
};
|
|
139
197
|
|
|
140
198
|
// Return poly in NTT representation
|
|
141
|
-
function RejNTTPoly(
|
|
142
|
-
|
|
199
|
+
function RejNTTPoly(xof_: TArg<XofGet>): TRet<Poly> {
|
|
200
|
+
const xof = xof_ as XofGet;
|
|
201
|
+
// Samples a polynomial ∈ Tq. xof() must return byte lengths divisible by 3.
|
|
143
202
|
const r = newPoly(N);
|
|
144
203
|
// NOTE: we can represent 3xu24 as 4xu32, but it doesn't improve perf :(
|
|
145
204
|
for (let j = 0; j < N; ) {
|
|
146
205
|
const b = xof();
|
|
147
206
|
if (b.length % 3) throw new Error('RejNTTPoly: unaligned block');
|
|
148
207
|
for (let i = 0; j < N && i <= b.length - 3; i += 3) {
|
|
208
|
+
// FIPS 204 Algorithm 14 clears the top bit of b2 before forming the 23-bit candidate.
|
|
149
209
|
const t = (b[i + 0] | (b[i + 1] << 8) | (b[i + 2] << 16)) & 0x7fffff; // 3 bytes
|
|
150
210
|
if (t < Q) r[j++] = t;
|
|
151
211
|
}
|
|
152
212
|
}
|
|
153
|
-
return r
|
|
213
|
+
return r as TRet<Poly>;
|
|
154
214
|
}
|
|
155
215
|
|
|
156
216
|
type DilithiumOpts = {
|
|
@@ -169,7 +229,10 @@ type DilithiumOpts = {
|
|
|
169
229
|
securityLevel: number;
|
|
170
230
|
};
|
|
171
231
|
|
|
172
|
-
|
|
232
|
+
// Instantiate one ML-DSA parameter set from the Table 1 lattice constants plus the
|
|
233
|
+
// Table 2 byte lengths / hash-width choices used by the public wrappers below.
|
|
234
|
+
function getDilithium(opts_: TArg<DilithiumOpts>): TRet<DSA> {
|
|
235
|
+
const opts = opts_ as DilithiumOpts;
|
|
173
236
|
const { K, L, GAMMA1, GAMMA2, TAU, ETA, OMEGA } = opts;
|
|
174
237
|
const { CRH_BYTES, TR_BYTES, C_TILDE_BYTES, XOF128, XOF256, securityLevel } = opts;
|
|
175
238
|
|
|
@@ -180,8 +243,9 @@ function getDilithium(opts: DilithiumOpts) {
|
|
|
180
243
|
|
|
181
244
|
const decompose = (r: number) => {
|
|
182
245
|
// Decomposes r into (r1, r0) such that r ≡ r1(2γ2) + r0 mod q.
|
|
183
|
-
const rPlus = mod(r);
|
|
184
|
-
const r0 = smod(rPlus, 2 * GAMMA2) | 0;
|
|
246
|
+
const rPlus = crystals.mod(r);
|
|
247
|
+
const r0 = crystals.smod(rPlus, 2 * GAMMA2) | 0;
|
|
248
|
+
// FIPS 204 Algorithm 36 folds the top bucket `q-1` back to `(r1, r0) = (0, r0-1)`.
|
|
185
249
|
if (rPlus - r0 === Q - 1) return { r1: 0 | 0, r0: (r0 - 1) | 0 };
|
|
186
250
|
const r1 = Math.floor((rPlus - r0) / (2 * GAMMA2)) | 0;
|
|
187
251
|
return { r1, r0 }; // r1 = HighBits, r0 = LowBits
|
|
@@ -191,6 +255,10 @@ function getDilithium(opts: DilithiumOpts) {
|
|
|
191
255
|
const LowBits = (r: number) => decompose(r).r0;
|
|
192
256
|
const MakeHint = (z: number, r: number) => {
|
|
193
257
|
// Compute hint bit indicating whether adding z to r alters the high bits of r.
|
|
258
|
+
// FIPS 204 §6.2 also permits the Section 5.1 alternative from [6], which uses the
|
|
259
|
+
// transformed low-bits/high-bits state at this call site instead of Algorithm 39 literally.
|
|
260
|
+
// This optimized predicate only applies to those transformed Section 5.1 inputs; it is
|
|
261
|
+
// not a drop-in replacement for Algorithm 39 on arbitrary `(z, r)` pairs.
|
|
194
262
|
|
|
195
263
|
// From dilithium code
|
|
196
264
|
const res0 = z <= GAMMA2 || z > Q - GAMMA2 || (z === Q - GAMMA2 && r === 0) ? 0 : 1;
|
|
@@ -201,8 +269,9 @@ function getDilithium(opts: DilithiumOpts) {
|
|
|
201
269
|
// But they return different results! However, decompose is same.
|
|
202
270
|
// So, either there is a bug in Dilithium ref implementation or in FIPS204.
|
|
203
271
|
// For now, lets use dilithium one, so test vectors can be passed.
|
|
204
|
-
//
|
|
205
|
-
//
|
|
272
|
+
// The round-3 Dilithium / ML-DSA code uses the same low-bits / high-bits convention after
|
|
273
|
+
// `r0 += ct0`.
|
|
274
|
+
// See dilithium-py README section "Optimising decomposition and making hints".
|
|
206
275
|
return res0;
|
|
207
276
|
};
|
|
208
277
|
|
|
@@ -212,42 +281,43 @@ function getDilithium(opts: DilithiumOpts) {
|
|
|
212
281
|
const { r1, r0 } = decompose(r);
|
|
213
282
|
// 3: if h = 1 and r0 > 0 return (r1 + 1) mod m
|
|
214
283
|
// 4: if h = 1 and r0 ≤ 0 return (r1 − 1) mod m
|
|
215
|
-
if (h === 1) return r0 > 0 ? mod(r1 + 1, m) | 0 : mod(r1 - 1, m) | 0;
|
|
284
|
+
if (h === 1) return r0 > 0 ? crystals.mod(r1 + 1, m) | 0 : crystals.mod(r1 - 1, m) | 0;
|
|
216
285
|
return r1 | 0;
|
|
217
286
|
};
|
|
218
287
|
const Power2Round = (r: number) => {
|
|
219
288
|
// Decomposes r into (r1, r0) such that r ≡ r1*(2**d) + r0 mod q.
|
|
220
|
-
const rPlus = mod(r);
|
|
221
|
-
const r0 = smod(rPlus, 2 ** D) | 0;
|
|
289
|
+
const rPlus = crystals.mod(r);
|
|
290
|
+
const r0 = crystals.smod(rPlus, 2 ** D) | 0;
|
|
222
291
|
return { r1: Math.floor((rPlus - r0) / 2 ** D) | 0, r0 };
|
|
223
292
|
};
|
|
224
293
|
|
|
225
294
|
const hintCoder: BytesCoderLen<Poly[] | false> = {
|
|
226
295
|
bytesLen: OMEGA + K,
|
|
227
|
-
encode: (
|
|
296
|
+
encode: (h_: TArg<Poly[] | false>): TRet<Uint8Array> => {
|
|
297
|
+
const h = h_ as Poly[] | false;
|
|
228
298
|
if (h === false) throw new Error('hint.encode: hint is false'); // should never happen
|
|
229
299
|
const res = new Uint8Array(OMEGA + K);
|
|
230
300
|
for (let i = 0, k = 0; i < K; i++) {
|
|
231
301
|
for (let j = 0; j < N; j++) if (h[i][j] !== 0) res[k++] = j;
|
|
232
302
|
res[OMEGA + i] = k;
|
|
233
303
|
}
|
|
234
|
-
return res
|
|
304
|
+
return res as TRet<Uint8Array>;
|
|
235
305
|
},
|
|
236
|
-
decode: (buf: Uint8Array) => {
|
|
306
|
+
decode: (buf: TArg<Uint8Array>): TRet<Poly[] | false> => {
|
|
237
307
|
const h = [];
|
|
238
308
|
let k = 0;
|
|
239
309
|
for (let i = 0; i < K; i++) {
|
|
240
310
|
const hi = newPoly(N);
|
|
241
|
-
if (buf[OMEGA + i] < k || buf[OMEGA + i] > OMEGA) return false
|
|
311
|
+
if (buf[OMEGA + i] < k || buf[OMEGA + i] > OMEGA) return false as TRet<false>;
|
|
242
312
|
for (let j = k; j < buf[OMEGA + i]; j++) {
|
|
243
|
-
if (j > k && buf[j] <= buf[j - 1]) return false
|
|
313
|
+
if (j > k && buf[j] <= buf[j - 1]) return false as TRet<false>;
|
|
244
314
|
hi[buf[j]] = 1;
|
|
245
315
|
}
|
|
246
316
|
k = buf[OMEGA + i];
|
|
247
317
|
h.push(hi);
|
|
248
318
|
}
|
|
249
|
-
for (let j = k; j < OMEGA; j++) if (buf[j] !== 0) return false
|
|
250
|
-
return h
|
|
319
|
+
for (let j = k; j < OMEGA; j++) if (buf[j] !== 0) return false as TRet<false>;
|
|
320
|
+
return h as TRet<Poly[]>;
|
|
251
321
|
},
|
|
252
322
|
};
|
|
253
323
|
|
|
@@ -263,7 +333,7 @@ function getDilithium(opts: DilithiumOpts) {
|
|
|
263
333
|
const T0Coder = polyCoder(13, (i: number) => (1 << (D - 1)) - i);
|
|
264
334
|
const T1Coder = polyCoder(10);
|
|
265
335
|
// Requires smod. Need to fix!
|
|
266
|
-
const ZCoder = polyCoder(GAMMA1 === 1 << 17 ? 18 : 20, (i: number) => smod(GAMMA1 - i));
|
|
336
|
+
const ZCoder = polyCoder(GAMMA1 === 1 << 17 ? 18 : 20, (i: number) => crystals.smod(GAMMA1 - i));
|
|
267
337
|
const W1Coder = polyCoder(GAMMA2 === GAMMA2_1 ? 6 : 4);
|
|
268
338
|
const W1Vec = vecCoder(W1Coder, K);
|
|
269
339
|
// Main structures
|
|
@@ -283,8 +353,11 @@ function getDilithium(opts: DilithiumOpts) {
|
|
|
283
353
|
? (n: number) => (n < 15 ? 2 - (n % 5) : false)
|
|
284
354
|
: (n: number) => (n < 9 ? 4 - n : false);
|
|
285
355
|
|
|
286
|
-
// Return poly in
|
|
287
|
-
|
|
356
|
+
// Return poly in ordinary representation.
|
|
357
|
+
// This helper returns ordinary-form `[-ETA, ETA]` coefficients for ExpandS; callers apply
|
|
358
|
+
// `NTT.encode()` later when needed.
|
|
359
|
+
function RejBoundedPoly(xof_: TArg<XofGet>): TRet<Poly> {
|
|
360
|
+
const xof = xof_ as XofGet;
|
|
288
361
|
// Samples an element a ∈ Rq with coeffcients in [−η, η] computed via rejection sampling from ρ.
|
|
289
362
|
const r: Poly = newPoly(N);
|
|
290
363
|
for (let j = 0; j < N; ) {
|
|
@@ -297,15 +370,17 @@ function getDilithium(opts: DilithiumOpts) {
|
|
|
297
370
|
if (j < N && d2 !== false) r[j++] = d2;
|
|
298
371
|
}
|
|
299
372
|
}
|
|
300
|
-
return r
|
|
373
|
+
return r as TRet<Poly>;
|
|
301
374
|
}
|
|
302
375
|
|
|
303
|
-
const SampleInBall = (seed: Uint8Array) => {
|
|
376
|
+
const SampleInBall = (seed: TArg<Uint8Array>): TRet<Poly> => {
|
|
304
377
|
// Samples a polynomial c ∈ Rq with coeffcients from {−1, 0, 1} and Hamming weight τ
|
|
305
378
|
const pre = newPoly(N);
|
|
306
379
|
const s = shake256.create({}).update(seed);
|
|
307
380
|
const buf = new Uint8Array(shake256.blockLen);
|
|
308
381
|
s.xofInto(buf);
|
|
382
|
+
// FIPS 204 Algorithm 29 uses the first 8 squeezed bytes as the 64 sign bits `h`,
|
|
383
|
+
// then rejection-samples coefficient positions from the remaining XOF stream.
|
|
309
384
|
const masks = buf.slice(0, 8);
|
|
310
385
|
for (let i = N - TAU, pos = 8, maskPos = 0, maskBit = 0; i < N; i++) {
|
|
311
386
|
let b = i + 1;
|
|
@@ -322,10 +397,11 @@ function getDilithium(opts: DilithiumOpts) {
|
|
|
322
397
|
maskBit = 0;
|
|
323
398
|
}
|
|
324
399
|
}
|
|
325
|
-
return pre
|
|
400
|
+
return pre as TRet<Poly>;
|
|
326
401
|
};
|
|
327
402
|
|
|
328
|
-
const polyPowerRound = (
|
|
403
|
+
const polyPowerRound = (p_: TArg<Poly>) => {
|
|
404
|
+
const p = p_ as Poly;
|
|
329
405
|
const res0 = newPoly(N);
|
|
330
406
|
const res1 = newPoly(N);
|
|
331
407
|
for (let i = 0; i < p.length; i++) {
|
|
@@ -335,11 +411,17 @@ function getDilithium(opts: DilithiumOpts) {
|
|
|
335
411
|
}
|
|
336
412
|
return { r0: res0, r1: res1 };
|
|
337
413
|
};
|
|
338
|
-
const polyUseHint = (
|
|
414
|
+
const polyUseHint = (u_: TArg<Poly>, h_: TArg<Poly>): TRet<Poly> => {
|
|
415
|
+
const u = u_ as Poly;
|
|
416
|
+
const h = h_ as Poly;
|
|
417
|
+
// In-place on `u`: verification only needs the recovered high bits, so reuse the
|
|
418
|
+
// temporary `wApprox` buffer instead of allocating another polynomial.
|
|
339
419
|
for (let i = 0; i < N; i++) u[i] = UseHint(h[i], u[i]);
|
|
340
|
-
return u
|
|
420
|
+
return u as TRet<Poly>;
|
|
341
421
|
};
|
|
342
|
-
const polyMakeHint = (
|
|
422
|
+
const polyMakeHint = (a_: TArg<Poly>, b_: TArg<Poly>) => {
|
|
423
|
+
const a = a_ as Poly;
|
|
424
|
+
const b = b_ as Poly;
|
|
343
425
|
const v = newPoly(N);
|
|
344
426
|
let cnt = 0;
|
|
345
427
|
for (let i = 0; i < N; i++) {
|
|
@@ -353,16 +435,16 @@ function getDilithium(opts: DilithiumOpts) {
|
|
|
353
435
|
const signRandBytes = 32;
|
|
354
436
|
const seedCoder = splitCoder('seed', 32, 64, 32);
|
|
355
437
|
// API & argument positions are exactly as in FIPS204.
|
|
356
|
-
const internal: DSAInternal = {
|
|
357
|
-
info: { type: 'internal-ml-dsa' },
|
|
358
|
-
lengths: {
|
|
438
|
+
const internal: TRet<DSAInternal> = Object.freeze({
|
|
439
|
+
info: Object.freeze({ type: 'internal-ml-dsa' }),
|
|
440
|
+
lengths: Object.freeze({
|
|
359
441
|
secretKey: secretCoder.bytesLen,
|
|
360
442
|
publicKey: publicCoder.bytesLen,
|
|
361
443
|
seed: 32,
|
|
362
444
|
signature: sigCoder.bytesLen,
|
|
363
445
|
signRand: signRandBytes,
|
|
364
|
-
},
|
|
365
|
-
keygen: (seed?: Uint8Array) => {
|
|
446
|
+
}),
|
|
447
|
+
keygen: (seed?: TArg<Uint8Array>) => {
|
|
366
448
|
// H(𝜉||IntegerToBytes(𝑘, 1)||IntegerToBytes(ℓ, 1), 128) 2: ▷ expand seed
|
|
367
449
|
const seedDst = new Uint8Array(32 + 2);
|
|
368
450
|
const randSeed = seed === undefined;
|
|
@@ -381,7 +463,7 @@ function getDilithium(opts: DilithiumOpts) {
|
|
|
381
463
|
const s2 = [];
|
|
382
464
|
for (let i = L; i < L + K; i++)
|
|
383
465
|
s2.push(RejBoundedPoly(xofPrime.get(i & 0xff, (i >> 8) & 0xff)));
|
|
384
|
-
const s1Hat = s1.map((i) => NTT.encode(i.slice()));
|
|
466
|
+
const s1Hat = s1.map((i) => crystals.NTT.encode(i.slice()));
|
|
385
467
|
const t0 = [];
|
|
386
468
|
const t1 = [];
|
|
387
469
|
const xof = XOF128(rho);
|
|
@@ -393,26 +475,33 @@ function getDilithium(opts: DilithiumOpts) {
|
|
|
393
475
|
const aij = RejNTTPoly(xof.get(j, i)); // super slow!
|
|
394
476
|
polyAdd(t, MultiplyNTTs(aij, s1Hat[j]));
|
|
395
477
|
}
|
|
396
|
-
NTT.decode(t);
|
|
478
|
+
crystals.NTT.decode(t);
|
|
397
479
|
const { r0, r1 } = polyPowerRound(polyAdd(t, s2[i])); // (t1, t0) ← Power2Round(t, d)
|
|
398
480
|
t0.push(r0);
|
|
399
481
|
t1.push(r1);
|
|
400
482
|
}
|
|
401
483
|
const publicKey = publicCoder.encode([rho, t1]); // pk ← pkEncode(ρ, t1)
|
|
402
484
|
const tr = shake256(publicKey, { dkLen: TR_BYTES }); // tr ← H(BytesToBits(pk), 512)
|
|
403
|
-
|
|
485
|
+
// sk ← skEncode(ρ, K,tr, s1, s2, t0)
|
|
486
|
+
const secretKey = secretCoder.encode([rho, K_, tr, s1, s2, t0]);
|
|
404
487
|
xof.clean();
|
|
405
488
|
xofPrime.clean();
|
|
406
489
|
// STATS
|
|
407
|
-
// Kyber512:
|
|
408
|
-
//
|
|
490
|
+
// Kyber512: { calls: 4, xofs: 12 }, Kyber768: { calls: 9, xofs: 27 },
|
|
491
|
+
// Kyber1024: { calls: 16, xofs: 48 }
|
|
492
|
+
// DSA44: { calls: 24, xofs: 24 }, DSA65: { calls: 41, xofs: 41 },
|
|
493
|
+
// DSA87: { calls: 71, xofs: 71 }
|
|
409
494
|
cleanBytes(rho, rhoPrime, K_, s1, s2, s1Hat, t, t0, t1, tr, seedDst);
|
|
410
|
-
return {
|
|
495
|
+
return {
|
|
496
|
+
publicKey: publicKey as TRet<Uint8Array>,
|
|
497
|
+
secretKey: secretKey as TRet<Uint8Array>,
|
|
498
|
+
};
|
|
411
499
|
},
|
|
412
|
-
getPublicKey: (secretKey: Uint8Array) => {
|
|
413
|
-
|
|
500
|
+
getPublicKey: (secretKey: TArg<Uint8Array>): TRet<Uint8Array> => {
|
|
501
|
+
// (ρ, K,tr, s1, s2, t0) ← skDecode(sk)
|
|
502
|
+
const [rho, _K, _tr, s1, s2, _t0] = secretCoder.decode(secretKey);
|
|
414
503
|
const xof = XOF128(rho);
|
|
415
|
-
const s1Hat = s1.map((p) => NTT.encode(p.slice()));
|
|
504
|
+
const s1Hat = s1.map((p) => crystals.NTT.encode(p.slice()));
|
|
416
505
|
const t1: Poly[] = [];
|
|
417
506
|
const tmp = newPoly(N);
|
|
418
507
|
for (let i = 0; i < K; i++) {
|
|
@@ -421,7 +510,7 @@ function getDilithium(opts: DilithiumOpts) {
|
|
|
421
510
|
const aij = RejNTTPoly(xof.get(j, i)); // A_ij in NTT
|
|
422
511
|
polyAdd(tmp, MultiplyNTTs(aij, s1Hat[j])); // += A_ij * s1_j
|
|
423
512
|
}
|
|
424
|
-
NTT.decode(tmp); // NTT⁻¹
|
|
513
|
+
crystals.NTT.decode(tmp); // NTT⁻¹
|
|
425
514
|
polyAdd(tmp, s2[i]); // t_i = A·s1 + s2
|
|
426
515
|
const { r1 } = polyPowerRound(tmp); // r1 = t1, r0 ≈ t0
|
|
427
516
|
t1.push(r1);
|
|
@@ -431,13 +520,18 @@ function getDilithium(opts: DilithiumOpts) {
|
|
|
431
520
|
return publicCoder.encode([rho, t1]);
|
|
432
521
|
},
|
|
433
522
|
// NOTE: random is optional.
|
|
434
|
-
sign: (
|
|
523
|
+
sign: (
|
|
524
|
+
msg: TArg<Uint8Array>,
|
|
525
|
+
secretKey: TArg<Uint8Array>,
|
|
526
|
+
opts: TArg<SigOpts & DSAInternalOpts> = {}
|
|
527
|
+
): TRet<Uint8Array> => {
|
|
435
528
|
validateSigOpts(opts);
|
|
436
529
|
validateInternalOpts(opts);
|
|
437
530
|
let { extraEntropy: random, externalMu = false } = opts;
|
|
438
531
|
// This part can be pre-cached per secretKey, but there is only minor performance improvement,
|
|
439
532
|
// since we re-use a lot of variables to computation.
|
|
440
|
-
|
|
533
|
+
// (ρ, K,tr, s1, s2, t0) ← skDecode(sk)
|
|
534
|
+
const [rho, _K, tr, s1, s2, t0] = secretCoder.decode(secretKey);
|
|
441
535
|
// Cache matrix to avoid re-compute later
|
|
442
536
|
const A: Poly[][] = []; // A ← ExpandA(ρ)
|
|
443
537
|
const xof = XOF128(rho);
|
|
@@ -447,15 +541,17 @@ function getDilithium(opts: DilithiumOpts) {
|
|
|
447
541
|
A.push(pv);
|
|
448
542
|
}
|
|
449
543
|
xof.clean();
|
|
450
|
-
for (let i = 0; i < L; i++) NTT.encode(s1[i]); // sˆ1 ← NTT(s1)
|
|
544
|
+
for (let i = 0; i < L; i++) crystals.NTT.encode(s1[i]); // sˆ1 ← NTT(s1)
|
|
451
545
|
for (let i = 0; i < K; i++) {
|
|
452
|
-
NTT.encode(s2[i]); // sˆ2 ← NTT(s2)
|
|
453
|
-
NTT.encode(t0[i]); // tˆ0 ← NTT(t0)
|
|
546
|
+
crystals.NTT.encode(s2[i]); // sˆ2 ← NTT(s2)
|
|
547
|
+
crystals.NTT.encode(t0[i]); // tˆ0 ← NTT(t0)
|
|
454
548
|
}
|
|
455
549
|
// This part is per msg
|
|
456
550
|
const mu = externalMu
|
|
457
551
|
? msg
|
|
458
|
-
:
|
|
552
|
+
: // 6: µ ← H(tr||M, 512)
|
|
553
|
+
// ▷ Compute message representative µ
|
|
554
|
+
shake256.create({ dkLen: CRH_BYTES }).update(tr).update(msg).digest();
|
|
459
555
|
|
|
460
556
|
// Compute private random seed
|
|
461
557
|
const rnd =
|
|
@@ -480,13 +576,13 @@ function getDilithium(opts: DilithiumOpts) {
|
|
|
480
576
|
// y ← ExpandMask(ρ , κ)
|
|
481
577
|
for (let i = 0; i < L; i++, kappa++)
|
|
482
578
|
y.push(ZCoder.decode(x256.get(kappa & 0xff, kappa >> 8)()));
|
|
483
|
-
const z = y.map((i) => NTT.encode(i.slice()));
|
|
579
|
+
const z = y.map((i) => crystals.NTT.encode(i.slice()));
|
|
484
580
|
const w = [];
|
|
485
581
|
for (let i = 0; i < K; i++) {
|
|
486
582
|
// w ← NTT−1(A ◦ NTT(y))
|
|
487
583
|
const wi = newPoly(N);
|
|
488
584
|
for (let j = 0; j < L; j++) polyAdd(wi, MultiplyNTTs(A[i][j], z[j]));
|
|
489
|
-
NTT.decode(wi);
|
|
585
|
+
crystals.NTT.decode(wi);
|
|
490
586
|
w.push(wi);
|
|
491
587
|
}
|
|
492
588
|
const w1 = w.map((j) => j.map(HighBits)); // w1 ← HighBits(w)
|
|
@@ -497,21 +593,22 @@ function getDilithium(opts: DilithiumOpts) {
|
|
|
497
593
|
.update(W1Vec.encode(w1))
|
|
498
594
|
.digest();
|
|
499
595
|
// Verifer’s challenge
|
|
500
|
-
|
|
596
|
+
// c ← SampleInBall(c˜1); cˆ ← NTT(c)
|
|
597
|
+
const cHat = crystals.NTT.encode(SampleInBall(cTilde));
|
|
501
598
|
// ⟨⟨cs1⟩⟩ ← NTT−1(cˆ◦ sˆ1)
|
|
502
599
|
const cs1 = s1.map((i) => MultiplyNTTs(i, cHat));
|
|
503
600
|
for (let i = 0; i < L; i++) {
|
|
504
|
-
polyAdd(NTT.decode(cs1[i]), y[i]); // z ← y + ⟨⟨cs1⟩⟩
|
|
601
|
+
polyAdd(crystals.NTT.decode(cs1[i]), y[i]); // z ← y + ⟨⟨cs1⟩⟩
|
|
505
602
|
if (polyChknorm(cs1[i], GAMMA1 - BETA)) continue main_loop; // ||z||∞ ≥ γ1 − β
|
|
506
603
|
}
|
|
507
604
|
// cs1 is now z (▷ Signer’s response)
|
|
508
605
|
let cnt = 0;
|
|
509
606
|
const h = [];
|
|
510
607
|
for (let i = 0; i < K; i++) {
|
|
511
|
-
const cs2 = NTT.decode(MultiplyNTTs(s2[i], cHat)); // ⟨⟨cs2⟩⟩ ← NTT−1(cˆ◦ sˆ2)
|
|
608
|
+
const cs2 = crystals.NTT.decode(MultiplyNTTs(s2[i], cHat)); // ⟨⟨cs2⟩⟩ ← NTT−1(cˆ◦ sˆ2)
|
|
512
609
|
const r0 = polySub(w[i], cs2).map(LowBits); // r0 ← LowBits(w − ⟨⟨cs2⟩⟩)
|
|
513
610
|
if (polyChknorm(r0, GAMMA2 - BETA)) continue main_loop; // ||r0||∞ ≥ γ2 − β
|
|
514
|
-
const ct0 = NTT.decode(MultiplyNTTs(t0[i], cHat)); // ⟨⟨ct0⟩⟩ ← NTT−1(cˆ◦ tˆ0)
|
|
611
|
+
const ct0 = crystals.NTT.decode(MultiplyNTTs(t0[i], cHat)); // ⟨⟨ct0⟩⟩ ← NTT−1(cˆ◦ tˆ0)
|
|
515
612
|
if (polyChknorm(ct0, GAMMA2)) continue main_loop;
|
|
516
613
|
polyAdd(r0, ct0);
|
|
517
614
|
// ▷ Signer’s hint
|
|
@@ -523,17 +620,21 @@ function getDilithium(opts: DilithiumOpts) {
|
|
|
523
620
|
x256.clean();
|
|
524
621
|
const res = sigCoder.encode([cTilde, cs1, h]); // σ ← sigEncode(c˜, z mod±q, h)
|
|
525
622
|
// rho, _K, tr is subarray of secretKey, cannot clean.
|
|
526
|
-
cleanBytes(cTilde, cs1, h, cHat, w1, w, z, y, rhoprime,
|
|
527
|
-
|
|
623
|
+
cleanBytes(cTilde, cs1, h, cHat, w1, w, z, y, rhoprime, s1, s2, t0, ...A);
|
|
624
|
+
// `externalMu` hands ownership of `mu` to the caller,
|
|
625
|
+
// so only wipe the internally derived digest form here;
|
|
626
|
+
// zeroizing caller memory would break the caller's own reuse / verify path.
|
|
627
|
+
if (!externalMu) cleanBytes(mu);
|
|
628
|
+
return res as TRet<Uint8Array>;
|
|
528
629
|
}
|
|
529
630
|
// @ts-ignore
|
|
530
631
|
throw new Error('Unreachable code path reached, report this error');
|
|
531
632
|
},
|
|
532
633
|
verify: (
|
|
533
|
-
sig: Uint8Array
|
|
534
|
-
msg: Uint8Array
|
|
535
|
-
publicKey: Uint8Array
|
|
536
|
-
opts: DSAInternalOpts = {}
|
|
634
|
+
sig: TArg<Uint8Array>,
|
|
635
|
+
msg: TArg<Uint8Array>,
|
|
636
|
+
publicKey: TArg<Uint8Array>,
|
|
637
|
+
opts: TArg<DSAInternalOpts> = {}
|
|
537
638
|
) => {
|
|
538
639
|
validateInternalOpts(opts);
|
|
539
640
|
const { externalMu = false } = opts;
|
|
@@ -542,27 +643,30 @@ function getDilithium(opts: DilithiumOpts) {
|
|
|
542
643
|
const tr = shake256(publicKey, { dkLen: TR_BYTES }); // 6: tr ← H(BytesToBits(pk), 512)
|
|
543
644
|
|
|
544
645
|
if (sig.length !== sigCoder.bytesLen) return false; // return false instead of exception
|
|
545
|
-
|
|
646
|
+
// (c˜, z, h) ← sigDecode(σ)
|
|
647
|
+
// ▷ Signer’s commitment hash c ˜, response z and hint
|
|
648
|
+
const [cTilde, z, h] = sigCoder.decode(sig);
|
|
546
649
|
if (h === false) return false; // if h = ⊥ then return false
|
|
547
650
|
for (let i = 0; i < L; i++) if (polyChknorm(z[i], GAMMA1 - BETA)) return false;
|
|
548
651
|
const mu = externalMu
|
|
549
652
|
? msg
|
|
550
|
-
:
|
|
653
|
+
: // 7: µ ← H(tr||M, 512)
|
|
654
|
+
shake256.create({ dkLen: CRH_BYTES }).update(tr).update(msg).digest();
|
|
551
655
|
// Compute verifer’s challenge from c˜
|
|
552
|
-
const c = NTT.encode(SampleInBall(cTilde)); // c ← SampleInBall(c˜1)
|
|
656
|
+
const c = crystals.NTT.encode(SampleInBall(cTilde)); // c ← SampleInBall(c˜1)
|
|
553
657
|
const zNtt = z.map((i) => i.slice()); // zNtt = NTT(z)
|
|
554
|
-
for (let i = 0; i < L; i++) NTT.encode(zNtt[i]);
|
|
658
|
+
for (let i = 0; i < L; i++) crystals.NTT.encode(zNtt[i]);
|
|
555
659
|
const wTick1 = [];
|
|
556
660
|
const xof = XOF128(rho);
|
|
557
661
|
for (let i = 0; i < K; i++) {
|
|
558
|
-
const ct12d = MultiplyNTTs(NTT.encode(polyShiftl(t1[i])), c); //c * t1 * (2**d)
|
|
662
|
+
const ct12d = MultiplyNTTs(crystals.NTT.encode(polyShiftl(t1[i])), c); //c * t1 * (2**d)
|
|
559
663
|
const Az = newPoly(N); // // A * z
|
|
560
664
|
for (let j = 0; j < L; j++) {
|
|
561
665
|
const aij = RejNTTPoly(xof.get(j, i)); // A[i][j] inplace
|
|
562
666
|
polyAdd(Az, MultiplyNTTs(aij, zNtt[j]));
|
|
563
667
|
}
|
|
564
668
|
// wApprox = A*z - c*t1 * (2**d)
|
|
565
|
-
const wApprox = NTT.decode(polySub(Az, ct12d));
|
|
669
|
+
const wApprox = crystals.NTT.decode(polySub(Az, ct12d));
|
|
566
670
|
// Reconstruction of signer’s commitment
|
|
567
671
|
wTick1.push(polyUseHint(wApprox, h[i])); // w ′ ← UseHint(h, w'approx )
|
|
568
672
|
}
|
|
@@ -582,78 +686,99 @@ function getDilithium(opts: DilithiumOpts) {
|
|
|
582
686
|
for (const t of z) if (polyChknorm(t, GAMMA1 - BETA)) return false;
|
|
583
687
|
return equalBytes(cTilde, c2);
|
|
584
688
|
},
|
|
585
|
-
};
|
|
586
|
-
return {
|
|
587
|
-
info: { type: 'ml-dsa' },
|
|
689
|
+
});
|
|
690
|
+
return Object.freeze({
|
|
691
|
+
info: Object.freeze({ type: 'ml-dsa' }),
|
|
588
692
|
internal,
|
|
589
693
|
securityLevel: securityLevel,
|
|
590
694
|
keygen: internal.keygen,
|
|
591
695
|
lengths: internal.lengths,
|
|
592
696
|
getPublicKey: internal.getPublicKey,
|
|
593
|
-
sign: (
|
|
697
|
+
sign: (
|
|
698
|
+
msg: TArg<Uint8Array>,
|
|
699
|
+
secretKey: TArg<Uint8Array>,
|
|
700
|
+
opts: TArg<SigOpts> = {}
|
|
701
|
+
): TRet<Uint8Array> => {
|
|
594
702
|
validateSigOpts(opts);
|
|
595
703
|
const M = getMessage(msg, opts.context);
|
|
596
704
|
const res = internal.sign(M, secretKey, opts);
|
|
597
705
|
cleanBytes(M);
|
|
598
|
-
return res
|
|
706
|
+
return res as TRet<Uint8Array>;
|
|
599
707
|
},
|
|
600
|
-
verify: (
|
|
708
|
+
verify: (
|
|
709
|
+
sig: TArg<Uint8Array>,
|
|
710
|
+
msg: TArg<Uint8Array>,
|
|
711
|
+
publicKey: TArg<Uint8Array>,
|
|
712
|
+
opts: TArg<VerOpts> = {}
|
|
713
|
+
) => {
|
|
601
714
|
validateVerOpts(opts);
|
|
602
715
|
return internal.verify(sig, getMessage(msg, opts.context), publicKey);
|
|
603
716
|
},
|
|
604
717
|
prehash: (hash: CHash) => {
|
|
605
718
|
checkHash(hash, securityLevel);
|
|
606
|
-
return {
|
|
607
|
-
info: { type: 'hashml-dsa' },
|
|
719
|
+
return Object.freeze({
|
|
720
|
+
info: Object.freeze({ type: 'hashml-dsa' }),
|
|
608
721
|
securityLevel: securityLevel,
|
|
609
722
|
lengths: internal.lengths,
|
|
610
723
|
keygen: internal.keygen,
|
|
611
724
|
getPublicKey: internal.getPublicKey,
|
|
612
|
-
sign: (
|
|
725
|
+
sign: (
|
|
726
|
+
msg: TArg<Uint8Array>,
|
|
727
|
+
secretKey: TArg<Uint8Array>,
|
|
728
|
+
opts: TArg<SigOpts> = {}
|
|
729
|
+
): TRet<Uint8Array> => {
|
|
613
730
|
validateSigOpts(opts);
|
|
614
731
|
const M = getMessagePrehash(hash, msg, opts.context);
|
|
615
732
|
const res = internal.sign(M, secretKey, opts);
|
|
616
733
|
cleanBytes(M);
|
|
617
|
-
return res
|
|
734
|
+
return res as TRet<Uint8Array>;
|
|
618
735
|
},
|
|
619
|
-
verify: (
|
|
736
|
+
verify: (
|
|
737
|
+
sig: TArg<Uint8Array>,
|
|
738
|
+
msg: TArg<Uint8Array>,
|
|
739
|
+
publicKey: TArg<Uint8Array>,
|
|
740
|
+
opts: TArg<VerOpts> = {}
|
|
741
|
+
) => {
|
|
620
742
|
validateVerOpts(opts);
|
|
621
743
|
return internal.verify(sig, getMessagePrehash(hash, msg, opts.context), publicKey);
|
|
622
744
|
},
|
|
623
|
-
};
|
|
745
|
+
});
|
|
624
746
|
},
|
|
625
|
-
};
|
|
747
|
+
});
|
|
626
748
|
}
|
|
627
749
|
|
|
628
750
|
/** ML-DSA-44 for 128-bit security level. Not recommended after 2030, as per ASD. */
|
|
629
|
-
export const ml_dsa44: DSA = /* @__PURE__ */
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
751
|
+
export const ml_dsa44: TRet<DSA> = /* @__PURE__ */ (() =>
|
|
752
|
+
getDilithium({
|
|
753
|
+
...PARAMS[2],
|
|
754
|
+
CRH_BYTES: 64,
|
|
755
|
+
TR_BYTES: 64,
|
|
756
|
+
C_TILDE_BYTES: 32,
|
|
757
|
+
XOF128,
|
|
758
|
+
XOF256,
|
|
759
|
+
securityLevel: 128,
|
|
760
|
+
}))();
|
|
638
761
|
|
|
639
762
|
/** ML-DSA-65 for 192-bit security level. Not recommended after 2030, as per ASD. */
|
|
640
|
-
export const ml_dsa65: DSA = /* @__PURE__ */
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
763
|
+
export const ml_dsa65: TRet<DSA> = /* @__PURE__ */ (() =>
|
|
764
|
+
getDilithium({
|
|
765
|
+
...PARAMS[3],
|
|
766
|
+
CRH_BYTES: 64,
|
|
767
|
+
TR_BYTES: 64,
|
|
768
|
+
C_TILDE_BYTES: 48,
|
|
769
|
+
XOF128,
|
|
770
|
+
XOF256,
|
|
771
|
+
securityLevel: 192,
|
|
772
|
+
}))();
|
|
649
773
|
|
|
650
774
|
/** ML-DSA-87 for 256-bit security level. OK after 2030, as per ASD. */
|
|
651
|
-
export const ml_dsa87: DSA = /* @__PURE__ */
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
775
|
+
export const ml_dsa87: TRet<DSA> = /* @__PURE__ */ (() =>
|
|
776
|
+
getDilithium({
|
|
777
|
+
...PARAMS[5],
|
|
778
|
+
CRH_BYTES: 64,
|
|
779
|
+
TR_BYTES: 64,
|
|
780
|
+
C_TILDE_BYTES: 64,
|
|
781
|
+
XOF128,
|
|
782
|
+
XOF256,
|
|
783
|
+
securityLevel: 256,
|
|
784
|
+
}))();
|