@theqrl/dilithium5 1.1.2 → 1.2.0

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
@@ -26,9 +26,12 @@ const pk = new Uint8Array(CryptoPublicKeyBytes); // 2592 bytes
26
26
  const sk = new Uint8Array(CryptoSecretKeyBytes); // 4896 bytes
27
27
  cryptoSignKeypair(null, pk, sk); // null = random seed
28
28
 
29
- // Sign a message
29
+ // Sign a message (hedged by default — recommended per TOB-QRLLIB-6).
30
+ // Pass `false` only when deterministic signatures are themselves a
31
+ // protocol requirement (e.g. KAT vector reproduction); for that case
32
+ // use `cryptoSignDeterministic`.
30
33
  const message = new TextEncoder().encode('Hello, quantum world!');
31
- const signedMessage = cryptoSign(message, sk, false); // false = deterministic
34
+ const signedMessage = cryptoSign(message, sk, true); // true = hedged (recommended)
32
35
 
33
36
  // Verify and extract
34
37
  const extracted = cryptoSignOpen(signedMessage, pk);
@@ -144,6 +147,7 @@ See [SECURITY.md](../../SECURITY.md) for important information about:
144
147
 
145
148
  - JavaScript memory security limitations
146
149
  - Constant-time verification
150
+ - **Signing timing variability** — signing is not constant-time due to the algorithm's rejection sampling loop; see SECURITY.md for measured impact and deployment mitigations
147
151
  - Secure key handling recommendations
148
152
 
149
153
  ## Requirements
