@theqrl/dilithium5 1.1.5 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/dist/cjs/dilithium5.d.cts +59 -1
- package/dist/cjs/dilithium5.js +385 -51
- package/dist/mjs/dilithium5.d.mts +59 -1
- package/dist/mjs/dilithium5.js +81 -2
- package/package.json +10 -10
- package/src/index.d.ts +59 -1
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,
|
|
34
|
+
const signedMessage = cryptoSign(message, sk, true); // true = hedged (recommended)
|
|
32
35
|
|
|
33
36
|
// Verify and extract
|
|
34
37
|
const extracted = cryptoSignOpen(signedMessage, pk);
|
|
@@ -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
|
/**
|
package/dist/cjs/dilithium5.js
CHANGED
|
@@ -62,18 +62,17 @@ const zetas = [
|
|
|
62
62
|
-1362209, 3937738, 1400424, -846154, 1976782,
|
|
63
63
|
];
|
|
64
64
|
|
|
65
|
-
/**
|
|
66
|
-
* Internal helpers for u64. BigUint64Array is too slow as per 2025, so we implement it using Uint32Array.
|
|
67
|
-
* @todo re-check https://issues.chromium.org/issues/42212588
|
|
68
|
-
* @module
|
|
69
|
-
*/
|
|
70
65
|
const U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1);
|
|
71
66
|
const _32n = /* @__PURE__ */ BigInt(32);
|
|
67
|
+
// Split bigint into two 32-bit halves. With `le=true`, returned fields become `{ h: low, l: high
|
|
68
|
+
// }` to match little-endian word order rather than the property names.
|
|
72
69
|
function fromBig(n, le = false) {
|
|
73
70
|
if (le)
|
|
74
71
|
return { h: Number(n & U32_MASK64), l: Number((n >> _32n) & U32_MASK64) };
|
|
75
72
|
return { h: Number((n >> _32n) & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
|
|
76
73
|
}
|
|
74
|
+
// Split bigint list into `[highWords, lowWords]` when `le=false`; with `le=true`, the first array
|
|
75
|
+
// holds the low halves because `fromBig(...)` swaps the semantic meaning of `h` and `l`.
|
|
77
76
|
function split(lst, le = false) {
|
|
78
77
|
const len = lst.length;
|
|
79
78
|
let Ah = new Uint32Array(len);
|
|
@@ -84,30 +83,72 @@ function split(lst, le = false) {
|
|
|
84
83
|
}
|
|
85
84
|
return [Ah, Al];
|
|
86
85
|
}
|
|
87
|
-
//
|
|
86
|
+
// High 32-bit half of a 64-bit left rotate, valid for `s` in `1..31`.
|
|
88
87
|
const rotlSH = (h, l, s) => (h << s) | (l >>> (32 - s));
|
|
88
|
+
// Low 32-bit half of a 64-bit left rotate, valid for `s` in `1..31`.
|
|
89
89
|
const rotlSL = (h, l, s) => (l << s) | (h >>> (32 - s));
|
|
90
|
-
//
|
|
90
|
+
// High 32-bit half of a 64-bit left rotate, valid for `s` in `33..63`; `32` uses `rotr32*`.
|
|
91
91
|
const rotlBH = (h, l, s) => (l << (s - 32)) | (h >>> (64 - s));
|
|
92
|
+
// Low 32-bit half of a 64-bit left rotate, valid for `s` in `33..63`; `32` uses `rotr32*`.
|
|
92
93
|
const rotlBL = (h, l, s) => (h << (s - 32)) | (l >>> (64 - s));
|
|
93
94
|
|
|
94
95
|
/**
|
|
95
|
-
*
|
|
96
|
-
* @
|
|
96
|
+
* Checks if something is Uint8Array. Be careful: nodejs Buffer will return true.
|
|
97
|
+
* @param a - value to test
|
|
98
|
+
* @returns `true` when the value is a Uint8Array-compatible view.
|
|
99
|
+
* @example
|
|
100
|
+
* Check whether a value is a Uint8Array-compatible view.
|
|
101
|
+
* ```ts
|
|
102
|
+
* isBytes(new Uint8Array([1, 2, 3]));
|
|
103
|
+
* ```
|
|
97
104
|
*/
|
|
98
|
-
/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
99
|
-
/** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */
|
|
100
105
|
function isBytes(a) {
|
|
101
|
-
|
|
106
|
+
// Plain `instanceof Uint8Array` is too strict for some Buffer / proxy / cross-realm cases.
|
|
107
|
+
// The fallback still requires a real ArrayBuffer view, so plain
|
|
108
|
+
// JSON-deserialized `{ constructor: ... }` spoofing is rejected, and
|
|
109
|
+
// `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views.
|
|
110
|
+
return (a instanceof Uint8Array ||
|
|
111
|
+
(ArrayBuffer.isView(a) &&
|
|
112
|
+
a.constructor.name === 'Uint8Array' &&
|
|
113
|
+
'BYTES_PER_ELEMENT' in a &&
|
|
114
|
+
a.BYTES_PER_ELEMENT === 1));
|
|
102
115
|
}
|
|
103
|
-
/**
|
|
116
|
+
/**
|
|
117
|
+
* Asserts something is a non-negative integer.
|
|
118
|
+
* @param n - number to validate
|
|
119
|
+
* @param title - label included in thrown errors
|
|
120
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
121
|
+
* @throws On wrong argument ranges or values. {@link RangeError}
|
|
122
|
+
* @example
|
|
123
|
+
* Validate a non-negative integer option.
|
|
124
|
+
* ```ts
|
|
125
|
+
* anumber(32, 'length');
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
104
128
|
function anumber(n, title = '') {
|
|
129
|
+
if (typeof n !== 'number') {
|
|
130
|
+
const prefix = title && `"${title}" `;
|
|
131
|
+
throw new TypeError(`${prefix}expected number, got ${typeof n}`);
|
|
132
|
+
}
|
|
105
133
|
if (!Number.isSafeInteger(n) || n < 0) {
|
|
106
134
|
const prefix = title && `"${title}" `;
|
|
107
|
-
throw new
|
|
135
|
+
throw new RangeError(`${prefix}expected integer >= 0, got ${n}`);
|
|
108
136
|
}
|
|
109
137
|
}
|
|
110
|
-
/**
|
|
138
|
+
/**
|
|
139
|
+
* Asserts something is Uint8Array.
|
|
140
|
+
* @param value - value to validate
|
|
141
|
+
* @param length - optional exact length constraint
|
|
142
|
+
* @param title - label included in thrown errors
|
|
143
|
+
* @returns The validated byte array.
|
|
144
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
145
|
+
* @throws On wrong argument ranges or values. {@link RangeError}
|
|
146
|
+
* @example
|
|
147
|
+
* Validate that a value is a byte array.
|
|
148
|
+
* ```ts
|
|
149
|
+
* abytes(new Uint8Array([1, 2, 3]));
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
111
152
|
function abytes(value, length, title = '') {
|
|
112
153
|
const bytes = isBytes(value);
|
|
113
154
|
const len = value?.length;
|
|
@@ -116,51 +157,130 @@ function abytes(value, length, title = '') {
|
|
|
116
157
|
const prefix = title && `"${title}" `;
|
|
117
158
|
const ofLen = '';
|
|
118
159
|
const got = bytes ? `length=${len}` : `type=${typeof value}`;
|
|
119
|
-
|
|
160
|
+
const message = prefix + 'expected Uint8Array' + ofLen + ', got ' + got;
|
|
161
|
+
if (!bytes)
|
|
162
|
+
throw new TypeError(message);
|
|
163
|
+
throw new RangeError(message);
|
|
120
164
|
}
|
|
121
165
|
return value;
|
|
122
166
|
}
|
|
123
|
-
/**
|
|
167
|
+
/**
|
|
168
|
+
* Asserts a hash instance has not been destroyed or finished.
|
|
169
|
+
* @param instance - hash instance to validate
|
|
170
|
+
* @param checkFinished - whether to reject finalized instances
|
|
171
|
+
* @throws If the hash instance has already been destroyed or finalized. {@link Error}
|
|
172
|
+
* @example
|
|
173
|
+
* Validate that a hash instance is still usable.
|
|
174
|
+
* ```ts
|
|
175
|
+
* import { aexists } from '@noble/hashes/utils.js';
|
|
176
|
+
* import { sha256 } from '@noble/hashes/sha2.js';
|
|
177
|
+
* const hash = sha256.create();
|
|
178
|
+
* aexists(hash);
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
124
181
|
function aexists(instance, checkFinished = true) {
|
|
125
182
|
if (instance.destroyed)
|
|
126
183
|
throw new Error('Hash instance has been destroyed');
|
|
127
184
|
if (checkFinished && instance.finished)
|
|
128
185
|
throw new Error('Hash#digest() has already been called');
|
|
129
186
|
}
|
|
130
|
-
/**
|
|
187
|
+
/**
|
|
188
|
+
* Asserts output is a sufficiently-sized byte array.
|
|
189
|
+
* @param out - destination buffer
|
|
190
|
+
* @param instance - hash instance providing output length
|
|
191
|
+
* Oversized buffers are allowed; downstream code only promises to fill the first `outputLen` bytes.
|
|
192
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
193
|
+
* @throws On wrong argument ranges or values. {@link RangeError}
|
|
194
|
+
* @example
|
|
195
|
+
* Validate a caller-provided digest buffer.
|
|
196
|
+
* ```ts
|
|
197
|
+
* import { aoutput } from '@noble/hashes/utils.js';
|
|
198
|
+
* import { sha256 } from '@noble/hashes/sha2.js';
|
|
199
|
+
* const hash = sha256.create();
|
|
200
|
+
* aoutput(new Uint8Array(hash.outputLen), hash);
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
131
203
|
function aoutput(out, instance) {
|
|
132
204
|
abytes(out, undefined, 'digestInto() output');
|
|
133
205
|
const min = instance.outputLen;
|
|
134
206
|
if (out.length < min) {
|
|
135
|
-
throw new
|
|
207
|
+
throw new RangeError('"digestInto() output" expected to be of length >=' + min);
|
|
136
208
|
}
|
|
137
209
|
}
|
|
138
|
-
/**
|
|
210
|
+
/**
|
|
211
|
+
* Casts a typed array view to Uint32Array.
|
|
212
|
+
* `arr.byteOffset` must already be 4-byte aligned or the platform
|
|
213
|
+
* Uint32Array constructor will throw.
|
|
214
|
+
* @param arr - source typed array
|
|
215
|
+
* @returns Uint32Array view over the same buffer.
|
|
216
|
+
* @example
|
|
217
|
+
* Reinterpret a byte array as 32-bit words.
|
|
218
|
+
* ```ts
|
|
219
|
+
* u32(new Uint8Array(8));
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
139
222
|
function u32(arr) {
|
|
140
223
|
return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
|
|
141
224
|
}
|
|
142
|
-
/**
|
|
225
|
+
/**
|
|
226
|
+
* Zeroizes typed arrays in place. Warning: JS provides no guarantees.
|
|
227
|
+
* @param arrays - arrays to overwrite with zeros
|
|
228
|
+
* @example
|
|
229
|
+
* Zeroize sensitive buffers in place.
|
|
230
|
+
* ```ts
|
|
231
|
+
* clean(new Uint8Array([1, 2, 3]));
|
|
232
|
+
* ```
|
|
233
|
+
*/
|
|
143
234
|
function clean(...arrays) {
|
|
144
235
|
for (let i = 0; i < arrays.length; i++) {
|
|
145
236
|
arrays[i].fill(0);
|
|
146
237
|
}
|
|
147
238
|
}
|
|
148
|
-
/**
|
|
239
|
+
/** Whether the current platform is little-endian. */
|
|
149
240
|
const isLE = /* @__PURE__ */ (() => new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();
|
|
150
|
-
/**
|
|
241
|
+
/**
|
|
242
|
+
* Byte-swap operation for uint32 values.
|
|
243
|
+
* @param word - source word
|
|
244
|
+
* @returns Word with reversed byte order.
|
|
245
|
+
* @example
|
|
246
|
+
* Reverse the byte order of a 32-bit word.
|
|
247
|
+
* ```ts
|
|
248
|
+
* byteSwap(0x11223344);
|
|
249
|
+
* ```
|
|
250
|
+
*/
|
|
151
251
|
function byteSwap(word) {
|
|
152
252
|
return (((word << 24) & 0xff000000) |
|
|
153
253
|
((word << 8) & 0xff0000) |
|
|
154
254
|
((word >>> 8) & 0xff00) |
|
|
155
255
|
((word >>> 24) & 0xff));
|
|
156
256
|
}
|
|
157
|
-
/**
|
|
257
|
+
/**
|
|
258
|
+
* Byte-swaps every word of a Uint32Array in place.
|
|
259
|
+
* @param arr - array to mutate
|
|
260
|
+
* @returns The same array after mutation; callers pass live state arrays here.
|
|
261
|
+
* @example
|
|
262
|
+
* Reverse the byte order of every word in place.
|
|
263
|
+
* ```ts
|
|
264
|
+
* byteSwap32(new Uint32Array([0x11223344]));
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
158
267
|
function byteSwap32(arr) {
|
|
159
268
|
for (let i = 0; i < arr.length; i++) {
|
|
160
269
|
arr[i] = byteSwap(arr[i]);
|
|
161
270
|
}
|
|
162
271
|
return arr;
|
|
163
272
|
}
|
|
273
|
+
/**
|
|
274
|
+
* Conditionally byte-swaps a Uint32Array on big-endian platforms.
|
|
275
|
+
* @param u - array to normalize for host endianness
|
|
276
|
+
* @returns Original or byte-swapped array depending on platform endianness.
|
|
277
|
+
* On big-endian runtimes this mutates `u` in place via `byteSwap32(...)`.
|
|
278
|
+
* @example
|
|
279
|
+
* Normalize a word array for host endianness.
|
|
280
|
+
* ```ts
|
|
281
|
+
* swap32IfBE(new Uint32Array([0x11223344]));
|
|
282
|
+
* ```
|
|
283
|
+
*/
|
|
164
284
|
const swap32IfBE = isLE
|
|
165
285
|
? (u) => u
|
|
166
286
|
: byteSwap32;
|
|
@@ -181,42 +301,89 @@ function asciiToBase16(ch) {
|
|
|
181
301
|
}
|
|
182
302
|
/**
|
|
183
303
|
* Convert hex string to byte array. Uses built-in function, when available.
|
|
184
|
-
* @
|
|
304
|
+
* @param hex - hexadecimal string to decode
|
|
305
|
+
* @returns Decoded bytes.
|
|
306
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
307
|
+
* @throws On wrong argument ranges or values. {@link RangeError}
|
|
308
|
+
* @example
|
|
309
|
+
* Decode lowercase hexadecimal into bytes.
|
|
310
|
+
* ```ts
|
|
311
|
+
* hexToBytes('cafe0123'); // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])
|
|
312
|
+
* ```
|
|
185
313
|
*/
|
|
186
314
|
function hexToBytes$1(hex) {
|
|
187
315
|
if (typeof hex !== 'string')
|
|
188
|
-
throw new
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
316
|
+
throw new TypeError('hex string expected, got ' + typeof hex);
|
|
317
|
+
if (hasHexBuiltin) {
|
|
318
|
+
try {
|
|
319
|
+
return Uint8Array.fromHex(hex);
|
|
320
|
+
}
|
|
321
|
+
catch (error) {
|
|
322
|
+
if (error instanceof SyntaxError)
|
|
323
|
+
throw new RangeError(error.message);
|
|
324
|
+
throw error;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
192
327
|
const hl = hex.length;
|
|
193
328
|
const al = hl / 2;
|
|
194
329
|
if (hl % 2)
|
|
195
|
-
throw new
|
|
330
|
+
throw new RangeError('hex string expected, got unpadded hex of length ' + hl);
|
|
196
331
|
const array = new Uint8Array(al);
|
|
197
332
|
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
|
|
198
333
|
const n1 = asciiToBase16(hex.charCodeAt(hi));
|
|
199
334
|
const n2 = asciiToBase16(hex.charCodeAt(hi + 1));
|
|
200
335
|
if (n1 === undefined || n2 === undefined) {
|
|
201
336
|
const char = hex[hi] + hex[hi + 1];
|
|
202
|
-
throw new
|
|
337
|
+
throw new RangeError('hex string expected, got non-hex character "' + char + '" at index ' + hi);
|
|
203
338
|
}
|
|
204
339
|
array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163
|
|
205
340
|
}
|
|
206
341
|
return array;
|
|
207
342
|
}
|
|
208
|
-
/**
|
|
343
|
+
/**
|
|
344
|
+
* Creates a callable hash function from a stateful class constructor.
|
|
345
|
+
* @param hashCons - hash constructor or factory
|
|
346
|
+
* @param info - optional metadata such as DER OID
|
|
347
|
+
* @returns Frozen callable hash wrapper with `.create()`.
|
|
348
|
+
* Wrapper construction eagerly calls `hashCons(undefined)` once to read
|
|
349
|
+
* `outputLen` / `blockLen`, so constructor side effects happen at module
|
|
350
|
+
* init time.
|
|
351
|
+
* @example
|
|
352
|
+
* Wrap a stateful hash constructor into a callable helper.
|
|
353
|
+
* ```ts
|
|
354
|
+
* import { createHasher } from '@noble/hashes/utils.js';
|
|
355
|
+
* import { sha256 } from '@noble/hashes/sha2.js';
|
|
356
|
+
* const wrapped = createHasher(sha256.create, { oid: sha256.oid });
|
|
357
|
+
* wrapped(new Uint8Array([1]));
|
|
358
|
+
* ```
|
|
359
|
+
*/
|
|
209
360
|
function createHasher(hashCons, info = {}) {
|
|
210
|
-
const hashC = (msg, opts) => hashCons(opts)
|
|
361
|
+
const hashC = (msg, opts) => hashCons(opts)
|
|
362
|
+
.update(msg)
|
|
363
|
+
.digest();
|
|
211
364
|
const tmp = hashCons(undefined);
|
|
212
365
|
hashC.outputLen = tmp.outputLen;
|
|
213
366
|
hashC.blockLen = tmp.blockLen;
|
|
367
|
+
hashC.canXOF = tmp.canXOF;
|
|
214
368
|
hashC.create = (opts) => hashCons(opts);
|
|
215
369
|
Object.assign(hashC, info);
|
|
216
370
|
return Object.freeze(hashC);
|
|
217
371
|
}
|
|
218
|
-
/**
|
|
372
|
+
/**
|
|
373
|
+
* Creates OID metadata for NIST hashes with prefix `06 09 60 86 48 01 65 03 04 02`.
|
|
374
|
+
* @param suffix - final OID byte for the selected hash.
|
|
375
|
+
* The helper accepts any byte even though only the documented NIST hash
|
|
376
|
+
* suffixes are meaningful downstream.
|
|
377
|
+
* @returns Object containing the DER-encoded OID.
|
|
378
|
+
* @example
|
|
379
|
+
* Build OID metadata for a NIST hash.
|
|
380
|
+
* ```ts
|
|
381
|
+
* oidNist(0x01);
|
|
382
|
+
* ```
|
|
383
|
+
*/
|
|
219
384
|
const oidNist = (suffix) => ({
|
|
385
|
+
// Current NIST hashAlgs suffixes used here fit in one DER subidentifier octet.
|
|
386
|
+
// Larger suffix values would need base-128 OID encoding and a different length byte.
|
|
220
387
|
oid: Uint8Array.from([0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, suffix]),
|
|
221
388
|
});
|
|
222
389
|
|
|
@@ -224,9 +391,11 @@ const oidNist = (suffix) => ({
|
|
|
224
391
|
* SHA3 (keccak) hash function, based on a new "Sponge function" design.
|
|
225
392
|
* Different from older hashes, the internal state is bigger than output size.
|
|
226
393
|
*
|
|
227
|
-
* Check out
|
|
228
|
-
*
|
|
229
|
-
*
|
|
394
|
+
* Check out
|
|
395
|
+
* {@link https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf | FIPS-202},
|
|
396
|
+
* {@link https://keccak.team/keccak.html | Website}, and
|
|
397
|
+
* {@link https://crypto.stackexchange.com/q/15727 | the differences between
|
|
398
|
+
* SHA-3 and Keccak}.
|
|
230
399
|
*
|
|
231
400
|
* Check out `sha3-addons` module for cSHAKE, k12, and others.
|
|
232
401
|
* @module
|
|
@@ -239,6 +408,8 @@ const _1n = BigInt(1);
|
|
|
239
408
|
const _2n = BigInt(2);
|
|
240
409
|
const _7n = BigInt(7);
|
|
241
410
|
const _256n = BigInt(256);
|
|
411
|
+
// FIPS 202 Algorithm 5 rc(): when the outgoing bit is 1, the 8-bit LFSR xors
|
|
412
|
+
// taps 0, 4, 5, and 6, which compresses to the feedback mask `0x71`.
|
|
242
413
|
const _0x71n = BigInt(0x71);
|
|
243
414
|
const SHA3_PI = [];
|
|
244
415
|
const SHA3_ROTL = [];
|
|
@@ -259,13 +430,31 @@ for (let round = 0, R = _1n, x = 1, y = 0; round < 24; round++) {
|
|
|
259
430
|
_SHA3_IOTA.push(t);
|
|
260
431
|
}
|
|
261
432
|
const IOTAS = split(_SHA3_IOTA, true);
|
|
433
|
+
// `split(..., true)` keeps the local little-endian lane-word layout used by
|
|
434
|
+
// `state32`, so these `H` / `L` tables follow the file's first-word /
|
|
435
|
+
// second-word lane slots rather than `_u64.ts`'s usual high/low naming.
|
|
262
436
|
const SHA3_IOTA_H = IOTAS[0];
|
|
263
437
|
const SHA3_IOTA_L = IOTAS[1];
|
|
264
438
|
// Left rotation (without 0, 32, 64)
|
|
265
439
|
const rotlH = (h, l, s) => (s > 32 ? rotlBH(h, l, s) : rotlSH(h, l, s));
|
|
266
440
|
const rotlL = (h, l, s) => (s > 32 ? rotlBL(h, l, s) : rotlSL(h, l, s));
|
|
267
|
-
/**
|
|
441
|
+
/**
|
|
442
|
+
* `keccakf1600` internal permutation, additionally allows adjusting the round count.
|
|
443
|
+
* @param s - 5x5 Keccak state encoded as 25 lanes split into 50 uint32 words
|
|
444
|
+
* in this file's local little-endian lane-word order
|
|
445
|
+
* @param rounds - number of rounds to execute
|
|
446
|
+
* @throws If `rounds` is outside the supported `1..24` range. {@link Error}
|
|
447
|
+
* @example
|
|
448
|
+
* Permute a Keccak state with the default 24 rounds.
|
|
449
|
+
* ```ts
|
|
450
|
+
* keccakP(new Uint32Array(50));
|
|
451
|
+
* ```
|
|
452
|
+
*/
|
|
268
453
|
function keccakP(s, rounds = 24) {
|
|
454
|
+
anumber(rounds, 'rounds');
|
|
455
|
+
// This implementation precomputes only the standard Keccak-f[1600] 24-round Iota table.
|
|
456
|
+
if (rounds < 1 || rounds > 24)
|
|
457
|
+
throw new Error('"rounds" expected integer 1..24');
|
|
269
458
|
const B = new Uint32Array(5 * 2);
|
|
270
459
|
// NOTE: all indices are x2 since we store state as u32 instead of u64 (bigints to slow in js)
|
|
271
460
|
for (let round = 24 - rounds; round < 24; round++) {
|
|
@@ -298,11 +487,21 @@ function keccakP(s, rounds = 24) {
|
|
|
298
487
|
s[PI + 1] = Tl;
|
|
299
488
|
}
|
|
300
489
|
// Chi (χ)
|
|
490
|
+
// Same as:
|
|
491
|
+
// for (let x = 0; x < 10; x++) B[x] = s[y + x];
|
|
492
|
+
// for (let x = 0; x < 10; x++) s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10];
|
|
301
493
|
for (let y = 0; y < 50; y += 10) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
494
|
+
const b0 = s[y], b1 = s[y + 1], b2 = s[y + 2], b3 = s[y + 3];
|
|
495
|
+
s[y] ^= ~s[y + 2] & s[y + 4];
|
|
496
|
+
s[y + 1] ^= ~s[y + 3] & s[y + 5];
|
|
497
|
+
s[y + 2] ^= ~s[y + 4] & s[y + 6];
|
|
498
|
+
s[y + 3] ^= ~s[y + 5] & s[y + 7];
|
|
499
|
+
s[y + 4] ^= ~s[y + 6] & s[y + 8];
|
|
500
|
+
s[y + 5] ^= ~s[y + 7] & s[y + 9];
|
|
501
|
+
s[y + 6] ^= ~s[y + 8] & b0;
|
|
502
|
+
s[y + 7] ^= ~s[y + 9] & b1;
|
|
503
|
+
s[y + 8] ^= ~b0 & b2;
|
|
504
|
+
s[y + 9] ^= ~b1 & b3;
|
|
306
505
|
}
|
|
307
506
|
// Iota (ι)
|
|
308
507
|
s[0] ^= SHA3_IOTA_H[round];
|
|
@@ -310,7 +509,23 @@ function keccakP(s, rounds = 24) {
|
|
|
310
509
|
}
|
|
311
510
|
clean(B);
|
|
312
511
|
}
|
|
313
|
-
/**
|
|
512
|
+
/**
|
|
513
|
+
* Keccak sponge function.
|
|
514
|
+
* @param blockLen - absorb/squeeze rate in bytes
|
|
515
|
+
* @param suffix - domain separation suffix byte
|
|
516
|
+
* @param outputLen - default digest length in bytes. This base sponge only
|
|
517
|
+
* requires a non-negative integer; wrappers that need positive output
|
|
518
|
+
* lengths must enforce that themselves.
|
|
519
|
+
* @param enableXOF - whether XOF output is allowed
|
|
520
|
+
* @param rounds - number of Keccak-f rounds
|
|
521
|
+
* @example
|
|
522
|
+
* Build a sponge state, absorb bytes, then finalize a digest.
|
|
523
|
+
* ```ts
|
|
524
|
+
* const hash = new Keccak(136, 0x06, 32);
|
|
525
|
+
* hash.update(new Uint8Array([1, 2, 3]));
|
|
526
|
+
* hash.digest();
|
|
527
|
+
* ```
|
|
528
|
+
*/
|
|
314
529
|
class Keccak {
|
|
315
530
|
state;
|
|
316
531
|
pos = 0;
|
|
@@ -321,6 +536,7 @@ class Keccak {
|
|
|
321
536
|
blockLen;
|
|
322
537
|
suffix;
|
|
323
538
|
outputLen;
|
|
539
|
+
canXOF;
|
|
324
540
|
enableXOF = false;
|
|
325
541
|
rounds;
|
|
326
542
|
// NOTE: we accept arguments in bytes instead of bits here.
|
|
@@ -329,6 +545,7 @@ class Keccak {
|
|
|
329
545
|
this.suffix = suffix;
|
|
330
546
|
this.outputLen = outputLen;
|
|
331
547
|
this.enableXOF = enableXOF;
|
|
548
|
+
this.canXOF = enableXOF;
|
|
332
549
|
this.rounds = rounds;
|
|
333
550
|
// Can be passed from user as dkLen
|
|
334
551
|
anumber(outputLen, 'outputLen');
|
|
@@ -368,8 +585,13 @@ class Keccak {
|
|
|
368
585
|
return;
|
|
369
586
|
this.finished = true;
|
|
370
587
|
const { state, suffix, pos, blockLen } = this;
|
|
371
|
-
//
|
|
588
|
+
// FIPS 202 appends the SHA3/SHAKE domain-separation suffix before pad10*1.
|
|
589
|
+
// These byte values already include the first padding bit, while the
|
|
590
|
+
// final `0x80` below supplies the closing `1` bit in the last rate byte.
|
|
372
591
|
state[pos] ^= suffix;
|
|
592
|
+
// If that combined suffix lands in the last rate byte and already sets
|
|
593
|
+
// bit 7, absorb it first so the final pad10*1 bit can be xored into a
|
|
594
|
+
// fresh block.
|
|
373
595
|
if ((suffix & 0x80) !== 0 && pos === blockLen - 1)
|
|
374
596
|
this.keccak();
|
|
375
597
|
state[blockLen - 1] ^= 0x80;
|
|
@@ -392,7 +614,9 @@ class Keccak {
|
|
|
392
614
|
return out;
|
|
393
615
|
}
|
|
394
616
|
xofInto(out) {
|
|
395
|
-
//
|
|
617
|
+
// Plain SHA3/Keccak usage with XOF is probably a mistake, but this base
|
|
618
|
+
// class is also reused by SHAKE/cSHAKE/KMAC/TupleHash/ParallelHash/
|
|
619
|
+
// TurboSHAKE/KangarooTwelve wrappers that intentionally enable XOF.
|
|
396
620
|
if (!this.enableXOF)
|
|
397
621
|
throw new Error('XOF is not possible for this instance');
|
|
398
622
|
return this.writeInto(out);
|
|
@@ -405,12 +629,14 @@ class Keccak {
|
|
|
405
629
|
aoutput(out, this);
|
|
406
630
|
if (this.finished)
|
|
407
631
|
throw new Error('digest() was already called');
|
|
408
|
-
|
|
632
|
+
// `aoutput(...)` allows oversized buffers; digestInto() must fill only the advertised digest.
|
|
633
|
+
this.writeInto(out.subarray(0, this.outputLen));
|
|
409
634
|
this.destroy();
|
|
410
|
-
return out;
|
|
411
635
|
}
|
|
412
636
|
digest() {
|
|
413
|
-
|
|
637
|
+
const out = new Uint8Array(this.outputLen);
|
|
638
|
+
this.digestInto(out);
|
|
639
|
+
return out;
|
|
414
640
|
}
|
|
415
641
|
destroy() {
|
|
416
642
|
this.destroyed = true;
|
|
@@ -419,6 +645,9 @@ class Keccak {
|
|
|
419
645
|
_cloneInto(to) {
|
|
420
646
|
const { blockLen, suffix, outputLen, rounds, enableXOF } = this;
|
|
421
647
|
to ||= new Keccak(blockLen, suffix, outputLen, enableXOF, rounds);
|
|
648
|
+
// Reused destinations can come from a different rate/capacity variant, so clone must rewrite
|
|
649
|
+
// the sponge geometry as well as the state words.
|
|
650
|
+
to.blockLen = blockLen;
|
|
422
651
|
to.state32.set(this.state32);
|
|
423
652
|
to.pos = this.pos;
|
|
424
653
|
to.posOut = this.posOut;
|
|
@@ -428,16 +657,39 @@ class Keccak {
|
|
|
428
657
|
to.suffix = suffix;
|
|
429
658
|
to.outputLen = outputLen;
|
|
430
659
|
to.enableXOF = enableXOF;
|
|
660
|
+
// Clones must preserve the public capability bit too; `_KMAC` reuses this path and deep clone
|
|
661
|
+
// tests compare instance fields directly, so leaving `canXOF` behind makes the clone lie.
|
|
662
|
+
to.canXOF = this.canXOF;
|
|
431
663
|
to.destroyed = this.destroyed;
|
|
432
664
|
return to;
|
|
433
665
|
}
|
|
434
666
|
}
|
|
435
667
|
const genShake = (suffix, blockLen, outputLen, info = {}) => createHasher((opts = {}) => new Keccak(blockLen, suffix, opts.dkLen === undefined ? outputLen : opts.dkLen, true), info);
|
|
436
|
-
/**
|
|
668
|
+
/**
|
|
669
|
+
* SHAKE128 XOF with 128-bit security and a 16-byte default output.
|
|
670
|
+
* @param msg - message bytes to hash
|
|
671
|
+
* @param opts - Optional output-length override. See {@link ShakeOpts}.
|
|
672
|
+
* @returns Digest bytes.
|
|
673
|
+
* @example
|
|
674
|
+
* Hash a message with SHAKE128.
|
|
675
|
+
* ```ts
|
|
676
|
+
* shake128(new Uint8Array([97, 98, 99]), { dkLen: 32 });
|
|
677
|
+
* ```
|
|
678
|
+
*/
|
|
437
679
|
const shake128 =
|
|
438
680
|
/* @__PURE__ */
|
|
439
681
|
genShake(0x1f, 168, 16, /* @__PURE__ */ oidNist(0x0b));
|
|
440
|
-
/**
|
|
682
|
+
/**
|
|
683
|
+
* SHAKE256 XOF with 256-bit security and a 32-byte default output.
|
|
684
|
+
* @param msg - message bytes to hash
|
|
685
|
+
* @param opts - Optional output-length override. See {@link ShakeOpts}.
|
|
686
|
+
* @returns Digest bytes.
|
|
687
|
+
* @example
|
|
688
|
+
* Hash a message with SHAKE256.
|
|
689
|
+
* ```ts
|
|
690
|
+
* shake256(new Uint8Array([97, 98, 99]), { dkLen: 64 });
|
|
691
|
+
* ```
|
|
692
|
+
*/
|
|
441
693
|
const shake256 =
|
|
442
694
|
/* @__PURE__ */
|
|
443
695
|
genShake(0x1f, 136, 32, /* @__PURE__ */ oidNist(0x0c));
|
|
@@ -1800,6 +2052,31 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
|
|
|
1800
2052
|
}
|
|
1801
2053
|
}
|
|
1802
2054
|
|
|
2055
|
+
/**
|
|
2056
|
+
* Create a **deterministic** Dilithium5 detached signature
|
|
2057
|
+
* (`randomizedSigning = false`).
|
|
2058
|
+
*
|
|
2059
|
+
* Convenience wrapper that hard-wires the deterministic mode so callers
|
|
2060
|
+
* who *need* byte-identical signatures for the same `(sk, message)`
|
|
2061
|
+
* — KAT vector reproduction, deterministic-test fixtures, RANDAO-style
|
|
2062
|
+
* protocols — get a clearly-named entry point rather than passing a
|
|
2063
|
+
* bare boolean.
|
|
2064
|
+
*
|
|
2065
|
+
* **Use only when the deterministic property is itself a requirement.**
|
|
2066
|
+
* For general-purpose signing prefer [cryptoSignSignature] with
|
|
2067
|
+
* `randomizedSigning = true` (hedged signing — TOB-QRLLIB-6 audit
|
|
2068
|
+
* recommendation for parity with the lattice-scheme guidance applied
|
|
2069
|
+
* to the Go and Rust ports).
|
|
2070
|
+
*
|
|
2071
|
+
* @param {Uint8Array} sig - Output buffer for signature (must be at least CryptoBytes bytes)
|
|
2072
|
+
* @param {string|Uint8Array} m - Message to sign
|
|
2073
|
+
* @param {Uint8Array} sk - Secret key (must be CryptoSecretKeyBytes bytes)
|
|
2074
|
+
* @returns {number} 0 on success
|
|
2075
|
+
*/
|
|
2076
|
+
function cryptoSignSignatureDeterministic(sig, m, sk) {
|
|
2077
|
+
return cryptoSignSignature(sig, m, sk, /* randomizedSigning */ false);
|
|
2078
|
+
}
|
|
2079
|
+
|
|
1803
2080
|
/**
|
|
1804
2081
|
* Sign a message, returning signature concatenated with message.
|
|
1805
2082
|
*
|
|
@@ -1835,6 +2112,24 @@ function cryptoSign(msg, sk, randomizedSigning) {
|
|
|
1835
2112
|
return sm;
|
|
1836
2113
|
}
|
|
1837
2114
|
|
|
2115
|
+
/**
|
|
2116
|
+
* Attached-form **deterministic** Dilithium5 signing
|
|
2117
|
+
* (`randomizedSigning = false`).
|
|
2118
|
+
*
|
|
2119
|
+
* Convenience wrapper that hard-wires the deterministic mode for the
|
|
2120
|
+
* attached `signature || message` form. Same recommendation as
|
|
2121
|
+
* [cryptoSignSignatureDeterministic]: use only when determinism is a
|
|
2122
|
+
* protocol requirement; for general-purpose signing prefer
|
|
2123
|
+
* [cryptoSign] with `randomizedSigning = true` (hedged — TOB-QRLLIB-6).
|
|
2124
|
+
*
|
|
2125
|
+
* @param {string|Uint8Array} msg - Message to sign
|
|
2126
|
+
* @param {Uint8Array} sk - Secret key
|
|
2127
|
+
* @returns {Uint8Array} Signed message (signature || message)
|
|
2128
|
+
*/
|
|
2129
|
+
function cryptoSignDeterministic(msg, sk) {
|
|
2130
|
+
return cryptoSign(msg, sk, /* randomizedSigning */ false);
|
|
2131
|
+
}
|
|
2132
|
+
|
|
1838
2133
|
/**
|
|
1839
2134
|
* Verify a detached signature.
|
|
1840
2135
|
*
|
|
@@ -1943,7 +2238,12 @@ function cryptoSignVerify(sig, m, pk) {
|
|
|
1943
2238
|
* }
|
|
1944
2239
|
*/
|
|
1945
2240
|
function cryptoSignOpen(sm, pk) {
|
|
1946
|
-
|
|
2241
|
+
// Type-guard `sm` so callers passing `null` / `undefined` / non-Uint8Array
|
|
2242
|
+
// get a clean `undefined` return rather than a `Cannot read properties of
|
|
2243
|
+
// null (reading 'length')` thrown deep in the call chain. Mirrors the
|
|
2244
|
+
// existing `pk` / `sig` instanceof checks in `cryptoSignVerify`.
|
|
2245
|
+
// (TOB-QRLLIB-11.)
|
|
2246
|
+
if (!(sm instanceof Uint8Array) || sm.length < CryptoBytes) {
|
|
1947
2247
|
return undefined;
|
|
1948
2248
|
}
|
|
1949
2249
|
|
|
@@ -1956,6 +2256,37 @@ function cryptoSignOpen(sm, pk) {
|
|
|
1956
2256
|
return msg;
|
|
1957
2257
|
}
|
|
1958
2258
|
|
|
2259
|
+
/**
|
|
2260
|
+
* Open a signed message with a typed failure-mode report.
|
|
2261
|
+
*
|
|
2262
|
+
* Behavioural twin of [cryptoSignOpen], but returns a discriminated
|
|
2263
|
+
* union so callers can distinguish between API-shape problems (input
|
|
2264
|
+
* was the wrong type / length / shape) and genuine cryptographic
|
|
2265
|
+
* verification failures. See the ML-DSA-87 sibling for the rationale
|
|
2266
|
+
* (TOB-QRLLIB-14).
|
|
2267
|
+
*
|
|
2268
|
+
* @param {Uint8Array} sm Signed message (signature || message).
|
|
2269
|
+
* @param {Uint8Array} pk Public key.
|
|
2270
|
+
* @returns {{ok: true, message: Uint8Array} | {ok: false, reason: 'invalid-sm-type'|'invalid-sm-length'|'invalid-pk'|'verification-failed'}}
|
|
2271
|
+
*/
|
|
2272
|
+
function cryptoSignOpenWithReason(sm, pk) {
|
|
2273
|
+
if (!(sm instanceof Uint8Array)) {
|
|
2274
|
+
return { ok: false, reason: 'invalid-sm-type' };
|
|
2275
|
+
}
|
|
2276
|
+
if (sm.length < CryptoBytes) {
|
|
2277
|
+
return { ok: false, reason: 'invalid-sm-length' };
|
|
2278
|
+
}
|
|
2279
|
+
if (!(pk instanceof Uint8Array) || pk.length !== CryptoPublicKeyBytes) {
|
|
2280
|
+
return { ok: false, reason: 'invalid-pk' };
|
|
2281
|
+
}
|
|
2282
|
+
const sig = sm.slice(0, CryptoBytes);
|
|
2283
|
+
const msg = sm.slice(CryptoBytes);
|
|
2284
|
+
if (!cryptoSignVerify(sig, msg, pk)) {
|
|
2285
|
+
return { ok: false, reason: 'verification-failed' };
|
|
2286
|
+
}
|
|
2287
|
+
return { ok: true, message: msg };
|
|
2288
|
+
}
|
|
2289
|
+
|
|
1959
2290
|
exports.BETA = BETA;
|
|
1960
2291
|
exports.CRHBytes = CRHBytes;
|
|
1961
2292
|
exports.CryptoBytes = CryptoBytes;
|
|
@@ -1993,9 +2324,12 @@ exports.TAU = TAU;
|
|
|
1993
2324
|
exports.TRBytes = TRBytes;
|
|
1994
2325
|
exports.cAddQ = cAddQ;
|
|
1995
2326
|
exports.cryptoSign = cryptoSign;
|
|
2327
|
+
exports.cryptoSignDeterministic = cryptoSignDeterministic;
|
|
1996
2328
|
exports.cryptoSignKeypair = cryptoSignKeypair;
|
|
1997
2329
|
exports.cryptoSignOpen = cryptoSignOpen;
|
|
2330
|
+
exports.cryptoSignOpenWithReason = cryptoSignOpenWithReason;
|
|
1998
2331
|
exports.cryptoSignSignature = cryptoSignSignature;
|
|
2332
|
+
exports.cryptoSignSignatureDeterministic = cryptoSignSignatureDeterministic;
|
|
1999
2333
|
exports.cryptoSignVerify = cryptoSignVerify;
|
|
2000
2334
|
exports.decompose = decompose;
|
|
2001
2335
|
exports.dilithiumShake128StreamInit = dilithiumShake128StreamInit;
|
|
@@ -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
|
/**
|
package/dist/mjs/dilithium5.js
CHANGED
|
@@ -1421,6 +1421,31 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
|
|
|
1421
1421
|
}
|
|
1422
1422
|
}
|
|
1423
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
|
+
|
|
1424
1449
|
/**
|
|
1425
1450
|
* Sign a message, returning signature concatenated with message.
|
|
1426
1451
|
*
|
|
@@ -1456,6 +1481,24 @@ function cryptoSign(msg, sk, randomizedSigning) {
|
|
|
1456
1481
|
return sm;
|
|
1457
1482
|
}
|
|
1458
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
|
+
|
|
1459
1502
|
/**
|
|
1460
1503
|
* Verify a detached signature.
|
|
1461
1504
|
*
|
|
@@ -1564,7 +1607,12 @@ function cryptoSignVerify(sig, m, pk) {
|
|
|
1564
1607
|
* }
|
|
1565
1608
|
*/
|
|
1566
1609
|
function cryptoSignOpen(sm, pk) {
|
|
1567
|
-
|
|
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) {
|
|
1568
1616
|
return undefined;
|
|
1569
1617
|
}
|
|
1570
1618
|
|
|
@@ -1577,4 +1625,35 @@ function cryptoSignOpen(sm, pk) {
|
|
|
1577
1625
|
return msg;
|
|
1578
1626
|
}
|
|
1579
1627
|
|
|
1580
|
-
|
|
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
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Dilithium-5 cryptography",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dilithium",
|
|
@@ -63,24 +63,24 @@
|
|
|
63
63
|
"@rollup/plugin-node-resolve": "16.0.3",
|
|
64
64
|
"c8": "11.0.0",
|
|
65
65
|
"chai": "6.2.2",
|
|
66
|
-
"eslint": "10.0
|
|
66
|
+
"eslint": "10.3.0",
|
|
67
67
|
"eslint-config-prettier": "10.1.8",
|
|
68
68
|
"eslint-plugin-import-x": "4.16.2",
|
|
69
69
|
"eslint-plugin-prettier": "5.5.5",
|
|
70
|
-
"globals": "17.
|
|
71
|
-
"minimatch": "10.2.
|
|
70
|
+
"globals": "17.6.0",
|
|
71
|
+
"minimatch": "10.2.5",
|
|
72
72
|
"mocha": "11.7.5",
|
|
73
|
-
"prettier": "3.8.
|
|
74
|
-
"rollup": "4.
|
|
75
|
-
"serialize-javascript": "7.0.
|
|
76
|
-
"tar": "7.5.
|
|
73
|
+
"prettier": "3.8.3",
|
|
74
|
+
"rollup": "4.60.3",
|
|
75
|
+
"serialize-javascript": "7.0.5",
|
|
76
|
+
"tar": "7.5.14"
|
|
77
77
|
},
|
|
78
78
|
"dependencies": {
|
|
79
|
-
"@noble/hashes": "2.0
|
|
79
|
+
"@noble/hashes": "2.2.0"
|
|
80
80
|
},
|
|
81
81
|
"overrides": {
|
|
82
82
|
"diff": "8.0.3",
|
|
83
|
-
"minimatch": "10.2.
|
|
83
|
+
"minimatch": "10.2.5"
|
|
84
84
|
},
|
|
85
85
|
"c8": {
|
|
86
86
|
"include": [
|
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
|
/**
|