@theqrl/dilithium5 1.2.0 → 1.2.2
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.d.cts +92 -1
- package/dist/cjs/dilithium5.js +373 -79
- package/dist/mjs/dilithium5.d.mts +92 -1
- package/dist/mjs/dilithium5.js +71 -30
- package/package.json +10 -10
- package/src/index.d.ts +92 -1
package/dist/cjs/dilithium5.js
CHANGED
|
@@ -62,18 +62,17 @@ const zetas = [
|
|
|
62
62
|
-1362209, 3937738, 1400424, -846154, 1976782,
|
|
63
63
|
];
|
|
64
64
|
|
|
65
|
-
/**
|
|
66
|
-
* Internal helpers for u64. BigUint64Array is too slow as per 2025, so we implement it using Uint32Array.
|
|
67
|
-
* @todo re-check https://issues.chromium.org/issues/42212588
|
|
68
|
-
* @module
|
|
69
|
-
*/
|
|
70
65
|
const U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1);
|
|
71
66
|
const _32n = /* @__PURE__ */ BigInt(32);
|
|
67
|
+
// Split bigint into two 32-bit halves. With `le=true`, returned fields become `{ h: low, l: high
|
|
68
|
+
// }` to match little-endian word order rather than the property names.
|
|
72
69
|
function fromBig(n, le = false) {
|
|
73
70
|
if (le)
|
|
74
71
|
return { h: Number(n & U32_MASK64), l: Number((n >> _32n) & U32_MASK64) };
|
|
75
72
|
return { h: Number((n >> _32n) & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
|
|
76
73
|
}
|
|
74
|
+
// Split bigint list into `[highWords, lowWords]` when `le=false`; with `le=true`, the first array
|
|
75
|
+
// holds the low halves because `fromBig(...)` swaps the semantic meaning of `h` and `l`.
|
|
77
76
|
function split(lst, le = false) {
|
|
78
77
|
const len = lst.length;
|
|
79
78
|
let Ah = new Uint32Array(len);
|
|
@@ -84,30 +83,72 @@ function split(lst, le = false) {
|
|
|
84
83
|
}
|
|
85
84
|
return [Ah, Al];
|
|
86
85
|
}
|
|
87
|
-
//
|
|
86
|
+
// High 32-bit half of a 64-bit left rotate, valid for `s` in `1..31`.
|
|
88
87
|
const rotlSH = (h, l, s) => (h << s) | (l >>> (32 - s));
|
|
88
|
+
// Low 32-bit half of a 64-bit left rotate, valid for `s` in `1..31`.
|
|
89
89
|
const rotlSL = (h, l, s) => (l << s) | (h >>> (32 - s));
|
|
90
|
-
//
|
|
90
|
+
// High 32-bit half of a 64-bit left rotate, valid for `s` in `33..63`; `32` uses `rotr32*`.
|
|
91
91
|
const rotlBH = (h, l, s) => (l << (s - 32)) | (h >>> (64 - s));
|
|
92
|
+
// Low 32-bit half of a 64-bit left rotate, valid for `s` in `33..63`; `32` uses `rotr32*`.
|
|
92
93
|
const rotlBL = (h, l, s) => (h << (s - 32)) | (l >>> (64 - s));
|
|
93
94
|
|
|
94
95
|
/**
|
|
95
|
-
*
|
|
96
|
-
* @
|
|
96
|
+
* Checks if something is Uint8Array. Be careful: nodejs Buffer will return true.
|
|
97
|
+
* @param a - value to test
|
|
98
|
+
* @returns `true` when the value is a Uint8Array-compatible view.
|
|
99
|
+
* @example
|
|
100
|
+
* Check whether a value is a Uint8Array-compatible view.
|
|
101
|
+
* ```ts
|
|
102
|
+
* isBytes(new Uint8Array([1, 2, 3]));
|
|
103
|
+
* ```
|
|
97
104
|
*/
|
|
98
|
-
/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
99
|
-
/** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */
|
|
100
105
|
function isBytes(a) {
|
|
101
|
-
|
|
106
|
+
// Plain `instanceof Uint8Array` is too strict for some Buffer / proxy / cross-realm cases.
|
|
107
|
+
// The fallback still requires a real ArrayBuffer view, so plain
|
|
108
|
+
// JSON-deserialized `{ constructor: ... }` spoofing is rejected, and
|
|
109
|
+
// `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views.
|
|
110
|
+
return (a instanceof Uint8Array ||
|
|
111
|
+
(ArrayBuffer.isView(a) &&
|
|
112
|
+
a.constructor.name === 'Uint8Array' &&
|
|
113
|
+
'BYTES_PER_ELEMENT' in a &&
|
|
114
|
+
a.BYTES_PER_ELEMENT === 1));
|
|
102
115
|
}
|
|
103
|
-
/**
|
|
116
|
+
/**
|
|
117
|
+
* Asserts something is a non-negative integer.
|
|
118
|
+
* @param n - number to validate
|
|
119
|
+
* @param title - label included in thrown errors
|
|
120
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
121
|
+
* @throws On wrong argument ranges or values. {@link RangeError}
|
|
122
|
+
* @example
|
|
123
|
+
* Validate a non-negative integer option.
|
|
124
|
+
* ```ts
|
|
125
|
+
* anumber(32, 'length');
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
104
128
|
function anumber(n, title = '') {
|
|
129
|
+
if (typeof n !== 'number') {
|
|
130
|
+
const prefix = title && `"${title}" `;
|
|
131
|
+
throw new TypeError(`${prefix}expected number, got ${typeof n}`);
|
|
132
|
+
}
|
|
105
133
|
if (!Number.isSafeInteger(n) || n < 0) {
|
|
106
134
|
const prefix = title && `"${title}" `;
|
|
107
|
-
throw new
|
|
135
|
+
throw new RangeError(`${prefix}expected integer >= 0, got ${n}`);
|
|
108
136
|
}
|
|
109
137
|
}
|
|
110
|
-
/**
|
|
138
|
+
/**
|
|
139
|
+
* Asserts something is Uint8Array.
|
|
140
|
+
* @param value - value to validate
|
|
141
|
+
* @param length - optional exact length constraint
|
|
142
|
+
* @param title - label included in thrown errors
|
|
143
|
+
* @returns The validated byte array.
|
|
144
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
145
|
+
* @throws On wrong argument ranges or values. {@link RangeError}
|
|
146
|
+
* @example
|
|
147
|
+
* Validate that a value is a byte array.
|
|
148
|
+
* ```ts
|
|
149
|
+
* abytes(new Uint8Array([1, 2, 3]));
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
111
152
|
function abytes(value, length, title = '') {
|
|
112
153
|
const bytes = isBytes(value);
|
|
113
154
|
const len = value?.length;
|
|
@@ -116,51 +157,130 @@ function abytes(value, length, title = '') {
|
|
|
116
157
|
const prefix = title && `"${title}" `;
|
|
117
158
|
const ofLen = '';
|
|
118
159
|
const got = bytes ? `length=${len}` : `type=${typeof value}`;
|
|
119
|
-
|
|
160
|
+
const message = prefix + 'expected Uint8Array' + ofLen + ', got ' + got;
|
|
161
|
+
if (!bytes)
|
|
162
|
+
throw new TypeError(message);
|
|
163
|
+
throw new RangeError(message);
|
|
120
164
|
}
|
|
121
165
|
return value;
|
|
122
166
|
}
|
|
123
|
-
/**
|
|
167
|
+
/**
|
|
168
|
+
* Asserts a hash instance has not been destroyed or finished.
|
|
169
|
+
* @param instance - hash instance to validate
|
|
170
|
+
* @param checkFinished - whether to reject finalized instances
|
|
171
|
+
* @throws If the hash instance has already been destroyed or finalized. {@link Error}
|
|
172
|
+
* @example
|
|
173
|
+
* Validate that a hash instance is still usable.
|
|
174
|
+
* ```ts
|
|
175
|
+
* import { aexists } from '@noble/hashes/utils.js';
|
|
176
|
+
* import { sha256 } from '@noble/hashes/sha2.js';
|
|
177
|
+
* const hash = sha256.create();
|
|
178
|
+
* aexists(hash);
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
124
181
|
function aexists(instance, checkFinished = true) {
|
|
125
182
|
if (instance.destroyed)
|
|
126
183
|
throw new Error('Hash instance has been destroyed');
|
|
127
184
|
if (checkFinished && instance.finished)
|
|
128
185
|
throw new Error('Hash#digest() has already been called');
|
|
129
186
|
}
|
|
130
|
-
/**
|
|
187
|
+
/**
|
|
188
|
+
* Asserts output is a sufficiently-sized byte array.
|
|
189
|
+
* @param out - destination buffer
|
|
190
|
+
* @param instance - hash instance providing output length
|
|
191
|
+
* Oversized buffers are allowed; downstream code only promises to fill the first `outputLen` bytes.
|
|
192
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
193
|
+
* @throws On wrong argument ranges or values. {@link RangeError}
|
|
194
|
+
* @example
|
|
195
|
+
* Validate a caller-provided digest buffer.
|
|
196
|
+
* ```ts
|
|
197
|
+
* import { aoutput } from '@noble/hashes/utils.js';
|
|
198
|
+
* import { sha256 } from '@noble/hashes/sha2.js';
|
|
199
|
+
* const hash = sha256.create();
|
|
200
|
+
* aoutput(new Uint8Array(hash.outputLen), hash);
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
131
203
|
function aoutput(out, instance) {
|
|
132
204
|
abytes(out, undefined, 'digestInto() output');
|
|
133
205
|
const min = instance.outputLen;
|
|
134
206
|
if (out.length < min) {
|
|
135
|
-
throw new
|
|
207
|
+
throw new RangeError('"digestInto() output" expected to be of length >=' + min);
|
|
136
208
|
}
|
|
137
209
|
}
|
|
138
|
-
/**
|
|
210
|
+
/**
|
|
211
|
+
* Casts a typed array view to Uint32Array.
|
|
212
|
+
* `arr.byteOffset` must already be 4-byte aligned or the platform
|
|
213
|
+
* Uint32Array constructor will throw.
|
|
214
|
+
* @param arr - source typed array
|
|
215
|
+
* @returns Uint32Array view over the same buffer.
|
|
216
|
+
* @example
|
|
217
|
+
* Reinterpret a byte array as 32-bit words.
|
|
218
|
+
* ```ts
|
|
219
|
+
* u32(new Uint8Array(8));
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
139
222
|
function u32(arr) {
|
|
140
223
|
return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
|
|
141
224
|
}
|
|
142
|
-
/**
|
|
225
|
+
/**
|
|
226
|
+
* Zeroizes typed arrays in place. Warning: JS provides no guarantees.
|
|
227
|
+
* @param arrays - arrays to overwrite with zeros
|
|
228
|
+
* @example
|
|
229
|
+
* Zeroize sensitive buffers in place.
|
|
230
|
+
* ```ts
|
|
231
|
+
* clean(new Uint8Array([1, 2, 3]));
|
|
232
|
+
* ```
|
|
233
|
+
*/
|
|
143
234
|
function clean(...arrays) {
|
|
144
235
|
for (let i = 0; i < arrays.length; i++) {
|
|
145
236
|
arrays[i].fill(0);
|
|
146
237
|
}
|
|
147
238
|
}
|
|
148
|
-
/**
|
|
239
|
+
/** Whether the current platform is little-endian. */
|
|
149
240
|
const isLE = /* @__PURE__ */ (() => new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();
|
|
150
|
-
/**
|
|
241
|
+
/**
|
|
242
|
+
* Byte-swap operation for uint32 values.
|
|
243
|
+
* @param word - source word
|
|
244
|
+
* @returns Word with reversed byte order.
|
|
245
|
+
* @example
|
|
246
|
+
* Reverse the byte order of a 32-bit word.
|
|
247
|
+
* ```ts
|
|
248
|
+
* byteSwap(0x11223344);
|
|
249
|
+
* ```
|
|
250
|
+
*/
|
|
151
251
|
function byteSwap(word) {
|
|
152
252
|
return (((word << 24) & 0xff000000) |
|
|
153
253
|
((word << 8) & 0xff0000) |
|
|
154
254
|
((word >>> 8) & 0xff00) |
|
|
155
255
|
((word >>> 24) & 0xff));
|
|
156
256
|
}
|
|
157
|
-
/**
|
|
257
|
+
/**
|
|
258
|
+
* Byte-swaps every word of a Uint32Array in place.
|
|
259
|
+
* @param arr - array to mutate
|
|
260
|
+
* @returns The same array after mutation; callers pass live state arrays here.
|
|
261
|
+
* @example
|
|
262
|
+
* Reverse the byte order of every word in place.
|
|
263
|
+
* ```ts
|
|
264
|
+
* byteSwap32(new Uint32Array([0x11223344]));
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
158
267
|
function byteSwap32(arr) {
|
|
159
268
|
for (let i = 0; i < arr.length; i++) {
|
|
160
269
|
arr[i] = byteSwap(arr[i]);
|
|
161
270
|
}
|
|
162
271
|
return arr;
|
|
163
272
|
}
|
|
273
|
+
/**
|
|
274
|
+
* Conditionally byte-swaps a Uint32Array on big-endian platforms.
|
|
275
|
+
* @param u - array to normalize for host endianness
|
|
276
|
+
* @returns Original or byte-swapped array depending on platform endianness.
|
|
277
|
+
* On big-endian runtimes this mutates `u` in place via `byteSwap32(...)`.
|
|
278
|
+
* @example
|
|
279
|
+
* Normalize a word array for host endianness.
|
|
280
|
+
* ```ts
|
|
281
|
+
* swap32IfBE(new Uint32Array([0x11223344]));
|
|
282
|
+
* ```
|
|
283
|
+
*/
|
|
164
284
|
const swap32IfBE = isLE
|
|
165
285
|
? (u) => u
|
|
166
286
|
: byteSwap32;
|
|
@@ -181,42 +301,89 @@ function asciiToBase16(ch) {
|
|
|
181
301
|
}
|
|
182
302
|
/**
|
|
183
303
|
* Convert hex string to byte array. Uses built-in function, when available.
|
|
184
|
-
* @
|
|
304
|
+
* @param hex - hexadecimal string to decode
|
|
305
|
+
* @returns Decoded bytes.
|
|
306
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
307
|
+
* @throws On wrong argument ranges or values. {@link RangeError}
|
|
308
|
+
* @example
|
|
309
|
+
* Decode lowercase hexadecimal into bytes.
|
|
310
|
+
* ```ts
|
|
311
|
+
* hexToBytes('cafe0123'); // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])
|
|
312
|
+
* ```
|
|
185
313
|
*/
|
|
186
314
|
function hexToBytes$1(hex) {
|
|
187
315
|
if (typeof hex !== 'string')
|
|
188
|
-
throw new
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
316
|
+
throw new TypeError('hex string expected, got ' + typeof hex);
|
|
317
|
+
if (hasHexBuiltin) {
|
|
318
|
+
try {
|
|
319
|
+
return Uint8Array.fromHex(hex);
|
|
320
|
+
}
|
|
321
|
+
catch (error) {
|
|
322
|
+
if (error instanceof SyntaxError)
|
|
323
|
+
throw new RangeError(error.message);
|
|
324
|
+
throw error;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
192
327
|
const hl = hex.length;
|
|
193
328
|
const al = hl / 2;
|
|
194
329
|
if (hl % 2)
|
|
195
|
-
throw new
|
|
330
|
+
throw new RangeError('hex string expected, got unpadded hex of length ' + hl);
|
|
196
331
|
const array = new Uint8Array(al);
|
|
197
332
|
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
|
|
198
333
|
const n1 = asciiToBase16(hex.charCodeAt(hi));
|
|
199
334
|
const n2 = asciiToBase16(hex.charCodeAt(hi + 1));
|
|
200
335
|
if (n1 === undefined || n2 === undefined) {
|
|
201
336
|
const char = hex[hi] + hex[hi + 1];
|
|
202
|
-
throw new
|
|
337
|
+
throw new RangeError('hex string expected, got non-hex character "' + char + '" at index ' + hi);
|
|
203
338
|
}
|
|
204
339
|
array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163
|
|
205
340
|
}
|
|
206
341
|
return array;
|
|
207
342
|
}
|
|
208
|
-
/**
|
|
343
|
+
/**
|
|
344
|
+
* Creates a callable hash function from a stateful class constructor.
|
|
345
|
+
* @param hashCons - hash constructor or factory
|
|
346
|
+
* @param info - optional metadata such as DER OID
|
|
347
|
+
* @returns Frozen callable hash wrapper with `.create()`.
|
|
348
|
+
* Wrapper construction eagerly calls `hashCons(undefined)` once to read
|
|
349
|
+
* `outputLen` / `blockLen`, so constructor side effects happen at module
|
|
350
|
+
* init time.
|
|
351
|
+
* @example
|
|
352
|
+
* Wrap a stateful hash constructor into a callable helper.
|
|
353
|
+
* ```ts
|
|
354
|
+
* import { createHasher } from '@noble/hashes/utils.js';
|
|
355
|
+
* import { sha256 } from '@noble/hashes/sha2.js';
|
|
356
|
+
* const wrapped = createHasher(sha256.create, { oid: sha256.oid });
|
|
357
|
+
* wrapped(new Uint8Array([1]));
|
|
358
|
+
* ```
|
|
359
|
+
*/
|
|
209
360
|
function createHasher(hashCons, info = {}) {
|
|
210
|
-
const hashC = (msg, opts) => hashCons(opts)
|
|
361
|
+
const hashC = (msg, opts) => hashCons(opts)
|
|
362
|
+
.update(msg)
|
|
363
|
+
.digest();
|
|
211
364
|
const tmp = hashCons(undefined);
|
|
212
365
|
hashC.outputLen = tmp.outputLen;
|
|
213
366
|
hashC.blockLen = tmp.blockLen;
|
|
367
|
+
hashC.canXOF = tmp.canXOF;
|
|
214
368
|
hashC.create = (opts) => hashCons(opts);
|
|
215
369
|
Object.assign(hashC, info);
|
|
216
370
|
return Object.freeze(hashC);
|
|
217
371
|
}
|
|
218
|
-
/**
|
|
372
|
+
/**
|
|
373
|
+
* Creates OID metadata for NIST hashes with prefix `06 09 60 86 48 01 65 03 04 02`.
|
|
374
|
+
* @param suffix - final OID byte for the selected hash.
|
|
375
|
+
* The helper accepts any byte even though only the documented NIST hash
|
|
376
|
+
* suffixes are meaningful downstream.
|
|
377
|
+
* @returns Object containing the DER-encoded OID.
|
|
378
|
+
* @example
|
|
379
|
+
* Build OID metadata for a NIST hash.
|
|
380
|
+
* ```ts
|
|
381
|
+
* oidNist(0x01);
|
|
382
|
+
* ```
|
|
383
|
+
*/
|
|
219
384
|
const oidNist = (suffix) => ({
|
|
385
|
+
// Current NIST hashAlgs suffixes used here fit in one DER subidentifier octet.
|
|
386
|
+
// Larger suffix values would need base-128 OID encoding and a different length byte.
|
|
220
387
|
oid: Uint8Array.from([0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, suffix]),
|
|
221
388
|
});
|
|
222
389
|
|
|
@@ -224,9 +391,11 @@ const oidNist = (suffix) => ({
|
|
|
224
391
|
* SHA3 (keccak) hash function, based on a new "Sponge function" design.
|
|
225
392
|
* Different from older hashes, the internal state is bigger than output size.
|
|
226
393
|
*
|
|
227
|
-
* Check out
|
|
228
|
-
*
|
|
229
|
-
*
|
|
394
|
+
* Check out
|
|
395
|
+
* {@link https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf | FIPS-202},
|
|
396
|
+
* {@link https://keccak.team/keccak.html | Website}, and
|
|
397
|
+
* {@link https://crypto.stackexchange.com/q/15727 | the differences between
|
|
398
|
+
* SHA-3 and Keccak}.
|
|
230
399
|
*
|
|
231
400
|
* Check out `sha3-addons` module for cSHAKE, k12, and others.
|
|
232
401
|
* @module
|
|
@@ -239,6 +408,8 @@ const _1n = BigInt(1);
|
|
|
239
408
|
const _2n = BigInt(2);
|
|
240
409
|
const _7n = BigInt(7);
|
|
241
410
|
const _256n = BigInt(256);
|
|
411
|
+
// FIPS 202 Algorithm 5 rc(): when the outgoing bit is 1, the 8-bit LFSR xors
|
|
412
|
+
// taps 0, 4, 5, and 6, which compresses to the feedback mask `0x71`.
|
|
242
413
|
const _0x71n = BigInt(0x71);
|
|
243
414
|
const SHA3_PI = [];
|
|
244
415
|
const SHA3_ROTL = [];
|
|
@@ -259,13 +430,31 @@ for (let round = 0, R = _1n, x = 1, y = 0; round < 24; round++) {
|
|
|
259
430
|
_SHA3_IOTA.push(t);
|
|
260
431
|
}
|
|
261
432
|
const IOTAS = split(_SHA3_IOTA, true);
|
|
433
|
+
// `split(..., true)` keeps the local little-endian lane-word layout used by
|
|
434
|
+
// `state32`, so these `H` / `L` tables follow the file's first-word /
|
|
435
|
+
// second-word lane slots rather than `_u64.ts`'s usual high/low naming.
|
|
262
436
|
const SHA3_IOTA_H = IOTAS[0];
|
|
263
437
|
const SHA3_IOTA_L = IOTAS[1];
|
|
264
438
|
// Left rotation (without 0, 32, 64)
|
|
265
439
|
const rotlH = (h, l, s) => (s > 32 ? rotlBH(h, l, s) : rotlSH(h, l, s));
|
|
266
440
|
const rotlL = (h, l, s) => (s > 32 ? rotlBL(h, l, s) : rotlSL(h, l, s));
|
|
267
|
-
/**
|
|
441
|
+
/**
|
|
442
|
+
* `keccakf1600` internal permutation, additionally allows adjusting the round count.
|
|
443
|
+
* @param s - 5x5 Keccak state encoded as 25 lanes split into 50 uint32 words
|
|
444
|
+
* in this file's local little-endian lane-word order
|
|
445
|
+
* @param rounds - number of rounds to execute
|
|
446
|
+
* @throws If `rounds` is outside the supported `1..24` range. {@link Error}
|
|
447
|
+
* @example
|
|
448
|
+
* Permute a Keccak state with the default 24 rounds.
|
|
449
|
+
* ```ts
|
|
450
|
+
* keccakP(new Uint32Array(50));
|
|
451
|
+
* ```
|
|
452
|
+
*/
|
|
268
453
|
function keccakP(s, rounds = 24) {
|
|
454
|
+
anumber(rounds, 'rounds');
|
|
455
|
+
// This implementation precomputes only the standard Keccak-f[1600] 24-round Iota table.
|
|
456
|
+
if (rounds < 1 || rounds > 24)
|
|
457
|
+
throw new Error('"rounds" expected integer 1..24');
|
|
269
458
|
const B = new Uint32Array(5 * 2);
|
|
270
459
|
// NOTE: all indices are x2 since we store state as u32 instead of u64 (bigints to slow in js)
|
|
271
460
|
for (let round = 24 - rounds; round < 24; round++) {
|
|
@@ -298,11 +487,21 @@ function keccakP(s, rounds = 24) {
|
|
|
298
487
|
s[PI + 1] = Tl;
|
|
299
488
|
}
|
|
300
489
|
// Chi (χ)
|
|
490
|
+
// Same as:
|
|
491
|
+
// for (let x = 0; x < 10; x++) B[x] = s[y + x];
|
|
492
|
+
// for (let x = 0; x < 10; x++) s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10];
|
|
301
493
|
for (let y = 0; y < 50; y += 10) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
494
|
+
const b0 = s[y], b1 = s[y + 1], b2 = s[y + 2], b3 = s[y + 3];
|
|
495
|
+
s[y] ^= ~s[y + 2] & s[y + 4];
|
|
496
|
+
s[y + 1] ^= ~s[y + 3] & s[y + 5];
|
|
497
|
+
s[y + 2] ^= ~s[y + 4] & s[y + 6];
|
|
498
|
+
s[y + 3] ^= ~s[y + 5] & s[y + 7];
|
|
499
|
+
s[y + 4] ^= ~s[y + 6] & s[y + 8];
|
|
500
|
+
s[y + 5] ^= ~s[y + 7] & s[y + 9];
|
|
501
|
+
s[y + 6] ^= ~s[y + 8] & b0;
|
|
502
|
+
s[y + 7] ^= ~s[y + 9] & b1;
|
|
503
|
+
s[y + 8] ^= ~b0 & b2;
|
|
504
|
+
s[y + 9] ^= ~b1 & b3;
|
|
306
505
|
}
|
|
307
506
|
// Iota (ι)
|
|
308
507
|
s[0] ^= SHA3_IOTA_H[round];
|
|
@@ -310,7 +509,23 @@ function keccakP(s, rounds = 24) {
|
|
|
310
509
|
}
|
|
311
510
|
clean(B);
|
|
312
511
|
}
|
|
313
|
-
/**
|
|
512
|
+
/**
|
|
513
|
+
* Keccak sponge function.
|
|
514
|
+
* @param blockLen - absorb/squeeze rate in bytes
|
|
515
|
+
* @param suffix - domain separation suffix byte
|
|
516
|
+
* @param outputLen - default digest length in bytes. This base sponge only
|
|
517
|
+
* requires a non-negative integer; wrappers that need positive output
|
|
518
|
+
* lengths must enforce that themselves.
|
|
519
|
+
* @param enableXOF - whether XOF output is allowed
|
|
520
|
+
* @param rounds - number of Keccak-f rounds
|
|
521
|
+
* @example
|
|
522
|
+
* Build a sponge state, absorb bytes, then finalize a digest.
|
|
523
|
+
* ```ts
|
|
524
|
+
* const hash = new Keccak(136, 0x06, 32);
|
|
525
|
+
* hash.update(new Uint8Array([1, 2, 3]));
|
|
526
|
+
* hash.digest();
|
|
527
|
+
* ```
|
|
528
|
+
*/
|
|
314
529
|
class Keccak {
|
|
315
530
|
state;
|
|
316
531
|
pos = 0;
|
|
@@ -321,6 +536,7 @@ class Keccak {
|
|
|
321
536
|
blockLen;
|
|
322
537
|
suffix;
|
|
323
538
|
outputLen;
|
|
539
|
+
canXOF;
|
|
324
540
|
enableXOF = false;
|
|
325
541
|
rounds;
|
|
326
542
|
// NOTE: we accept arguments in bytes instead of bits here.
|
|
@@ -329,6 +545,7 @@ class Keccak {
|
|
|
329
545
|
this.suffix = suffix;
|
|
330
546
|
this.outputLen = outputLen;
|
|
331
547
|
this.enableXOF = enableXOF;
|
|
548
|
+
this.canXOF = enableXOF;
|
|
332
549
|
this.rounds = rounds;
|
|
333
550
|
// Can be passed from user as dkLen
|
|
334
551
|
anumber(outputLen, 'outputLen');
|
|
@@ -368,8 +585,13 @@ class Keccak {
|
|
|
368
585
|
return;
|
|
369
586
|
this.finished = true;
|
|
370
587
|
const { state, suffix, pos, blockLen } = this;
|
|
371
|
-
//
|
|
588
|
+
// FIPS 202 appends the SHA3/SHAKE domain-separation suffix before pad10*1.
|
|
589
|
+
// These byte values already include the first padding bit, while the
|
|
590
|
+
// final `0x80` below supplies the closing `1` bit in the last rate byte.
|
|
372
591
|
state[pos] ^= suffix;
|
|
592
|
+
// If that combined suffix lands in the last rate byte and already sets
|
|
593
|
+
// bit 7, absorb it first so the final pad10*1 bit can be xored into a
|
|
594
|
+
// fresh block.
|
|
373
595
|
if ((suffix & 0x80) !== 0 && pos === blockLen - 1)
|
|
374
596
|
this.keccak();
|
|
375
597
|
state[blockLen - 1] ^= 0x80;
|
|
@@ -392,7 +614,9 @@ class Keccak {
|
|
|
392
614
|
return out;
|
|
393
615
|
}
|
|
394
616
|
xofInto(out) {
|
|
395
|
-
//
|
|
617
|
+
// Plain SHA3/Keccak usage with XOF is probably a mistake, but this base
|
|
618
|
+
// class is also reused by SHAKE/cSHAKE/KMAC/TupleHash/ParallelHash/
|
|
619
|
+
// TurboSHAKE/KangarooTwelve wrappers that intentionally enable XOF.
|
|
396
620
|
if (!this.enableXOF)
|
|
397
621
|
throw new Error('XOF is not possible for this instance');
|
|
398
622
|
return this.writeInto(out);
|
|
@@ -405,12 +629,14 @@ class Keccak {
|
|
|
405
629
|
aoutput(out, this);
|
|
406
630
|
if (this.finished)
|
|
407
631
|
throw new Error('digest() was already called');
|
|
408
|
-
|
|
632
|
+
// `aoutput(...)` allows oversized buffers; digestInto() must fill only the advertised digest.
|
|
633
|
+
this.writeInto(out.subarray(0, this.outputLen));
|
|
409
634
|
this.destroy();
|
|
410
|
-
return out;
|
|
411
635
|
}
|
|
412
636
|
digest() {
|
|
413
|
-
|
|
637
|
+
const out = new Uint8Array(this.outputLen);
|
|
638
|
+
this.digestInto(out);
|
|
639
|
+
return out;
|
|
414
640
|
}
|
|
415
641
|
destroy() {
|
|
416
642
|
this.destroyed = true;
|
|
@@ -419,6 +645,9 @@ class Keccak {
|
|
|
419
645
|
_cloneInto(to) {
|
|
420
646
|
const { blockLen, suffix, outputLen, rounds, enableXOF } = this;
|
|
421
647
|
to ||= new Keccak(blockLen, suffix, outputLen, enableXOF, rounds);
|
|
648
|
+
// Reused destinations can come from a different rate/capacity variant, so clone must rewrite
|
|
649
|
+
// the sponge geometry as well as the state words.
|
|
650
|
+
to.blockLen = blockLen;
|
|
422
651
|
to.state32.set(this.state32);
|
|
423
652
|
to.pos = this.pos;
|
|
424
653
|
to.posOut = this.posOut;
|
|
@@ -428,16 +657,39 @@ class Keccak {
|
|
|
428
657
|
to.suffix = suffix;
|
|
429
658
|
to.outputLen = outputLen;
|
|
430
659
|
to.enableXOF = enableXOF;
|
|
660
|
+
// Clones must preserve the public capability bit too; `_KMAC` reuses this path and deep clone
|
|
661
|
+
// tests compare instance fields directly, so leaving `canXOF` behind makes the clone lie.
|
|
662
|
+
to.canXOF = this.canXOF;
|
|
431
663
|
to.destroyed = this.destroyed;
|
|
432
664
|
return to;
|
|
433
665
|
}
|
|
434
666
|
}
|
|
435
667
|
const genShake = (suffix, blockLen, outputLen, info = {}) => createHasher((opts = {}) => new Keccak(blockLen, suffix, opts.dkLen === undefined ? outputLen : opts.dkLen, true), info);
|
|
436
|
-
/**
|
|
668
|
+
/**
|
|
669
|
+
* SHAKE128 XOF with 128-bit security and a 16-byte default output.
|
|
670
|
+
* @param msg - message bytes to hash
|
|
671
|
+
* @param opts - Optional output-length override. See {@link ShakeOpts}.
|
|
672
|
+
* @returns Digest bytes.
|
|
673
|
+
* @example
|
|
674
|
+
* Hash a message with SHAKE128.
|
|
675
|
+
* ```ts
|
|
676
|
+
* shake128(new Uint8Array([97, 98, 99]), { dkLen: 32 });
|
|
677
|
+
* ```
|
|
678
|
+
*/
|
|
437
679
|
const shake128 =
|
|
438
680
|
/* @__PURE__ */
|
|
439
681
|
genShake(0x1f, 168, 16, /* @__PURE__ */ oidNist(0x0b));
|
|
440
|
-
/**
|
|
682
|
+
/**
|
|
683
|
+
* SHAKE256 XOF with 256-bit security and a 32-byte default output.
|
|
684
|
+
* @param msg - message bytes to hash
|
|
685
|
+
* @param opts - Optional output-length override. See {@link ShakeOpts}.
|
|
686
|
+
* @returns Digest bytes.
|
|
687
|
+
* @example
|
|
688
|
+
* Hash a message with SHAKE256.
|
|
689
|
+
* ```ts
|
|
690
|
+
* shake256(new Uint8Array([97, 98, 99]), { dkLen: 64 });
|
|
691
|
+
* ```
|
|
692
|
+
*/
|
|
441
693
|
const shake256 =
|
|
442
694
|
/* @__PURE__ */
|
|
443
695
|
genShake(0x1f, 136, 32, /* @__PURE__ */ oidNist(0x0c));
|
|
@@ -455,7 +707,6 @@ genShake(0x1f, 136, 32, /* @__PURE__ */ oidNist(0x0c));
|
|
|
455
707
|
class KeccakState {
|
|
456
708
|
constructor() {
|
|
457
709
|
this.hasher = null;
|
|
458
|
-
this.finalized = false;
|
|
459
710
|
}
|
|
460
711
|
}
|
|
461
712
|
|
|
@@ -463,17 +714,18 @@ class KeccakState {
|
|
|
463
714
|
|
|
464
715
|
function shake128Init(state) {
|
|
465
716
|
state.hasher = shake128.create({});
|
|
466
|
-
state.finalized = false;
|
|
467
717
|
}
|
|
468
718
|
|
|
469
719
|
function shake128Absorb(state, input) {
|
|
470
720
|
state.hasher.update(input);
|
|
471
721
|
}
|
|
472
722
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
723
|
+
/**
|
|
724
|
+
* No-op retained for API parity with the C reference's absorb/finalize/squeeze
|
|
725
|
+
* flow: @noble/hashes finalizes the sponge automatically on the first
|
|
726
|
+
* xofInto() call, so there is no separate finalize step to perform.
|
|
727
|
+
*/
|
|
728
|
+
function shake128Finalize() {}
|
|
477
729
|
|
|
478
730
|
function shake128SqueezeBlocks(out, outputOffset, nBlocks, state) {
|
|
479
731
|
const len = nBlocks * Shake128Rate;
|
|
@@ -485,17 +737,18 @@ function shake128SqueezeBlocks(out, outputOffset, nBlocks, state) {
|
|
|
485
737
|
|
|
486
738
|
function shake256Init(state) {
|
|
487
739
|
state.hasher = shake256.create({});
|
|
488
|
-
state.finalized = false;
|
|
489
740
|
}
|
|
490
741
|
|
|
491
742
|
function shake256Absorb(state, input) {
|
|
492
743
|
state.hasher.update(input);
|
|
493
744
|
}
|
|
494
745
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
746
|
+
/**
|
|
747
|
+
* No-op retained for API parity with the C reference's absorb/finalize/squeeze
|
|
748
|
+
* flow: @noble/hashes finalizes the sponge automatically on the first
|
|
749
|
+
* xofInto() call, so there is no separate finalize step to perform.
|
|
750
|
+
*/
|
|
751
|
+
function shake256Finalize() {}
|
|
499
752
|
|
|
500
753
|
function shake256SqueezeBlocks(out, outputOffset, nBlocks, state) {
|
|
501
754
|
const len = nBlocks * Shake256Rate;
|
|
@@ -514,7 +767,6 @@ function dilithiumShake128StreamInit(state, seed, nonce) {
|
|
|
514
767
|
shake128Init(state);
|
|
515
768
|
shake128Absorb(state, seed);
|
|
516
769
|
shake128Absorb(state, t);
|
|
517
|
-
shake128Finalize(state);
|
|
518
770
|
}
|
|
519
771
|
|
|
520
772
|
function dilithiumShake256StreamInit(state, seed, nonce) {
|
|
@@ -528,7 +780,6 @@ function dilithiumShake256StreamInit(state, seed, nonce) {
|
|
|
528
780
|
shake256Init(state);
|
|
529
781
|
shake256Absorb(state, seed);
|
|
530
782
|
shake256Absorb(state, t);
|
|
531
|
-
shake256Finalize(state);
|
|
532
783
|
}
|
|
533
784
|
|
|
534
785
|
function montgomeryReduce(a) {
|
|
@@ -827,6 +1078,8 @@ function polyUniformGamma1(a, seed, nonce) {
|
|
|
827
1078
|
}
|
|
828
1079
|
|
|
829
1080
|
function polyChallenge(cP, seed) {
|
|
1081
|
+
// Invariant tripwire: internal callers always pass a SeedBytes-long
|
|
1082
|
+
// challenge hash; anything else indicates a regression in sign/verify.
|
|
830
1083
|
if (seed.length !== SeedBytes) throw new Error('invalid seed length');
|
|
831
1084
|
|
|
832
1085
|
let b;
|
|
@@ -837,7 +1090,6 @@ function polyChallenge(cP, seed) {
|
|
|
837
1090
|
const state = new KeccakState();
|
|
838
1091
|
shake256Init(state);
|
|
839
1092
|
shake256Absorb(state, seed);
|
|
840
|
-
shake256Finalize(state);
|
|
841
1093
|
shake256SqueezeBlocks(buf, 0, 1, state);
|
|
842
1094
|
|
|
843
1095
|
let signs = 0n;
|
|
@@ -1144,6 +1396,9 @@ function polyVecLChkNorm(v, bound) {
|
|
|
1144
1396
|
|
|
1145
1397
|
function polyVecKUniformEta(v, seed, nonceP) {
|
|
1146
1398
|
let nonce = nonceP;
|
|
1399
|
+
if (seed.length !== CRHBytes) {
|
|
1400
|
+
throw new Error(`invalid seed length ${seed.length} | Expected length ${CRHBytes}`);
|
|
1401
|
+
}
|
|
1147
1402
|
for (let i = 0; i < K; ++i) {
|
|
1148
1403
|
polyUniformEta(v.vec[i], seed, nonce++);
|
|
1149
1404
|
}
|
|
@@ -1350,6 +1605,10 @@ function packSig(sigP, c, z, h) {
|
|
|
1350
1605
|
sig[sigOffset + i] = 0;
|
|
1351
1606
|
}
|
|
1352
1607
|
|
|
1608
|
+
// Invariant tripwires: h produced by polyVecKMakeHint is always binary
|
|
1609
|
+
// with at most OMEGA set coefficients (the sign loop re-samples
|
|
1610
|
+
// otherwise). A violation here means an internal regression upstream —
|
|
1611
|
+
// fail loudly rather than emit a malformed signature.
|
|
1353
1612
|
let k = 0;
|
|
1354
1613
|
for (let i = 0; i < K; ++i) {
|
|
1355
1614
|
for (let j = 0; j < N; ++j) {
|
|
@@ -1443,6 +1702,9 @@ function randomBytes(size) {
|
|
|
1443
1702
|
cryptoObj.getRandomValues(out.subarray(i, Math.min(size, i + MAX_BYTES)));
|
|
1444
1703
|
}
|
|
1445
1704
|
if (size >= 16) {
|
|
1705
|
+
// Invariant tripwire: a healthy CSPRNG never returns 16 leading zero
|
|
1706
|
+
// bytes (p = 2^-128). All-zero output means the platform RNG is
|
|
1707
|
+
// catastrophically broken — refuse to hand it to key generation.
|
|
1446
1708
|
let acc = 0;
|
|
1447
1709
|
for (let i = 0; i < 16; i++) acc |= out[i];
|
|
1448
1710
|
if (acc === 0) throw new Error('getRandomValues returned all zeros');
|
|
@@ -1491,6 +1753,22 @@ function zeroize(buffer) {
|
|
|
1491
1753
|
}
|
|
1492
1754
|
}
|
|
1493
1755
|
|
|
1756
|
+
/**
|
|
1757
|
+
* Attempts to zero the coefficient arrays of a polynomial vector
|
|
1758
|
+
* (PolyVecL/PolyVecK). Centralizes the secret-wiping pattern used by the
|
|
1759
|
+
* signing paths so every sensitive PolyVec is cleared the same way.
|
|
1760
|
+
*
|
|
1761
|
+
* Same BEST-EFFORT caveats as zeroize() — see SECURITY.md.
|
|
1762
|
+
*
|
|
1763
|
+
* @param {{vec: {coeffs: Int32Array}[]}} polyVec - The polynomial vector to zero
|
|
1764
|
+
* @returns {void}
|
|
1765
|
+
*/
|
|
1766
|
+
function zeroizePolyVec(polyVec) {
|
|
1767
|
+
for (let i = 0; i < polyVec.vec.length; i++) {
|
|
1768
|
+
polyVec.vec[i].coeffs.fill(0);
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1494
1772
|
/**
|
|
1495
1773
|
* Checks if a buffer is all zeros.
|
|
1496
1774
|
* Uses constant-time comparison to avoid timing leaks.
|
|
@@ -1522,6 +1800,8 @@ function isZero(buffer) {
|
|
|
1522
1800
|
* @private
|
|
1523
1801
|
*/
|
|
1524
1802
|
function hexToBytes(hex) {
|
|
1803
|
+
// Unreachable via the public API: messageToBytes routes only strings here.
|
|
1804
|
+
// Kept as defense-in-depth for any future direct internal caller.
|
|
1525
1805
|
/* c8 ignore start */
|
|
1526
1806
|
if (typeof hex !== 'string') {
|
|
1527
1807
|
throw new Error('message must be a hex string');
|
|
@@ -1571,13 +1851,18 @@ function messageToBytes(message) {
|
|
|
1571
1851
|
* Pass null or undefined for random key generation.
|
|
1572
1852
|
* @param {Uint8Array} pk - Output buffer for public key (must be CryptoPublicKeyBytes = 2592 bytes)
|
|
1573
1853
|
* @param {Uint8Array} sk - Output buffer for secret key (must be CryptoSecretKeyBytes = 4896 bytes)
|
|
1574
|
-
* @returns {Uint8Array} The seed used for key generation (useful when passedSeed is null)
|
|
1854
|
+
* @returns {Uint8Array} The seed used for key generation (useful when passedSeed is null).
|
|
1855
|
+
* **The returned seed is secret-key-equivalent**: anyone holding it can
|
|
1856
|
+
* regenerate the full keypair. Store it with the same care as `sk` and
|
|
1857
|
+
* `zeroize()` it as soon as it is no longer needed.
|
|
1575
1858
|
* @throws {Error} If pk/sk buffers are null or wrong size, or if seed is wrong size
|
|
1576
1859
|
*
|
|
1577
1860
|
* @example
|
|
1578
1861
|
* const pk = new Uint8Array(CryptoPublicKeyBytes);
|
|
1579
1862
|
* const sk = new Uint8Array(CryptoSecretKeyBytes);
|
|
1580
1863
|
* const seed = cryptoSignKeypair(null, pk, sk);
|
|
1864
|
+
* // ... persist or use seed (it can regenerate sk!) ...
|
|
1865
|
+
* zeroize(seed);
|
|
1581
1866
|
*/
|
|
1582
1867
|
function cryptoSignKeypair(passedSeed, pk, sk) {
|
|
1583
1868
|
try {
|
|
@@ -1651,10 +1936,10 @@ function cryptoSignKeypair(passedSeed, pk, sk) {
|
|
|
1651
1936
|
zeroize(seedBuf);
|
|
1652
1937
|
zeroize(rhoPrime);
|
|
1653
1938
|
zeroize(key);
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
if (s1hat)
|
|
1657
|
-
|
|
1939
|
+
zeroizePolyVec(s1);
|
|
1940
|
+
zeroizePolyVec(s2);
|
|
1941
|
+
if (s1hat) zeroizePolyVec(s1hat);
|
|
1942
|
+
zeroizePolyVec(t0);
|
|
1658
1943
|
}
|
|
1659
1944
|
}
|
|
1660
1945
|
|
|
@@ -1719,7 +2004,10 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
|
|
|
1719
2004
|
const mu = shake256.create({}).update(tr).update(mBytes).xof(CRHBytes);
|
|
1720
2005
|
|
|
1721
2006
|
if (randomizedSigning) {
|
|
1722
|
-
|
|
2007
|
+
// randomBytes already returns a fresh Uint8Array; assign it directly so
|
|
2008
|
+
// no unwiped intermediate copy is left behind (rhoPrime is zeroized in
|
|
2009
|
+
// the finally block).
|
|
2010
|
+
rhoPrime = randomBytes(CRHBytes);
|
|
1723
2011
|
} else {
|
|
1724
2012
|
rhoPrime = shake256.create({}).update(key).update(mu).xof(CRHBytes);
|
|
1725
2013
|
}
|
|
@@ -1746,7 +2034,7 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
|
|
|
1746
2034
|
const cHash = shake256
|
|
1747
2035
|
.create({})
|
|
1748
2036
|
.update(mu)
|
|
1749
|
-
.update(sig.
|
|
2037
|
+
.update(sig.subarray(0, K * PolyW1PackedBytes))
|
|
1750
2038
|
.xof(SeedBytes);
|
|
1751
2039
|
sig.set(cHash);
|
|
1752
2040
|
|
|
@@ -1773,6 +2061,9 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
|
|
|
1773
2061
|
polyVecKPointWisePolyMontgomery(h, cp, t0);
|
|
1774
2062
|
polyVecKInvNTTToMont(h);
|
|
1775
2063
|
polyVecKReduce(h);
|
|
2064
|
+
// Statistically rare rejection (depends on key/challenge interaction);
|
|
2065
|
+
// no deterministic trigger is known, so it is exercised by long fuzz
|
|
2066
|
+
// campaigns rather than unit vectors.
|
|
1776
2067
|
/* c8 ignore start */
|
|
1777
2068
|
if (polyVecKChkNorm(h, GAMMA2) !== 0) {
|
|
1778
2069
|
continue;
|
|
@@ -1781,6 +2072,7 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
|
|
|
1781
2072
|
|
|
1782
2073
|
polyVecKAdd(w0, w0, h);
|
|
1783
2074
|
const n = polyVecKMakeHint(h, w0, w1);
|
|
2075
|
+
// Statistically rare rejection — same rationale as the ct0 check above.
|
|
1784
2076
|
/* c8 ignore start */
|
|
1785
2077
|
if (n > OMEGA) {
|
|
1786
2078
|
continue;
|
|
@@ -1793,10 +2085,10 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
|
|
|
1793
2085
|
} finally {
|
|
1794
2086
|
zeroize(key);
|
|
1795
2087
|
zeroize(rhoPrime);
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
2088
|
+
zeroizePolyVec(s1);
|
|
2089
|
+
zeroizePolyVec(s2);
|
|
2090
|
+
zeroizePolyVec(t0);
|
|
2091
|
+
zeroizePolyVec(y);
|
|
1800
2092
|
}
|
|
1801
2093
|
}
|
|
1802
2094
|
|
|
@@ -1845,13 +2137,14 @@ function cryptoSignSignatureDeterministic(sig, m, sk) {
|
|
|
1845
2137
|
function cryptoSign(msg, sk, randomizedSigning) {
|
|
1846
2138
|
const msgBytes = messageToBytes(msg);
|
|
1847
2139
|
|
|
2140
|
+
// Place the message after the signature area. (The C reference uses a
|
|
2141
|
+
// backwards copy because its sm/m buffers may alias; here they never do.)
|
|
1848
2142
|
const sm = new Uint8Array(CryptoBytes + msgBytes.length);
|
|
1849
|
-
|
|
1850
|
-
for (let i = 0; i < mLen; ++i) {
|
|
1851
|
-
sm[CryptoBytes + mLen - 1 - i] = msgBytes[mLen - 1 - i];
|
|
1852
|
-
}
|
|
2143
|
+
sm.set(msgBytes, CryptoBytes);
|
|
1853
2144
|
const result = cryptoSignSignature(sm, msgBytes, sk, randomizedSigning);
|
|
1854
2145
|
|
|
2146
|
+
// Unreachable: cryptoSignSignature returns 0 or throws — defensive
|
|
2147
|
+
// tripwire in case a future change introduces a non-zero failure return.
|
|
1855
2148
|
/* c8 ignore start */
|
|
1856
2149
|
if (result !== 0) {
|
|
1857
2150
|
throw new Error('failed to sign');
|
|
@@ -2159,4 +2452,5 @@ exports.unpackSig = unpackSig;
|
|
|
2159
2452
|
exports.unpackSk = unpackSk;
|
|
2160
2453
|
exports.useHint = useHint;
|
|
2161
2454
|
exports.zeroize = zeroize;
|
|
2455
|
+
exports.zeroizePolyVec = zeroizePolyVec;
|
|
2162
2456
|
exports.zetas = zetas;
|