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