@noble/post-quantum 0.5.4 → 0.6.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/src/utils.ts CHANGED
@@ -9,87 +9,417 @@ import {
9
9
  abytes,
10
10
  abytes as abytes_,
11
11
  concatBytes,
12
- isBytes,
12
+ isLE,
13
13
  randomBytes as randb,
14
14
  } from '@noble/hashes/utils.js';
15
- export { abytes } from '@noble/hashes/utils.js';
16
- export { concatBytes };
15
+ /**
16
+ * Bytes API type helpers for old + new TypeScript.
17
+ *
18
+ * TS 5.6 has `Uint8Array`, while TS 5.9+ made it generic `Uint8Array<ArrayBuffer>`.
19
+ * We can't use specific return type, because TS 5.6 will error.
20
+ * We can't use generic return type, because most TS 5.9 software will expect specific type.
21
+ *
22
+ * Maps typed-array input leaves to broad forms.
23
+ * These are compatibility adapters, not ownership guarantees.
24
+ *
25
+ * - `TArg` keeps byte inputs broad.
26
+ * - `TRet` marks byte outputs for TS 5.6 and TS 5.9+ compatibility.
27
+ */
28
+ export type TypedArg<T> = T extends BigInt64Array
29
+ ? BigInt64Array
30
+ : T extends BigUint64Array
31
+ ? BigUint64Array
32
+ : T extends Float32Array
33
+ ? Float32Array
34
+ : T extends Float64Array
35
+ ? Float64Array
36
+ : T extends Int16Array
37
+ ? Int16Array
38
+ : T extends Int32Array
39
+ ? Int32Array
40
+ : T extends Int8Array
41
+ ? Int8Array
42
+ : T extends Uint16Array
43
+ ? Uint16Array
44
+ : T extends Uint32Array
45
+ ? Uint32Array
46
+ : T extends Uint8ClampedArray
47
+ ? Uint8ClampedArray
48
+ : T extends Uint8Array
49
+ ? Uint8Array
50
+ : never;
51
+ /** Maps typed-array output leaves to narrow TS-compatible forms. */
52
+ export type TypedRet<T> = T extends BigInt64Array
53
+ ? ReturnType<typeof BigInt64Array.of>
54
+ : T extends BigUint64Array
55
+ ? ReturnType<typeof BigUint64Array.of>
56
+ : T extends Float32Array
57
+ ? ReturnType<typeof Float32Array.of>
58
+ : T extends Float64Array
59
+ ? ReturnType<typeof Float64Array.of>
60
+ : T extends Int16Array
61
+ ? ReturnType<typeof Int16Array.of>
62
+ : T extends Int32Array
63
+ ? ReturnType<typeof Int32Array.of>
64
+ : T extends Int8Array
65
+ ? ReturnType<typeof Int8Array.of>
66
+ : T extends Uint16Array
67
+ ? ReturnType<typeof Uint16Array.of>
68
+ : T extends Uint32Array
69
+ ? ReturnType<typeof Uint32Array.of>
70
+ : T extends Uint8ClampedArray
71
+ ? ReturnType<typeof Uint8ClampedArray.of>
72
+ : T extends Uint8Array
73
+ ? ReturnType<typeof Uint8Array.of>
74
+ : never;
75
+ /** Recursively adapts byte-carrying API input types. See {@link TypedArg}. */
76
+ export type TArg<T> =
77
+ | T
78
+ | ([TypedArg<T>] extends [never]
79
+ ? T extends (...args: infer A) => infer R
80
+ ? ((...args: { [K in keyof A]: TRet<A[K]> }) => TArg<R>) & {
81
+ [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TArg<T[K]>;
82
+ }
83
+ : T extends [infer A, ...infer R]
84
+ ? [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]
85
+ : T extends readonly [infer A, ...infer R]
86
+ ? readonly [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]
87
+ : T extends (infer A)[]
88
+ ? TArg<A>[]
89
+ : T extends readonly (infer A)[]
90
+ ? readonly TArg<A>[]
91
+ : T extends Promise<infer A>
92
+ ? Promise<TArg<A>>
93
+ : T extends object
94
+ ? { [K in keyof T]: TArg<T[K]> }
95
+ : T
96
+ : TypedArg<T>);
97
+ /** Recursively adapts byte-carrying API output types. See {@link TypedArg}. */
98
+ export type TRet<T> = T extends unknown
99
+ ? T &
100
+ ([TypedRet<T>] extends [never]
101
+ ? T extends (...args: infer A) => infer R
102
+ ? ((...args: { [K in keyof A]: TArg<A[K]> }) => TRet<R>) & {
103
+ [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TRet<T[K]>;
104
+ }
105
+ : T extends [infer A, ...infer R]
106
+ ? [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]
107
+ : T extends readonly [infer A, ...infer R]
108
+ ? readonly [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]
109
+ : T extends (infer A)[]
110
+ ? TRet<A>[]
111
+ : T extends readonly (infer A)[]
112
+ ? readonly TRet<A>[]
113
+ : T extends Promise<infer A>
114
+ ? Promise<TRet<A>>
115
+ : T extends object
116
+ ? { [K in keyof T]: TRet<T[K]> }
117
+ : T
118
+ : TypedRet<T>)
119
+ : never;
120
+ /**
121
+ * Asserts that a value is a byte array and optionally checks its length.
122
+ * Returns the original reference unchanged on success, and currently also accepts Node `Buffer`
123
+ * values through the upstream validator.
124
+ * This helper throws on malformed input, so APIs that must return `false` need to guard lengths
125
+ * before decoding or before calling it.
126
+ * @example
127
+ * Validate that a value is a byte array with the expected length.
128
+ * ```ts
129
+ * abytes(new Uint8Array([1]), 1);
130
+ * ```
131
+ */
132
+ const abytesDoc: typeof abytes = abytes;
133
+ export { abytesDoc as abytes };
134
+ /**
135
+ * Concatenates byte arrays into a new `Uint8Array`.
136
+ * Zero arguments return an empty `Uint8Array`.
137
+ * Invalid segments throw before allocation because each argument is validated first.
138
+ * @example
139
+ * Concatenate two byte arrays into one result.
140
+ * ```ts
141
+ * concatBytes(new Uint8Array([1]), new Uint8Array([2]));
142
+ * ```
143
+ */
144
+ const concatBytesDoc: typeof concatBytes = concatBytes;
145
+ export { concatBytesDoc as concatBytes };
146
+ /**
147
+ * Returns cryptographically secure random bytes.
148
+ * Requires `globalThis.crypto.getRandomValues` and throws if that API is unavailable.
149
+ * `bytesLength` is validated by the upstream helper as a non-negative integer before allocation,
150
+ * so negative and fractional values both throw instead of truncating through JS `ToIndex`.
151
+ * @param bytesLength - Number of random bytes to generate.
152
+ * @returns Fresh random bytes.
153
+ * @example
154
+ * Generate a fresh random seed.
155
+ * ```ts
156
+ * const seed = randomBytes(4);
157
+ * ```
158
+ */
17
159
  export const randomBytes: typeof randb = randb;
18
160
 
19
- // Compares 2 u8a-s in kinda constant time
20
- export function equalBytes(a: Uint8Array, b: Uint8Array): boolean {
161
+ /**
162
+ * Compares two byte arrays in a length-constant way for equal lengths.
163
+ * Unequal lengths return `false` immediately, and there is no runtime type validation.
164
+ * @param a - First byte array.
165
+ * @param b - Second byte array.
166
+ * @returns Whether both arrays contain the same bytes.
167
+ * @example
168
+ * Compare two byte arrays for equality.
169
+ * ```ts
170
+ * equalBytes(new Uint8Array([1]), new Uint8Array([1]));
171
+ * ```
172
+ */
173
+ export function equalBytes(a: TArg<Uint8Array>, b: TArg<Uint8Array>): boolean {
21
174
  if (a.length !== b.length) return false;
22
175
  let diff = 0;
23
176
  for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
24
177
  return diff === 0;
25
178
  }
26
179
 
27
- // copy bytes to new u8a (aligned). Because Buffer.slice is broken.
28
- export function copyBytes(bytes: Uint8Array): Uint8Array {
29
- return Uint8Array.from(bytes);
180
+ /**
181
+ * Copies bytes into a fresh `Uint8Array`.
182
+ * Returns a detached plain `Uint8Array` after validating that the input is real bytes.
183
+ * @param bytes - Source bytes.
184
+ * @returns Copy of the input bytes.
185
+ * @example
186
+ * Copy bytes into a fresh array.
187
+ * ```ts
188
+ * copyBytes(new Uint8Array([1, 2]));
189
+ * ```
190
+ */
191
+ export function copyBytes(bytes: TArg<Uint8Array>): TRet<Uint8Array> {
192
+ // `Uint8Array.from(...)` would also accept arrays / other typed arrays. Keep this helper strict
193
+ // because callers use it at byte-validation boundaries before mutating the detached copy.
194
+ return Uint8Array.from(abytes(bytes)) as TRet<Uint8Array>;
195
+ }
196
+
197
+ /**
198
+ * Byte-swaps each 64-bit lane in place.
199
+ * Falcon's exact binary64 tables are stored as little-endian byte payloads, so BE runtimes need
200
+ * this boundary helper before aliasing them as host `Float64Array` lanes.
201
+ * @param arr - Byte buffer whose length is a multiple of 8.
202
+ * @returns The same buffer after in-place 64-bit lane byte swaps.
203
+ * @example
204
+ * Byte-swap one 64-bit lane in place.
205
+ * ```ts
206
+ * byteSwap64(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]));
207
+ * ```
208
+ */
209
+ export function byteSwap64<T extends ArrayBufferView>(arr: T): T {
210
+ const bytes = new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
211
+ for (let i = 0; i < bytes.length; i += 8) {
212
+ const a0 = bytes[i + 0];
213
+ const a1 = bytes[i + 1];
214
+ const a2 = bytes[i + 2];
215
+ const a3 = bytes[i + 3];
216
+ bytes[i + 0] = bytes[i + 7];
217
+ bytes[i + 1] = bytes[i + 6];
218
+ bytes[i + 2] = bytes[i + 5];
219
+ bytes[i + 3] = bytes[i + 4];
220
+ bytes[i + 4] = a3;
221
+ bytes[i + 5] = a2;
222
+ bytes[i + 6] = a1;
223
+ bytes[i + 7] = a0;
224
+ }
225
+ return arr;
30
226
  }
227
+ /**
228
+ * Byte-swaps 64-bit lanes on big-endian runtimes and returns the input unchanged on little-endian.
229
+ * This keeps Falcon's binary64 tables in canonical little-endian order before aliasing them as
230
+ * `Float64Array` lanes on the current host.
231
+ * @param arr - Buffer to pass through or swap in place.
232
+ * @returns The same buffer, normalized for Falcon's little-endian table layout.
233
+ * @example
234
+ * Normalize one host-endian buffer for Falcon's float tables.
235
+ * ```ts
236
+ * baswap64If(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]));
237
+ * ```
238
+ */
239
+ export const baswap64If: <T extends ArrayBufferView>(arr: T) => T = isLE
240
+ ? (arr) => arr
241
+ : byteSwap64;
31
242
 
243
+ /** Shared key-generation surface for signers and KEMs. */
32
244
  export type CryptoKeys = {
245
+ /** Optional metadata about the algorithm family or variant. */
33
246
  info?: { type?: string };
247
+ /** Public byte lengths for the exported key material. */
34
248
  lengths: { seed?: number; publicKey?: number; secretKey?: number };
35
- keygen: (seed?: Uint8Array) => { secretKey: Uint8Array; publicKey: Uint8Array };
36
- getPublicKey: (secretKey: Uint8Array) => Uint8Array;
249
+ /**
250
+ * Generate one secret/public keypair.
251
+ * @param seed - Optional seed bytes for deterministic key generation.
252
+ * @returns Fresh secret/public keypair.
253
+ */
254
+ keygen: (seed?: TArg<Uint8Array>) => {
255
+ secretKey: TRet<Uint8Array>;
256
+ publicKey: TRet<Uint8Array>;
257
+ };
258
+ /**
259
+ * Derive one public key from a secret key.
260
+ * @param secretKey - Secret key bytes.
261
+ * @returns Public key bytes.
262
+ */
263
+ getPublicKey: (secretKey: TArg<Uint8Array>) => TRet<Uint8Array>;
37
264
  };
38
265
 
266
+ /** Verification options shared by the signature APIs. */
39
267
  export type VerOpts = {
268
+ /** Optional application-defined context string. */
40
269
  context?: Uint8Array;
41
270
  };
271
+ /** Signing options shared by the signature APIs. */
42
272
  export type SigOpts = VerOpts & {
43
273
  // Compatibility with @noble/curves: false to disable, enabled by default, user can pass U8A
274
+ /** Optional extra entropy or `false` to disable randomized signing. */
44
275
  extraEntropy?: Uint8Array | false;
45
276
  };
46
277
 
278
+ /**
279
+ * Validates that an options bag is a plain object.
280
+ * @param opts - Options object to validate.
281
+ * @throws On wrong argument types. {@link TypeError}
282
+ * @example
283
+ * Validate that an options bag is a plain object.
284
+ * ```ts
285
+ * validateOpts({});
286
+ * ```
287
+ */
47
288
  export function validateOpts(opts: object): void {
48
- // We try to catch u8a, since it was previously valid argument at this position
49
- if (typeof opts !== 'object' || opts === null || isBytes(opts))
50
- throw new Error('expected opts to be an object');
289
+ // Arrays silently passed here before, but these call sites expect named option-bag fields.
290
+ if (Object.prototype.toString.call(opts) !== '[object Object]')
291
+ throw new TypeError('expected valid options object');
51
292
  }
52
293
 
53
- export function validateVerOpts(opts: VerOpts): void {
294
+ /**
295
+ * Validates common verification options.
296
+ * `context` itself is validated with `abytes(...)`, and individual algorithms may narrow support
297
+ * further after this shared plain-object gate.
298
+ * @param opts - Verification options. See {@link VerOpts}.
299
+ * @throws On wrong argument types. {@link TypeError}
300
+ * @example
301
+ * Validate common verification options.
302
+ * ```ts
303
+ * validateVerOpts({ context: new Uint8Array([1]) });
304
+ * ```
305
+ */
306
+ export function validateVerOpts(opts: TArg<VerOpts>): void {
54
307
  validateOpts(opts);
55
308
  if (opts.context !== undefined) abytes(opts.context, undefined, 'opts.context');
56
309
  }
57
310
 
58
- export function validateSigOpts(opts: SigOpts): void {
311
+ /**
312
+ * Validates common signing options.
313
+ * `extraEntropy` is validated with `abytes(...)`; exact lengths and extra algorithm-specific
314
+ * restrictions are enforced later by callers.
315
+ * @param opts - Signing options. See {@link SigOpts}.
316
+ * @throws On wrong argument types. {@link TypeError}
317
+ * @example
318
+ * Validate common signing options.
319
+ * ```ts
320
+ * validateSigOpts({ extraEntropy: new Uint8Array([1]) });
321
+ * ```
322
+ */
323
+ export function validateSigOpts(opts: TArg<SigOpts>): void {
59
324
  validateVerOpts(opts);
60
325
  if (opts.extraEntropy !== false && opts.extraEntropy !== undefined)
61
326
  abytes(opts.extraEntropy, undefined, 'opts.extraEntropy');
62
327
  }
63
328
 
64
- /** Generic interface for signatures. Has keygen, sign and verify. */
329
+ /** Generic signature interface with key generation, signing, and verification. */
65
330
  export type Signer = CryptoKeys & {
331
+ /** Public byte lengths for signatures and signing randomness. */
66
332
  lengths: { signRand?: number; signature?: number };
67
- sign: (msg: Uint8Array, secretKey: Uint8Array, opts?: SigOpts) => Uint8Array;
68
- verify: (sig: Uint8Array, msg: Uint8Array, publicKey: Uint8Array, opts?: VerOpts) => boolean;
333
+ /**
334
+ * Sign one message.
335
+ * @param msg - Message bytes to sign.
336
+ * @param secretKey - Secret key bytes.
337
+ * @param opts - Optional signing options.
338
+ * @returns Signature bytes.
339
+ */
340
+ sign: (
341
+ msg: TArg<Uint8Array>,
342
+ secretKey: TArg<Uint8Array>,
343
+ opts?: TArg<SigOpts>
344
+ ) => TRet<Uint8Array>;
345
+ /**
346
+ * Verify one signature.
347
+ * @param sig - Signature bytes.
348
+ * @param msg - Signed message bytes.
349
+ * @param publicKey - Public key bytes.
350
+ * @param opts - Optional verification options.
351
+ * @returns `true` when the signature is valid, `false` when all inputs are well-formed but the
352
+ * signature check does not pass. Some implementations also treat malformed signature encodings as
353
+ * a verification failure and return `false`.
354
+ * @throws On malformed API arguments or unsupported verification options.
355
+ */
356
+ verify: (
357
+ sig: TArg<Uint8Array>,
358
+ msg: TArg<Uint8Array>,
359
+ publicKey: TArg<Uint8Array>,
360
+ opts?: TArg<VerOpts>
361
+ ) => boolean;
69
362
  };
70
363
 
364
+ /** Generic key encapsulation mechanism interface. */
71
365
  export type KEM = CryptoKeys & {
366
+ /** Public byte lengths for ciphertexts and optional message randomness. */
72
367
  lengths: { cipherText?: number; msg?: number; msgRand?: number };
368
+ /**
369
+ * Encapsulate one shared secret to a recipient public key.
370
+ * @param publicKey - Recipient public key bytes.
371
+ * @param msg - Optional caller-provided randomness/message seed.
372
+ * @returns Ciphertext plus shared secret.
373
+ */
73
374
  encapsulate: (
74
- publicKey: Uint8Array,
75
- msg?: Uint8Array
375
+ publicKey: TArg<Uint8Array>,
376
+ msg?: TArg<Uint8Array>
76
377
  ) => {
77
- cipherText: Uint8Array;
78
- sharedSecret: Uint8Array;
378
+ cipherText: TRet<Uint8Array>;
379
+ sharedSecret: TRet<Uint8Array>;
79
380
  };
80
- decapsulate: (cipherText: Uint8Array, secretKey: Uint8Array) => Uint8Array;
381
+ /**
382
+ * Recover the shared secret from a ciphertext and recipient secret key.
383
+ * @param cipherText - Ciphertext bytes.
384
+ * @param secretKey - Recipient secret key bytes.
385
+ * @returns Decapsulated shared secret.
386
+ */
387
+ decapsulate: (cipherText: TArg<Uint8Array>, secretKey: TArg<Uint8Array>) => TRet<Uint8Array>;
81
388
  };
82
389
 
390
+ /** Bidirectional encoder/decoder interface. */
83
391
  export interface Coder<F, T> {
392
+ /**
393
+ * Serialize one value.
394
+ * @param from - Value to encode.
395
+ * @returns Encoded representation.
396
+ */
84
397
  encode(from: F): T;
398
+ /**
399
+ * Parse one serialized value.
400
+ * @param to - Encoded representation.
401
+ * @returns Decoded value.
402
+ */
85
403
  decode(to: T): F;
86
404
  }
87
405
 
406
+ /** Encoder/decoder interface specialized for byte arrays. */
88
407
  export interface BytesCoder<T> extends Coder<T, Uint8Array> {
408
+ /**
409
+ * Serialize one value into bytes.
410
+ * @param data - Value to encode.
411
+ * @returns Encoded bytes.
412
+ */
89
413
  encode: (data: T) => Uint8Array;
414
+ /**
415
+ * Parse one byte array into a value.
416
+ * @param bytes - Encoded bytes.
417
+ * @returns Decoded value.
418
+ */
90
419
  decode: (bytes: Uint8Array) => T;
91
420
  }
92
421
 
422
+ /** Fixed-length byte encoder/decoder. */
93
423
  export type BytesCoderLen<T> = BytesCoder<T> & { bytesLen: number };
94
424
 
95
425
  // nano-packed, because struct encoding is hard.
@@ -97,11 +427,27 @@ type UnCoder<T> = T extends BytesCoder<infer U> ? U : never;
97
427
  type SplitOut<T extends (number | BytesCoderLen<any>)[]> = {
98
428
  [K in keyof T]: T[K] extends number ? Uint8Array : UnCoder<T[K]>;
99
429
  };
430
+ /**
431
+ * Builds a fixed-layout coder from byte lengths and nested coders.
432
+ * Raw-length fields decode as zero-copy `subarray(...)` views, and nested coders may preserve that
433
+ * aliasing too. Nested coder `encode(...)` results are treated as owned scratch: `splitCoder`
434
+ * copies them into the output and then zeroizes them with `fill(0)`. If a nested encoder forwards
435
+ * caller-owned bytes, it must do so only after detaching them into a disposable copy.
436
+ * @param label - Label used in validation errors.
437
+ * @param lengths - Field lengths or nested coders.
438
+ * @returns Composite fixed-length coder.
439
+ * @example
440
+ * Build a fixed-layout coder from byte lengths and nested coders.
441
+ * ```ts
442
+ * splitCoder('demo', 1, 2).encode([new Uint8Array([1]), new Uint8Array([2, 3])]);
443
+ * ```
444
+ */
100
445
  export function splitCoder<T extends (number | BytesCoderLen<any>)[]>(
101
446
  label: string,
102
447
  ...lengths: T
103
- ): BytesCoder<SplitOut<T>> & { bytesLen: number } {
104
- const getLength = (c: number | BytesCoderLen<any>) => (typeof c === 'number' ? c : c.bytesLen);
448
+ ): TRet<BytesCoder<SplitOut<T>> & { bytesLen: number }> {
449
+ const getLength = (c: TArg<number | BytesCoderLen<any>>) =>
450
+ typeof c === 'number' ? c : (c as BytesCoderLen<any>).bytesLen;
105
451
  const bytesLen: number = lengths.reduce((sum: number, a) => sum + getLength(a), 0);
106
452
  return {
107
453
  bytesLen,
@@ -118,7 +464,7 @@ export function splitCoder<T extends (number | BytesCoderLen<any>)[]>(
118
464
  }
119
465
  return res;
120
466
  },
121
- decode: (buf: Uint8Array) => {
467
+ decode: (buf: TArg<Uint8Array>) => {
122
468
  abytes_(buf, bytesLen, label);
123
469
  const res = [];
124
470
  for (const c of lengths) {
@@ -132,33 +478,63 @@ export function splitCoder<T extends (number | BytesCoderLen<any>)[]>(
132
478
  } as any;
133
479
  }
134
480
  // nano-packed.array (fixed size)
135
- export function vecCoder<T>(c: BytesCoderLen<T>, vecLen: number): BytesCoderLen<T[]> {
136
- const bytesLen = vecLen * c.bytesLen;
481
+ /**
482
+ * Builds a fixed-length vector coder from another fixed-length coder.
483
+ * Element decoding receives `subarray(...)` views, so aliasing depends on the element coder.
484
+ * Element coder `encode(...)` results are treated as owned scratch: `vecCoder` copies them into
485
+ * the output and then zeroizes them with `fill(0)`. If an element encoder forwards caller-owned
486
+ * bytes, it must do so only after detaching them into a disposable copy. `vecCoder` also trusts
487
+ * the `BytesCoderLen` contract: each encoded element must already be exactly `c.bytesLen` bytes.
488
+ * @param c - Element coder.
489
+ * @param vecLen - Number of elements in the vector.
490
+ * @returns Fixed-length vector coder.
491
+ * @example
492
+ * Build a fixed-length vector coder from another fixed-length coder.
493
+ * ```ts
494
+ * vecCoder(
495
+ * { bytesLen: 1, encode: (n: number) => Uint8Array.of(n), decode: (b: Uint8Array) => b[0] || 0 },
496
+ * 2
497
+ * ).encode([1, 2]);
498
+ * ```
499
+ */
500
+ export function vecCoder<T>(c: TArg<BytesCoderLen<T>>, vecLen: number): TRet<BytesCoderLen<T[]>> {
501
+ const coder = c as BytesCoderLen<T>;
502
+ const bytesLen = vecLen * coder.bytesLen;
137
503
  return {
138
504
  bytesLen,
139
- encode: (u: T[]): Uint8Array => {
505
+ encode: (u: TArg<T[]>): TRet<Uint8Array> => {
140
506
  if (u.length !== vecLen)
141
- throw new Error(`vecCoder.encode: wrong length=${u.length}. Expected: ${vecLen}`);
507
+ throw new RangeError(`vecCoder.encode: wrong length=${u.length}. Expected: ${vecLen}`);
142
508
  const res = new Uint8Array(bytesLen);
143
509
  for (let i = 0, pos = 0; i < u.length; i++) {
144
- const b = c.encode(u[i]);
510
+ const b = coder.encode(u[i] as T);
145
511
  res.set(b, pos);
146
512
  b.fill(0); // clean
147
513
  pos += b.length;
148
514
  }
149
- return res;
515
+ return res as TRet<Uint8Array>;
150
516
  },
151
- decode: (a: Uint8Array): T[] => {
517
+ decode: (a: TArg<Uint8Array>): TRet<T[]> => {
152
518
  abytes_(a, bytesLen);
153
519
  const r: T[] = [];
154
- for (let i = 0; i < a.length; i += c.bytesLen)
155
- r.push(c.decode(a.subarray(i, i + c.bytesLen)));
156
- return r;
520
+ for (let i = 0; i < a.length; i += coder.bytesLen)
521
+ r.push(coder.decode(a.subarray(i, i + coder.bytesLen)));
522
+ return r as TRet<T[]>;
157
523
  },
158
- };
524
+ } as any;
159
525
  }
160
526
 
161
- // cleanBytes(Uint8Array.of(), [Uint16Array.of(), Uint32Array.of()])
527
+ /**
528
+ * Overwrites supported typed-array inputs with zeroes in place.
529
+ * Accepts direct typed arrays and one-level arrays of them.
530
+ * @param list - Typed arrays or one-level lists of typed arrays to clear.
531
+ * @example
532
+ * Overwrite typed arrays with zeroes.
533
+ * ```ts
534
+ * const buf = Uint8Array.of(1, 2, 3);
535
+ * cleanBytes(buf);
536
+ * ```
537
+ */
162
538
  export function cleanBytes(...list: (TypedArray | TypedArray[])[]): void {
163
539
  for (const t of list) {
164
540
  if (Array.isArray(t)) for (const b of t) b.fill(0);
@@ -166,25 +542,76 @@ export function cleanBytes(...list: (TypedArray | TypedArray[])[]): void {
166
542
  }
167
543
  }
168
544
 
545
+ /**
546
+ * Creates a 32-bit mask with the lowest `bits` bits set.
547
+ * @param bits - Number of low bits to keep.
548
+ * @returns Bit mask with `bits` ones.
549
+ * @throws On wrong argument ranges or values. {@link RangeError}
550
+ * @example
551
+ * Create a low-bit mask for packed-field operations.
552
+ * ```ts
553
+ * const mask = getMask(4);
554
+ * ```
555
+ */
169
556
  export function getMask(bits: number): number {
170
- return (1 << bits) - 1; // 4 -> 0b1111
557
+ if (!Number.isSafeInteger(bits) || bits < 0 || bits > 32)
558
+ throw new RangeError(`expected bits in [0..32], got ${bits}`);
559
+ // JS shifts are modulo 32, so bit 32 needs an explicit full-width mask.
560
+ return bits === 32 ? 0xffffffff : ~(-1 << bits) >>> 0;
171
561
  }
172
562
 
173
- export const EMPTY: Uint8Array = Uint8Array.of();
563
+ /** Shared empty byte array used as the default context. */
564
+ export const EMPTY: TRet<Uint8Array> = /* @__PURE__ */ Uint8Array.of();
174
565
 
175
- export function getMessage(msg: Uint8Array, ctx: Uint8Array = EMPTY): Uint8Array {
566
+ /**
567
+ * Builds the domain-separated message payload for the pure sign/verify paths.
568
+ * Context length `255` is valid; only `ctx.length > 255` is rejected.
569
+ * @param msg - Message bytes.
570
+ * @param ctx - Optional context bytes.
571
+ * @returns Domain-separated message payload.
572
+ * @throws On wrong argument ranges or values. {@link RangeError}
573
+ * @example
574
+ * Build the domain-separated payload before direct signing.
575
+ * ```ts
576
+ * const payload = getMessage(new Uint8Array([1, 2]));
577
+ * ```
578
+ */
579
+ export function getMessage(msg: TArg<Uint8Array>, ctx: TArg<Uint8Array> = EMPTY): TRet<Uint8Array> {
176
580
  abytes_(msg);
177
581
  abytes_(ctx);
178
- if (ctx.length > 255) throw new Error('context should be less than 255 bytes');
582
+ if (ctx.length > 255) throw new RangeError('context should be 255 bytes or less');
179
583
  return concatBytes(new Uint8Array([0, ctx.length]), ctx, msg);
180
584
  }
181
585
 
586
+ // DER tag+length plus the shared NIST hash OID arc 2.16.840.1.101.3.4.2.* used by the
587
+ // FIPS 204 / FIPS 205 pre-hash wrappers; the final byte selects SHA-256, SHA-512, SHAKE128,
588
+ // SHAKE256, or another approved hash/XOF under that subtree.
182
589
  // 06 09 60 86 48 01 65 03 04 02
183
590
  const oidNistP = /* @__PURE__ */ Uint8Array.from([6, 9, 0x60, 0x86, 0x48, 1, 0x65, 3, 4, 2]);
184
591
 
592
+ /**
593
+ * Validates that a hash exposes a NIST hash OID and enough collision resistance.
594
+ * Current accepted surface is broader than the FIPS algorithm tables: any hash/XOF under the NIST
595
+ * `2.16.840.1.101.3.4.2.*` subtree is accepted if its effective `outputLen` is strong enough.
596
+ * XOF callers must pass a callable whose `outputLen` matches the digest length they actually intend
597
+ * to sign; bare `shake128` / `shake256` defaults are too short for the stronger prehash modes.
598
+ * @param hash - Hash function to validate.
599
+ * @param requiredStrength - Minimum required collision-resistance strength in bits.
600
+ * @throws If the hash metadata or collision resistance is insufficient. {@link Error}
601
+ * @example
602
+ * Validate that a hash exposes a NIST hash OID and enough collision resistance.
603
+ * ```ts
604
+ * import { sha256 } from '@noble/hashes/sha2.js';
605
+ * import { checkHash } from '@noble/post-quantum/utils.js';
606
+ * checkHash(sha256, 128);
607
+ * ```
608
+ */
185
609
  export function checkHash(hash: CHash, requiredStrength: number = 0): void {
186
610
  if (!hash.oid || !equalBytes(hash.oid.subarray(0, 10), oidNistP))
187
611
  throw new Error('hash.oid is invalid: expected NIST hash');
612
+ // FIPS 204 / FIPS 205 require both collision and second-preimage strength; for approved NIST
613
+ // hashes/XOFs under this OID subtree, the collision bound from the configured digest length is
614
+ // the tighter runtime check, so enforce that lower bound here.
188
615
  const collisionResistance = (hash.outputLen * 8) / 2;
189
616
  if (requiredStrength > collisionResistance) {
190
617
  throw new Error(
@@ -196,14 +623,32 @@ export function checkHash(hash: CHash, requiredStrength: number = 0): void {
196
623
  }
197
624
  }
198
625
 
626
+ /**
627
+ * Builds the domain-separated prehash payload for the prehash sign/verify paths.
628
+ * Callers are expected to vet `hash.oid` first, e.g. via `checkHash(...)`; calling this helper
629
+ * directly with a hash object that lacks `oid` currently throws later inside `concatBytes(...)`.
630
+ * Context length `255` is valid; only `ctx.length > 255` is rejected.
631
+ * @param hash - Prehash function.
632
+ * @param msg - Message bytes.
633
+ * @param ctx - Optional context bytes.
634
+ * @returns Domain-separated prehash payload.
635
+ * @throws On wrong argument ranges or values. {@link RangeError}
636
+ * @example
637
+ * Build the domain-separated prehash payload for external hashing.
638
+ * ```ts
639
+ * import { sha256 } from '@noble/hashes/sha2.js';
640
+ * import { getMessagePrehash } from '@noble/post-quantum/utils.js';
641
+ * getMessagePrehash(sha256, new Uint8Array([1, 2]));
642
+ * ```
643
+ */
199
644
  export function getMessagePrehash(
200
645
  hash: CHash,
201
- msg: Uint8Array,
202
- ctx: Uint8Array = EMPTY
203
- ): Uint8Array {
646
+ msg: TArg<Uint8Array>,
647
+ ctx: TArg<Uint8Array> = EMPTY
648
+ ): TRet<Uint8Array> {
204
649
  abytes_(msg);
205
650
  abytes_(ctx);
206
- if (ctx.length > 255) throw new Error('context should be less than 255 bytes');
651
+ if (ctx.length > 255) throw new RangeError('context should be 255 bytes or less');
207
652
  const hashed = hash(msg);
208
653
  return concatBytes(new Uint8Array([1, ctx.length]), ctx, hash.oid!, hashed);
209
654
  }