@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 +8 -1
- package/dist/cjs/dilithium5.js +65 -28
- package/dist/mjs/dilithium5.js +65 -28
- package/package.json +33 -14
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 `
|
|
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:
|
package/dist/cjs/dilithium5.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
*
|
|
1494
|
-
*
|
|
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
|
-
|
|
1512
|
-
|
|
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 {
|
|
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
|
|
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 {
|
|
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
|
|
package/dist/mjs/dilithium5.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
*
|
|
1115
|
-
*
|
|
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
|
-
|
|
1133
|
-
|
|
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 {
|
|
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
|
|
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 {
|
|
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.
|
|
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": "
|
|
57
|
-
"@rollup/plugin-node-resolve": "
|
|
58
|
-
"c8": "
|
|
59
|
-
"chai": "
|
|
60
|
-
"eslint": "
|
|
61
|
-
"eslint-config-prettier": "
|
|
62
|
-
"eslint-plugin-import-x": "
|
|
63
|
-
"eslint-plugin-prettier": "
|
|
64
|
-
"globals": "
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
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": "
|
|
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
|
}
|