@theqrl/dilithium5 1.1.0 → 1.1.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/README.md CHANGED
@@ -55,7 +55,7 @@ console.log(new TextDecoder().decode(extracted)); // "Hello, quantum world!"
55
55
 
56
56
  Generate a keypair from a seed.
57
57
 
58
- - `seed`: `Uint8Array(32)` or `null` for random
58
+ - `seed`: `Uint8Array(32)`, `null`, or `undefined` for random
59
59
  - `pk`: `Uint8Array(2592)` - output buffer for public key
60
60
  - `sk`: `Uint8Array(4896)` - output buffer for secret key
61
61
  - Returns: The seed used (useful when `seed` is `null`)
@@ -102,10 +102,17 @@ Verify a detached signature.
102
102
 
103
103
  Zero out sensitive data (best-effort, see security notes).
104
104
 
105
+ - `buffer`: `Uint8Array` - the buffer to zero
106
+ - Throws: `TypeError` if buffer is not a `Uint8Array`
107
+
105
108
  #### `isZero(buffer)`
106
109
 
107
110
  Check if buffer is all zeros (constant-time).
108
111
 
112
+ - `buffer`: `Uint8Array` - the buffer to check
113
+ - Returns: `true` if all bytes are zero
114
+ - Throws: `TypeError` if buffer is not a `Uint8Array`
115
+
109
116
  ## Interoperability with go-qrllib
110
117
 
111
118
  go-qrllib pre-hashes seeds with SHAKE256 before key generation. To generate matching keys:
@@ -1,8 +1,5 @@
1
1
  'use strict';
2
2
 
3
- var sha3_js = require('@noble/hashes/sha3.js');
4
- var utils_js = require('@noble/hashes/utils.js');
5
-
6
3
  const Shake128Rate = 168;
7
4
  const Shake256Rate = 136;
8
5
  const Stream128BlockBytes = Shake128Rate;
@@ -65,6 +62,386 @@ const zetas = [
65
62
  -1362209, 3937738, 1400424, -846154, 1976782,
66
63
  ];
