@theqrl/dilithium5 0.1.1 → 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/dist/cjs/dilithium5.js +251 -511
- package/dist/mjs/dilithium5.js +249 -498
- package/package.json +7 -5
- package/src/index.d.ts +194 -0
- package/src/index.js +1 -0
package/dist/mjs/dilithium5.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { shake128, shake256 } from '@noble/hashes/sha3';
|
|
1
2
|
import pkg from 'randombytes';
|
|
2
|
-
import { SHAKE } from 'sha3';
|
|
3
3
|
|
|
4
4
|
const Shake128Rate = 168;
|
|
5
5
|
const Shake256Rate = 136;
|
|
@@ -8,6 +8,7 @@ const Stream256BlockBytes = Shake256Rate;
|
|
|
8
8
|
|
|
9
9
|
const SeedBytes = 32;
|
|
10
10
|
const CRHBytes = 64;
|
|
11
|
+
const TRBytes = 64;
|
|
11
12
|
const N = 256;
|
|
12
13
|
const Q = 8380417;
|
|
13
14
|
const QInv = 58728449;
|
|
@@ -31,7 +32,7 @@ const PolyW1PackedBytes = 128;
|
|
|
31
32
|
|
|
32
33
|
const CryptoPublicKeyBytes = SeedBytes + K * PolyT1PackedBytes;
|
|
33
34
|
const CryptoSecretKeyBytes =
|
|
34
|
-
|
|
35
|
+
2 * SeedBytes + TRBytes + L * PolyETAPackedBytes + K * PolyETAPackedBytes + K * PolyT0PackedBytes;
|
|
35
36
|
const CryptoBytes = SeedBytes + L * PolyZPackedBytes + PolyVecHPackedBytes;
|
|
36
37
|
|
|
37
38
|
const PolyUniformNBlocks = Math.floor((768 + Stream128BlockBytes - 1) / Stream128BlockBytes);
|
|
@@ -62,452 +63,65 @@ const zetas = [
|
|
|
62
63
|
-1362209, 3937738, 1400424, -846154, 1976782,
|
|
63
64
|
];
|
|
64
65
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
0x0000000000008082n,
|
|
70
|
-
0x800000000000808an,
|
|
71
|
-
0x8000000080008000n,
|
|
72
|
-
0x000000000000808bn,
|
|
73
|
-
0x0000000080000001n,
|
|
74
|
-
0x8000000080008081n,
|
|
75
|
-
0x8000000000008009n,
|
|
76
|
-
0x000000000000008an,
|
|
77
|
-
0x0000000000000088n,
|
|
78
|
-
0x0000000080008009n,
|
|
79
|
-
0x000000008000000an,
|
|
80
|
-
0x000000008000808bn,
|
|
81
|
-
0x800000000000008bn,
|
|
82
|
-
0x8000000000008089n,
|
|
83
|
-
0x8000000000008003n,
|
|
84
|
-
0x8000000000008002n,
|
|
85
|
-
0x8000000000000080n,
|
|
86
|
-
0x000000000000800an,
|
|
87
|
-
0x800000008000000an,
|
|
88
|
-
0x8000000080008081n,
|
|
89
|
-
0x8000000000008080n,
|
|
90
|
-
0x0000000080000001n,
|
|
91
|
-
0x8000000080008008n,
|
|
92
|
-
]);
|
|
66
|
+
/**
|
|
67
|
+
* FIPS 202 SHAKE functions using @noble/hashes
|
|
68
|
+
* Provides streaming XOF (extendable output function) interface
|
|
69
|
+
*/
|
|
93
70
|
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Keccak state wrapper for @noble/hashes
|
|
74
|
+
* Maintains hasher instance for streaming operations
|
|
75
|
+
*/
|
|
94
76
|
class KeccakState {
|
|
95
77
|
constructor() {
|
|
96
|
-
this.
|
|
97
|
-
this.
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function ROL(a, offset) {
|
|
102
|
-
return BigInt.asUintN(64, BigInt.asUintN(64, a << offset) ^ (a >> (64n - offset)));
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function load64(x, xOffset) {
|
|
106
|
-
let r = BigInt(0);
|
|
107
|
-
|
|
108
|
-
for (let i = 0; i < 8; i++) r = BigInt.asUintN(64, r | BigInt.asUintN(64, BigInt(x[xOffset + i]) << BigInt(8 * i)));
|
|
109
|
-
|
|
110
|
-
return r;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function store64(xP, xOffset, u) {
|
|
114
|
-
const x = xP;
|
|
115
|
-
for (let i = 0; i < 8; i++) x[xOffset + i] = Number((u >> BigInt(8 * i)) & 0xffn);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function KeccakF1600StatePermute(stateP) {
|
|
119
|
-
const state = stateP;
|
|
120
|
-
// copyFromState(A, state)
|
|
121
|
-
let Aba = state[0];
|
|
122
|
-
let Abe = state[1];
|
|
123
|
-
let Abi = state[2];
|
|
124
|
-
let Abo = state[3];
|
|
125
|
-
let Abu = state[4];
|
|
126
|
-
let Aga = state[5];
|
|
127
|
-
let Age = state[6];
|
|
128
|
-
let Agi = state[7];
|
|
129
|
-
let Ago = state[8];
|
|
130
|
-
let Agu = state[9];
|
|
131
|
-
let Aka = state[10];
|
|
132
|
-
let Ake = state[11];
|
|
133
|
-
let Aki = state[12];
|
|
134
|
-
let Ako = state[13];
|
|
135
|
-
let Aku = state[14];
|
|
136
|
-
let Ama = state[15];
|
|
137
|
-
let Ame = state[16];
|
|
138
|
-
let Ami = state[17];
|
|
139
|
-
let Amo = state[18];
|
|
140
|
-
let Amu = state[19];
|
|
141
|
-
let Asa = state[20];
|
|
142
|
-
let Ase = state[21];
|
|
143
|
-
let Asi = state[22];
|
|
144
|
-
let Aso = state[23];
|
|
145
|
-
let Asu = state[24];
|
|
146
|
-
|
|
147
|
-
for (let round = 0; round < NRounds; round += 2) {
|
|
148
|
-
// prepareTheta
|
|
149
|
-
let BCa = BigInt.asUintN(64, Aba ^ Aga ^ Aka ^ Ama ^ Asa);
|
|
150
|
-
let BCe = BigInt.asUintN(64, Abe ^ Age ^ Ake ^ Ame ^ Ase);
|
|
151
|
-
let BCi = BigInt.asUintN(64, Abi ^ Agi ^ Aki ^ Ami ^ Asi);
|
|
152
|
-
let BCo = BigInt.asUintN(64, Abo ^ Ago ^ Ako ^ Amo ^ Aso);
|
|
153
|
-
let BCu = BigInt.asUintN(64, Abu ^ Agu ^ Aku ^ Amu ^ Asu);
|
|
154
|
-
|
|
155
|
-
// thetaRhoPiChiIotaPrepareTheta(round, A, E)
|
|
156
|
-
let Da = BigInt.asUintN(64, BCu ^ ROL(BCe, 1n));
|
|
157
|
-
let De = BigInt.asUintN(64, BCa ^ ROL(BCi, 1n));
|
|
158
|
-
let Di = BigInt.asUintN(64, BCe ^ ROL(BCo, 1n));
|
|
159
|
-
let Do = BigInt.asUintN(64, BCi ^ ROL(BCu, 1n));
|
|
160
|
-
let Du = BigInt.asUintN(64, BCo ^ ROL(BCa, 1n));
|
|
161
|
-
|
|
162
|
-
Aba = BigInt.asUintN(64, Aba ^ Da);
|
|
163
|
-
BCa = Aba;
|
|
164
|
-
Age = BigInt.asUintN(64, Age ^ De);
|
|
165
|
-
BCe = ROL(Age, 44n);
|
|
166
|
-
Aki = BigInt.asUintN(64, Aki ^ Di);
|
|
167
|
-
BCi = ROL(Aki, 43n);
|
|
168
|
-
Amo = BigInt.asUintN(64, Amo ^ Do);
|
|
169
|
-
BCo = ROL(Amo, 21n);
|
|
170
|
-
Asu = BigInt.asUintN(64, Asu ^ Du);
|
|
171
|
-
BCu = ROL(Asu, 14n);
|
|
172
|
-
let Eba = BigInt.asUintN(64, BCa ^ (~BCe & BCi));
|
|
173
|
-
Eba = BigInt.asUintN(64, Eba ^ KeccakFRoundConstants[round]);
|
|
174
|
-
let Ebe = BigInt.asUintN(64, BCe ^ (~BCi & BCo));
|
|
175
|
-
let Ebi = BigInt.asUintN(64, BCi ^ (~BCo & BCu));
|
|
176
|
-
let Ebo = BigInt.asUintN(64, BCo ^ (~BCu & BCa));
|
|
177
|
-
let Ebu = BigInt.asUintN(64, BCu ^ (~BCa & BCe));
|
|
178
|
-
|
|
179
|
-
Abo = BigInt.asUintN(64, Abo ^ Do);
|
|
180
|
-
BCa = ROL(Abo, 28n);
|
|
181
|
-
Agu = BigInt.asUintN(64, Agu ^ Du);
|
|
182
|
-
BCe = ROL(Agu, 20n);
|
|
183
|
-
Aka = BigInt.asUintN(64, Aka ^ Da);
|
|
184
|
-
BCi = ROL(Aka, 3n);
|
|
185
|
-
Ame = BigInt.asUintN(64, Ame ^ De);
|
|
186
|
-
BCo = ROL(Ame, 45n);
|
|
187
|
-
Asi = BigInt.asUintN(64, Asi ^ Di);
|
|
188
|
-
BCu = ROL(Asi, 61n);
|
|
189
|
-
let Ega = BigInt.asUintN(64, BCa ^ (~BCe & BCi));
|
|
190
|
-
let Ege = BigInt.asUintN(64, BCe ^ (~BCi & BCo));
|
|
191
|
-
let Egi = BigInt.asUintN(64, BCi ^ (~BCo & BCu));
|
|
192
|
-
let Ego = BigInt.asUintN(64, BCo ^ (~BCu & BCa));
|
|
193
|
-
let Egu = BigInt.asUintN(64, BCu ^ (~BCa & BCe));
|
|
194
|
-
|
|
195
|
-
Abe = BigInt.asUintN(64, Abe ^ De);
|
|
196
|
-
BCa = ROL(Abe, 1n);
|
|
197
|
-
Agi = BigInt.asUintN(64, Agi ^ Di);
|
|
198
|
-
BCe = ROL(Agi, 6n);
|
|
199
|
-
Ako = BigInt.asUintN(64, Ako ^ Do);
|
|
200
|
-
BCi = ROL(Ako, 25n);
|
|
201
|
-
Amu = BigInt.asUintN(64, Amu ^ Du);
|
|
202
|
-
BCo = ROL(Amu, 8n);
|
|
203
|
-
Asa = BigInt.asUintN(64, Asa ^ Da);
|
|
204
|
-
BCu = ROL(Asa, 18n);
|
|
205
|
-
let Eka = BigInt.asUintN(64, BCa ^ (~BCe & BCi));
|
|
206
|
-
let Eke = BigInt.asUintN(64, BCe ^ (~BCi & BCo));
|
|
207
|
-
let Eki = BigInt.asUintN(64, BCi ^ (~BCo & BCu));
|
|
208
|
-
let Eko = BigInt.asUintN(64, BCo ^ (~BCu & BCa));
|
|
209
|
-
let Eku = BigInt.asUintN(64, BCu ^ (~BCa & BCe));
|
|
210
|
-
|
|
211
|
-
Abu = BigInt.asUintN(64, Abu ^ Du);
|
|
212
|
-
BCa = ROL(Abu, 27n);
|
|
213
|
-
Aga = BigInt.asUintN(64, Aga ^ Da);
|
|
214
|
-
BCe = ROL(Aga, 36n);
|
|
215
|
-
Ake = BigInt.asUintN(64, Ake ^ De);
|
|
216
|
-
BCi = ROL(Ake, 10n);
|
|
217
|
-
Ami = BigInt.asUintN(64, Ami ^ Di);
|
|
218
|
-
BCo = ROL(Ami, 15n);
|
|
219
|
-
Aso = BigInt.asUintN(64, Aso ^ Do);
|
|
220
|
-
BCu = ROL(Aso, 56n);
|
|
221
|
-
let Ema = BigInt.asUintN(64, BCa ^ (~BCe & BCi));
|
|
222
|
-
let Eme = BigInt.asUintN(64, BCe ^ (~BCi & BCo));
|
|
223
|
-
let Emi = BigInt.asUintN(64, BCi ^ (~BCo & BCu));
|
|
224
|
-
let Emo = BigInt.asUintN(64, BCo ^ (~BCu & BCa));
|
|
225
|
-
let Emu = BigInt.asUintN(64, BCu ^ (~BCa & BCe));
|
|
226
|
-
|
|
227
|
-
Abi = BigInt.asUintN(64, Abi ^ Di);
|
|
228
|
-
BCa = ROL(Abi, 62n);
|
|
229
|
-
Ago = BigInt.asUintN(64, Ago ^ Do);
|
|
230
|
-
BCe = ROL(Ago, 55n);
|
|
231
|
-
Aku = BigInt.asUintN(64, Aku ^ Du);
|
|
232
|
-
BCi = ROL(Aku, 39n);
|
|
233
|
-
Ama = BigInt.asUintN(64, Ama ^ Da);
|
|
234
|
-
BCo = ROL(Ama, 41n);
|
|
235
|
-
Ase = BigInt.asUintN(64, Ase ^ De);
|
|
236
|
-
BCu = ROL(Ase, 2n);
|
|
237
|
-
let Esa = BigInt.asUintN(64, BCa ^ (~BCe & BCi));
|
|
238
|
-
let Ese = BigInt.asUintN(64, BCe ^ (~BCi & BCo));
|
|
239
|
-
let Esi = BigInt.asUintN(64, BCi ^ (~BCo & BCu));
|
|
240
|
-
let Eso = BigInt.asUintN(64, BCo ^ (~BCu & BCa));
|
|
241
|
-
let Esu = BigInt.asUintN(64, BCu ^ (~BCa & BCe));
|
|
242
|
-
|
|
243
|
-
// prepareTheta
|
|
244
|
-
BCa = BigInt.asUintN(64, Eba ^ Ega ^ Eka ^ Ema ^ Esa);
|
|
245
|
-
BCe = BigInt.asUintN(64, Ebe ^ Ege ^ Eke ^ Eme ^ Ese);
|
|
246
|
-
BCi = BigInt.asUintN(64, Ebi ^ Egi ^ Eki ^ Emi ^ Esi);
|
|
247
|
-
BCo = BigInt.asUintN(64, Ebo ^ Ego ^ Eko ^ Emo ^ Eso);
|
|
248
|
-
BCu = BigInt.asUintN(64, Ebu ^ Egu ^ Eku ^ Emu ^ Esu);
|
|
249
|
-
|
|
250
|
-
// thetaRhoPiChiIotaPrepareTheta(round+1, E, A)
|
|
251
|
-
Da = BigInt.asUintN(64, BCu ^ ROL(BCe, 1n));
|
|
252
|
-
De = BigInt.asUintN(64, BCa ^ ROL(BCi, 1n));
|
|
253
|
-
Di = BigInt.asUintN(64, BCe ^ ROL(BCo, 1n));
|
|
254
|
-
Do = BigInt.asUintN(64, BCi ^ ROL(BCu, 1n));
|
|
255
|
-
Du = BigInt.asUintN(64, BCo ^ ROL(BCa, 1n));
|
|
256
|
-
|
|
257
|
-
Eba = BigInt.asUintN(64, Eba ^ Da);
|
|
258
|
-
BCa = Eba;
|
|
259
|
-
Ege = BigInt.asUintN(64, Ege ^ De);
|
|
260
|
-
BCe = ROL(Ege, 44n);
|
|
261
|
-
Eki = BigInt.asUintN(64, Eki ^ Di);
|
|
262
|
-
BCi = ROL(Eki, 43n);
|
|
263
|
-
Emo = BigInt.asUintN(64, Emo ^ Do);
|
|
264
|
-
BCo = ROL(Emo, 21n);
|
|
265
|
-
Esu = BigInt.asUintN(64, Esu ^ Du);
|
|
266
|
-
BCu = ROL(Esu, 14n);
|
|
267
|
-
Aba = BigInt.asUintN(64, BCa ^ (~BCe & BCi));
|
|
268
|
-
Aba = BigInt.asUintN(64, Aba ^ KeccakFRoundConstants[round + 1]);
|
|
269
|
-
Abe = BigInt.asUintN(64, BCe ^ (~BCi & BCo));
|
|
270
|
-
Abi = BigInt.asUintN(64, BCi ^ (~BCo & BCu));
|
|
271
|
-
Abo = BigInt.asUintN(64, BCo ^ (~BCu & BCa));
|
|
272
|
-
Abu = BigInt.asUintN(64, BCu ^ (~BCa & BCe));
|
|
273
|
-
|
|
274
|
-
Ebo = BigInt.asUintN(64, Ebo ^ Do);
|
|
275
|
-
BCa = ROL(Ebo, 28n);
|
|
276
|
-
Egu = BigInt.asUintN(64, Egu ^ Du);
|
|
277
|
-
BCe = ROL(Egu, 20n);
|
|
278
|
-
Eka = BigInt.asUintN(64, Eka ^ Da);
|
|
279
|
-
BCi = ROL(Eka, 3n);
|
|
280
|
-
Eme = BigInt.asUintN(64, Eme ^ De);
|
|
281
|
-
BCo = ROL(Eme, 45n);
|
|
282
|
-
Esi = BigInt.asUintN(64, Esi ^ Di);
|
|
283
|
-
BCu = ROL(Esi, 61n);
|
|
284
|
-
Aga = BigInt.asUintN(64, BCa ^ (~BCe & BCi));
|
|
285
|
-
Age = BigInt.asUintN(64, BCe ^ (~BCi & BCo));
|
|
286
|
-
Agi = BigInt.asUintN(64, BCi ^ (~BCo & BCu));
|
|
287
|
-
Ago = BigInt.asUintN(64, BCo ^ (~BCu & BCa));
|
|
288
|
-
Agu = BigInt.asUintN(64, BCu ^ (~BCa & BCe));
|
|
289
|
-
|
|
290
|
-
Ebe = BigInt.asUintN(64, Ebe ^ De);
|
|
291
|
-
BCa = ROL(Ebe, 1n);
|
|
292
|
-
Egi = BigInt.asUintN(64, Egi ^ Di);
|
|
293
|
-
BCe = ROL(Egi, 6n);
|
|
294
|
-
Eko = BigInt.asUintN(64, Eko ^ Do);
|
|
295
|
-
BCi = ROL(Eko, 25n);
|
|
296
|
-
Emu = BigInt.asUintN(64, Emu ^ Du);
|
|
297
|
-
BCo = ROL(Emu, 8n);
|
|
298
|
-
Esa = BigInt.asUintN(64, Esa ^ Da);
|
|
299
|
-
BCu = ROL(Esa, 18n);
|
|
300
|
-
Aka = BigInt.asUintN(64, BCa ^ (~BCe & BCi));
|
|
301
|
-
Ake = BigInt.asUintN(64, BCe ^ (~BCi & BCo));
|
|
302
|
-
Aki = BigInt.asUintN(64, BCi ^ (~BCo & BCu));
|
|
303
|
-
Ako = BigInt.asUintN(64, BCo ^ (~BCu & BCa));
|
|
304
|
-
Aku = BigInt.asUintN(64, BCu ^ (~BCa & BCe));
|
|
305
|
-
|
|
306
|
-
Ebu = BigInt.asUintN(64, Ebu ^ Du);
|
|
307
|
-
BCa = ROL(Ebu, 27n);
|
|
308
|
-
Ega = BigInt.asUintN(64, Ega ^ Da);
|
|
309
|
-
BCe = ROL(Ega, 36n);
|
|
310
|
-
Eke = BigInt.asUintN(64, Eke ^ De);
|
|
311
|
-
BCi = ROL(Eke, 10n);
|
|
312
|
-
Emi = BigInt.asUintN(64, Emi ^ Di);
|
|
313
|
-
BCo = ROL(Emi, 15n);
|
|
314
|
-
Eso = BigInt.asUintN(64, Eso ^ Do);
|
|
315
|
-
BCu = ROL(Eso, 56n);
|
|
316
|
-
Ama = BigInt.asUintN(64, BCa ^ (~BCe & BCi));
|
|
317
|
-
Ame = BigInt.asUintN(64, BCe ^ (~BCi & BCo));
|
|
318
|
-
Ami = BigInt.asUintN(64, BCi ^ (~BCo & BCu));
|
|
319
|
-
Amo = BigInt.asUintN(64, BCo ^ (~BCu & BCa));
|
|
320
|
-
Amu = BigInt.asUintN(64, BCu ^ (~BCa & BCe));
|
|
321
|
-
|
|
322
|
-
Ebi = BigInt.asUintN(64, Ebi ^ Di);
|
|
323
|
-
BCa = ROL(Ebi, 62n);
|
|
324
|
-
Ego = BigInt.asUintN(64, Ego ^ Do);
|
|
325
|
-
BCe = ROL(Ego, 55n);
|
|
326
|
-
Eku = BigInt.asUintN(64, Eku ^ Du);
|
|
327
|
-
BCi = ROL(Eku, 39n);
|
|
328
|
-
Ema = BigInt.asUintN(64, Ema ^ Da);
|
|
329
|
-
BCo = ROL(Ema, 41n);
|
|
330
|
-
Ese = BigInt.asUintN(64, Ese ^ De);
|
|
331
|
-
BCu = ROL(Ese, 2n);
|
|
332
|
-
Asa = BigInt.asUintN(64, BCa ^ (~BCe & BCi));
|
|
333
|
-
Ase = BigInt.asUintN(64, BCe ^ (~BCi & BCo));
|
|
334
|
-
Asi = BigInt.asUintN(64, BCi ^ (~BCo & BCu));
|
|
335
|
-
Aso = BigInt.asUintN(64, BCo ^ (~BCu & BCa));
|
|
336
|
-
Asu = BigInt.asUintN(64, BCu ^ (~BCa & BCe));
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
state[0] = Aba;
|
|
340
|
-
state[1] = Abe;
|
|
341
|
-
state[2] = Abi;
|
|
342
|
-
state[3] = Abo;
|
|
343
|
-
state[4] = Abu;
|
|
344
|
-
state[5] = Aga;
|
|
345
|
-
state[6] = Age;
|
|
346
|
-
state[7] = Agi;
|
|
347
|
-
state[8] = Ago;
|
|
348
|
-
state[9] = Agu;
|
|
349
|
-
state[10] = Aka;
|
|
350
|
-
state[11] = Ake;
|
|
351
|
-
state[12] = Aki;
|
|
352
|
-
state[13] = Ako;
|
|
353
|
-
state[14] = Aku;
|
|
354
|
-
state[15] = Ama;
|
|
355
|
-
state[16] = Ame;
|
|
356
|
-
state[17] = Ami;
|
|
357
|
-
state[18] = Amo;
|
|
358
|
-
state[19] = Amu;
|
|
359
|
-
state[20] = Asa;
|
|
360
|
-
state[21] = Ase;
|
|
361
|
-
state[22] = Asi;
|
|
362
|
-
state[23] = Aso;
|
|
363
|
-
state[24] = Asu;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
function keccakInit(sP) {
|
|
367
|
-
const s = sP;
|
|
368
|
-
for (let i = 0; i < 25; i++) s[i] = 0n;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
function keccakAbsorb(sP, posP, r, input) {
|
|
372
|
-
const s = sP;
|
|
373
|
-
let pos = posP;
|
|
374
|
-
let inLen = input.length;
|
|
375
|
-
let i;
|
|
376
|
-
let inputOffset = 0;
|
|
377
|
-
while (pos + inLen >= r) {
|
|
378
|
-
for (i = pos; i < r; i++)
|
|
379
|
-
s[Math.floor(i / 8)] = BigInt.asUintN(
|
|
380
|
-
64,
|
|
381
|
-
s[Math.floor(i / 8)] ^ (BigInt(input[inputOffset++]) << BigInt(8 * (i % 8)))
|
|
382
|
-
);
|
|
383
|
-
inLen -= r - pos;
|
|
384
|
-
KeccakF1600StatePermute(s);
|
|
385
|
-
pos = 0;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
for (i = pos; i < pos + inLen; i++) {
|
|
389
|
-
s[Math.floor(i / 8)] = BigInt.asUintN(
|
|
390
|
-
64,
|
|
391
|
-
s[Math.floor(i / 8)] ^ (BigInt(input[inputOffset++]) << BigInt(8 * (i % 8)))
|
|
392
|
-
);
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
return i;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
function keccakFinalize(sP, pos, r, p) {
|
|
399
|
-
const s = sP;
|
|
400
|
-
s[Math.floor(pos / 8)] = BigInt.asUintN(64, s[Math.floor(pos / 8)] ^ (BigInt(p) << BigInt(8 * (pos % 8))));
|
|
401
|
-
s[Math.floor(r / 8) - 1] = BigInt.asUintN(64, s[Math.floor(r / 8) - 1] ^ (1n << 63n));
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
function keccakSqueeze(outP, s, posP, r) {
|
|
405
|
-
let pos = posP;
|
|
406
|
-
const out = outP;
|
|
407
|
-
let outLen = out.length;
|
|
408
|
-
let outputOffset = 0;
|
|
409
|
-
let i = 0;
|
|
410
|
-
|
|
411
|
-
while (outLen) {
|
|
412
|
-
if (pos === r) {
|
|
413
|
-
KeccakF1600StatePermute(s);
|
|
414
|
-
pos = 0;
|
|
415
|
-
}
|
|
416
|
-
for (i = pos; i < r && i < pos + outLen; i++) out[outputOffset++] = s[Math.floor(i / 8)] >> BigInt(8 * (i % 8));
|
|
417
|
-
outLen -= i - pos;
|
|
418
|
-
pos = i;
|
|
78
|
+
this.hasher = null;
|
|
79
|
+
this.finalized = false;
|
|
419
80
|
}
|
|
420
|
-
|
|
421
|
-
return pos;
|
|
422
81
|
}
|
|
423
82
|
|
|
424
|
-
|
|
425
|
-
const s = sP;
|
|
426
|
-
let inLen = input.length;
|
|
427
|
-
let inputOffset = 0;
|
|
428
|
-
let i;
|
|
429
|
-
|
|
430
|
-
for (i = 0; i < 25; i++) s[i] = 0;
|
|
83
|
+
// SHAKE-128 functions
|
|
431
84
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
inLen -= r;
|
|
436
|
-
KeccakF1600StatePermute(s);
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
for (i = 0; i < inLen; i++)
|
|
440
|
-
s[Math.floor(i / 8)] = BigInt.asUintN(
|
|
441
|
-
64,
|
|
442
|
-
s[Math.floor(i / 8)] ^ (BigInt(input[inputOffset + i]) << BigInt(8 * (i % 8)))
|
|
443
|
-
);
|
|
444
|
-
|
|
445
|
-
s[Math.floor(i / 8)] = BigInt.asUintN(64, s[Math.floor(i / 8)] ^ (BigInt(p) << BigInt(8 * (i % 8))));
|
|
446
|
-
s[Math.floor((r - 1) / 8)] = BigInt.asUintN(64, s[Math.floor((r - 1) / 8)] ^ (1n << 63n));
|
|
85
|
+
function shake128Init(state) {
|
|
86
|
+
state.hasher = shake128.create({});
|
|
87
|
+
state.finalized = false;
|
|
447
88
|
}
|
|
448
89
|
|
|
449
|
-
function
|
|
450
|
-
|
|
451
|
-
let outputOffset = outputOffsetP;
|
|
452
|
-
while (nBlocks) {
|
|
453
|
-
KeccakF1600StatePermute(s);
|
|
454
|
-
for (let i = 0; i < Math.floor(r / 8); i++) store64(output, outputOffset + 8 * i, s[i]);
|
|
455
|
-
outputOffset += r;
|
|
456
|
-
nBlocks -= 1;
|
|
457
|
-
}
|
|
90
|
+
function shake128Absorb(state, input) {
|
|
91
|
+
state.hasher.update(input);
|
|
458
92
|
}
|
|
459
93
|
|
|
460
|
-
function
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
state.pos = 0;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
function shake128Absorb(stateP, input) {
|
|
467
|
-
const state = stateP;
|
|
468
|
-
state.pos = keccakAbsorb(state.s, state.pos, Shake128Rate, input);
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
function shake128Finalize(stateP) {
|
|
472
|
-
const state = stateP;
|
|
473
|
-
keccakFinalize(state.s, state.pos, Shake128Rate, 0x1f);
|
|
474
|
-
state.pos = Shake128Rate;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
function shake128Squeeze(out, stateP) {
|
|
478
|
-
const state = stateP;
|
|
479
|
-
state.pos = keccakSqueeze(out, state.s, state.pos, Shake128Rate);
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
function shake128AbsorbOnce(stateP, input) {
|
|
483
|
-
const state = stateP;
|
|
484
|
-
keccakAbsorbOnce(state.s, Shake128Rate, input, 0x1f);
|
|
485
|
-
state.pos = Shake128Rate;
|
|
94
|
+
function shake128Finalize(state) {
|
|
95
|
+
// Mark as finalized - actual finalization happens on first xofInto call
|
|
96
|
+
state.finalized = true;
|
|
486
97
|
}
|
|
487
98
|
|
|
488
99
|
function shake128SqueezeBlocks(out, outputOffset, nBlocks, state) {
|
|
489
|
-
|
|
100
|
+
const len = nBlocks * Shake128Rate;
|
|
101
|
+
const output = out.subarray(outputOffset, outputOffset + len);
|
|
102
|
+
state.hasher.xofInto(output);
|
|
490
103
|
}
|
|
491
104
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
state.
|
|
105
|
+
// SHAKE-256 functions
|
|
106
|
+
|
|
107
|
+
function shake256Init(state) {
|
|
108
|
+
state.hasher = shake256.create({});
|
|
109
|
+
state.finalized = false;
|
|
496
110
|
}
|
|
497
111
|
|
|
498
|
-
function shake256Absorb(
|
|
499
|
-
|
|
500
|
-
state.pos = keccakAbsorb(state.s, state.pos, Shake256Rate, input);
|
|
112
|
+
function shake256Absorb(state, input) {
|
|
113
|
+
state.hasher.update(input);
|
|
501
114
|
}
|
|
502
115
|
|
|
503
|
-
function shake256Finalize(
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
state.pos = Shake256Rate;
|
|
116
|
+
function shake256Finalize(state) {
|
|
117
|
+
// Mark as finalized - actual finalization happens on first xofInto call
|
|
118
|
+
state.finalized = true;
|
|
507
119
|
}
|
|
508
120
|
|
|
509
121
|
function shake256SqueezeBlocks(out, outputOffset, nBlocks, state) {
|
|
510
|
-
|
|
122
|
+
const len = nBlocks * Shake256Rate;
|
|
123
|
+
const output = out.subarray(outputOffset, outputOffset + len);
|
|
124
|
+
state.hasher.xofInto(output);
|
|
511
125
|
}
|
|
512
126
|
|
|
513
127
|
function dilithiumShake128StreamInit(state, seed, nonce) {
|
|
@@ -565,9 +179,8 @@ function ntt(a) {
|
|
|
565
179
|
const zeta = zetas[++k];
|
|
566
180
|
for (j = start; j < start + len; ++j) {
|
|
567
181
|
const t = Number(montgomeryReduce(BigInt.asIntN(64, BigInt(zeta) * BigInt(a[j + len]))));
|
|
568
|
-
a[j + len] = a[j] - t;
|
|
569
|
-
|
|
570
|
-
a[j] = a[j] + t;
|
|
182
|
+
a[j + len] = a[j] - t;
|
|
183
|
+
a[j] += t;
|
|
571
184
|
}
|
|
572
185
|
}
|
|
573
186
|
}
|
|
@@ -583,15 +196,14 @@ function invNTTToMont(a) {
|
|
|
583
196
|
const zeta = BigInt.asIntN(32, BigInt(-zetas[--k]));
|
|
584
197
|
for (j = start; j < start + len; ++j) {
|
|
585
198
|
const t = a[j];
|
|
586
|
-
a[j] = t + a[j + len];
|
|
587
|
-
a[j + len] = t - a[j + len];
|
|
588
|
-
a[j + len] = Number(montgomeryReduce(BigInt.asIntN(64, zeta * BigInt(a[j + len]))));
|
|
199
|
+
a[j] = t + a[j + len];
|
|
200
|
+
a[j + len] = t - a[j + len];
|
|
201
|
+
a[j + len] = Number(montgomeryReduce(BigInt.asIntN(64, zeta * BigInt(a[j + len]))));
|
|
589
202
|
}
|
|
590
203
|
}
|
|
591
204
|
}
|
|
592
|
-
// eslint-disable-next-line no-shadow
|
|
593
205
|
for (let j = 0; j < N; ++j) {
|
|
594
|
-
a[j] = Number(montgomeryReduce(BigInt.asIntN(64, f * BigInt(a[j]))));
|
|
206
|
+
a[j] = Number(montgomeryReduce(BigInt.asIntN(64, f * BigInt(a[j]))));
|
|
595
207
|
}
|
|
596
208
|
}
|
|
597
209
|
|
|
@@ -944,7 +556,7 @@ function polyT0Pack(rP, rOffset, a) {
|
|
|
944
556
|
t[6] = (1 << (D - 1)) - a.coeffs[8 * i + 6];
|
|
945
557
|
t[7] = (1 << (D - 1)) - a.coeffs[8 * i + 7];
|
|
946
558
|
|
|
947
|
-
r[rOffset + 13 * i] = t[0];
|
|
559
|
+
r[rOffset + 13 * i] = t[0];
|
|
948
560
|
r[rOffset + 13 * i + 1] = t[0] >> 8;
|
|
949
561
|
r[rOffset + 13 * i + 1] |= t[1] << 5;
|
|
950
562
|
r[rOffset + 13 * i + 2] = t[1] >> 3;
|
|
@@ -1024,7 +636,7 @@ function polyZPack(rP, rOffset, a) {
|
|
|
1024
636
|
t[0] = GAMMA1 - a.coeffs[2 * i];
|
|
1025
637
|
t[1] = GAMMA1 - a.coeffs[2 * i + 1];
|
|
1026
638
|
|
|
1027
|
-
r[rOffset + 5 * i] = t[0];
|
|
639
|
+
r[rOffset + 5 * i] = t[0];
|
|
1028
640
|
r[rOffset + 5 * i + 1] = t[0] >> 8;
|
|
1029
641
|
r[rOffset + 5 * i + 2] = t[0] >> 16;
|
|
1030
642
|
r[rOffset + 5 * i + 2] |= t[1] << 4;
|
|
@@ -1071,7 +683,7 @@ function polyVecMatrixExpand(mat, rho) {
|
|
|
1071
683
|
|
|
1072
684
|
function polyVecMatrixPointWiseMontgomery(t, mat, v) {
|
|
1073
685
|
for (let i = 0; i < K; ++i) {
|
|
1074
|
-
polyVecLPointWiseAccMontgomery(t.vec[i], mat[i], v);
|
|
686
|
+
polyVecLPointWiseAccMontgomery(t.vec[i], mat[i], v);
|
|
1075
687
|
}
|
|
1076
688
|
}
|
|
1077
689
|
|
|
@@ -1272,10 +884,10 @@ function packSk(skp, rho, tr, key, t0, s1, s2) {
|
|
|
1272
884
|
}
|
|
1273
885
|
skOffset += SeedBytes;
|
|
1274
886
|
|
|
1275
|
-
for (let i = 0; i <
|
|
887
|
+
for (let i = 0; i < TRBytes; ++i) {
|
|
1276
888
|
sk[skOffset + i] = tr[i];
|
|
1277
889
|
}
|
|
1278
|
-
skOffset +=
|
|
890
|
+
skOffset += TRBytes;
|
|
1279
891
|
|
|
1280
892
|
for (let i = 0; i < L; ++i) {
|
|
1281
893
|
polyEtaPack(sk, skOffset + i * PolyETAPackedBytes, s1.vec[i]);
|
|
@@ -1307,10 +919,10 @@ function unpackSk(rhoP, trP, keyP, t0, s1, s2, sk) {
|
|
|
1307
919
|
}
|
|
1308
920
|
skOffset += SeedBytes;
|
|
1309
921
|
|
|
1310
|
-
for (let i = 0; i <
|
|
922
|
+
for (let i = 0; i < TRBytes; ++i) {
|
|
1311
923
|
tr[i] = sk[skOffset + i];
|
|
1312
924
|
}
|
|
1313
|
-
skOffset +=
|
|
925
|
+
skOffset += TRBytes;
|
|
1314
926
|
|
|
1315
927
|
for (let i = 0; i < L; ++i) {
|
|
1316
928
|
polyEtaUnpack(s1.vec[i], sk, skOffset + i * PolyETAPackedBytes);
|
|
@@ -1404,6 +1016,36 @@ function unpackSig(cP, z, hP, sig) {
|
|
|
1404
1016
|
|
|
1405
1017
|
const randomBytes = pkg;
|
|
1406
1018
|
|
|
1019
|
+
/**
|
|
1020
|
+
* Convert hex string to Uint8Array
|
|
1021
|
+
* @param {string} hex - Hex-encoded string
|
|
1022
|
+
* @returns {Uint8Array} Decoded bytes
|
|
1023
|
+
* @private
|
|
1024
|
+
*/
|
|
1025
|
+
function hexToBytes(hex) {
|
|
1026
|
+
const len = hex.length / 2;
|
|
1027
|
+
const result = new Uint8Array(len);
|
|
1028
|
+
for (let i = 0; i < len; i++) {
|
|
1029
|
+
result[i] = parseInt(hex.substr(i * 2, 2), 16);
|
|
1030
|
+
}
|
|
1031
|
+
return result;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
/**
|
|
1035
|
+
* Generate a Dilithium-5 key pair.
|
|
1036
|
+
*
|
|
1037
|
+
* @param {Uint8Array|null} passedSeed - Optional 32-byte seed for deterministic key generation.
|
|
1038
|
+
* Pass null for random key generation.
|
|
1039
|
+
* @param {Uint8Array} pk - Output buffer for public key (must be CryptoPublicKeyBytes = 2592 bytes)
|
|
1040
|
+
* @param {Uint8Array} sk - Output buffer for secret key (must be CryptoSecretKeyBytes = 4896 bytes)
|
|
1041
|
+
* @returns {Uint8Array} The seed used for key generation (useful when passedSeed is null)
|
|
1042
|
+
* @throws {Error} If pk/sk buffers are null or wrong size, or if seed is wrong size
|
|
1043
|
+
*
|
|
1044
|
+
* @example
|
|
1045
|
+
* const pk = new Uint8Array(CryptoPublicKeyBytes);
|
|
1046
|
+
* const sk = new Uint8Array(CryptoSecretKeyBytes);
|
|
1047
|
+
* const seed = cryptoSignKeypair(null, pk, sk);
|
|
1048
|
+
*/
|
|
1407
1049
|
function cryptoSignKeypair(passedSeed, pk, sk) {
|
|
1408
1050
|
try {
|
|
1409
1051
|
if (pk.length !== CryptoPublicKeyBytes) {
|
|
@@ -1419,8 +1061,15 @@ function cryptoSignKeypair(passedSeed, pk, sk) {
|
|
|
1419
1061
|
throw new Error(`${e.message}`);
|
|
1420
1062
|
}
|
|
1421
1063
|
}
|
|
1422
|
-
|
|
1423
|
-
|
|
1064
|
+
|
|
1065
|
+
// Validate seed length if provided
|
|
1066
|
+
if (passedSeed !== null && passedSeed !== undefined) {
|
|
1067
|
+
if (passedSeed.length !== SeedBytes) {
|
|
1068
|
+
throw new Error(`invalid seed length ${passedSeed.length} | Expected length ${SeedBytes}`);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
const mat = new Array(K).fill().map(() => new PolyVecL());
|
|
1424
1073
|
const s1 = new PolyVecL();
|
|
1425
1074
|
const s2 = new PolyVecK();
|
|
1426
1075
|
const t1 = new PolyVecK();
|
|
@@ -1429,10 +1078,8 @@ function cryptoSignKeypair(passedSeed, pk, sk) {
|
|
|
1429
1078
|
// Get randomness for rho, rhoPrime and key
|
|
1430
1079
|
const seed = passedSeed || randomBytes(SeedBytes);
|
|
1431
1080
|
|
|
1432
|
-
const
|
|
1433
|
-
|
|
1434
|
-
state.update(seed);
|
|
1435
|
-
const seedBuf = state.digest({ buffer: Buffer.alloc(outputLength) });
|
|
1081
|
+
const outputLength = 2 * SeedBytes + CRHBytes;
|
|
1082
|
+
const seedBuf = shake256.create({}).update(seed).xof(outputLength);
|
|
1436
1083
|
const rho = seedBuf.slice(0, SeedBytes);
|
|
1437
1084
|
const rhoPrime = seedBuf.slice(SeedBytes, SeedBytes + CRHBytes);
|
|
1438
1085
|
const key = seedBuf.slice(SeedBytes + CRHBytes);
|
|
@@ -1461,30 +1108,42 @@ function cryptoSignKeypair(passedSeed, pk, sk) {
|
|
|
1461
1108
|
packPk(pk, rho, t1);
|
|
1462
1109
|
|
|
1463
1110
|
// Compute H(rho, t1) and write secret key
|
|
1464
|
-
const
|
|
1465
|
-
outputLength = SeedBytes;
|
|
1466
|
-
hasher.update(Buffer.from(pk, 'hex'));
|
|
1467
|
-
const tr = new Uint8Array(hasher.digest());
|
|
1111
|
+
const tr = shake256.create({}).update(pk).xof(TRBytes);
|
|
1468
1112
|
packSk(sk, rho, tr, key, t0, s1, s2);
|
|
1469
1113
|
|
|
1470
1114
|
return seed;
|
|
1471
1115
|
}
|
|
1472
1116
|
|
|
1117
|
+
/**
|
|
1118
|
+
* Create a detached signature for a message.
|
|
1119
|
+
*
|
|
1120
|
+
* Uses the Dilithium-5 (Round 3) signing algorithm with rejection sampling.
|
|
1121
|
+
*
|
|
1122
|
+
* @param {Uint8Array} sig - Output buffer for signature (must be at least CryptoBytes = 4595 bytes)
|
|
1123
|
+
* @param {string|Uint8Array} m - Message to sign (hex string or Uint8Array)
|
|
1124
|
+
* @param {Uint8Array} sk - Secret key (must be CryptoSecretKeyBytes = 4896 bytes)
|
|
1125
|
+
* @param {boolean} randomizedSigning - If true, use random nonce for hedged signing.
|
|
1126
|
+
* If false, use deterministic nonce derived from message and key.
|
|
1127
|
+
* @returns {number} 0 on success
|
|
1128
|
+
* @throws {Error} If sk is wrong size
|
|
1129
|
+
*
|
|
1130
|
+
* @example
|
|
1131
|
+
* const sig = new Uint8Array(CryptoBytes);
|
|
1132
|
+
* cryptoSignSignature(sig, message, sk, false);
|
|
1133
|
+
*/
|
|
1473
1134
|
function cryptoSignSignature(sig, m, sk, randomizedSigning) {
|
|
1474
1135
|
if (sk.length !== CryptoSecretKeyBytes) {
|
|
1475
1136
|
throw new Error(`invalid sk length ${sk.length} | Expected length ${CryptoSecretKeyBytes}`);
|
|
1476
1137
|
}
|
|
1477
1138
|
|
|
1478
1139
|
const rho = new Uint8Array(SeedBytes);
|
|
1479
|
-
const tr = new Uint8Array(
|
|
1140
|
+
const tr = new Uint8Array(TRBytes);
|
|
1480
1141
|
const key = new Uint8Array(SeedBytes);
|
|
1481
1142
|
let rhoPrime = new Uint8Array(CRHBytes);
|
|
1482
1143
|
let nonce = 0;
|
|
1483
|
-
let state = null;
|
|
1484
1144
|
const mat = Array(K)
|
|
1485
1145
|
.fill()
|
|
1486
|
-
|
|
1487
|
-
.map((_) => new PolyVecL());
|
|
1146
|
+
.map(() => new PolyVecL());
|
|
1488
1147
|
const s1 = new PolyVecL();
|
|
1489
1148
|
const y = new PolyVecL();
|
|
1490
1149
|
const z = new PolyVecL();
|
|
@@ -1497,19 +1156,14 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
|
|
|
1497
1156
|
|
|
1498
1157
|
unpackSk(rho, tr, key, t0, s1, s2, sk);
|
|
1499
1158
|
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
state.update(Buffer.from(m, 'hex'));
|
|
1504
|
-
const mu = new Uint8Array(state.digest({ buffer: Buffer.alloc(outputLength) }));
|
|
1159
|
+
// Convert hex message to bytes
|
|
1160
|
+
const mBytes = typeof m === 'string' ? hexToBytes(m) : m;
|
|
1161
|
+
const mu = shake256.create({}).update(tr).update(mBytes).xof(CRHBytes);
|
|
1505
1162
|
|
|
1506
|
-
if (randomizedSigning)
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
state.update(Buffer.from(key, 'hex'));
|
|
1511
|
-
state.update(Buffer.from(mu, 'hex'));
|
|
1512
|
-
rhoPrime.set(state.digest({ buffer: Buffer.alloc(outputLength) }));
|
|
1163
|
+
if (randomizedSigning) {
|
|
1164
|
+
rhoPrime = new Uint8Array(randomBytes(CRHBytes));
|
|
1165
|
+
} else {
|
|
1166
|
+
rhoPrime = shake256.create({}).update(key).update(mu).xof(CRHBytes);
|
|
1513
1167
|
}
|
|
1514
1168
|
|
|
1515
1169
|
polyVecMatrixExpand(mat, rho);
|
|
@@ -1517,7 +1171,6 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
|
|
|
1517
1171
|
polyVecKNTT(s2);
|
|
1518
1172
|
polyVecKNTT(t0);
|
|
1519
1173
|
|
|
1520
|
-
// eslint-disable-next-line no-constant-condition
|
|
1521
1174
|
while (true) {
|
|
1522
1175
|
polyVecLUniformGamma1(y, rhoPrime, nonce++);
|
|
1523
1176
|
// Matrix-vector multiplication
|
|
@@ -1532,11 +1185,12 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
|
|
|
1532
1185
|
polyVecKDecompose(w1, w0, w1);
|
|
1533
1186
|
polyVecKPackW1(sig, w1);
|
|
1534
1187
|
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1188
|
+
const cHash = shake256
|
|
1189
|
+
.create({})
|
|
1190
|
+
.update(mu)
|
|
1191
|
+
.update(sig.slice(0, K * PolyW1PackedBytes))
|
|
1192
|
+
.xof(SeedBytes);
|
|
1193
|
+
sig.set(cHash);
|
|
1540
1194
|
|
|
1541
1195
|
polyChallenge(cp, sig);
|
|
1542
1196
|
polyNTT(cp);
|
|
@@ -1547,7 +1201,7 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
|
|
|
1547
1201
|
polyVecLAdd(z, z, y);
|
|
1548
1202
|
polyVecLReduce(z);
|
|
1549
1203
|
if (polyVecLChkNorm(z, GAMMA1 - BETA) !== 0) {
|
|
1550
|
-
continue;
|
|
1204
|
+
continue;
|
|
1551
1205
|
}
|
|
1552
1206
|
|
|
1553
1207
|
polyVecKPointWisePolyMontgomery(h, cp, s2);
|
|
@@ -1555,20 +1209,20 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
|
|
|
1555
1209
|
polyVecKSub(w0, w0, h);
|
|
1556
1210
|
polyVecKReduce(w0);
|
|
1557
1211
|
if (polyVecKChkNorm(w0, GAMMA2 - BETA) !== 0) {
|
|
1558
|
-
continue;
|
|
1212
|
+
continue;
|
|
1559
1213
|
}
|
|
1560
1214
|
|
|
1561
1215
|
polyVecKPointWisePolyMontgomery(h, cp, t0);
|
|
1562
1216
|
polyVecKInvNTTToMont(h);
|
|
1563
1217
|
polyVecKReduce(h);
|
|
1564
1218
|
if (polyVecKChkNorm(h, GAMMA2) !== 0) {
|
|
1565
|
-
continue;
|
|
1219
|
+
continue;
|
|
1566
1220
|
}
|
|
1567
1221
|
|
|
1568
1222
|
polyVecKAdd(w0, w0, h);
|
|
1569
1223
|
const n = polyVecKMakeHint(h, w0, w1);
|
|
1570
1224
|
if (n > OMEGA) {
|
|
1571
|
-
continue;
|
|
1225
|
+
continue;
|
|
1572
1226
|
}
|
|
1573
1227
|
|
|
1574
1228
|
packSig(sig, sig, z, h);
|
|
@@ -1576,6 +1230,22 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
|
|
|
1576
1230
|
}
|
|
1577
1231
|
}
|
|
1578
1232
|
|
|
1233
|
+
/**
|
|
1234
|
+
* Sign a message, returning signature concatenated with message.
|
|
1235
|
+
*
|
|
1236
|
+
* This is the combined sign operation that produces a "signed message" containing
|
|
1237
|
+
* both the signature and the original message (signature || message).
|
|
1238
|
+
*
|
|
1239
|
+
* @param {Uint8Array} msg - Message to sign
|
|
1240
|
+
* @param {Uint8Array} sk - Secret key (must be CryptoSecretKeyBytes = 4896 bytes)
|
|
1241
|
+
* @param {boolean} randomizedSigning - If true, use random nonce; if false, deterministic
|
|
1242
|
+
* @returns {Uint8Array} Signed message (CryptoBytes + msg.length bytes)
|
|
1243
|
+
* @throws {Error} If signing fails
|
|
1244
|
+
*
|
|
1245
|
+
* @example
|
|
1246
|
+
* const signedMsg = cryptoSign(message, sk, false);
|
|
1247
|
+
* // signedMsg contains: signature (4595 bytes) || message
|
|
1248
|
+
*/
|
|
1579
1249
|
function cryptoSign(msg, sk, randomizedSigning) {
|
|
1580
1250
|
const sm = new Uint8Array(CryptoBytes + msg.length);
|
|
1581
1251
|
const mLen = msg.length;
|
|
@@ -1590,6 +1260,22 @@ function cryptoSign(msg, sk, randomizedSigning) {
|
|
|
1590
1260
|
return sm;
|
|
1591
1261
|
}
|
|
1592
1262
|
|
|
1263
|
+
/**
|
|
1264
|
+
* Verify a detached signature.
|
|
1265
|
+
*
|
|
1266
|
+
* Performs constant-time verification to prevent timing side-channel attacks.
|
|
1267
|
+
*
|
|
1268
|
+
* @param {Uint8Array} sig - Signature to verify (must be CryptoBytes = 4595 bytes)
|
|
1269
|
+
* @param {string|Uint8Array} m - Message that was signed (hex string or Uint8Array)
|
|
1270
|
+
* @param {Uint8Array} pk - Public key (must be CryptoPublicKeyBytes = 2592 bytes)
|
|
1271
|
+
* @returns {boolean} true if signature is valid, false otherwise
|
|
1272
|
+
*
|
|
1273
|
+
* @example
|
|
1274
|
+
* const isValid = cryptoSignVerify(signature, message, pk);
|
|
1275
|
+
* if (!isValid) {
|
|
1276
|
+
* throw new Error('Invalid signature');
|
|
1277
|
+
* }
|
|
1278
|
+
*/
|
|
1593
1279
|
function cryptoSignVerify(sig, m, pk) {
|
|
1594
1280
|
let i;
|
|
1595
1281
|
const buf = new Uint8Array(K * PolyW1PackedBytes);
|
|
@@ -1598,8 +1284,7 @@ function cryptoSignVerify(sig, m, pk) {
|
|
|
1598
1284
|
const c = new Uint8Array(SeedBytes);
|
|
1599
1285
|
const c2 = new Uint8Array(SeedBytes);
|
|
1600
1286
|
const cp = new Poly();
|
|
1601
|
-
|
|
1602
|
-
const mat = new Array(K).fill().map((_) => new PolyVecL());
|
|
1287
|
+
const mat = new Array(K).fill().map(() => new PolyVecL());
|
|
1603
1288
|
const z = new PolyVecL();
|
|
1604
1289
|
const t1 = new PolyVecK();
|
|
1605
1290
|
const w1 = new PolyVecK();
|
|
@@ -1621,16 +1306,13 @@ function cryptoSignVerify(sig, m, pk) {
|
|
|
1621
1306
|
}
|
|
1622
1307
|
|
|
1623
1308
|
/* Compute CRH(H(rho, t1), msg) */
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
state.update(pk.slice(0, CryptoPublicKeyBytes));
|
|
1627
|
-
mu.set(state.digest({ buffer: Buffer.alloc(outputLength) }));
|
|
1309
|
+
const tr = shake256.create({}).update(pk).xof(TRBytes);
|
|
1310
|
+
mu.set(tr);
|
|
1628
1311
|
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
mu.set(state.digest({ buffer: Buffer.alloc(outputLength) }));
|
|
1312
|
+
// Convert hex message to bytes
|
|
1313
|
+
const mBytes = typeof m === 'string' ? hexToBytes(m) : m;
|
|
1314
|
+
const muFull = shake256.create({}).update(mu.slice(0, TRBytes)).update(mBytes).xof(CRHBytes);
|
|
1315
|
+
mu.set(muFull);
|
|
1634
1316
|
|
|
1635
1317
|
/* Matrix-vector multiplication; compute Az - c2^dt1 */
|
|
1636
1318
|
polyChallenge(cp, c);
|
|
@@ -1654,16 +1336,33 @@ function cryptoSignVerify(sig, m, pk) {
|
|
|
1654
1336
|
polyVecKPackW1(buf, w1);
|
|
1655
1337
|
|
|
1656
1338
|
/* Call random oracle and verify challenge */
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1339
|
+
const c2Hash = shake256.create({}).update(mu).update(buf).xof(SeedBytes);
|
|
1340
|
+
c2.set(c2Hash);
|
|
1341
|
+
|
|
1342
|
+
// Constant-time comparison to prevent timing attacks
|
|
1343
|
+
let diff = 0;
|
|
1344
|
+
for (i = 0; i < SeedBytes; ++i) {
|
|
1345
|
+
diff |= c[i] ^ c2[i];
|
|
1346
|
+
}
|
|
1347
|
+
return diff === 0;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
/**
|
|
1351
|
+
* Open a signed message (verify and extract message).
|
|
1352
|
+
*
|
|
1353
|
+
* This is the counterpart to cryptoSign(). It verifies the signature and
|
|
1354
|
+
* extracts the original message from a signed message.
|
|
1355
|
+
*
|
|
1356
|
+
* @param {Uint8Array} sm - Signed message (signature || message)
|
|
1357
|
+
* @param {Uint8Array} pk - Public key (must be CryptoPublicKeyBytes = 2592 bytes)
|
|
1358
|
+
* @returns {Uint8Array|undefined} The original message if valid, undefined if verification fails
|
|
1359
|
+
*
|
|
1360
|
+
* @example
|
|
1361
|
+
* const message = cryptoSignOpen(signedMsg, pk);
|
|
1362
|
+
* if (message === undefined) {
|
|
1363
|
+
* throw new Error('Invalid signature');
|
|
1364
|
+
* }
|
|
1365
|
+
*/
|
|
1667
1366
|
function cryptoSignOpen(sm, pk) {
|
|
1668
1367
|
if (sm.length < CryptoBytes) {
|
|
1669
1368
|
return undefined;
|
|
@@ -1678,4 +1377,56 @@ function cryptoSignOpen(sm, pk) {
|
|
|
1678
1377
|
return msg;
|
|
1679
1378
|
}
|
|
1680
1379
|
|
|
1681
|
-
|
|
1380
|
+
/**
|
|
1381
|
+
* Security utilities for Dilithium5
|
|
1382
|
+
*
|
|
1383
|
+
* IMPORTANT: JavaScript cannot guarantee secure memory zeroization.
|
|
1384
|
+
* See SECURITY.md for details on limitations.
|
|
1385
|
+
*/
|
|
1386
|
+
|
|
1387
|
+
/**
|
|
1388
|
+
* Attempts to zero out a Uint8Array buffer.
|
|
1389
|
+
*
|
|
1390
|
+
* WARNING: This is a BEST-EFFORT operation. Due to JavaScript/JIT limitations:
|
|
1391
|
+
* - The write may be optimized away if the buffer is unused afterward
|
|
1392
|
+
* - Copies may exist in garbage collector memory
|
|
1393
|
+
* - Data may have been swapped to disk
|
|
1394
|
+
*
|
|
1395
|
+
* For high-security applications, consider native implementations (go-qrllib)
|
|
1396
|
+
* or hardware security modules.
|
|
1397
|
+
*
|
|
1398
|
+
* @param {Uint8Array} buffer - The buffer to zero
|
|
1399
|
+
* @returns {void}
|
|
1400
|
+
*/
|
|
1401
|
+
function zeroize(buffer) {
|
|
1402
|
+
if (!(buffer instanceof Uint8Array)) {
|
|
1403
|
+
throw new TypeError('zeroize requires a Uint8Array');
|
|
1404
|
+
}
|
|
1405
|
+
// Use fill(0) for zeroing - best effort
|
|
1406
|
+
buffer.fill(0);
|
|
1407
|
+
// Additional volatile-like access to discourage optimization
|
|
1408
|
+
// (This is a hint to the JIT, not a guarantee)
|
|
1409
|
+
if (buffer.length > 0 && buffer[0] !== 0) {
|
|
1410
|
+
throw new Error('zeroize failed'); // Should never happen
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
/**
|
|
1415
|
+
* Checks if a buffer is all zeros.
|
|
1416
|
+
* Uses constant-time comparison to avoid timing leaks.
|
|
1417
|
+
*
|
|
1418
|
+
* @param {Uint8Array} buffer - The buffer to check
|
|
1419
|
+
* @returns {boolean} True if all bytes are zero
|
|
1420
|
+
*/
|
|
1421
|
+
function isZero(buffer) {
|
|
1422
|
+
if (!(buffer instanceof Uint8Array)) {
|
|
1423
|
+
throw new TypeError('isZero requires a Uint8Array');
|
|
1424
|
+
}
|
|
1425
|
+
let acc = 0;
|
|
1426
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
1427
|
+
acc |= buffer[i];
|
|
1428
|
+
}
|
|
1429
|
+
return acc === 0;
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
export { BETA, CRHBytes, CryptoBytes, CryptoPublicKeyBytes, CryptoSecretKeyBytes, D, ETA, GAMMA1, GAMMA2, K, KeccakState, L, N, OMEGA, Poly, PolyETAPackedBytes, PolyT0PackedBytes, PolyT1PackedBytes, PolyUniformETANBlocks, PolyUniformGamma1NBlocks, PolyUniformNBlocks, PolyVecHPackedBytes, PolyVecK, PolyVecL, PolyW1PackedBytes, PolyZPackedBytes, Q, QInv, SeedBytes, Shake128Rate, Shake256Rate, Stream128BlockBytes, Stream256BlockBytes, TAU, TRBytes, cAddQ, cryptoSign, cryptoSignKeypair, cryptoSignOpen, cryptoSignSignature, cryptoSignVerify, decompose, dilithiumShake128StreamInit, dilithiumShake256StreamInit, invNTTToMont, isZero, makeHint, montgomeryReduce, ntt, packPk, packSig, packSk, polyAdd, polyCAddQ, polyChallenge, polyChkNorm, polyDecompose, polyEtaPack, polyEtaUnpack, polyInvNTTToMont, polyMakeHint, polyNTT, polyPointWiseMontgomery, polyPower2round, polyReduce, polyShiftL, polySub, polyT0Pack, polyT0Unpack, polyT1Pack, polyT1Unpack, polyUniform, polyUniformEta, polyUniformGamma1, polyUseHint, polyVecKAdd, polyVecKCAddQ, polyVecKChkNorm, polyVecKDecompose, polyVecKInvNTTToMont, polyVecKMakeHint, polyVecKNTT, polyVecKPackW1, polyVecKPointWisePolyMontgomery, polyVecKPower2round, polyVecKReduce, polyVecKShiftL, polyVecKSub, polyVecKUniformEta, polyVecKUseHint, polyVecLAdd, polyVecLChkNorm, polyVecLInvNTTToMont, polyVecLNTT, polyVecLPointWiseAccMontgomery, polyVecLPointWisePolyMontgomery, polyVecLReduce, polyVecLUniformEta, polyVecLUniformGamma1, polyVecMatrixExpand, polyVecMatrixPointWiseMontgomery, polyW1Pack, polyZPack, polyZUnpack, power2round, reduce32, rejEta, rejUniform, shake128Absorb, shake128Finalize, shake128Init, shake128SqueezeBlocks, shake256Absorb, shake256Finalize, shake256Init, shake256SqueezeBlocks, unpackPk, unpackSig, unpackSk, useHint, zeroize, zetas };
|