@@ -0,0 +1,367 @@
1
+ /**
2
+ * TypeScript definitions for @theqrl/dilithium5
3
+ * Dilithium-5 post-quantum digital signature scheme
4
+ */
5
+
6
+ // Constants
7
+ export const Shake128Rate: number;
8
+ export const Shake256Rate: number;
9
+ export const Stream128BlockBytes: number;
10
+ export const Stream256BlockBytes: number;
11
+ export const SeedBytes: number;
12
+ export const CRHBytes: number;
13
+ export const TRBytes: number;
14
+ export const N: number;
15
+ export const Q: number;
16
+ export const QInv: number;
17
+ export const D: number;
18
+ export const K: number;
19
+ export const L: number;
20
+ export const ETA: number;
21
+ export const TAU: number;
22
+ export const BETA: number;
23
+ export const GAMMA1: number;
24
+ export const GAMMA2: number;
25
+ export const OMEGA: number;
26
+ export const PolyT1PackedBytes: number;
27
+ export const PolyT0PackedBytes: number;
28
+ export const PolyETAPackedBytes: number;
29
+ export const PolyZPackedBytes: number;
30
+ export const PolyVecHPackedBytes: number;
31
+ export const PolyW1PackedBytes: number;
32
+ export const CryptoPublicKeyBytes: number;
33
+ export const CryptoSecretKeyBytes: number;
34
+ export const CryptoBytes: number;
35
+ export const PolyUniformNBlocks: number;
36
+ export const PolyUniformETANBlocks: number;
37
+ export const PolyUniformGamma1NBlocks: number;
38
+ export const zetas: readonly number[];
39
+
40
+ // Core signing functions
41
+
42
+ /**
43
+ * Generate a Dilithium-5 key pair
44
+ * @param seed - Optional 32-byte seed for deterministic key generation (null for random)
45
+ * @param pk - Output buffer for public key (must be CryptoPublicKeyBytes length)
46
+ * @param sk - Output buffer for secret key (must be CryptoSecretKeyBytes length)
47
+ * @returns The seed used for key generation
48
+ * @throws Error if pk/sk buffers are wrong size or null
49
+ */
50
+ export function cryptoSignKeypair(
51
+ seed: Uint8Array | null,
52
+ pk: Uint8Array,
53
+ sk: Uint8Array
54
+ ): Uint8Array;
55
+
56
+ /**
57
+ * Create a signature for a message
58
+ * @param sig - Output buffer for signature (must be CryptoBytes length minimum)
59
+ * @param m - Message to sign (hex string or Uint8Array; strings are parsed as hex only)
60
+ * @param sk - Secret key
61
+ * @param randomizedSigning - If true, use random nonce; if false, deterministic
62
+ * @returns 0 on success
63
+ * @throws Error if sk is wrong size
64
+ */
65
+ export function cryptoSignSignature(
66
+ sig: Uint8Array,
67
+ m: Uint8Array | string,
68
+ sk: Uint8Array,
69
+ randomizedSigning: boolean
70
+ ): number;
71
+
72
+ /**
73
+ * Sign a message, returning signature concatenated with message
74
+ * @param msg - Message to sign
75
+ * @param sk - Secret key
76
+ * @param randomizedSigning - If true, use random nonce; if false, deterministic
77
+ * @returns Signed message (signature || message)
78
+ * @throws Error if signing fails
79
+ */
80
+ export function cryptoSign(
81
+ msg: Uint8Array | string,
82
+ sk: Uint8Array,
83
+ randomizedSigning: boolean
84
+ ): Uint8Array;
85
+
86
+ /**
87
+ * Create a deterministic Dilithium5 detached signature
88
+ * (`randomizedSigning = false` wrapper for `cryptoSignSignature`).
89
+ *
90
+ * **Use only when the deterministic property is itself a requirement**.
91
+ * For general-purpose signing prefer `cryptoSignSignature` with
92
+ * `randomizedSigning = true` (hedged — TOB-QRLLIB-6).
93
+ */
94
+ export function cryptoSignSignatureDeterministic(
95
+ sig: Uint8Array,
96
+ m: Uint8Array | string,
97
+ sk: Uint8Array
98
+ ): number;
99
+
100
+ /**
101
+ * Attached-form deterministic Dilithium5 signing
102
+ * (`randomizedSigning = false` wrapper for `cryptoSign`).
103
+ * Same recommendation as `cryptoSignSignatureDeterministic`.
104
+ * (TOB-QRLLIB-6.)
105
+ */
106
+ export function cryptoSignDeterministic(
107
+ msg: Uint8Array | string,
108
+ sk: Uint8Array
109
+ ): Uint8Array;
110
+
111
+ /**
112
+ * Verify a signature
113
+ * @param sig - Signature to verify
114
+ * @param m - Message that was signed (hex string or Uint8Array; strings are parsed as hex only)
115
+ * @param pk - Public key
116
+ * @returns true if signature is valid, false otherwise
117
+ */
118
+ export function cryptoSignVerify(
119
+ sig: Uint8Array,
120
+ m: Uint8Array | string,
121
+ pk: Uint8Array
122
+ ): boolean;
123
+
124
+ /**
125
+ * Open a signed message (verify and extract message)
126
+ * @param sm - Signed message (signature || message)
127
+ * @param pk - Public key
128
+ * @returns Message if valid, undefined if verification fails (or if
129
+ * sm is null / undefined / non-Uint8Array / shorter than
130
+ * CryptoBytes — see `cryptoSignOpenWithReason` for distinct
131
+ * failure-mode reporting)
132
+ */
133
+ export function cryptoSignOpen(
134
+ sm: Uint8Array,
135
+ pk: Uint8Array
136
+ ): Uint8Array | undefined;
137
+
138
+ /**
139
+ * Failure-mode discriminator returned by `cryptoSignOpenWithReason`.
140
+ * (TOB-QRLLIB-14: distinct failure modes for Open.)
141
+ */
142
+ export type CryptoSignOpenReason =
143
+ | 'invalid-sm-type'
144
+ | 'invalid-sm-length'
145
+ | 'invalid-pk'
146
+ | 'verification-failed';
147
+
148
+ /**
149
+ * Open a signed message with a typed failure-mode report.
150
+ * (TOB-QRLLIB-14.) Behavioural twin of `cryptoSignOpen` that
151
+ * distinguishes API-shape problems (input wrong type / length /
152
+ * shape) from genuine verification failures.
153
+ *
154
+ * `cryptoSignOpen` is kept unchanged and continues to return
155
+ * `undefined` for any failure mode. Use this variant when you need
156
+ * to log or route on specific failure modes.
157
+ *
158
+ * @param sm - Signed message (signature || message)
159
+ * @param pk - Public key
160
+ */
161
+ export function cryptoSignOpenWithReason(
162
+ sm: Uint8Array,
163
+ pk: Uint8Array
164
+ ):
165
+ | { ok: true; message: Uint8Array }
166
+ | { ok: false; reason: CryptoSignOpenReason };
167
+
168
+ // Utility functions
169
+
170
+ /**
171
+ * Zero out a buffer (best-effort, see SECURITY.md for limitations)
172
+ * @param buffer - Buffer to zero
173
+ * @throws TypeError if buffer is not Uint8Array
174
+ */
175
+ export function zeroize(buffer: Uint8Array): void;
176
+
177
+ /**
178
+ * Check if buffer is all zeros using constant-time comparison
179
+ * @param buffer - Buffer to check
180
+ * @returns true if all bytes are zero
181
+ * @throws TypeError if buffer is not Uint8Array
182
+ */
183
+ export function isZero(buffer: Uint8Array): boolean;
184
+
185
+ // Internal classes (exported but primarily for internal use)
186
+
187
+ export class Poly {
188
+ coeffs: Int32Array;
189
+ constructor();
190
+ copy(poly: Poly): void;
191
+ }
192
+
193
+ export class PolyVecK {
194
+ vec: Poly[];
195
+ constructor();
196
+ }
197
+
198
+ export class PolyVecL {
199
+ vec: Poly[];
200
+ constructor();
201
+ copy(polyVecL: PolyVecL): void;
202
+ }
203
+
204
+ export class KeccakState {
205
+ constructor();
206
+ }
207
+
208
+ // Internal functions (exported but primarily for internal use)
209
+ export function polyNTT(a: Poly): void;
210
+ export function polyInvNTTToMont(a: Poly): void;
211
+ export function polyChallenge(c: Poly, seed: Uint8Array): void;
212
+ export function ntt(a: Int32Array): void;
213
+ export function invNTTToMont(a: Int32Array): void;
214
+ export function montgomeryReduce(a: bigint): bigint;
215
+ export function reduce32(a: number): number;
216
+ export function cAddQ(a: number): number;
217
+ export function decompose(a0: Int32Array, i: number, a: number): number;
218
+ export function power2round(a0: Int32Array, i: number, a: number): number;
219
+ export function makeHint(a0: number, a1: number): number;
220
+ export function useHint(a: number, hint: number): number;
221
+ export function packPk(pk: Uint8Array, rho: Uint8Array, t1: PolyVecK): void;
222
+ export function packSk(
223
+ sk: Uint8Array,
224
+ rho: Uint8Array,
225
+ tr: Uint8Array,
226
+ key: Uint8Array,
227
+ t0: PolyVecK,
228
+ s1: PolyVecL,
229
+ s2: PolyVecK
230
+ ): void;
231
+ export function packSig(
232
+ sig: Uint8Array,
233
+ c: Uint8Array,
234
+ z: PolyVecL,
235
+ h: PolyVecK
236
+ ): void;
237
+ export function unpackPk(rho: Uint8Array, t1: PolyVecK, pk: Uint8Array): void;
238
+ export function unpackSk(
239
+ rho: Uint8Array,
240
+ tr: Uint8Array,
241
+ key: Uint8Array,
242
+ t0: PolyVecK,
243
+ s1: PolyVecL,
244
+ s2: PolyVecK,
245
+ sk: Uint8Array
246
+ ): void;
247
+ export function unpackSig(
248
+ c: Uint8Array,
249
+ z: PolyVecL,
250
+ h: PolyVecK,
251
+ sig: Uint8Array
252
+ ): number;
253
+
254
+ // FIPS 202 SHAKE primitives (low-level XOF interface, primarily internal)
255
+ export function shake128Init(state: KeccakState): void;
256
+ export function shake128Absorb(state: KeccakState, input: Uint8Array): void;
257
+ export function shake128Finalize(state: KeccakState): void;
258
+ export function shake128SqueezeBlocks(
259
+ out: Uint8Array,
260
+ outputOffset: number,
261
+ nBlocks: number,
262
+ state: KeccakState
263
+ ): void;
264
+ export function shake256Init(state: KeccakState): void;
265
+ export function shake256Absorb(state: KeccakState, input: Uint8Array): void;
266
+ export function shake256Finalize(state: KeccakState): void;
267
+ export function shake256SqueezeBlocks(
268
+ out: Uint8Array,
269
+ outputOffset: number,
270
+ nBlocks: number,
271
+ state: KeccakState
272
+ ): void;
273
+
274
+ // Dilithium-specific stream initializers
275
+ export function dilithiumShake128StreamInit(
276
+ state: KeccakState,
277
+ seed: Uint8Array,
278
+ nonce: number
279
+ ): void;
280
+ export function dilithiumShake256StreamInit(
281
+ state: KeccakState,
282
+ seed: Uint8Array,
283
+ nonce: number
284
+ ): void;
285
+
286
+ // Polynomial operations (internal)
287
+ export function polyReduce(a: Poly): void;
288
+ export function polyCAddQ(a: Poly): void;
289
+ export function polyAdd(c: Poly, a: Poly, b: Poly): void;
290
+ export function polySub(c: Poly, a: Poly, b: Poly): void;
291
+ export function polyShiftL(a: Poly): void;
292
+ export function polyPointWiseMontgomery(c: Poly, a: Poly, b: Poly): void;
293
+ export function polyPower2round(a1: Poly, a0: Poly, a: Poly): void;
294
+ export function polyDecompose(a1: Poly, a0: Poly, a: Poly): void;
295
+ export function polyMakeHint(h: Poly, a0: Poly, a1: Poly): number;
296
+ export function polyUseHint(b: Poly, a: Poly, h: Poly): void;
297
+ export function polyChkNorm(a: Poly, b: number): number;
298
+ export function rejUniform(
299
+ a: Int32Array,
300
+ aOffset: number,
301
+ len: number,
302
+ buf: Uint8Array,
303
+ bufLen: number
304
+ ): number;
305
+ export function polyUniform(a: Poly, seed: Uint8Array, nonce: number): void;
306
+ export function rejEta(
307
+ a: Int32Array,
308
+ aOffset: number,
309
+ len: number,
310
+ buf: Uint8Array,
311
+ bufLen: number
312
+ ): number;
313
+ export function polyUniformEta(a: Poly, seed: Uint8Array, nonce: number): void;
314
+ export function polyZUnpack(r: Poly, a: Uint8Array, aOffset: number): void;
315
+ export function polyUniformGamma1(a: Poly, seed: Uint8Array, nonce: number): void;
316
+ export function polyEtaPack(r: Uint8Array, rOffset: number, a: Poly): void;
317
+ export function polyEtaUnpack(r: Poly, a: Uint8Array, aOffset: number): void;
318
+ export function polyT1Pack(r: Uint8Array, rOffset: number, a: Poly): void;
319
+ export function polyT1Unpack(r: Poly, a: Uint8Array, aOffset: number): void;
320
+ export function polyT0Pack(r: Uint8Array, rOffset: number, a: Poly): void;
321
+ export function polyT0Unpack(r: Poly, a: Uint8Array, aOffset: number): void;
322
+ export function polyZPack(r: Uint8Array, rOffset: number, a: Poly): void;
323
+ export function polyW1Pack(r: Uint8Array, rOffset: number, a: Poly): void;
324
+
325
+ // Polynomial vector operations (internal)
326
+ export function polyVecMatrixExpand(mat: PolyVecL[], rho: Uint8Array): void;
327
+ export function polyVecMatrixPointWiseMontgomery(
328
+ t: PolyVecK,
329
+ mat: PolyVecL[],
330
+ v: PolyVecL
331
+ ): void;
332
+ export function polyVecLUniformEta(v: PolyVecL, seed: Uint8Array, nonce: number): void;
333
+ export function polyVecLUniformGamma1(v: PolyVecL, seed: Uint8Array, nonce: number): void;
334
+ export function polyVecLReduce(v: PolyVecL): void;
335
+ export function polyVecLAdd(w: PolyVecL, u: PolyVecL, v: PolyVecL): void;
336
+ export function polyVecLNTT(v: PolyVecL): void;
337
+ export function polyVecLInvNTTToMont(v: PolyVecL): void;
338
+ export function polyVecLPointWisePolyMontgomery(
339
+ r: PolyVecL,
340
+ a: Poly,
341
+ v: PolyVecL
342
+ ): void;
343
+ export function polyVecLPointWiseAccMontgomery(
344
+ w: Poly,
345
+ u: PolyVecL,
346
+ v: PolyVecL
347
+ ): void;
348
+ export function polyVecLChkNorm(v: PolyVecL, bound: number): number;
349
+ export function polyVecKUniformEta(v: PolyVecK, seed: Uint8Array, nonce: number): void;
350
+ export function polyVecKReduce(v: PolyVecK): void;
351
+ export function polyVecKCAddQ(v: PolyVecK): void;
352
+ export function polyVecKAdd(w: PolyVecK, u: PolyVecK, v: PolyVecK): void;
353
+ export function polyVecKSub(w: PolyVecK, u: PolyVecK, v: PolyVecK): void;
354
+ export function polyVecKShiftL(v: PolyVecK): void;
355
+ export function polyVecKNTT(v: PolyVecK): void;
356
+ export function polyVecKInvNTTToMont(v: PolyVecK): void;
357
+ export function polyVecKPointWisePolyMontgomery(
358
+ r: PolyVecK,
359
+ a: Poly,
360
+ v: PolyVecK
361
+ ): void;
362
+ export function polyVecKChkNorm(v: PolyVecK, bound: number): number;
363
+ export function polyVecKPower2round(v1: PolyVecK, v0: PolyVecK, v: PolyVecK): void;
364
+ export function polyVecKDecompose(v1: PolyVecK, v0: PolyVecK, v: PolyVecK): void;
365
+ export function polyVecKMakeHint(h: PolyVecK, v0: PolyVecK, v1: PolyVecK): number;
366
+ export function polyVecKUseHint(w: PolyVecK, u: PolyVecK, h: PolyVecK): void;
367
+ export function polyVecKPackW1(r: Uint8Array, w1: PolyVecK): void;
@@ -1354,6 +1354,12 @@ function packSig(sigP, c, z, h) {
1354
1354
  for (let i = 0; i < K; ++i) {
1355
1355
  for (let j = 0; j < N; ++j) {
1356
1356
  if (h.vec[i].coeffs[j] !== 0) {
1357
+ if (h.vec[i].coeffs[j] !== 1) {
1358
+ throw new Error('hint coefficients must be binary (0 or 1)');
1359
+ }
1360
+ if (k >= OMEGA) {
1361
+ throw new Error(`hint count exceeds OMEGA (${OMEGA})`);
1362
+ }
1357
1363
  sig[sigOffset + k++] = j;
1358
1364
  }
1359
1365
  }
@@ -1794,6 +1800,31 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
1794
1800
  }
1795
1801
  }