67
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
+ const U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1);
71
+ const _32n = /* @__PURE__ */ BigInt(32);
72
+ function fromBig(n, le = false) {
73
+ if (le)
74
+ return { h: Number(n & U32_MASK64), l: Number((n >> _32n) & U32_MASK64) };
75
+ return { h: Number((n >> _32n) & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
76
+ }
77
+ function split(lst, le = false) {
78
+ const len = lst.length;
79
+ let Ah = new Uint32Array(len);
80
+ let Al = new Uint32Array(len);
81
+ for (let i = 0; i < len; i++) {
82
+ const { h, l } = fromBig(lst[i], le);
83
+ [Ah[i], Al[i]] = [h, l];
84
+ }
85
+ return [Ah, Al];
86
+ }
87
+ // Left rotate for Shift in [1, 32)
88
+ const rotlSH = (h, l, s) => (h << s) | (l >>> (32 - s));
89
+ const rotlSL = (h, l, s) => (l << s) | (h >>> (32 - s));
90
+ // Left rotate for Shift in (32, 64), NOTE: 32 is special case.
91
+ const rotlBH = (h, l, s) => (l << (s - 32)) | (h >>> (64 - s));
92
+ const rotlBL = (h, l, s) => (h << (s - 32)) | (l >>> (64 - s));
93
+
94
+ /**
95
+ * Utilities for hex, bytes, CSPRNG.
96
+ * @module
97
+ */
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
+ function isBytes(a) {
101
+ return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');
102
+ }
103
+ /** Asserts something is positive integer. */
104
+ function anumber(n, title = '') {
105
+ if (!Number.isSafeInteger(n) || n < 0) {
106
+ const prefix = title && `"${title}" `;
107
+ throw new Error(`${prefix}expected integer >= 0, got ${n}`);
108
+ }
109
+ }
110
+ /** Asserts something is Uint8Array. */
111
+ function abytes(value, length, title = '') {
112
+ const bytes = isBytes(value);
113
+ const len = value?.length;
114
+ const needsLen = length !== undefined;
115
+ if (!bytes || (needsLen)) {
116
+ const prefix = title && `"${title}" `;
117
+ const ofLen = '';
118
+ const got = bytes ? `length=${len}` : `type=${typeof value}`;
119
+ throw new Error(prefix + 'expected Uint8Array' + ofLen + ', got ' + got);
120
+ }
121
+ return value;
122
+ }
123
+ /** Asserts a hash instance has not been destroyed / finished */
124
+ function aexists(instance, checkFinished = true) {
125
+ if (instance.destroyed)
126
+ throw new Error('Hash instance has been destroyed');
127
+ if (checkFinished && instance.finished)
128
+ throw new Error('Hash#digest() has already been called');
129
+ }
130
+ /** Asserts output is properly-sized byte array */
131
+ function aoutput(out, instance) {
132
+ abytes(out, undefined, 'digestInto() output');
133
+ const min = instance.outputLen;
134
+ if (out.length < min) {
135
+ throw new Error('"digestInto() output" expected to be of length >=' + min);
136
+ }
137
+ }
138
+ /** Cast u8 / u16 / u32 to u32. */
139
+ function u32(arr) {
140
+ return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
141
+ }
142
+ /** Zeroize a byte array. Warning: JS provides no guarantees. */
143
+ function clean(...arrays) {
144
+ for (let i = 0; i < arrays.length; i++) {
145
+ arrays[i].fill(0);
146
+ }
147
+ }
148
+ /** Is current platform little-endian? Most are. Big-Endian platform: IBM */
149
+ const isLE = /* @__PURE__ */ (() => new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();
150
+ /** The byte swap operation for uint32 */
151
+ function byteSwap(word) {
152
+ return (((word << 24) & 0xff000000) |
153
+ ((word << 8) & 0xff0000) |
154
+ ((word >>> 8) & 0xff00) |
155
+ ((word >>> 24) & 0xff));
156
+ }
157
+ /** In place byte swap for Uint32Array */
158
+ function byteSwap32(arr) {
159
+ for (let i = 0; i < arr.length; i++) {
160
+ arr[i] = byteSwap(arr[i]);
161
+ }
162
+ return arr;
163
+ }
164
+ const swap32IfBE = isLE
165
+ ? (u) => u
166
+ : byteSwap32;
167
+ // Built-in hex conversion https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex
168
+ const hasHexBuiltin = /* @__PURE__ */ (() =>
169
+ // @ts-ignore
170
+ typeof Uint8Array.from([]).toHex === 'function' && typeof Uint8Array.fromHex === 'function')();
171
+ // We use optimized technique to convert hex string to byte array
172
+ const asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 };
173
+ function asciiToBase16(ch) {
174
+ if (ch >= asciis._0 && ch <= asciis._9)
175
+ return ch - asciis._0; // '2' => 50-48
176
+ if (ch >= asciis.A && ch <= asciis.F)
177
+ return ch - (asciis.A - 10); // 'B' => 66-(65-10)
178
+ if (ch >= asciis.a && ch <= asciis.f)
179
+ return ch - (asciis.a - 10); // 'b' => 98-(97-10)
180
+ return;
181
+ }
182
+ /**
183
+ * Convert hex string to byte array. Uses built-in function, when available.
184
+ * @example hexToBytes('cafe0123') // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])
185
+ */
186
+ function hexToBytes$1(hex) {
187
+ if (typeof hex !== 'string')
188
+ throw new Error('hex string expected, got ' + typeof hex);
189
+ // @ts-ignore
190
+ if (hasHexBuiltin)
191
+ return Uint8Array.fromHex(hex);
192
+ const hl = hex.length;
193
+ const al = hl / 2;
194
+ if (hl % 2)
195
+ throw new Error('hex string expected, got unpadded hex of length ' + hl);
196
+ const array = new Uint8Array(al);
197
+ for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
198
+ const n1 = asciiToBase16(hex.charCodeAt(hi));
199
+ const n2 = asciiToBase16(hex.charCodeAt(hi + 1));
200
+ if (n1 === undefined || n2 === undefined) {
201
+ const char = hex[hi] + hex[hi + 1];
202
+ throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi);
203
+ }
204
+ array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163
205
+ }
206
+ return array;
207
+ }
208
+ /** Creates function with outputLen, blockLen, create properties from a class constructor. */
209
+ function createHasher(hashCons, info = {}) {
210
+ const hashC = (msg, opts) => hashCons(opts).update(msg).digest();
211
+ const tmp = hashCons(undefined);
212
+ hashC.outputLen = tmp.outputLen;
213
+ hashC.blockLen = tmp.blockLen;
214
+ hashC.create = (opts) => hashCons(opts);
215
+ Object.assign(hashC, info);
216
+ return Object.freeze(hashC);
217
+ }
218
+ /** Creates OID opts for NIST hashes, with prefix 06 09 60 86 48 01 65 03 04 02. */
219
+ const oidNist = (suffix) => ({
220
+ oid: Uint8Array.from([0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, suffix]),
221
+ });
222
+
223
+ /**
224
+ * SHA3 (keccak) hash function, based on a new "Sponge function" design.
225
+ * Different from older hashes, the internal state is bigger than output size.
226
+ *
227
+ * Check out [FIPS-202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf),
228
+ * [Website](https://keccak.team/keccak.html),
229
+ * [the differences between SHA-3 and Keccak](https://crypto.stackexchange.com/questions/15727/what-are-the-key-differences-between-the-draft-sha-3-standard-and-the-keccak-sub).
230
+ *
231
+ * Check out `sha3-addons` module for cSHAKE, k12, and others.
232
+ * @module
233
+ */
234
+ // No __PURE__ annotations in sha3 header:
235
+ // EVERYTHING is in fact used on every export.
236
+ // Various per round constants calculations
237
+ const _0n = BigInt(0);
238
+ const _1n = BigInt(1);
239
+ const _2n = BigInt(2);
240
+ const _7n = BigInt(7);
241
+ const _256n = BigInt(256);
242
+ const _0x71n = BigInt(0x71);
243
+ const SHA3_PI = [];
244
+ const SHA3_ROTL = [];
245
+ const _SHA3_IOTA = []; // no pure annotation: var is always used
246
+ for (let round = 0, R = _1n, x = 1, y = 0; round < 24; round++) {
247
+ // Pi
248
+ [x, y] = [y, (2 * x + 3 * y) % 5];
249
+ SHA3_PI.push(2 * (5 * y + x));
250
+ // Rotational
251
+ SHA3_ROTL.push((((round + 1) * (round + 2)) / 2) % 64);
252
+ // Iota
253
+ let t = _0n;
254
+ for (let j = 0; j < 7; j++) {
255
+ R = ((R << _1n) ^ ((R >> _7n) * _0x71n)) % _256n;
256
+ if (R & _2n)
257
+ t ^= _1n << ((_1n << BigInt(j)) - _1n);
258
+ }
259
+ _SHA3_IOTA.push(t);
260
+ }
261
+ const IOTAS = split(_SHA3_IOTA, true);
262
+ const SHA3_IOTA_H = IOTAS[0];
263
+ const SHA3_IOTA_L = IOTAS[1];
264
+ // Left rotation (without 0, 32, 64)
265
+ const rotlH = (h, l, s) => (s > 32 ? rotlBH(h, l, s) : rotlSH(h, l, s));
266
+ const rotlL = (h, l, s) => (s > 32 ? rotlBL(h, l, s) : rotlSL(h, l, s));
267
+ /** `keccakf1600` internal function, additionally allows to adjust round count. */
268
+ function keccakP(s, rounds = 24) {
269
+ const B = new Uint32Array(5 * 2);
270
+ // NOTE: all indices are x2 since we store state as u32 instead of u64 (bigints to slow in js)
271
+ for (let round = 24 - rounds; round < 24; round++) {
272
+ // Theta θ
273
+ for (let x = 0; x < 10; x++)
274
+ B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40];
275
+ for (let x = 0; x < 10; x += 2) {
276
+ const idx1 = (x + 8) % 10;
277
+ const idx0 = (x + 2) % 10;
278
+ const B0 = B[idx0];
279
+ const B1 = B[idx0 + 1];
280
+ const Th = rotlH(B0, B1, 1) ^ B[idx1];
281
+ const Tl = rotlL(B0, B1, 1) ^ B[idx1 + 1];
282
+ for (let y = 0; y < 50; y += 10) {
283
+ s[x + y] ^= Th;
284
+ s[x + y + 1] ^= Tl;
285
+ }
286
+ }
287
+ // Rho (ρ) and Pi (π)
288
+ let curH = s[2];
289
+ let curL = s[3];
290
+ for (let t = 0; t < 24; t++) {
291
+ const shift = SHA3_ROTL[t];
292
+ const Th = rotlH(curH, curL, shift);
293
+ const Tl = rotlL(curH, curL, shift);
294
+ const PI = SHA3_PI[t];
295
+ curH = s[PI];
296
+ curL = s[PI + 1];
297
+ s[PI] = Th;
298
+ s[PI + 1] = Tl;
299
+ }
300
+ // Chi (χ)
301
+ for (let y = 0; y < 50; y += 10) {
302
+ for (let x = 0; x < 10; x++)
303
+ B[x] = s[y + x];
304
+ for (let x = 0; x < 10; x++)
305
+ s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10];
306
+ }
307
+ // Iota (ι)
308
+ s[0] ^= SHA3_IOTA_H[round];
309
+ s[1] ^= SHA3_IOTA_L[round];
310
+ }
311
+ clean(B);
312
+ }
313
+ /** Keccak sponge function. */
314
+ class Keccak {
315
+ state;
316
+ pos = 0;
317
+ posOut = 0;
318
+ finished = false;
319
+ state32;
320
+ destroyed = false;
321
+ blockLen;
322
+ suffix;
323
+ outputLen;
324
+ enableXOF = false;
325
+ rounds;
326
+ // NOTE: we accept arguments in bytes instead of bits here.
327
+ constructor(blockLen, suffix, outputLen, enableXOF = false, rounds = 24) {
328
+ this.blockLen = blockLen;
329
+ this.suffix = suffix;
330
+ this.outputLen = outputLen;
331
+ this.enableXOF = enableXOF;
332
+ this.rounds = rounds;
333
+ // Can be passed from user as dkLen
334
+ anumber(outputLen, 'outputLen');
335
+ // 1600 = 5x5 matrix of 64bit. 1600 bits === 200 bytes
336
+ // 0 < blockLen < 200
337
+ if (!(0 < blockLen && blockLen < 200))
338
+ throw new Error('only keccak-f1600 function is supported');
339
+ this.state = new Uint8Array(200);
340
+ this.state32 = u32(this.state);
341
+ }
342
+ clone() {
343
+ return this._cloneInto();
344
+ }
345
+ keccak() {
346
+ swap32IfBE(this.state32);
347
+ keccakP(this.state32, this.rounds);
348
+ swap32IfBE(this.state32);
349
+ this.posOut = 0;
350
+ this.pos = 0;
351
+ }
352
+ update(data) {
353
+ aexists(this);
354
+ abytes(data);
355
+ const { blockLen, state } = this;
356
+ const len = data.length;
357
+ for (let pos = 0; pos < len;) {
358
+ const take = Math.min(blockLen - this.pos, len - pos);
359
+ for (let i = 0; i < take; i++)
360
+ state[this.pos++] ^= data[pos++];
361
+ if (this.pos === blockLen)
362
+ this.keccak();
363
+ }
364
+ return this;
365
+ }
366
+ finish() {
367
+ if (this.finished)
368
+ return;
369
+ this.finished = true;
370
+ const { state, suffix, pos, blockLen } = this;
371
+ // Do the padding
372
+ state[pos] ^= suffix;
373
+ if ((suffix & 0x80) !== 0 && pos === blockLen - 1)
374
+ this.keccak();
375
+ state[blockLen - 1] ^= 0x80;
376
+ this.keccak();
377
+ }
378
+ writeInto(out) {
379
+ aexists(this, false);
380
+ abytes(out);
381
+ this.finish();
382
+ const bufferOut = this.state;
383
+ const { blockLen } = this;
384
+ for (let pos = 0, len = out.length; pos < len;) {
385
+ if (this.posOut >= blockLen)
386
+ this.keccak();
387
+ const take = Math.min(blockLen - this.posOut, len - pos);
388
+ out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos);
389
+ this.posOut += take;
390
+ pos += take;
391
+ }
392
+ return out;
393
+ }
394
+ xofInto(out) {
395
+ // Sha3/Keccak usage with XOF is probably mistake, only SHAKE instances can do XOF
396
+ if (!this.enableXOF)
397
+ throw new Error('XOF is not possible for this instance');
398
+ return this.writeInto(out);
399
+ }
400
+ xof(bytes) {
401
+ anumber(bytes);
402
+ return this.xofInto(new Uint8Array(bytes));
403
+ }
404
+ digestInto(out) {
405
+ aoutput(out, this);
406
+ if (this.finished)
407
+ throw new Error('digest() was already called');
408
+ this.writeInto(out);
409
+ this.destroy();
410
+ return out;
411
+ }
412
+ digest() {
413
+ return this.digestInto(new Uint8Array(this.outputLen));
414
+ }
415
+ destroy() {
416
+ this.destroyed = true;
417
+ clean(this.state);
418
+ }
419
+ _cloneInto(to) {
420
+ const { blockLen, suffix, outputLen, rounds, enableXOF } = this;
421
+ to ||= new Keccak(blockLen, suffix, outputLen, enableXOF, rounds);
422
+ to.state32.set(this.state32);
423
+ to.pos = this.pos;
424
+ to.posOut = this.posOut;
425
+ to.finished = this.finished;
426
+ to.rounds = rounds;
427
+ // Suffix can change in cSHAKE
428
+ to.suffix = suffix;
429
+ to.outputLen = outputLen;
430
+ to.enableXOF = enableXOF;
431
+ to.destroyed = this.destroyed;
432
+ return to;
433
+ }
434
+ }
435
+ const genShake = (suffix, blockLen, outputLen, info = {}) => createHasher((opts = {}) => new Keccak(blockLen, suffix, opts.dkLen === undefined ? outputLen : opts.dkLen, true), info);
436
+ /** SHAKE128 XOF with 128-bit security. */
437
+ const shake128 =
438
+ /* @__PURE__ */
439
+ genShake(0x1f, 168, 16, /* @__PURE__ */ oidNist(0x0b));
440
+ /** SHAKE256 XOF with 256-bit security. */
441
+ const shake256 =
442
+ /* @__PURE__ */
443
+ genShake(0x1f, 136, 32, /* @__PURE__ */ oidNist(0x0c));
444
+
68
445
  /**
69
446
  * FIPS 202 SHAKE functions using @noble/hashes
70
447
  * Provides streaming XOF (extendable output function) interface
@@ -85,7 +462,7 @@ class KeccakState {
85
462
  // SHAKE-128 functions
86
463
 
87
464
  function shake128Init(state) {
88
- state.hasher = sha3_js.shake128.create({});
465
+ state.hasher = shake128.create({});
89
466
  state.finalized = false;
90
467
  }
91
468
 
@@ -107,7 +484,7 @@ function shake128SqueezeBlocks(out, outputOffset, nBlocks, state) {
107
484
  // SHAKE-256 functions
108
485
 
109
486
  function shake256Init(state) {
110
- state.hasher = sha3_js.shake256.create({});
487
+ state.hasher = shake256.create({});
111
488
  state.finalized = false;
112
489
  }
113
490
 
@@ -160,12 +537,16 @@ function montgomeryReduce(a) {
160
537
  return t;
161
538
  }
162
539
 
540
+ // Partial reduction modulo Q. Input must satisfy |a| < 2^31 - 2^22.
541
+ // Output is in (-Q, Q). Mirrors the reference C implementation.
163
542
  function reduce32(a) {
164
543
  let t = (a + (1 << 22)) >> 23;
165
544
  t = a - t * Q;
166
545
  return t;
167
546
  }
168
547
 
548
+ // Conditional add Q: if a is negative, add Q. Input must satisfy -Q < a < 2^31.
549
+ // Output is in [0, Q). Mirrors the reference C implementation.
169
550
  function cAddQ(a) {
170
551
  let ar = a;
171
552
  ar += (ar >> 31) & Q;
@@ -174,7 +555,7 @@ function cAddQ(a) {
174
555
 
175
556
  function ntt(a) {
176
557
  let k = 0;
177
- let j = 0;
558
+ let j;
178
559
 
179
560
  for (let len = 128; len > 0; len >>= 1) {
180
561
  for (let start = 0; start < N; start = j + len) {
@@ -190,7 +571,7 @@ function ntt(a) {
190
571
 
191
572
  function invNTTToMont(a) {
192
573
  const f = 41978n; // mont^2/256
193
- let j = 0;
574
+ let j;
194
575
  let k = 256;
195
576
 
196
577
  for (let len = 1; len < N; len <<= 1) {
@@ -327,10 +708,7 @@ function polyChkNorm(a, b) {
327
708
  }
328
709
 
329
710
  for (let i = 0; i < N; i++) {
330
- let t = a.coeffs[i] >> 31;
331
- t = a.coeffs[i] - (t & (2 * a.coeffs[i]));
332
-
333
- if (t >= b) {
711
+ if (Math.abs(a.coeffs[i]) >= b) {
334
712
  return 1;
335
713
  }
336
714
  }
@@ -449,6 +827,8 @@ function polyUniformGamma1(a, seed, nonce) {
449
827
  }
450
828
 
451
829
  function polyChallenge(cP, seed) {
830
+ if (seed.length !== SeedBytes) throw new Error('invalid seed length');
831
+
452
832
  let b;
453
833
  let pos;
454
834
  const c = cP;
@@ -456,7 +836,7 @@ function polyChallenge(cP, seed) {
456
836
 
457
837
  const state = new KeccakState();
458
838
  shake256Init(state);
459
- shake256Absorb(state, seed.slice(0, SeedBytes));
839
+ shake256Absorb(state, seed);
460
840
  shake256Finalize(state);
461
841
  shake256SqueezeBlocks(buf, 0, 1, state);
462
842
 
@@ -869,6 +1249,9 @@ function packPk(pkp, rho, t1) {
869
1249
  }
870
1250
 
871
1251
  function unpackPk(rhop, t1, pk) {
1252
+ if (!(pk instanceof Uint8Array) || pk.length !== CryptoPublicKeyBytes) {
1253
+ throw new Error(`pk must be a Uint8Array of ${CryptoPublicKeyBytes} bytes`);
1254
+ }
872
1255
  const rho = rhop;
873
1256
  for (let i = 0; i < SeedBytes; ++i) {
874
1257
  rho[i] = pk[i];
@@ -913,6 +1296,9 @@ function packSk(skp, rho, tr, key, t0, s1, s2) {
913
1296
  }
914
1297
 
915
1298
  function unpackSk(rhoP, trP, keyP, t0, s1, s2, sk) {
1299
+ if (!(sk instanceof Uint8Array) || sk.length !== CryptoSecretKeyBytes) {
1300
+ throw new Error(`sk must be a Uint8Array of ${CryptoSecretKeyBytes} bytes`);
1301
+ }
916
1302
  let skOffset = 0;
917
1303
  const rho = rhoP;
918
1304
  const tr = trP;
@@ -976,7 +1362,12 @@ function packSig(sigP, c, z, h) {
976
1362
  }
977
1363
  }
978
1364
 
1365
+ // Returns 0 on success, 1 on failure. On failure, output buffers (c, z, h)
1366
+ // may contain partial data and must not be used.
979
1367
  function unpackSig(cP, z, hP, sig) {
1368
+ if (!(sig instanceof Uint8Array) || sig.length !== CryptoBytes) {
1369
+ throw new Error(`sig must be a Uint8Array of ${CryptoBytes} bytes`);
1370
+ }
980
1371
  let sigOffset = 0;
981
1372
  const c = cP;
982
1373
  const h = hP;
@@ -1076,6 +1467,8 @@ function randomBytes(size) {
1076
1467
  *
1077
1468
  * @param {Uint8Array} buffer - The buffer to zero
1078
1469
  * @returns {void}
1470
+ * @throws {TypeError} If buffer is not a Uint8Array
1471
+ * @throws {Error} If zeroization verification fails
1079
1472
  */
