@theqrl/dilithium5 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/dilithium5.js +302 -50
- package/package.json +10 -10
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));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theqrl/dilithium5",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Dilithium-5 cryptography",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dilithium",
|
|
@@ -63,24 +63,24 @@
|
|
|
63
63
|
"@rollup/plugin-node-resolve": "16.0.3",
|
|
64
64
|
"c8": "11.0.0",
|
|
65
65
|
"chai": "6.2.2",
|
|
66
|
-
"eslint": "10.0
|
|
66
|
+
"eslint": "10.3.0",
|
|
67
67
|
"eslint-config-prettier": "10.1.8",
|
|
68
68
|
"eslint-plugin-import-x": "4.16.2",
|
|
69
69
|
"eslint-plugin-prettier": "5.5.5",
|
|
70
|
-
"globals": "17.
|
|
71
|
-
"minimatch": "10.2.
|
|
70
|
+
"globals": "17.6.0",
|
|
71
|
+
"minimatch": "10.2.5",
|
|
72
72
|
"mocha": "11.7.5",
|
|
73
|
-
"prettier": "3.8.
|
|
74
|
-
"rollup": "4.
|
|
75
|
-
"serialize-javascript": "7.0.
|
|
76
|
-
"tar": "7.5.
|
|
73
|
+
"prettier": "3.8.3",
|
|
74
|
+
"rollup": "4.60.3",
|
|
75
|
+
"serialize-javascript": "7.0.5",
|
|
76
|
+
"tar": "7.5.14"
|
|
77
77
|
},
|
|
78
78
|
"dependencies": {
|
|
79
|
-
"@noble/hashes": "2.0
|
|
79
|
+
"@noble/hashes": "2.2.0"
|
|
80
80
|
},
|
|
81
81
|
"overrides": {
|
|
82
82
|
"diff": "8.0.3",
|
|
83
|
-
"minimatch": "10.2.
|
|
83
|
+
"minimatch": "10.2.5"
|
|
84
84
|
},
|
|
85
85
|
"c8": {
|
|
86
86
|
"include": [
|