1796
1802
 
1803
+ /**
1804
+ * Create a **deterministic** Dilithium5 detached signature
1805
+ * (`randomizedSigning = false`).
1806
+ *
1807
+ * Convenience wrapper that hard-wires the deterministic mode so callers
1808
+ * who *need* byte-identical signatures for the same `(sk, message)`
1809
+ * — KAT vector reproduction, deterministic-test fixtures, RANDAO-style
1810
+ * protocols — get a clearly-named entry point rather than passing a
1811
+ * bare boolean.
1812
+ *
1813
+ * **Use only when the deterministic property is itself a requirement.**
1814
+ * For general-purpose signing prefer [cryptoSignSignature] with
1815
+ * `randomizedSigning = true` (hedged signing — TOB-QRLLIB-6 audit
1816
+ * recommendation for parity with the lattice-scheme guidance applied
1817
+ * to the Go and Rust ports).
1818
+ *
1819
+ * @param {Uint8Array} sig - Output buffer for signature (must be at least CryptoBytes bytes)
1820
+ * @param {string|Uint8Array} m - Message to sign
1821
+ * @param {Uint8Array} sk - Secret key (must be CryptoSecretKeyBytes bytes)
1822
+ * @returns {number} 0 on success
1823
+ */
1824
+ function cryptoSignSignatureDeterministic(sig, m, sk) {
1825
+ return cryptoSignSignature(sig, m, sk, /* randomizedSigning */ false);
1826
+ }
1827
+
1797
1828
  /**
1798
1829
  * Sign a message, returning signature concatenated with message.
1799
1830
  *
@@ -1829,6 +1860,24 @@ function cryptoSign(msg, sk, randomizedSigning) {
1829
1860
  return sm;
1830
1861
  }
1831
1862
 
1863
+ /**
1864
+ * Attached-form **deterministic** Dilithium5 signing
1865
+ * (`randomizedSigning = false`).
1866
+ *
1867
+ * Convenience wrapper that hard-wires the deterministic mode for the
1868
+ * attached `signature || message` form. Same recommendation as
1869
+ * [cryptoSignSignatureDeterministic]: use only when determinism is a
1870
+ * protocol requirement; for general-purpose signing prefer
1871
+ * [cryptoSign] with `randomizedSigning = true` (hedged — TOB-QRLLIB-6).
1872
+ *
1873
+ * @param {string|Uint8Array} msg - Message to sign
1874
+ * @param {Uint8Array} sk - Secret key
1875
+ * @returns {Uint8Array} Signed message (signature || message)
1876
+ */
1877
+ function cryptoSignDeterministic(msg, sk) {
1878
+ return cryptoSign(msg, sk, /* randomizedSigning */ false);
1879
+ }
1880
+
1832
1881
  /**
1833
1882
  * Verify a detached signature.
1834
1883
  *
@@ -1937,7 +1986,12 @@ function cryptoSignVerify(sig, m, pk) {
1937
1986
  * }
1938
1987
  */