1080
1473
  function zeroize(buffer) {
1081
1474
  if (!(buffer instanceof Uint8Array)) {
@@ -1098,6 +1491,7 @@ function zeroize(buffer) {
1098
1491
  *
1099
1492
  * @param {Uint8Array} buffer - The buffer to check
1100
1493
  * @returns {boolean} True if all bytes are zero
1494
+ * @throws {TypeError} If buffer is not a Uint8Array
1101
1495
  */
1102
1496
  function isZero(buffer) {
1103
1497
  if (!(buffer instanceof Uint8Array)) {
@@ -1113,16 +1507,12 @@ function isZero(buffer) {
1113
1507
  /**
1114
1508
  * Convert hex string to Uint8Array with strict validation.
1115
1509
  *
1116
- * NOTE: This function accepts multiple hex formats (with/without 0x prefix,
1117
- * leading/trailing whitespace). While user-friendly, this flexibility could
1118
- * mask input errors. Applications requiring strict format validation should
1119
- * validate hex format before calling cryptographic functions, e.g.:
1120
- * - Reject strings with 0x prefix if raw hex is expected
1121
- * - Reject strings with whitespace
1122
- * - Enforce consistent casing (lowercase/uppercase)
1510
+ * Accepts an optional 0x/0X prefix. Leading/trailing whitespace is rejected.
1511
+ * Empty strings and whitespace-only strings are rejected.
1123
1512
  *
1124
- * @param {string} hex - Hex string (optional 0x prefix, even length).
1513
+ * @param {string} hex - Hex string (optional 0x prefix, even length, no whitespace).
1125
1514
  * @returns {Uint8Array} Decoded bytes.
1515
+ * @throws {Error} If input is not a valid hex string
1126
1516
  * @private
1127
1517
  */
1128
1518
  function hexToBytes(hex) {
@@ -1131,20 +1521,33 @@ function hexToBytes(hex) {
1131
1521
  throw new Error('message must be a hex string');
1132
1522
  }
1133
1523
  /* c8 ignore stop */
1134
- let clean = hex.trim();
1135
- // Accepts both "0x..." and raw hex formats for convenience
1524
+ if (hex !== hex.trim()) {
1525
+ throw new Error('hex string must not have leading or trailing whitespace');
1526
+ }
1527
+ let clean = hex;
1136
1528
  if (clean.startsWith('0x') || clean.startsWith('0X')) {
1137
1529
  clean = clean.slice(2);
1138
1530
  }
1531
+ if (clean.length === 0) {
1532
+ throw new Error('hex string must not be empty');
1533
+ }
1139
1534
  if (clean.length % 2 !== 0) {
1140
1535
  throw new Error('hex string must have an even length');
1141
1536
  }
1142
1537
  if (!/^[0-9a-fA-F]*$/.test(clean)) {
1143
1538
  throw new Error('hex string contains non-hex characters');
1144
1539
  }
1145
- return utils_js.hexToBytes(clean);
1540
+ return hexToBytes$1(clean);
1146
1541
  }
1147
1542
 
1543
+ /**
1544
+ * Convert a message to Uint8Array.
1545
+ *
1546
+ * @param {string|Uint8Array} message - Message as hex string (optional 0x prefix) or Uint8Array.
1547
+ * @returns {Uint8Array} Message bytes.
1548
+ * @throws {Error} If message is not a Uint8Array or valid hex string
1549
+ * @private
1550
+ */
1148
1551
  function messageToBytes(message) {
1149
1552
  if (typeof message === 'string') {
1150
1553
  return hexToBytes(message);
@@ -1158,8 +1561,8 @@ function messageToBytes(message) {
1158
1561
  /**
1159
1562
  * Generate a Dilithium-5 key pair.
1160
1563
  *
1161
- * @param {Uint8Array|null} passedSeed - Optional 32-byte seed for deterministic key generation.
1162
- * Pass null for random key generation.
1564
+ * @param {Uint8Array|null} [passedSeed=null] - Optional 32-byte seed for deterministic key generation.
1565
+ * Pass null or undefined for random key generation.
1163
1566
  * @param {Uint8Array} pk - Output buffer for public key (must be CryptoPublicKeyBytes = 2592 bytes)
1164
1567
  * @param {Uint8Array} sk - Output buffer for secret key (must be CryptoSecretKeyBytes = 4896 bytes)
1165
1568
  * @returns {Uint8Array} The seed used for key generation (useful when passedSeed is null)
@@ -1180,9 +1583,9 @@ function cryptoSignKeypair(passedSeed, pk, sk) {
1180
1583
  }
1181
1584
  } catch (e) {
1182
1585
  if (e instanceof TypeError) {
1183
- throw new Error(`pk/sk cannot be null`);
1586
+ throw new Error(`pk/sk cannot be null`, { cause: e });
1184
1587
  } else {
1185
- throw new Error(`${e.message}`);
1588
+ throw new Error(`${e.message}`, { cause: e });
1186
1589
  }
1187
1590
  }
1188
1591
 
@@ -1203,7 +1606,7 @@ function cryptoSignKeypair(passedSeed, pk, sk) {
1203
1606
  const seed = passedSeed || randomBytes(SeedBytes);
1204
1607
 
1205
1608
  const outputLength = 2 * SeedBytes + CRHBytes;
1206
- const seedBuf = sha3_js.shake256.create({}).update(seed).xof(outputLength);
1609
+ const seedBuf = shake256.create({}).update(seed).xof(outputLength);
1207
1610
  const rho = seedBuf.slice(0, SeedBytes);
1208
1611
  const rhoPrime = seedBuf.slice(SeedBytes, SeedBytes + CRHBytes);
1209
1612
  const key = seedBuf.slice(SeedBytes + CRHBytes);
@@ -1234,7 +1637,7 @@ function cryptoSignKeypair(passedSeed, pk, sk) {
1234
1637
  packPk(pk, rho, t1);
1235
1638
 
1236
1639
  // Compute H(rho, t1) and write secret key
1237
- const tr = sha3_js.shake256.create({}).update(pk).xof(TRBytes);
1640
+ const tr = shake256.create({}).update(pk).xof(TRBytes);
1238
1641
  packSk(sk, rho, tr, key, t0, s1, s2);
1239
1642
 
1240
1643
  return seed;
@@ -1260,15 +1663,25 @@ function cryptoSignKeypair(passedSeed, pk, sk) {
1260
1663
  * @param {boolean} randomizedSigning - If true, use random nonce for hedged signing.
1261
1664
  * If false, use deterministic nonce derived from message and key.
1262
1665
  * @returns {number} 0 on success
1263
- * @throws {Error} If sk is wrong size
1666
+ * @throws {TypeError} If sig is not a Uint8Array or is smaller than CryptoBytes
1667
+ * @throws {TypeError} If sk is not a Uint8Array
1668
+ * @throws {TypeError} If randomizedSigning is not a boolean
1669
+ * @throws {Error} If sk length does not equal CryptoSecretKeyBytes
1670
+ * @throws {Error} If message is not a Uint8Array or valid hex string
1264
1671
  *
1265
1672
  * @example
1266
1673
  * const sig = new Uint8Array(CryptoBytes);
1267
1674
  * cryptoSignSignature(sig, message, sk, false);
1268
1675
  */
1269
1676
  function cryptoSignSignature(sig, m, sk, randomizedSigning) {
1270
- if (!sig || sig.length < CryptoBytes) {
1271
- throw new Error(`sig must be at least ${CryptoBytes} bytes`);
1677
+ if (!(sig instanceof Uint8Array) || sig.length < CryptoBytes) {
1678
+ throw new TypeError(`sig must be at least ${CryptoBytes} bytes and a Uint8Array`);
1679
+ }
1680
+ if (!(sk instanceof Uint8Array)) {
1681
+ throw new TypeError('sk must be a Uint8Array');
1682
+ }
1683
+ if (typeof randomizedSigning !== 'boolean') {
1684
+ throw new TypeError('randomizedSigning must be a boolean');
1272
1685
  }
1273
1686
  if (sk.length !== CryptoSecretKeyBytes) {
1274
1687
  throw new Error(`invalid sk length ${sk.length} | Expected length ${CryptoSecretKeyBytes}`);
@@ -1297,12 +1710,12 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
1297
1710
  try {
1298
1711
  unpackSk(rho, tr, key, t0, s1, s2, sk);
1299
1712
 
1300
- const mu = sha3_js.shake256.create({}).update(tr).update(mBytes).xof(CRHBytes);
1713
+ const mu = shake256.create({}).update(tr).update(mBytes).xof(CRHBytes);
1301
1714
 
1302
1715
  if (randomizedSigning) {
1303
1716
  rhoPrime = new Uint8Array(randomBytes(CRHBytes));
1304
1717
  } else {
1305
- rhoPrime = sha3_js.shake256.create({}).update(key).update(mu).xof(CRHBytes);
1718
+ rhoPrime = shake256.create({}).update(key).update(mu).xof(CRHBytes);
1306
1719
  }
1307
1720
 
1308
1721
  polyVecMatrixExpand(mat, rho);
@@ -1324,14 +1737,14 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
1324
1737
  polyVecKDecompose(w1, w0, w1);
1325
1738
  polyVecKPackW1(sig, w1);
1326
1739
 
1327
- const cHash = sha3_js.shake256
1740
+ const cHash = shake256
1328
1741
  .create({})
1329
1742
  .update(mu)
1330
1743
  .update(sig.slice(0, K * PolyW1PackedBytes))
1331
1744
  .xof(SeedBytes);
1332
1745
  sig.set(cHash);
1333
1746
 
1334
- polyChallenge(cp, sig);
1747
+ polyChallenge(cp, sig.slice(0, SeedBytes));
1335
1748
  polyNTT(cp);
1336
1749
 
1337
1750
  // Compute z, reject if it reveals secret
@@ -1391,7 +1804,8 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
1391
1804
  * @param {Uint8Array} sk - Secret key (must be CryptoSecretKeyBytes = 4896 bytes)
1392
1805
  * @param {boolean} randomizedSigning - If true, use random nonce; if false, deterministic
1393
1806
  * @returns {Uint8Array} Signed message (CryptoBytes + msg.length bytes)
1394
- * @throws {Error} If signing fails
1807
+ * @throws {TypeError} If sk or randomizedSigning fail type validation (see cryptoSignSignature)
1808
+ * @throws {Error} If signing fails or message/sk are invalid
1395
1809
  *
1396
1810
  * @example
1397
1811
  * const signedMsg = cryptoSign(message, sk, false);
@@ -1445,10 +1859,10 @@ function cryptoSignVerify(sig, m, pk) {
1445
1859
  const w1 = new PolyVecK();
1446
1860
  const h = new PolyVecK();
1447
1861
 
1448
- if (sig.length !== CryptoBytes) {
1862
+ if (!(sig instanceof Uint8Array) || sig.length !== CryptoBytes) {
1449
1863
  return false;
1450
1864
  }
1451
- if (pk.length !== CryptoPublicKeyBytes) {
1865
+ if (!(pk instanceof Uint8Array) || pk.length !== CryptoPublicKeyBytes) {
1452
1866
  return false;
1453
1867
  }
1454
1868
 
@@ -1461,7 +1875,7 @@ function cryptoSignVerify(sig, m, pk) {
1461
1875
  }
1462
1876
 
1463
1877
  /* Compute CRH(H(rho, t1), msg) */
1464
- const tr = sha3_js.shake256.create({}).update(pk).xof(TRBytes);
1878
+ const tr = shake256.create({}).update(pk).xof(TRBytes);
1465
1879
  mu.set(tr);
1466
1880
 
1467
1881
  let mBytes;
@@ -1470,7 +1884,7 @@ function cryptoSignVerify(sig, m, pk) {
1470
1884
  } catch {
1471
1885
  return false;
1472
1886
  }
1473
- const muFull = sha3_js.shake256.create({}).update(mu.slice(0, TRBytes)).update(mBytes).xof(CRHBytes);
1887
+ const muFull = shake256.create({}).update(mu.slice(0, TRBytes)).update(mBytes).xof(CRHBytes);
1474
1888
  mu.set(muFull);
1475
1889
 
1476
1890
  /* Matrix-vector multiplication; compute Az - c2^dt1 */
@@ -1495,7 +1909,7 @@ function cryptoSignVerify(sig, m, pk) {
1495
1909
  polyVecKPackW1(buf, w1);
1496
1910
 
1497
1911
  /* Call random oracle and verify challenge */
1498
- const c2Hash = sha3_js.shake256.create({}).update(mu).update(buf).xof(SeedBytes);
1912
+ const c2Hash = shake256.create({}).update(mu).update(buf).xof(SeedBytes);
1499
1913
  c2.set(c2Hash);
1500
1914
 
1501
1915
  // Constant-time comparison to prevent timing attacks
@@ -158,12 +158,16 @@ function montgomeryReduce(a) {
158
158
  return t;
159
159
  }
160
160
 
161
+ // Partial reduction modulo Q. Input must satisfy |a| < 2^31 - 2^22.
162
+ // Output is in (-Q, Q). Mirrors the reference C implementation.
161
163
  function reduce32(a) {
162
164
  let t = (a + (1 << 22)) >> 23;
163
165
  t = a - t * Q;
164
166
  return t;
165
167
  }
166
168
 
169
+ // Conditional add Q: if a is negative, add Q. Input must satisfy -Q < a < 2^31.
170
+ // Output is in [0, Q). Mirrors the reference C implementation.
167
171
  function cAddQ(a) {
168
172
  let ar = a;
169
173
  ar += (ar >> 31) & Q;
@@ -172,7 +176,7 @@ function cAddQ(a) {
172
176
 
173
177
  function ntt(a) {
174
178
  let k = 0;
175
- let j = 0;
179
+ let j;
176
180
 
177
181
  for (let len = 128; len > 0; len >>= 1) {
178
182
  for (let start = 0; start < N; start = j + len) {
@@ -188,7 +192,7 @@ function ntt(a) {
188
192
 
189
193
  function invNTTToMont(a) {
190
194
  const f = 41978n; // mont^2/256
191
- let j = 0;
195
+ let j;
192
196
  let k = 256;
193
197
 
194
198
  for (let len = 1; len < N; len <<= 1) {
@@ -325,10 +329,7 @@ function polyChkNorm(a, b) {
325
329
  }
326
330
 
327
331
  for (let i = 0; i < N; i++) {
328
- let t = a.coeffs[i] >> 31;
329
- t = a.coeffs[i] - (t & (2 * a.coeffs[i]));
330
-
331
- if (t >= b) {
332
+ if (Math.abs(a.coeffs[i]) >= b) {
332
333
  return 1;
333
334
  }
334
335
  }
@@ -447,6 +448,8 @@ function polyUniformGamma1(a, seed, nonce) {
447
448
  }
448
449
 
449
450
  function polyChallenge(cP, seed) {
451
+ if (seed.length !== SeedBytes) throw new Error('invalid seed length');
452
+
450
453
  let b;
451
454
  let pos;
452
455
  const c = cP;
@@ -454,7 +457,7 @@ function polyChallenge(cP, seed) {
454
457
 
455
458
  const state = new KeccakState();
456
459
  shake256Init(state);
457
- shake256Absorb(state, seed.slice(0, SeedBytes));
460
+ shake256Absorb(state, seed);
458
461
  shake256Finalize(state);
459
462
  shake256SqueezeBlocks(buf, 0, 1, state);
460
463
 
@@ -867,6 +870,9 @@ function packPk(pkp, rho, t1) {
867
870
  }
868
871
 
869
872
  function unpackPk(rhop, t1, pk) {
873
+ if (!(pk instanceof Uint8Array) || pk.length !== CryptoPublicKeyBytes) {
874
+ throw new Error(`pk must be a Uint8Array of ${CryptoPublicKeyBytes} bytes`);
875
+ }
870
876
  const rho = rhop;
871
877
  for (let i = 0; i < SeedBytes; ++i) {
872
878
  rho[i] = pk[i];
@@ -911,6 +917,9 @@ function packSk(skp, rho, tr, key, t0, s1, s2) {
911
917
  }
912
918
 
913
919
  function unpackSk(rhoP, trP, keyP, t0, s1, s2, sk) {
920
+ if (!(sk instanceof Uint8Array) || sk.length !== CryptoSecretKeyBytes) {
921
+ throw new Error(`sk must be a Uint8Array of ${CryptoSecretKeyBytes} bytes`);
922
+ }
914
923
  let skOffset = 0;
915
924
  const rho = rhoP;
916
925
  const tr = trP;
@@ -974,7 +983,12 @@ function packSig(sigP, c, z, h) {
974
983
  }
975
984
  }
976
985
 
986
+ // Returns 0 on success, 1 on failure. On failure, output buffers (c, z, h)
987
+ // may contain partial data and must not be used.
977
988
  function unpackSig(cP, z, hP, sig) {
989
+ if (!(sig instanceof Uint8Array) || sig.length !== CryptoBytes) {
990
+ throw new Error(`sig must be a Uint8Array of ${CryptoBytes} bytes`);
991
+ }
978
992
  let sigOffset = 0;
979
993
  const c = cP;
980
994
  const h = hP;
@@ -1074,6 +1088,8 @@ function randomBytes(size) {
1074
1088
  *
1075
1089
  * @param {Uint8Array} buffer - The buffer to zero
1076
1090
  * @returns {void}
1091
+ * @throws {TypeError} If buffer is not a Uint8Array
1092
+ * @throws {Error} If zeroization verification fails
1077
1093
  */
1078
1094
  function zeroize(buffer) {
1079
1095
  if (!(buffer instanceof Uint8Array)) {
@@ -1096,6 +1112,7 @@ function zeroize(buffer) {
1096
1112
  *
1097
1113
  * @param {Uint8Array} buffer - The buffer to check
1098
1114
  * @returns {boolean} True if all bytes are zero
1115
+ * @throws {TypeError} If buffer is not a Uint8Array
1099
1116
  */
1100
1117
  function isZero(buffer) {
1101
1118
  if (!(buffer instanceof Uint8Array)) {
@@ -1111,16 +1128,12 @@ function isZero(buffer) {
1111
1128
  /**
1112
1129
  * Convert hex string to Uint8Array with strict validation.
1113
1130
  *
1114
- * NOTE: This function accepts multiple hex formats (with/without 0x prefix,
1115
- * leading/trailing whitespace). While user-friendly, this flexibility could
1116
- * mask input errors. Applications requiring strict format validation should
1117
- * validate hex format before calling cryptographic functions, e.g.:
1118
- * - Reject strings with 0x prefix if raw hex is expected
1119
- * - Reject strings with whitespace
1120
- * - Enforce consistent casing (lowercase/uppercase)
1131
+ * Accepts an optional 0x/0X prefix. Leading/trailing whitespace is rejected.
1132
+ * Empty strings and whitespace-only strings are rejected.
1121
1133
  *
1122
- * @param {string} hex - Hex string (optional 0x prefix, even length).
1134
+ * @param {string} hex - Hex string (optional 0x prefix, even length, no whitespace).
1123
1135
  * @returns {Uint8Array} Decoded bytes.
1136
+ * @throws {Error} If input is not a valid hex string
1124
1137
  * @private
1125
1138
  */
1126
1139
  function hexToBytes(hex) {
@@ -1129,11 +1142,16 @@ function hexToBytes(hex) {
1129
1142
  throw new Error('message must be a hex string');
1130
1143
  }
1131
1144
  /* c8 ignore stop */
1132
- let clean = hex.trim();
1133
- // Accepts both "0x..." and raw hex formats for convenience
1145
+ if (hex !== hex.trim()) {
1146
+ throw new Error('hex string must not have leading or trailing whitespace');
1147
+ }
1148
+ let clean = hex;
1134
1149
  if (clean.startsWith('0x') || clean.startsWith('0X')) {
1135
1150
  clean = clean.slice(2);
1136
1151
  }
1152
+ if (clean.length === 0) {
1153
+ throw new Error('hex string must not be empty');
1154
+ }
1137
1155
  if (clean.length % 2 !== 0) {
1138
1156
  throw new Error('hex string must have an even length');
1139
1157
  }
@@ -1143,6 +1161,14 @@ function hexToBytes(hex) {
1143
1161
  return hexToBytes$1(clean);
1144
1162
  }
1145
1163
 
1164
+ /**
1165
+ * Convert a message to Uint8Array.
1166
+ *
1167
+ * @param {string|Uint8Array} message - Message as hex string (optional 0x prefix) or Uint8Array.
1168
+ * @returns {Uint8Array} Message bytes.
1169
+ * @throws {Error} If message is not a Uint8Array or valid hex string
1170
+ * @private
1171
+ */
1146
1172
  function messageToBytes(message) {
1147
1173
  if (typeof message === 'string') {
1148
1174
  return hexToBytes(message);
@@ -1156,8 +1182,8 @@ function messageToBytes(message) {
1156
1182
  /**
1157
1183
  * Generate a Dilithium-5 key pair.
1158
1184
  *
1159
- * @param {Uint8Array|null} passedSeed - Optional 32-byte seed for deterministic key generation.
1160
- * Pass null for random key generation.
1185
+ * @param {Uint8Array|null} [passedSeed=null] - Optional 32-byte seed for deterministic key generation.
1186
+ * Pass null or undefined for random key generation.
1161
1187
  * @param {Uint8Array} pk - Output buffer for public key (must be CryptoPublicKeyBytes = 2592 bytes)
1162
1188
  * @param {Uint8Array} sk - Output buffer for secret key (must be CryptoSecretKeyBytes = 4896 bytes)
1163
1189
  * @returns {Uint8Array} The seed used for key generation (useful when passedSeed is null)
@@ -1178,9 +1204,9 @@ function cryptoSignKeypair(passedSeed, pk, sk) {
1178
1204
  }
1179
1205
  } catch (e) {
1180
1206
  if (e instanceof TypeError) {
1181
- throw new Error(`pk/sk cannot be null`);
1207
+ throw new Error(`pk/sk cannot be null`, { cause: e });
1182
1208
  } else {
1183
- throw new Error(`${e.message}`);
1209
+ throw new Error(`${e.message}`, { cause: e });
1184
1210
  }
1185
1211
  }
1186
1212
 
@@ -1258,15 +1284,25 @@ function cryptoSignKeypair(passedSeed, pk, sk) {
1258
1284
  * @param {boolean} randomizedSigning - If true, use random nonce for hedged signing.
1259
1285
  * If false, use deterministic nonce derived from message and key.
1260
1286
  * @returns {number} 0 on success
1261
- * @throws {Error} If sk is wrong size
1287
+ * @throws {TypeError} If sig is not a Uint8Array or is smaller than CryptoBytes
1288
+ * @throws {TypeError} If sk is not a Uint8Array
1289
+ * @throws {TypeError} If randomizedSigning is not a boolean
1290
+ * @throws {Error} If sk length does not equal CryptoSecretKeyBytes
1291
+ * @throws {Error} If message is not a Uint8Array or valid hex string
1262
1292
  *
1263
1293
  * @example
1264
1294
  * const sig = new Uint8Array(CryptoBytes);
1265
1295
  * cryptoSignSignature(sig, message, sk, false);
1266
1296
  */
1267
1297
  function cryptoSignSignature(sig, m, sk, randomizedSigning) {
1268
- if (!sig || sig.length < CryptoBytes) {
1269
- throw new Error(`sig must be at least ${CryptoBytes} bytes`);
1298
+ if (!(sig instanceof Uint8Array) || sig.length < CryptoBytes) {
1299
+ throw new TypeError(`sig must be at least ${CryptoBytes} bytes and a Uint8Array`);
1300
+ }
1301
+ if (!(sk instanceof Uint8Array)) {
1302
+ throw new TypeError('sk must be a Uint8Array');
1303
+ }
1304
+ if (typeof randomizedSigning !== 'boolean') {
1305
+ throw new TypeError('randomizedSigning must be a boolean');
1270
1306
  }
1271
1307
  if (sk.length !== CryptoSecretKeyBytes) {
1272
1308
  throw new Error(`invalid sk length ${sk.length} | Expected length ${CryptoSecretKeyBytes}`);
@@ -1329,7 +1365,7 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
1329
1365
  .xof(SeedBytes);
1330
1366
  sig.set(cHash);
1331
1367
 
1332
- polyChallenge(cp, sig);
1368
+ polyChallenge(cp, sig.slice(0, SeedBytes));
1333
1369
  polyNTT(cp);
1334
1370
 
1335
1371
  // Compute z, reject if it reveals secret
@@ -1389,7 +1425,8 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
1389
1425
  * @param {Uint8Array} sk - Secret key (must be CryptoSecretKeyBytes = 4896 bytes)
1390
1426
  * @param {boolean} randomizedSigning - If true, use random nonce; if false, deterministic
1391
1427
  * @returns {Uint8Array} Signed message (CryptoBytes + msg.length bytes)
1392
- * @throws {Error} If signing fails
1428
+ * @throws {TypeError} If sk or randomizedSigning fail type validation (see cryptoSignSignature)
1429
+ * @throws {Error} If signing fails or message/sk are invalid
1393
1430
  *
1394
1431
  * @example
1395
1432
  * const signedMsg = cryptoSign(message, sk, false);
@@ -1443,10 +1480,10 @@ function cryptoSignVerify(sig, m, pk) {
1443
1480
  const w1 = new PolyVecK();
1444
1481
  const h = new PolyVecK();
1445
1482
 
1446
- if (sig.length !== CryptoBytes) {
1483
+ if (!(sig instanceof Uint8Array) || sig.length !== CryptoBytes) {
1447
1484
  return false;
1448
1485
  }
1449
- if (pk.length !== CryptoPublicKeyBytes) {
1486
+ if (!(pk instanceof Uint8Array) || pk.length !== CryptoPublicKeyBytes) {
1450
1487
  return false;
1451
1488
  }
1452
1489
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theqrl/dilithium5",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "Dilithium-5 cryptography",
5
5
  "keywords": [
6
6
  "dilithium",
@@ -33,7 +33,7 @@
33
33
  "scripts": {
34
34
  "test": "../../node_modules/mocha/bin/mocha.js --require ../../scripts/node-test-setup.cjs --timeout 10000",
35
35
  "test:browser": "playwright test",
36
- "build": "rollup src/index.js --file ./dist/cjs/dilithium5.js --format cjs && rollup src/index.js --file ./dist/mjs/dilithium5.js --format esm && ./fixup",
36
+ "build": "rollup -c && ./fixup",
37
37
  "lint-check": "eslint 'src/**/*.js' 'test/**/*.js'",
38
38
  "lint": "eslint --fix 'src/**/*.js' 'test/**/*.js'",
39
39
  "coverage": "c8 npm run test",
@@ -53,19 +53,39 @@
53
53
  "node": ">=20.19.0"
54
54
  },
55
55
  "devDependencies": {
56
- "@eslint/js": "^10.0.1",
57
- "c8": "^10.1.3",
58
- "chai": "^6.2.2",
59
- "eslint": "^10.0.0",
60
- "eslint-config-prettier": "^10.1.8",
61
- "eslint-plugin-import-x": "^4.15.0",
62
- "eslint-plugin-prettier": "^5.5.4",
63
- "globals": "^17.3.0",
64
- "mocha": "^11.7.5",
65
- "prettier": "^3.8.1",
66
- "rollup": "^4.57.1"
56
+ "@eslint/js": "10.0.1",
57
+ "@rollup/plugin-node-resolve": "16.0.3",
58
+ "c8": "11.0.0",
59
+ "chai": "6.2.2",
60
+ "eslint": "10.0.3",
61
+ "eslint-config-prettier": "10.1.8",
62
+ "eslint-plugin-import-x": "4.16.2",
63
+ "eslint-plugin-prettier": "5.5.5",
64
+ "globals": "17.4.0",
65
+ "minimatch": "10.2.4",
66
+ "mocha": "11.7.5",
67
+ "prettier": "3.8.1",
68
+ "rollup": "4.59.0",
69
+ "serialize-javascript": "7.0.4",
70
+ "tar": "7.5.11"
67
71
  },
68
72
  "dependencies": {
69
- "@noble/hashes": "^2.0.1"
73
+ "@noble/hashes": "2.0.1"
74
+ },
75
+ "overrides": {
76
+ "diff": "8.0.3",
77
+ "minimatch": "10.2.4"
78
+ },
79
+ "c8": {
80
+ "include": [
81
+ "src/**"
82
+ ],
83
+ "exclude": [
84
+ "**/dist/**",
85
+ "**/test/**",
86
+ "**/browser-tests/**",
87
+ "**/*.d.ts"
88
+ ],
89
+ "all": true
70
90
  }
71
91
  }