@theqrl/dilithium5 1.1.1 → 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:
@@ -537,12 +537,16 @@ function montgomeryReduce(a) {
537
537
  return t;
538
538
  }
539
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.
540
542
  function reduce32(a) {
541
543
  let t = (a + (1 << 22)) >> 23;
542
544
  t = a - t * Q;
543
545
  return t;
544
546
  }
545
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.
546
550
  function cAddQ(a) {
547
551
  let ar = a;
548
552
  ar += (ar >> 31) & Q;
@@ -551,7 +555,7 @@ function cAddQ(a) {
551
555
 
552
556
  function ntt(a) {
553
557
  let k = 0;
554
- let j = 0;
558
+ let j;
555
559
 
556
560
  for (let len = 128; len > 0; len >>= 1) {
557
561
  for (let start = 0; start < N; start = j + len) {
@@ -567,7 +571,7 @@ function ntt(a) {
567
571
 
568
572
  function invNTTToMont(a) {
569
573
  const f = 41978n; // mont^2/256
570
- let j = 0;
574
+ let j;
571
575
  let k = 256;
572
576
 
573
577
  for (let len = 1; len < N; len <<= 1) {
@@ -704,10 +708,7 @@ function polyChkNorm(a, b) {
704
708
  }
705
709
 
706
710
  for (let i = 0; i < N; i++) {
707
- let t = a.coeffs[i] >> 31;
708
- t = a.coeffs[i] - (t & (2 * a.coeffs[i]));
709
-
710
- if (t >= b) {
711
+ if (Math.abs(a.coeffs[i]) >= b) {
711
712
  return 1;
712
713
  }
713
714
  }
@@ -826,6 +827,8 @@ function polyUniformGamma1(a, seed, nonce) {
826
827
  }
827
828
 
828
829
  function polyChallenge(cP, seed) {
830
+ if (seed.length !== SeedBytes) throw new Error('invalid seed length');
831
+
829
832
  let b;
830
833
  let pos;
831
834
  const c = cP;
@@ -833,7 +836,7 @@ function polyChallenge(cP, seed) {
833
836
 
834
837
  const state = new KeccakState();
835
838
  shake256Init(state);
836
- shake256Absorb(state, seed.slice(0, SeedBytes));
839
+ shake256Absorb(state, seed);
837
840
  shake256Finalize(state);
838
841
  shake256SqueezeBlocks(buf, 0, 1, state);
839
842
 
@@ -1246,6 +1249,9 @@ function packPk(pkp, rho, t1) {
1246
1249
  }
1247
1250
 
1248
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
+ }
1249
1255
  const rho = rhop;
1250
1256
  for (let i = 0; i < SeedBytes; ++i) {
1251
1257
  rho[i] = pk[i];
@@ -1290,6 +1296,9 @@ function packSk(skp, rho, tr, key, t0, s1, s2) {
1290
1296
  }
1291
1297
 
1292
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
+ }
1293
1302
  let skOffset = 0;
1294
1303
  const rho = rhoP;
1295
1304
  const tr = trP;
@@ -1353,7 +1362,12 @@ function packSig(sigP, c, z, h) {
1353
1362
  }
1354
1363
  }
1355
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.
1356
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
+ }
1357
1371
  let sigOffset = 0;
1358
1372
  const c = cP;
1359
1373
  const h = hP;
@@ -1453,6 +1467,8 @@ function randomBytes(size) {
1453
1467
  *
1454
1468
  * @param {Uint8Array} buffer - The buffer to zero
1455
1469
  * @returns {void}
1470
+ * @throws {TypeError} If buffer is not a Uint8Array
1471
+ * @throws {Error} If zeroization verification fails
1456
1472
  */
1457
1473
  function zeroize(buffer) {
1458
1474
  if (!(buffer instanceof Uint8Array)) {
@@ -1475,6 +1491,7 @@ function zeroize(buffer) {
1475
1491
  *
1476
1492
  * @param {Uint8Array} buffer - The buffer to check
1477
1493
  * @returns {boolean} True if all bytes are zero
1494
+ * @throws {TypeError} If buffer is not a Uint8Array
1478
1495
  */
1479
1496
  function isZero(buffer) {
1480
1497
  if (!(buffer instanceof Uint8Array)) {
@@ -1490,16 +1507,12 @@ function isZero(buffer) {
1490
1507
  /**
1491
1508
  * Convert hex string to Uint8Array with strict validation.
1492
1509
  *
1493
- * NOTE: This function accepts multiple hex formats (with/without 0x prefix,
1494
- * leading/trailing whitespace). While user-friendly, this flexibility could
1495
- * mask input errors. Applications requiring strict format validation should
1496
- * validate hex format before calling cryptographic functions, e.g.:
1497
- * - Reject strings with 0x prefix if raw hex is expected
1498
- * - Reject strings with whitespace
1499
- * - 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.
1500
1512
  *
1501
- * @param {string} hex - Hex string (optional 0x prefix, even length).
1513
+ * @param {string} hex - Hex string (optional 0x prefix, even length, no whitespace).
1502
1514
  * @returns {Uint8Array} Decoded bytes.
1515
+ * @throws {Error} If input is not a valid hex string
1503
1516
  * @private
1504
1517
  */
1505
1518
  function hexToBytes(hex) {
@@ -1508,11 +1521,16 @@ function hexToBytes(hex) {
1508
1521
  throw new Error('message must be a hex string');
1509
1522
  }
1510
1523
  /* c8 ignore stop */
1511
- let clean = hex.trim();
1512
- // 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;
1513
1528
  if (clean.startsWith('0x') || clean.startsWith('0X')) {
1514
1529
  clean = clean.slice(2);
1515
1530
  }
1531
+ if (clean.length === 0) {
1532
+ throw new Error('hex string must not be empty');
1533
+ }
1516
1534
  if (clean.length % 2 !== 0) {
1517
1535
  throw new Error('hex string must have an even length');
1518
1536
  }
@@ -1522,6 +1540,14 @@ function hexToBytes(hex) {
1522
1540
  return hexToBytes$1(clean);
1523
1541
  }
1524
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
+ */
1525
1551
  function messageToBytes(message) {
1526
1552
  if (typeof message === 'string') {
1527
1553
  return hexToBytes(message);
@@ -1535,8 +1561,8 @@ function messageToBytes(message) {
1535
1561
  /**
1536
1562
  * Generate a Dilithium-5 key pair.
1537
1563
  *
1538
- * @param {Uint8Array|null} passedSeed - Optional 32-byte seed for deterministic key generation.
1539
- * 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.
1540
1566
  * @param {Uint8Array} pk - Output buffer for public key (must be CryptoPublicKeyBytes = 2592 bytes)
1541
1567
  * @param {Uint8Array} sk - Output buffer for secret key (must be CryptoSecretKeyBytes = 4896 bytes)
1542
1568
  * @returns {Uint8Array} The seed used for key generation (useful when passedSeed is null)
@@ -1557,9 +1583,9 @@ function cryptoSignKeypair(passedSeed, pk, sk) {
1557
1583
  }
1558
1584
  } catch (e) {
1559
1585
  if (e instanceof TypeError) {
1560
- throw new Error(`pk/sk cannot be null`);
1586
+ throw new Error(`pk/sk cannot be null`, { cause: e });
1561
1587
  } else {
1562
- throw new Error(`${e.message}`);
1588
+ throw new Error(`${e.message}`, { cause: e });
1563
1589
  }
1564
1590
  }
1565
1591
 
@@ -1637,15 +1663,25 @@ function cryptoSignKeypair(passedSeed, pk, sk) {
1637
1663
  * @param {boolean} randomizedSigning - If true, use random nonce for hedged signing.
1638
1664
  * If false, use deterministic nonce derived from message and key.
1639
1665
  * @returns {number} 0 on success
1640
- * @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
1641
1671
  *
1642
1672
  * @example
1643
1673
  * const sig = new Uint8Array(CryptoBytes);
1644
1674
  * cryptoSignSignature(sig, message, sk, false);
1645
1675
  */
1646
1676
  function cryptoSignSignature(sig, m, sk, randomizedSigning) {
1647
- if (!sig || sig.length < CryptoBytes) {
1648
- 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');
1649
1685
  }
1650
1686
  if (sk.length !== CryptoSecretKeyBytes) {
1651
1687
  throw new Error(`invalid sk length ${sk.length} | Expected length ${CryptoSecretKeyBytes}`);
@@ -1708,7 +1744,7 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
1708
1744
  .xof(SeedBytes);
1709
1745
  sig.set(cHash);
1710
1746
 
1711
- polyChallenge(cp, sig);
1747
+ polyChallenge(cp, sig.slice(0, SeedBytes));
1712
1748
  polyNTT(cp);
1713
1749
 
1714
1750
  // Compute z, reject if it reveals secret
@@ -1768,7 +1804,8 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
1768
1804
  * @param {Uint8Array} sk - Secret key (must be CryptoSecretKeyBytes = 4896 bytes)
1769
1805
  * @param {boolean} randomizedSigning - If true, use random nonce; if false, deterministic
1770
1806
  * @returns {Uint8Array} Signed message (CryptoBytes + msg.length bytes)
1771
- * @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
1772
1809
  *
1773
1810
  * @example
1774
1811
  * const signedMsg = cryptoSign(message, sk, false);
@@ -1822,10 +1859,10 @@ function cryptoSignVerify(sig, m, pk) {
1822
1859
  const w1 = new PolyVecK();
1823
1860
  const h = new PolyVecK();
1824
1861
 
1825
- if (sig.length !== CryptoBytes) {
1862
+ if (!(sig instanceof Uint8Array) || sig.length !== CryptoBytes) {
1826
1863
  return false;
1827
1864
  }
1828
- if (pk.length !== CryptoPublicKeyBytes) {
1865
+ if (!(pk instanceof Uint8Array) || pk.length !== CryptoPublicKeyBytes) {
1829
1866
  return false;
1830
1867
  }
1831
1868
 
@@ -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.1",
3
+ "version": "1.1.2",
4
4
  "description": "Dilithium-5 cryptography",
5
5
  "keywords": [
6
6
  "dilithium",
@@ -53,20 +53,39 @@
53
53
  "node": ">=20.19.0"
54
54
  },
55
55
  "devDependencies": {
56
- "@eslint/js": "^10.0.1",
57
- "@rollup/plugin-node-resolve": "^16.0.3",
58
- "c8": "^10.1.3",
59
- "chai": "^6.2.2",
60
- "eslint": "^10.0.0",
61
- "eslint-config-prettier": "^10.1.8",
62
- "eslint-plugin-import-x": "^4.15.0",
63
- "eslint-plugin-prettier": "^5.5.4",
64
- "globals": "^17.3.0",
65
- "mocha": "^11.7.5",
66
- "prettier": "^3.8.1",
67
- "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"
68
71
  },
69
72
  "dependencies": {
70
- "@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
71
90
  }
72
91
  }