1939
1988
  function cryptoSignOpen(sm, pk) {
1940
- if (sm.length < CryptoBytes) {
1989
+ // Type-guard `sm` so callers passing `null` / `undefined` / non-Uint8Array
1990
+ // get a clean `undefined` return rather than a `Cannot read properties of
1991
+ // null (reading 'length')` thrown deep in the call chain. Mirrors the
1992
+ // existing `pk` / `sig` instanceof checks in `cryptoSignVerify`.
1993
+ // (TOB-QRLLIB-11.)
1994
+ if (!(sm instanceof Uint8Array) || sm.length < CryptoBytes) {
1941
1995
  return undefined;
1942
1996
  }
1943
1997
 
@@ -1950,6 +2004,37 @@ function cryptoSignOpen(sm, pk) {
1950
2004
  return msg;
1951
2005
  }
1952
2006
 
2007
+ /**
2008
+ * Open a signed message with a typed failure-mode report.
2009
+ *
2010
+ * Behavioural twin of [cryptoSignOpen], but returns a discriminated
2011
+ * union so callers can distinguish between API-shape problems (input
2012
+ * was the wrong type / length / shape) and genuine cryptographic
2013
+ * verification failures. See the ML-DSA-87 sibling for the rationale
2014
+ * (TOB-QRLLIB-14).
2015
+ *
2016
+ * @param {Uint8Array} sm Signed message (signature || message).
2017
+ * @param {Uint8Array} pk Public key.
2018
+ * @returns {{ok: true, message: Uint8Array} | {ok: false, reason: 'invalid-sm-type'|'invalid-sm-length'|'invalid-pk'|'verification-failed'}}
2019
+ */
2020
+ function cryptoSignOpenWithReason(sm, pk) {
2021
+ if (!(sm instanceof Uint8Array)) {
2022
+ return { ok: false, reason: 'invalid-sm-type' };
2023
+ }
2024
+ if (sm.length < CryptoBytes) {
2025
+ return { ok: false, reason: 'invalid-sm-length' };
2026
+ }
2027
+ if (!(pk instanceof Uint8Array) || pk.length !== CryptoPublicKeyBytes) {
2028
+ return { ok: false, reason: 'invalid-pk' };
2029
+ }
2030
+ const sig = sm.slice(0, CryptoBytes);
2031
+ const msg = sm.slice(CryptoBytes);
2032
+ if (!cryptoSignVerify(sig, msg, pk)) {
2033
+ return { ok: false, reason: 'verification-failed' };
2034
+ }
2035
+ return { ok: true, message: msg };
2036
+ }
2037
+
1953
2038
  exports.BETA = BETA;
1954
2039
  exports.CRHBytes = CRHBytes;
1955
2040
  exports.CryptoBytes = CryptoBytes;
@@ -1987,9 +2072,12 @@ exports.TAU = TAU;
1987
2072
  exports.TRBytes = TRBytes;
1988
2073
  exports.cAddQ = cAddQ;
1989
2074
  exports.cryptoSign = cryptoSign;
2075
+ exports.cryptoSignDeterministic = cryptoSignDeterministic;
1990
2076
  exports.cryptoSignKeypair = cryptoSignKeypair;
1991
2077
  exports.cryptoSignOpen = cryptoSignOpen;
2078
+ exports.cryptoSignOpenWithReason = cryptoSignOpenWithReason;
1992
2079
  exports.cryptoSignSignature = cryptoSignSignature;
2080
+ exports.cryptoSignSignatureDeterministic = cryptoSignSignatureDeterministic;
1993
2081
  exports.cryptoSignVerify = cryptoSignVerify;
1994
2082
  exports.decompose = decompose;
1995
2083
  exports.dilithiumShake128StreamInit = dilithiumShake128StreamInit;
@@ -0,0 +1,367 @@
1
+ /**
2
+ * TypeScript definitions for @theqrl/dilithium5
3
+ * Dilithium-5 post-quantum digital signature scheme
4
+ */
5
+
6
+ // Constants
7
+ export const Shake128Rate: number;
8
+ export const Shake256Rate: number;
9
+ export const Stream128BlockBytes: number;
10
+ export const Stream256BlockBytes: number;
11
+ export const SeedBytes: number;
12
+ export const CRHBytes: number;
13
+ export const TRBytes: number;
14
+ export const N: number;
15
+ export const Q: number;
16
+ export const QInv: number;
17
+ export const D: number;
18
+ export const K: number;
19
+ export const L: number;
20
+ export const ETA: number;
21
+ export const TAU: number;
22
+ export const BETA: number;
23
+ export const GAMMA1: number;
24
+ export const GAMMA2: number;
25
+ export const OMEGA: number;
26
+ export const PolyT1PackedBytes: number;
27
+ export const PolyT0PackedBytes: number;
28
+ export const PolyETAPackedBytes: number;
29
+ export const PolyZPackedBytes: number;
30
+ export const PolyVecHPackedBytes: number;
31
+ export const PolyW1PackedBytes: number;
32
+ export const CryptoPublicKeyBytes: number;
33
+ export const CryptoSecretKeyBytes: number;
34
+ export const CryptoBytes: number;
35
+ export const PolyUniformNBlocks: number;
36
+ export const PolyUniformETANBlocks: number;
37
+ export const PolyUniformGamma1NBlocks: number;
38
+ export const zetas: readonly number[];
39
+
40
+ // Core signing functions
41
+
42
+ /**
43
+ * Generate a Dilithium-5 key pair
44
+ * @param seed - Optional 32-byte seed for deterministic key generation (null for random)
45
+ * @param pk - Output buffer for public key (must be CryptoPublicKeyBytes length)
46
+ * @param sk - Output buffer for secret key (must be CryptoSecretKeyBytes length)
47
+ * @returns The seed used for key generation
48
+ * @throws Error if pk/sk buffers are wrong size or null
49
+ */
50
+ export function cryptoSignKeypair(
51
+ seed: Uint8Array | null,
52
+ pk: Uint8Array,
53
+ sk: Uint8Array
54
+ ): Uint8Array;
55
+
56
+ /**
57
+ * Create a signature for a message
58
+ * @param sig - Output buffer for signature (must be CryptoBytes length minimum)
59
+ * @param m - Message to sign (hex string or Uint8Array; strings are parsed as hex only)
60
+ * @param sk - Secret key
61
+ * @param randomizedSigning - If true, use random nonce; if false, deterministic
62
+ * @returns 0 on success
63
+ * @throws Error if sk is wrong size
64
+ */
65
+ export function cryptoSignSignature(
66
+ sig: Uint8Array,
67
+ m: Uint8Array | string,
68
+ sk: Uint8Array,
69
+ randomizedSigning: boolean
70
+ ): number;
71
+
72
+ /**
73
+ * Sign a message, returning signature concatenated with message
74
+ * @param msg - Message to sign
75
+ * @param sk - Secret key
76
+ * @param randomizedSigning - If true, use random nonce; if false, deterministic
77
+ * @returns Signed message (signature || message)
78
+ * @throws Error if signing fails
79
+ */
80
+ export function cryptoSign(
81
+ msg: Uint8Array | string,
82
+ sk: Uint8Array,
83
+ randomizedSigning: boolean
84
+ ): Uint8Array;
85
+
86
+ /**
87
+ * Create a deterministic Dilithium5 detached signature
88
+ * (`randomizedSigning = false` wrapper for `cryptoSignSignature`).
89
+ *
90
+ * **Use only when the deterministic property is itself a requirement**.
91
+ * For general-purpose signing prefer `cryptoSignSignature` with
92
+ * `randomizedSigning = true` (hedged — TOB-QRLLIB-6).
93
+ */
94
+ export function cryptoSignSignatureDeterministic(
95
+ sig: Uint8Array,
96
+ m: Uint8Array | string,
97
+ sk: Uint8Array
98
+ ): number;
99
+
100
+ /**
101
+ * Attached-form deterministic Dilithium5 signing
102
+ * (`randomizedSigning = false` wrapper for `cryptoSign`).
103
+ * Same recommendation as `cryptoSignSignatureDeterministic`.
104
+ * (TOB-QRLLIB-6.)
105
+ */
106
+ export function cryptoSignDeterministic(
107
+ msg: Uint8Array | string,
108
+ sk: Uint8Array
109
+ ): Uint8Array;
110
+
111
+ /**
112
+ * Verify a signature
113
+ * @param sig - Signature to verify
114
+ * @param m - Message that was signed (hex string or Uint8Array; strings are parsed as hex only)
115
+ * @param pk - Public key
116
+ * @returns true if signature is valid, false otherwise
117
+ */
118
+ export function cryptoSignVerify(
119
+ sig: Uint8Array,
120
+ m: Uint8Array | string,
121
+ pk: Uint8Array
122
+ ): boolean;
123
+
124
+ /**
125
+ * Open a signed message (verify and extract message)
126
+ * @param sm - Signed message (signature || message)
127
+ * @param pk - Public key
128
+ * @returns Message if valid, undefined if verification fails (or if
129
+ * sm is null / undefined / non-Uint8Array / shorter than
130
+ * CryptoBytes — see `cryptoSignOpenWithReason` for distinct
131
+ * failure-mode reporting)
132
+ */
133
+ export function cryptoSignOpen(
134
+ sm: Uint8Array,
135
+ pk: Uint8Array
136
+ ): Uint8Array | undefined;
137
+
138
+ /**
139
+ * Failure-mode discriminator returned by `cryptoSignOpenWithReason`.
140
+ * (TOB-QRLLIB-14: distinct failure modes for Open.)
141
+ */
142
+ export type CryptoSignOpenReason =
143
+ | 'invalid-sm-type'
144
+ | 'invalid-sm-length'
145
+ | 'invalid-pk'
146
+ | 'verification-failed';
147
+
148
+ /**
149
+ * Open a signed message with a typed failure-mode report.
150
+ * (TOB-QRLLIB-14.) Behavioural twin of `cryptoSignOpen` that
151
+ * distinguishes API-shape problems (input wrong type / length /
152
+ * shape) from genuine verification failures.
153
+ *
154
+ * `cryptoSignOpen` is kept unchanged and continues to return
155
+ * `undefined` for any failure mode. Use this variant when you need
156
+ * to log or route on specific failure modes.
157
+ *
158
+ * @param sm - Signed message (signature || message)
159
+ * @param pk - Public key
160
+ */
161
+ export function cryptoSignOpenWithReason(
162
+ sm: Uint8Array,
163
+ pk: Uint8Array
164
+ ):
165
+ | { ok: true; message: Uint8Array }
166
+ | { ok: false; reason: CryptoSignOpenReason };
167
+
168
+ // Utility functions
169
+
170
+ /**
171
+ * Zero out a buffer (best-effort, see SECURITY.md for limitations)
172
+ * @param buffer - Buffer to zero
173
+ * @throws TypeError if buffer is not Uint8Array
174
+ */
175
+ export function zeroize(buffer: Uint8Array): void;
176
+
177
+ /**
178
+ * Check if buffer is all zeros using constant-time comparison
179
+ * @param buffer - Buffer to check
180
+ * @returns true if all bytes are zero
181
+ * @throws TypeError if buffer is not Uint8Array
182
+ */
183
+ export function isZero(buffer: Uint8Array): boolean;
184
+
185
+ // Internal classes (exported but primarily for internal use)
186
+
187
+ export class Poly {
188
+ coeffs: Int32Array;
189
+ constructor();
190
+ copy(poly: Poly): void;
191
+ }
192
+
193
+ export class PolyVecK {
194
+ vec: Poly[];
195
+ constructor();
196
+ }
197
+
198
+ export class PolyVecL {
199
+ vec: Poly[];
200
+ constructor();
201
+ copy(polyVecL: PolyVecL): void;
202
+ }
203
+
204
+ export class KeccakState {
205
+ constructor();
206
+ }
207
+
208
+ // Internal functions (exported but primarily for internal use)
209
+ export function polyNTT(a: Poly): void;
210
+ export function polyInvNTTToMont(a: Poly): void;
211
+ export function polyChallenge(c: Poly, seed: Uint8Array): void;
212
+ export function ntt(a: Int32Array): void;
213
+ export function invNTTToMont(a: Int32Array): void;
214
+ export function montgomeryReduce(a: bigint): bigint;
215
+ export function reduce32(a: number): number;
216
+ export function cAddQ(a: number): number;
217
+ export function decompose(a0: Int32Array, i: number, a: number): number;
218
+ export function power2round(a0: Int32Array, i: number, a: number): number;
219
+ export function makeHint(a0: number, a1: number): number;
220
+ export function useHint(a: number, hint: number): number;
221
+ export function packPk(pk: Uint8Array, rho: Uint8Array, t1: PolyVecK): void;
222
+ export function packSk(
223
+ sk: Uint8Array,
224
+ rho: Uint8Array,
225
+ tr: Uint8Array,
226
+ key: Uint8Array,
227
+ t0: PolyVecK,
228
+ s1: PolyVecL,
229
+ s2: PolyVecK
230
+ ): void;
231
+ export function packSig(
232
+ sig: Uint8Array,
233
+ c: Uint8Array,
234
+ z: PolyVecL,
235
+ h: PolyVecK
236
+ ): void;
237
+ export function unpackPk(rho: Uint8Array, t1: PolyVecK, pk: Uint8Array): void;
238
+ export function unpackSk(
239
+ rho: Uint8Array,
240
+ tr: Uint8Array,
241
+ key: Uint8Array,
242
+ t0: PolyVecK,
243
+ s1: PolyVecL,
244
+ s2: PolyVecK,
245
+ sk: Uint8Array
246
+ ): void;
247
+ export function unpackSig(
248
+ c: Uint8Array,
249
+ z: PolyVecL,
250
+ h: PolyVecK,
251
+ sig: Uint8Array
252
+ ): number;
253
+
254
+ // FIPS 202 SHAKE primitives (low-level XOF interface, primarily internal)
255
+ export function shake128Init(state: KeccakState): void;
256
+ export function shake128Absorb(state: KeccakState, input: Uint8Array): void;
257
+ export function shake128Finalize(state: KeccakState): void;
258
+ export function shake128SqueezeBlocks(
259
+ out: Uint8Array,
260
+ outputOffset: number,
261
+ nBlocks: number,
262
+ state: KeccakState
263
+ ): void;
264
+ export function shake256Init(state: KeccakState): void;
265
+ export function shake256Absorb(state: KeccakState, input: Uint8Array): void;
266
+ export function shake256Finalize(state: KeccakState): void;
267
+ export function shake256SqueezeBlocks(
268
+ out: Uint8Array,
269
+ outputOffset: number,
270
+ nBlocks: number,
271
+ state: KeccakState
272
+ ): void;
273
+
274
+ // Dilithium-specific stream initializers
275
+ export function dilithiumShake128StreamInit(
276
+ state: KeccakState,
277
+ seed: Uint8Array,
278
+ nonce: number
279
+ ): void;
280
+ export function dilithiumShake256StreamInit(
281
+ state: KeccakState,
282
+ seed: Uint8Array,
283
+ nonce: number
284
+ ): void;
285
+
286
+ // Polynomial operations (internal)
287
+ export function polyReduce(a: Poly): void;
288
+ export function polyCAddQ(a: Poly): void;
289
+ export function polyAdd(c: Poly, a: Poly, b: Poly): void;
290
+ export function polySub(c: Poly, a: Poly, b: Poly): void;
291
+ export function polyShiftL(a: Poly): void;
292
+ export function polyPointWiseMontgomery(c: Poly, a: Poly, b: Poly): void;
293
+ export function polyPower2round(a1: Poly, a0: Poly, a: Poly): void;
294
+ export function polyDecompose(a1: Poly, a0: Poly, a: Poly): void;
295
+ export function polyMakeHint(h: Poly, a0: Poly, a1: Poly): number;
296
+ export function polyUseHint(b: Poly, a: Poly, h: Poly): void;
297
+ export function polyChkNorm(a: Poly, b: number): number;
298
+ export function rejUniform(
299
+ a: Int32Array,
300
+ aOffset: number,
301
+ len: number,
302
+ buf: Uint8Array,
303
+ bufLen: number
304
+ ): number;
305
+ export function polyUniform(a: Poly, seed: Uint8Array, nonce: number): void;
306
+ export function rejEta(
307
+ a: Int32Array,
308
+ aOffset: number,
309
+ len: number,
310
+ buf: Uint8Array,
311
+ bufLen: number
312
+ ): number;
313
+ export function polyUniformEta(a: Poly, seed: Uint8Array, nonce: number): void;
314
+ export function polyZUnpack(r: Poly, a: Uint8Array, aOffset: number): void;
315
+ export function polyUniformGamma1(a: Poly, seed: Uint8Array, nonce: number): void;
316
+ export function polyEtaPack(r: Uint8Array, rOffset: number, a: Poly): void;
317
+ export function polyEtaUnpack(r: Poly, a: Uint8Array, aOffset: number): void;
318
+ export function polyT1Pack(r: Uint8Array, rOffset: number, a: Poly): void;
319
+ export function polyT1Unpack(r: Poly, a: Uint8Array, aOffset: number): void;
320
+ export function polyT0Pack(r: Uint8Array, rOffset: number, a: Poly): void;
321
+ export function polyT0Unpack(r: Poly, a: Uint8Array, aOffset: number): void;
322
+ export function polyZPack(r: Uint8Array, rOffset: number, a: Poly): void;
323
+ export function polyW1Pack(r: Uint8Array, rOffset: number, a: Poly): void;
324
+
325
+ // Polynomial vector operations (internal)
326
+ export function polyVecMatrixExpand(mat: PolyVecL[], rho: Uint8Array): void;
327
+ export function polyVecMatrixPointWiseMontgomery(
328
+ t: PolyVecK,
329
+ mat: PolyVecL[],
330
+ v: PolyVecL
331
+ ): void;
332
+ export function polyVecLUniformEta(v: PolyVecL, seed: Uint8Array, nonce: number): void;
333
+ export function polyVecLUniformGamma1(v: PolyVecL, seed: Uint8Array, nonce: number): void;
334
+ export function polyVecLReduce(v: PolyVecL): void;
335
+ export function polyVecLAdd(w: PolyVecL, u: PolyVecL, v: PolyVecL): void;
336
+ export function polyVecLNTT(v: PolyVecL): void;
337
+ export function polyVecLInvNTTToMont(v: PolyVecL): void;
338
+ export function polyVecLPointWisePolyMontgomery(
339
+ r: PolyVecL,
340
+ a: Poly,
341
+ v: PolyVecL
342
+ ): void;
343
+ export function polyVecLPointWiseAccMontgomery(
344
+ w: Poly,
345
+ u: PolyVecL,
346
+ v: PolyVecL
347
+ ): void;
348
+ export function polyVecLChkNorm(v: PolyVecL, bound: number): number;
349
+ export function polyVecKUniformEta(v: PolyVecK, seed: Uint8Array, nonce: number): void;
350
+ export function polyVecKReduce(v: PolyVecK): void;
351
+ export function polyVecKCAddQ(v: PolyVecK): void;
352
+ export function polyVecKAdd(w: PolyVecK, u: PolyVecK, v: PolyVecK): void;
353
+ export function polyVecKSub(w: PolyVecK, u: PolyVecK, v: PolyVecK): void;
354
+ export function polyVecKShiftL(v: PolyVecK): void;
355
+ export function polyVecKNTT(v: PolyVecK): void;
356
+ export function polyVecKInvNTTToMont(v: PolyVecK): void;
357
+ export function polyVecKPointWisePolyMontgomery(
358
+ r: PolyVecK,
359
+ a: Poly,
360
+ v: PolyVecK
361
+ ): void;
362
+ export function polyVecKChkNorm(v: PolyVecK, bound: number): number;
363
+ export function polyVecKPower2round(v1: PolyVecK, v0: PolyVecK, v: PolyVecK): void;
364
+ export function polyVecKDecompose(v1: PolyVecK, v0: PolyVecK, v: PolyVecK): void;
365
+ export function polyVecKMakeHint(h: PolyVecK, v0: PolyVecK, v1: PolyVecK): number;
366
+ export function polyVecKUseHint(w: PolyVecK, u: PolyVecK, h: PolyVecK): void;
367
+ export function polyVecKPackW1(r: Uint8Array, w1: PolyVecK): void;
@@ -975,6 +975,12 @@ function packSig(sigP, c, z, h) {
975
975
  for (let i = 0; i < K; ++i) {
976
976
  for (let j = 0; j < N; ++j) {
977
977
  if (h.vec[i].coeffs[j] !== 0) {
978
+ if (h.vec[i].coeffs[j] !== 1) {
979
+ throw new Error('hint coefficients must be binary (0 or 1)');
980
+ }
981
+ if (k >= OMEGA) {
982
+ throw new Error(`hint count exceeds OMEGA (${OMEGA})`);
983
+ }
978
984
  sig[sigOffset + k++] = j;
979
985
  }
980
986
  }
@@ -1415,6 +1421,31 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
1415
1421
  }
1416
1422
  }
1417
1423
 
1424
+ /**
1425
+ * Create a **deterministic** Dilithium5 detached signature
1426
+ * (`randomizedSigning = false`).
1427
+ *
1428
+ * Convenience wrapper that hard-wires the deterministic mode so callers
1429
+ * who *need* byte-identical signatures for the same `(sk, message)`
1430
+ * — KAT vector reproduction, deterministic-test fixtures, RANDAO-style
1431
+ * protocols — get a clearly-named entry point rather than passing a
1432
+ * bare boolean.
1433
+ *
1434
+ * **Use only when the deterministic property is itself a requirement.**
1435
+ * For general-purpose signing prefer [cryptoSignSignature] with
1436
+ * `randomizedSigning = true` (hedged signing — TOB-QRLLIB-6 audit
1437
+ * recommendation for parity with the lattice-scheme guidance applied
1438
+ * to the Go and Rust ports).
1439
+ *
1440
+ * @param {Uint8Array} sig - Output buffer for signature (must be at least CryptoBytes bytes)
1441
+ * @param {string|Uint8Array} m - Message to sign
1442
+ * @param {Uint8Array} sk - Secret key (must be CryptoSecretKeyBytes bytes)
1443
+ * @returns {number} 0 on success
1444
+ */
1445
+ function cryptoSignSignatureDeterministic(sig, m, sk) {
1446
+ return cryptoSignSignature(sig, m, sk, /* randomizedSigning */ false);
1447
+ }
1448
+
1418
1449
  /**
1419
1450
  * Sign a message, returning signature concatenated with message.
1420
1451
  *
@@ -1450,6 +1481,24 @@ function cryptoSign(msg, sk, randomizedSigning) {
1450
1481
  return sm;
1451
1482
  }
1452
1483
 
1484
+ /**
1485
+ * Attached-form **deterministic** Dilithium5 signing
1486
+ * (`randomizedSigning = false`).
1487
+ *
1488
+ * Convenience wrapper that hard-wires the deterministic mode for the
1489
+ * attached `signature || message` form. Same recommendation as
1490
+ * [cryptoSignSignatureDeterministic]: use only when determinism is a
1491
+ * protocol requirement; for general-purpose signing prefer
1492
+ * [cryptoSign] with `randomizedSigning = true` (hedged — TOB-QRLLIB-6).
1493
+ *
1494
+ * @param {string|Uint8Array} msg - Message to sign
1495
+ * @param {Uint8Array} sk - Secret key
1496
+ * @returns {Uint8Array} Signed message (signature || message)
1497
+ */
1498
+ function cryptoSignDeterministic(msg, sk) {
1499
+ return cryptoSign(msg, sk, /* randomizedSigning */ false);
1500
+ }
1501
+
1453
1502
  /**
1454
1503
  * Verify a detached signature.
1455
1504
  *
@@ -1558,7 +1607,12 @@ function cryptoSignVerify(sig, m, pk) {
1558
1607
  * }
1559
1608
  */
1560
1609
  function cryptoSignOpen(sm, pk) {
1561
- if (sm.length < CryptoBytes) {
1610
+ // Type-guard `sm` so callers passing `null` / `undefined` / non-Uint8Array
1611
+ // get a clean `undefined` return rather than a `Cannot read properties of
1612
+ // null (reading 'length')` thrown deep in the call chain. Mirrors the
1613
+ // existing `pk` / `sig` instanceof checks in `cryptoSignVerify`.
1614
+ // (TOB-QRLLIB-11.)
1615
+ if (!(sm instanceof Uint8Array) || sm.length < CryptoBytes) {
1562
1616
  return undefined;
1563
1617
  }
1564
1618
 
@@ -1571,4 +1625,35 @@ function cryptoSignOpen(sm, pk) {
1571
1625
  return msg;
1572
1626
  }
1573
1627
 
1574
- export { BETA, CRHBytes, CryptoBytes, CryptoPublicKeyBytes, CryptoSecretKeyBytes, D, ETA, GAMMA1, GAMMA2, K, KeccakState, L, N, OMEGA, Poly, PolyETAPackedBytes, PolyT0PackedBytes, PolyT1PackedBytes, PolyUniformETANBlocks, PolyUniformGamma1NBlocks, PolyUniformNBlocks, PolyVecHPackedBytes, PolyVecK, PolyVecL, PolyW1PackedBytes, PolyZPackedBytes, Q, QInv, SeedBytes, Shake128Rate, Shake256Rate, Stream128BlockBytes, Stream256BlockBytes, TAU, TRBytes, cAddQ, cryptoSign, cryptoSignKeypair, cryptoSignOpen, cryptoSignSignature, cryptoSignVerify, decompose, dilithiumShake128StreamInit, dilithiumShake256StreamInit, invNTTToMont, isZero, makeHint, montgomeryReduce, ntt, packPk, packSig, packSk, polyAdd, polyCAddQ, polyChallenge, polyChkNorm, polyDecompose, polyEtaPack, polyEtaUnpack, polyInvNTTToMont, polyMakeHint, polyNTT, polyPointWiseMontgomery, polyPower2round, polyReduce, polyShiftL, polySub, polyT0Pack, polyT0Unpack, polyT1Pack, polyT1Unpack, polyUniform, polyUniformEta, polyUniformGamma1, polyUseHint, polyVecKAdd, polyVecKCAddQ, polyVecKChkNorm, polyVecKDecompose, polyVecKInvNTTToMont, polyVecKMakeHint, polyVecKNTT, polyVecKPackW1, polyVecKPointWisePolyMontgomery, polyVecKPower2round, polyVecKReduce, polyVecKShiftL, polyVecKSub, polyVecKUniformEta, polyVecKUseHint, polyVecLAdd, polyVecLChkNorm, polyVecLInvNTTToMont, polyVecLNTT, polyVecLPointWiseAccMontgomery, polyVecLPointWisePolyMontgomery, polyVecLReduce, polyVecLUniformEta, polyVecLUniformGamma1, polyVecMatrixExpand, polyVecMatrixPointWiseMontgomery, polyW1Pack, polyZPack, polyZUnpack, power2round, reduce32, rejEta, rejUniform, shake128Absorb, shake128Finalize, shake128Init, shake128SqueezeBlocks, shake256Absorb, shake256Finalize, shake256Init, shake256SqueezeBlocks, unpackPk, unpackSig, unpackSk, useHint, zeroize, zetas };
1628
+ /**
1629
+ * Open a signed message with a typed failure-mode report.
1630
+ *
1631
+ * Behavioural twin of [cryptoSignOpen], but returns a discriminated
1632
+ * union so callers can distinguish between API-shape problems (input
1633
+ * was the wrong type / length / shape) and genuine cryptographic
1634
+ * verification failures. See the ML-DSA-87 sibling for the rationale
1635
+ * (TOB-QRLLIB-14).
1636
+ *
1637
+ * @param {Uint8Array} sm Signed message (signature || message).
1638
+ * @param {Uint8Array} pk Public key.
1639
+ * @returns {{ok: true, message: Uint8Array} | {ok: false, reason: 'invalid-sm-type'|'invalid-sm-length'|'invalid-pk'|'verification-failed'}}
1640
+ */
1641
+ function cryptoSignOpenWithReason(sm, pk) {
1642
+ if (!(sm instanceof Uint8Array)) {
1643
+ return { ok: false, reason: 'invalid-sm-type' };
1644
+ }
1645
+ if (sm.length < CryptoBytes) {
1646
+ return { ok: false, reason: 'invalid-sm-length' };
1647
+ }
1648
+ if (!(pk instanceof Uint8Array) || pk.length !== CryptoPublicKeyBytes) {
1649
+ return { ok: false, reason: 'invalid-pk' };
1650
+ }
1651
+ const sig = sm.slice(0, CryptoBytes);
1652
+ const msg = sm.slice(CryptoBytes);
1653
+ if (!cryptoSignVerify(sig, msg, pk)) {
1654
+ return { ok: false, reason: 'verification-failed' };
1655
+ }
1656
+ return { ok: true, message: msg };
1657
+ }
1658
+
1659
+ export { BETA, CRHBytes, CryptoBytes, CryptoPublicKeyBytes, CryptoSecretKeyBytes, D, ETA, GAMMA1, GAMMA2, K, KeccakState, L, N, OMEGA, Poly, PolyETAPackedBytes, PolyT0PackedBytes, PolyT1PackedBytes, PolyUniformETANBlocks, PolyUniformGamma1NBlocks, PolyUniformNBlocks, PolyVecHPackedBytes, PolyVecK, PolyVecL, PolyW1PackedBytes, PolyZPackedBytes, Q, QInv, SeedBytes, Shake128Rate, Shake256Rate, Stream128BlockBytes, Stream256BlockBytes, TAU, TRBytes, cAddQ, cryptoSign, cryptoSignDeterministic, cryptoSignKeypair, cryptoSignOpen, cryptoSignOpenWithReason, cryptoSignSignature, cryptoSignSignatureDeterministic, cryptoSignVerify, decompose, dilithiumShake128StreamInit, dilithiumShake256StreamInit, invNTTToMont, isZero, makeHint, montgomeryReduce, ntt, packPk, packSig, packSk, polyAdd, polyCAddQ, polyChallenge, polyChkNorm, polyDecompose, polyEtaPack, polyEtaUnpack, polyInvNTTToMont, polyMakeHint, polyNTT, polyPointWiseMontgomery, polyPower2round, polyReduce, polyShiftL, polySub, polyT0Pack, polyT0Unpack, polyT1Pack, polyT1Unpack, polyUniform, polyUniformEta, polyUniformGamma1, polyUseHint, polyVecKAdd, polyVecKCAddQ, polyVecKChkNorm, polyVecKDecompose, polyVecKInvNTTToMont, polyVecKMakeHint, polyVecKNTT, polyVecKPackW1, polyVecKPointWisePolyMontgomery, polyVecKPower2round, polyVecKReduce, polyVecKShiftL, polyVecKSub, polyVecKUniformEta, polyVecKUseHint, polyVecLAdd, polyVecLChkNorm, polyVecLInvNTTToMont, polyVecLNTT, polyVecLPointWiseAccMontgomery, polyVecLPointWisePolyMontgomery, polyVecLReduce, polyVecLUniformEta, polyVecLUniformGamma1, polyVecMatrixExpand, polyVecMatrixPointWiseMontgomery, polyW1Pack, polyZPack, polyZUnpack, power2round, reduce32, rejEta, rejUniform, shake128Absorb, shake128Finalize, shake128Init, shake128SqueezeBlocks, shake256Absorb, shake256Finalize, shake256Init, shake256SqueezeBlocks, unpackPk, unpackSig, unpackSk, useHint, zeroize, zetas };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theqrl/dilithium5",
3
- "version": "1.1.2",
3
+ "version": "1.2.0",
4
4
  "description": "Dilithium-5 cryptography",
5
5
  "keywords": [
6
6
  "dilithium",
@@ -44,8 +44,14 @@
44
44
  },
45
45
  "exports": {
46
46
  ".": {
47
- "import": "./dist/mjs/dilithium5.js",
48
- "require": "./dist/cjs/dilithium5.js"
47
+ "import": {
48
+ "types": "./dist/mjs/dilithium5.d.mts",
49
+ "default": "./dist/mjs/dilithium5.js"
50
+ },
51
+ "require": {
52
+ "types": "./dist/cjs/dilithium5.d.cts",
53
+ "default": "./dist/cjs/dilithium5.js"
54
+ }
49
55
  }
50
56
  },
51
57
  "type": "module",
package/src/index.d.ts CHANGED
@@ -83,6 +83,31 @@ export function cryptoSign(
83
83
  randomizedSigning: boolean
84
84
  ): Uint8Array;
85
85
 
86
+ /**
87
+ * Create a deterministic Dilithium5 detached signature
88
+ * (`randomizedSigning = false` wrapper for `cryptoSignSignature`).
89
+ *
90
+ * **Use only when the deterministic property is itself a requirement**.
91
+ * For general-purpose signing prefer `cryptoSignSignature` with
92
+ * `randomizedSigning = true` (hedged — TOB-QRLLIB-6).
93
+ */
94
+ export function cryptoSignSignatureDeterministic(
95
+ sig: Uint8Array,
96
+ m: Uint8Array | string,
97
+ sk: Uint8Array
98
+ ): number;
99
+
100
+ /**
101
+ * Attached-form deterministic Dilithium5 signing
102
+ * (`randomizedSigning = false` wrapper for `cryptoSign`).
103
+ * Same recommendation as `cryptoSignSignatureDeterministic`.
104
+ * (TOB-QRLLIB-6.)
105
+ */
106
+ export function cryptoSignDeterministic(
107
+ msg: Uint8Array | string,
108
+ sk: Uint8Array
109
+ ): Uint8Array;
110
+
86
111
  /**
87
112
  * Verify a signature
88
113
  * @param sig - Signature to verify
@@ -100,13 +125,46 @@ export function cryptoSignVerify(
100
125
  * Open a signed message (verify and extract message)
101
126
  * @param sm - Signed message (signature || message)
102
127
  * @param pk - Public key
103
- * @returns Message if valid, undefined if verification fails
128
+ * @returns Message if valid, undefined if verification fails (or if
129
+ * sm is null / undefined / non-Uint8Array / shorter than
130
+ * CryptoBytes — see `cryptoSignOpenWithReason` for distinct
131
+ * failure-mode reporting)
104
132
  */
105
133
  export function cryptoSignOpen(
106
134
  sm: Uint8Array,
107
135
  pk: Uint8Array
108
136
  ): Uint8Array | undefined;
109
137
 
138
+ /**
139
+ * Failure-mode discriminator returned by `cryptoSignOpenWithReason`.
140
+ * (TOB-QRLLIB-14: distinct failure modes for Open.)
141
+ */
142
+ export type CryptoSignOpenReason =
143
+ | 'invalid-sm-type'
144
+ | 'invalid-sm-length'
145
+ | 'invalid-pk'
146
+ | 'verification-failed';
147
+
148
+ /**
149
+ * Open a signed message with a typed failure-mode report.
150
+ * (TOB-QRLLIB-14.) Behavioural twin of `cryptoSignOpen` that
151
+ * distinguishes API-shape problems (input wrong type / length /
152
+ * shape) from genuine verification failures.
153
+ *
154
+ * `cryptoSignOpen` is kept unchanged and continues to return
155
+ * `undefined` for any failure mode. Use this variant when you need
156
+ * to log or route on specific failure modes.
157
+ *
158
+ * @param sm - Signed message (signature || message)
159
+ * @param pk - Public key
160
+ */
161
+ export function cryptoSignOpenWithReason(
162
+ sm: Uint8Array,
163
+ pk: Uint8Array
164
+ ):
165
+ | { ok: true; message: Uint8Array }
166
+ | { ok: false; reason: CryptoSignOpenReason };
167
+
110
168
  // Utility functions
111
169
 
112
170
  /**
@@ -192,3 +250,118 @@ export function unpackSig(
192
250
  h: PolyVecK,
193
251
  sig: Uint8Array
194
252
  ): number;
253
+
254
+ // FIPS 202 SHAKE primitives (low-level XOF interface, primarily internal)
255
+ export function shake128Init(state: KeccakState): void;
256
+ export function shake128Absorb(state: KeccakState, input: Uint8Array): void;
257
+ export function shake128Finalize(state: KeccakState): void;
258
+ export function shake128SqueezeBlocks(
259
+ out: Uint8Array,
260
+ outputOffset: number,
261
+ nBlocks: number,
262
+ state: KeccakState
263
+ ): void;
264
+ export function shake256Init(state: KeccakState): void;
265
+ export function shake256Absorb(state: KeccakState, input: Uint8Array): void;
266
+ export function shake256Finalize(state: KeccakState): void;
267
+ export function shake256SqueezeBlocks(
268
+ out: Uint8Array,
269
+ outputOffset: number,
270
+ nBlocks: number,
271
+ state: KeccakState
272
+ ): void;
273
+
274
+ // Dilithium-specific stream initializers
275
+ export function dilithiumShake128StreamInit(
276
+ state: KeccakState,
277
+ seed: Uint8Array,
278
+ nonce: number
279
+ ): void;
280
+ export function dilithiumShake256StreamInit(
281
+ state: KeccakState,
282
+ seed: Uint8Array,
283
+ nonce: number
284
+ ): void;
285
+
286
+ // Polynomial operations (internal)
287
+ export function polyReduce(a: Poly): void;
288
+ export function polyCAddQ(a: Poly): void;
289
+ export function polyAdd(c: Poly, a: Poly, b: Poly): void;
290
+ export function polySub(c: Poly, a: Poly, b: Poly): void;
291
+ export function polyShiftL(a: Poly): void;
292
+ export function polyPointWiseMontgomery(c: Poly, a: Poly, b: Poly): void;
293
+ export function polyPower2round(a1: Poly, a0: Poly, a: Poly): void;
294
+ export function polyDecompose(a1: Poly, a0: Poly, a: Poly): void;
295
+ export function polyMakeHint(h: Poly, a0: Poly, a1: Poly): number;
296
+ export function polyUseHint(b: Poly, a: Poly, h: Poly): void;
297
+ export function polyChkNorm(a: Poly, b: number): number;
298
+ export function rejUniform(
299
+ a: Int32Array,
300
+ aOffset: number,
301
+ len: number,
302
+ buf: Uint8Array,
303
+ bufLen: number
304
+ ): number;
305
+ export function polyUniform(a: Poly, seed: Uint8Array, nonce: number): void;
306
+ export function rejEta(
307
+ a: Int32Array,
308
+ aOffset: number,
309
+ len: number,
310
+ buf: Uint8Array,
311
+ bufLen: number
312
+ ): number;
313
+ export function polyUniformEta(a: Poly, seed: Uint8Array, nonce: number): void;
314
+ export function polyZUnpack(r: Poly, a: Uint8Array, aOffset: number): void;
315
+ export function polyUniformGamma1(a: Poly, seed: Uint8Array, nonce: number): void;
316
+ export function polyEtaPack(r: Uint8Array, rOffset: number, a: Poly): void;
317
+ export function polyEtaUnpack(r: Poly, a: Uint8Array, aOffset: number): void;
318
+ export function polyT1Pack(r: Uint8Array, rOffset: number, a: Poly): void;
319
+ export function polyT1Unpack(r: Poly, a: Uint8Array, aOffset: number): void;
320
+ export function polyT0Pack(r: Uint8Array, rOffset: number, a: Poly): void;
321
+ export function polyT0Unpack(r: Poly, a: Uint8Array, aOffset: number): void;
322
+ export function polyZPack(r: Uint8Array, rOffset: number, a: Poly): void;
323
+ export function polyW1Pack(r: Uint8Array, rOffset: number, a: Poly): void;
324
+
325
+ // Polynomial vector operations (internal)
326
+ export function polyVecMatrixExpand(mat: PolyVecL[], rho: Uint8Array): void;
327
+ export function polyVecMatrixPointWiseMontgomery(
328
+ t: PolyVecK,
329
+ mat: PolyVecL[],
330
+ v: PolyVecL
331
+ ): void;
332
+ export function polyVecLUniformEta(v: PolyVecL, seed: Uint8Array, nonce: number): void;
333
+ export function polyVecLUniformGamma1(v: PolyVecL, seed: Uint8Array, nonce: number): void;
334
+ export function polyVecLReduce(v: PolyVecL): void;
335
+ export function polyVecLAdd(w: PolyVecL, u: PolyVecL, v: PolyVecL): void;
336
+ export function polyVecLNTT(v: PolyVecL): void;
337
+ export function polyVecLInvNTTToMont(v: PolyVecL): void;
338
+ export function polyVecLPointWisePolyMontgomery(
339
+ r: PolyVecL,
340
+ a: Poly,
341
+ v: PolyVecL
342
+ ): void;
343
+ export function polyVecLPointWiseAccMontgomery(
344
+ w: Poly,
345
+ u: PolyVecL,
346
+ v: PolyVecL
347
+ ): void;
348
+ export function polyVecLChkNorm(v: PolyVecL, bound: number): number;
349
+ export function polyVecKUniformEta(v: PolyVecK, seed: Uint8Array, nonce: number): void;
350
+ export function polyVecKReduce(v: PolyVecK): void;
351
+ export function polyVecKCAddQ(v: PolyVecK): void;
352
+ export function polyVecKAdd(w: PolyVecK, u: PolyVecK, v: PolyVecK): void;
353
+ export function polyVecKSub(w: PolyVecK, u: PolyVecK, v: PolyVecK): void;
354
+ export function polyVecKShiftL(v: PolyVecK): void;
355
+ export function polyVecKNTT(v: PolyVecK): void;
356
+ export function polyVecKInvNTTToMont(v: PolyVecK): void;
357
+ export function polyVecKPointWisePolyMontgomery(
358
+ r: PolyVecK,
359
+ a: Poly,
360
+ v: PolyVecK
361
+ ): void;
362
+ export function polyVecKChkNorm(v: PolyVecK, bound: number): number;
363
+ export function polyVecKPower2round(v1: PolyVecK, v0: PolyVecK, v: PolyVecK): void;
364
+ export function polyVecKDecompose(v1: PolyVecK, v0: PolyVecK, v: PolyVecK): void;
365
+ export function polyVecKMakeHint(h: PolyVecK, v0: PolyVecK, v1: PolyVecK): number;
366
+ export function polyVecKUseHint(w: PolyVecK, u: PolyVecK, h: PolyVecK): void;
367
+ export function polyVecKPackW1(r: Uint8Array, w1: PolyVecK): void;