@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/slh-dsa.ts CHANGED
@@ -53,6 +53,8 @@ import {
53
53
  vecCoder,
54
54
  type Signer,
55
55
  type SigOpts,
56
+ type TArg,
57
+ type TRet,
56
58
  type VerOpts,
57
59
  } from './utils.ts';
58
60
 
@@ -62,30 +64,52 @@ import {
62
64
  * * K: FORS trees numbers. A: FORS trees height
63
65
  */
64
66
  export type SphincsOpts = {
67
+ /** Security parameter in bytes. */
65
68
  N: number;
69
+ /** Winternitz parameter. */
66
70
  W: number;
71
+ /** Total hypertree height. */
67
72
  H: number;
73
+ /** Number of hypertree layers. */
68
74
  D: number;
75
+ /** Number of FORS trees. */
69
76
  K: number;
77
+ /** Height of each FORS tree. */
70
78
  A: number;
79
+ /** Target security level in bits. */
71
80
  securityLevel: number;
72
81
  };
73
82
 
83
+ /** Hash customization options for SLH-DSA context creation. */
74
84
  export type SphincsHashOpts = {
85
+ /** Whether to use the compressed-address variant from the standard. */
75
86
  isCompressed?: boolean;
87
+ /** Factory that binds one parameter set to one per-key hash context generator. */
76
88
  getContext: GetContext;
77
89
  };
78
90
 
79
91
  /** Winternitz signature params. */
80
- export const PARAMS: Record<string, SphincsOpts> = {
81
- '128f': { W: 16, N: 16, H: 66, D: 22, K: 33, A: 6, securityLevel: 128 },
82
- '128s': { W: 16, N: 16, H: 63, D: 7, K: 14, A: 12, securityLevel: 128 },
83
- '192f': { W: 16, N: 24, H: 66, D: 22, K: 33, A: 8, securityLevel: 192 },
84
- '192s': { W: 16, N: 24, H: 63, D: 7, K: 17, A: 14, securityLevel: 192 },
85
- '256f': { W: 16, N: 32, H: 68, D: 17, K: 35, A: 9, securityLevel: 256 },
86
- '256s': { W: 16, N: 32, H: 64, D: 8, K: 22, A: 14, securityLevel: 256 },
87
- } as const;
88
-
92
+ /**
93
+ * Built-in SLH-DSA Table 2 subset keyed by strength/profile.
94
+ * SHA2 and SHAKE pairs share the same numeric rows here, so the hash family is chosen separately.
95
+ * `securityLevel` stores 128/192/256-bit strengths for `checkHash(...)`,
96
+ * not Table 2's category labels 1/3/5.
97
+ * Other Table 2 columns such as `m`, public-key bytes, and signature bytes
98
+ * stay derived at the export layer.
99
+ */
100
+ export const PARAMS: Record<string, SphincsOpts> = /* @__PURE__ */ (() =>
101
+ Object.freeze({
102
+ '128f': Object.freeze({ W: 16, N: 16, H: 66, D: 22, K: 33, A: 6, securityLevel: 128 }),
103
+ '128s': Object.freeze({ W: 16, N: 16, H: 63, D: 7, K: 14, A: 12, securityLevel: 128 }),
104
+ '192f': Object.freeze({ W: 16, N: 24, H: 66, D: 22, K: 33, A: 8, securityLevel: 192 }),
105
+ '192s': Object.freeze({ W: 16, N: 24, H: 63, D: 7, K: 17, A: 14, securityLevel: 192 }),
106
+ '256f': Object.freeze({ W: 16, N: 32, H: 68, D: 17, K: 35, A: 9, securityLevel: 256 }),
107
+ '256s': Object.freeze({ W: 16, N: 32, H: 64, D: 8, K: 22, A: 14, securityLevel: 256 }),
108
+ } as const))();
109
+
110
+ // FIPS 205 `ADRS.setTypeAndClear(...)` selectors. Local names shorten the spec labels
111
+ // (`WOTS_HASH` -> `WOTS`, `TREE` -> `HASHTREE`, `FORS_ROOTS` -> `FORSPK`), and `setAddr({ type })`
112
+ // below only writes the type word; callers still need to preserve or overwrite the trailing words.
89
113
  const AddressType = {
90
114
  WOTS: 0,
91
115
  WOTSPK: 1,
@@ -96,39 +120,87 @@ const AddressType = {
96
120
  FORSPRF: 6,
97
121
  } as const;
98
122
 
99
- /** Address, byte array of size ADDR_BYTES */
123
+ /** Address byte array of size `ADDR_BYTES`. */
100
124
  export type ADRS = Uint8Array;
101
125
 
126
+ /** Hash and tweakable-hash callbacks bound to one SLH-DSA keypair context. */
102
127
  export type Context = {
103
- PRFaddr: (addr: ADRS) => Uint8Array;
104
- PRFmsg: (skPRF: Uint8Array, random: Uint8Array, msg: Uint8Array) => Uint8Array;
105
- Hmsg: (R: Uint8Array, pk: Uint8Array, m: Uint8Array, outLen: number) => Uint8Array;
106
- thash1: (input: Uint8Array, addr: ADRS) => Uint8Array;
107
- thashN: (blocks: number, input: Uint8Array, addr: ADRS) => Uint8Array;
128
+ /**
129
+ * Derive a PRF output for one address.
130
+ * @param addr - Address bytes.
131
+ * @returns PRF output bytes.
132
+ */
133
+ PRFaddr: (addr: TArg<ADRS>) => TRet<Uint8Array>;
134
+ /**
135
+ * Derive the randomized message hash prefix.
136
+ * @param skPRF - Secret PRF seed.
137
+ * @param random - Per-signature randomness.
138
+ * @param msg - Message bytes.
139
+ * @returns PRF output bytes.
140
+ */
141
+ PRFmsg: (
142
+ skPRF: TArg<Uint8Array>,
143
+ random: TArg<Uint8Array>,
144
+ msg: TArg<Uint8Array>
145
+ ) => TRet<Uint8Array>;
146
+ /**
147
+ * Hash one randomized message transcript.
148
+ * @param R - Randomized message prefix.
149
+ * @param pk - Public key bytes.
150
+ * @param m - Message bytes.
151
+ * @param outLen - Output length in bytes.
152
+ * @returns Transcript hash bytes.
153
+ */
154
+ Hmsg: (
155
+ R: TArg<Uint8Array>,
156
+ pk: TArg<Uint8Array>,
157
+ m: TArg<Uint8Array>,
158
+ outLen: number
159
+ ) => TRet<Uint8Array>;
160
+ /**
161
+ * Tweakable hash over one input block.
162
+ * @param input - Input block.
163
+ * @param addr - Address bytes.
164
+ * @returns Hash output bytes.
165
+ */
166
+ thash1: (input: TArg<Uint8Array>, addr: TArg<ADRS>) => TRet<Uint8Array>;
167
+ /**
168
+ * Tweakable hash over multiple input blocks.
169
+ * @param blocks - Number of input blocks.
170
+ * @param input - Concatenated input bytes.
171
+ * @param addr - Address bytes.
172
+ * @returns Hash output bytes.
173
+ */
174
+ thashN: (blocks: number, input: TArg<Uint8Array>, addr: TArg<ADRS>) => TRet<Uint8Array>;
175
+ /** Wipe any buffered hash state for the current context. */
108
176
  clean: () => void;
109
177
  };
178
+ /** Factory that creates a context generator for one SLH-DSA parameter set. */
110
179
  export type GetContext = (
111
180
  opts: SphincsOpts
112
- ) => (pub_seed: Uint8Array, sk_seed?: Uint8Array) => Context;
181
+ ) => (pub_seed: TArg<Uint8Array>, sk_seed?: TArg<Uint8Array>) => TRet<Context>;
113
182
 
114
183
  function hexToNumber(hex: string): bigint {
115
184
  if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);
116
185
  return BigInt(hex === '' ? '0' : '0x' + hex); // Big Endian
117
186
  }
118
187
 
119
- // BE: Big Endian, LE: Little Endian
120
- function bytesToNumberBE(bytes: Uint8Array): bigint {
188
+ // BE: Big Endian, LE: Little Endian. This is the local FIPS 205 `toInt(...)` equivalent.
189
+ function bytesToNumberBE(bytes: TArg<Uint8Array>): bigint {
121
190
  return hexToNumber(bytesToHex(bytes));
122
191
  }
123
192
 
124
- function numberToBytesBE(n: number | bigint, len: number): Uint8Array {
193
+ // Local in-range FIPS 205 `toByte(x, n)` equivalent; callers must keep `n < 256^len`.
194
+ function numberToBytesBE(n: number | bigint, len: number): TRet<Uint8Array> {
125
195
  return hexToBytes(n.toString(16).padStart(len * 2, '0'));
126
196
  }
127
197
 
128
- // Same as bitsCoder.decode, but maybe spec will change and unify with base2bBE.
198
+ // Local FIPS 205 Algorithm 4 `base_2^b(...)` implementation. Bits are consumed in big-endian
199
+ // order within each input byte, and callers must provide at least `ceil(outLen * b / 8)` bytes;
200
+ // short inputs are not rejected and would zero-extend implicitly.
129
201
  const base2b = (outLen: number, b: number) => {
130
202
  const mask = getMask(b);
131
- return (bytes: Uint8Array) => {
203
+ return (bytes: TArg<Uint8Array>): TRet<Uint32Array> => {
132
204
  const baseB = new Uint32Array(outLen);
133
205
  for (let out = 0, pos = 0, bits = 0, total = 0; out < outLen; out++) {
134
206
  while (bits < b) {
@@ -138,7 +210,7 @@ const base2b = (outLen: number, b: number) => {
138
210
  bits -= b;
139
211
  baseB[out] = (total >>> bits) & mask;
140
212
  }
141
- return baseB;
213
+ return baseB as TRet<Uint32Array>;
142
214
  };
143
215
  };
144
216
 
@@ -146,13 +218,20 @@ function getMaskBig(bits: number) {
146
218
  return (1n << BigInt(bits)) - 1n; // 4 -> 0b1111
147
219
  }
148
220
 
221
+ /** Public SLH-DSA signer with prehash customization. */
149
222
  export type SphincsSigner = Signer & {
150
- internal: Signer;
223
+ internal: TRet<Signer>;
151
224
  securityLevel: number;
152
- prehash: (hash: CHash) => Signer;
225
+ prehash: (hash: TArg<CHash>) => TRet<Signer>;
153
226
  };
154
227
 
155
- function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
228
+ /** One parameter/hash instantiation of the public SLH-DSA API.
229
+ * `keygen(seed)` is a deterministic 3N-byte library hook around the internal keygen flow,
230
+ * and `getPublicKey(secretKey)` only extracts the embedded public key
231
+ * instead of recomputing `PK.root`.
232
+ */
233
+ function gen(opts: SphincsOpts, hashOpts_: TArg<SphincsHashOpts>): TRet<SphincsSigner> {
234
+ const hashOpts = hashOpts_ as SphincsHashOpts;
156
235
  const { N, W, H, D, K, A, securityLevel: securityLevel } = opts;
157
236
  const getContext = hashOpts.getContext(opts);
158
237
  if (W !== 16) throw new Error('Unsupported Winternitz parameter');
@@ -183,8 +262,13 @@ function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
183
262
  OFFSET_HASH_ADDR += 10;
184
263
  }
185
264
 
265
+ // Mutates and returns `addr` in place. For the built-in parameter sets, the layer / chain /
266
+ // hash / height / keypair values fit in the low byte(s), and the tree value fits in 64 bits,
267
+ // so the untouched leading bytes in the wider FIPS 205 ADRS / ADRS_c fields stay zero.
268
+ // `height` / `chain` and `index` / `hash` share the same spec words, so callers must use the
269
+ // address-type-specific combinations instead of mixing both meanings in one call.
186
270
  const setAddr = (
187
- opts: {
271
+ opts: TArg<{
188
272
  type?: (typeof AddressType)[keyof typeof AddressType];
189
273
  height?: number;
190
274
  tree?: bigint;
@@ -195,8 +279,8 @@ function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
195
279
  keypair?: number;
196
280
  subtreeAddr?: ADRS;
197
281
  keypairAddr?: ADRS;
198
- },
199
- addr: ADRS = new Uint8Array(ADDR_BYTES)
282
+ }>,
283
+ addr: TArg<ADRS> = new Uint8Array(ADDR_BYTES)
200
284
  ) => {
201
285
  const { type, height, tree, layer, index, chain, hash, keypair } = opts;
202
286
  const { subtreeAddr, keypairAddr } = opts;
@@ -223,11 +307,12 @@ function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
223
307
  };
224
308
 
225
309
  const chainCoder = base2b(WOTS_LEN2, WOTS_LOGW);
226
- const chainLengths = (msg: Uint8Array) => {
310
+ const chainLengths = (msg: TArg<Uint8Array>) => {
227
311
  const W1 = base2b(WOTS_LEN1, WOTS_LOGW)(msg);
228
312
  let csum = 0;
229
313
  for (let i = 0; i < W1.length; i++) csum += W - 1 - W1[i]; // ▷ Compute checksum
230
- csum <<= (8 - ((WOTS_LEN2 * WOTS_LOGW) % 8)) % 8; // csum ← csum ≪ ((8 − ((len2 · lg(w)) mod 8)) mod 8
314
+ // csum ← csum ≪ ((8 − ((len2 · lg(w)) mod 8)) mod 8
315
+ csum <<= (8 - ((WOTS_LEN2 * WOTS_LOGW) % 8)) % 8;
231
316
  // Checksum to base(LOG_W)
232
317
  const W2 = chainCoder(numberToBytesBE(csum, Math.ceil((WOTS_LEN2 * WOTS_LOGW) / 8)));
233
318
  // W1 || W2 (concatBytes cannot concat TypedArrays)
@@ -246,25 +331,44 @@ function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
246
331
  Math.ceil(TREE_BITS / 8),
247
332
  Math.ceil(TREE_HEIGHT / 8)
248
333
  );
249
- const hashMessage = (R: Uint8Array, pkSeed: Uint8Array, msg: Uint8Array, context: Context) => {
250
- const digest = context.Hmsg(R, pkSeed, msg, hashMsgCoder.bytesLen); // digest Hmsg(R, PK.seed, PK.root, M)
334
+ // `pkSeed` is the full public key byte string `PK.seed || PK.root`; after splitting `Hmsg`,
335
+ // mask away any spare high bits so `idx_tree` / `idx_leaf` match the spec's final mod-2^k steps.
336
+ const hashMessage = (
337
+ R: TArg<Uint8Array>,
338
+ pkSeed: TArg<Uint8Array>,
339
+ msg: TArg<Uint8Array>,
340
+ context: TArg<Context>
341
+ ) => {
342
+ const rawContext = context as Context;
343
+ // digest ← Hmsg(R, PK.seed, PK.root, M)
344
+ const digest = rawContext.Hmsg(R, pkSeed, msg, hashMsgCoder.bytesLen);
251
345
  const [md, tmpIdxTree, tmpIdxLeaf] = hashMsgCoder.decode(digest);
252
346
  const tree = bytesToNumberBE(tmpIdxTree) & getMaskBig(TREE_BITS);
253
347
  const leafIdx = Number(bytesToNumberBE(tmpIdxLeaf)) & getMask(LEAF_BITS);
254
348
  return { tree, leafIdx, md };
255
349
  };
256
350
 
351
+ // Iterative `xmss_node` / `xmss_sign` core: mutate `treeAddr` in place, collapse completed
352
+ // sibling pairs on `stack`, and record the sibling whenever the current subtree is the auth-path
353
+ // neighbor of the target leaf at that height.
257
354
  const treehash = <T>(
258
355
  height: number,
259
- fn: (leafIdx: number, addrOffset: number, context: Context, info: T) => Uint8Array
356
+ fn: TArg<(leafIdx: number, addrOffset: number, context: Context, info: T) => Uint8Array>
260
357
  ) =>
261
358
  function treehash_i(
262
- context: Context,
359
+ context: TArg<Context>,
263
360
  leafIdx: number,
264
361
  idxOffset: number,
265
- treeAddr: ADRS,
362
+ treeAddr: TArg<ADRS>,
266
363
  info: T
267
364
  ) {
365
+ const rawContext = context as Context;
366
+ const leafFn = fn as (
367
+ leafIdx: number,
368
+ addrOffset: number,
369
+ context: Context,
370
+ info: T
371
+ ) => Uint8Array;
268
372
  const maxIdx = (1 << height) - 1;
269
373
  const stack = new Uint8Array(height * N);
270
374
  const authPath = new Uint8Array(height * N);
@@ -273,7 +377,7 @@ function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
273
377
  const cur0 = current.subarray(0, N);
274
378
  const cur1 = current.subarray(N);
275
379
  const addrOffset = idx + idxOffset;
276
- cur1.set(fn(leafIdx, addrOffset, context, info));
380
+ cur1.set(leafFn(leafIdx, addrOffset, rawContext, info));
277
381
  let h = 0;
278
382
  for (let i = idx, o = idxOffset, l = leafIdx; ; h++, i >>>= 1, l >>>= 1, o >>>= 1) {
279
383
  if (h === height) return { root: cur1, authPath }; // Returns from here
@@ -281,7 +385,7 @@ function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
281
385
  if ((i & 1) === 0 && idx < maxIdx) break;
282
386
  setAddr({ height: h + 1, index: (i >> 1) + (o >> 1) }, treeAddr);
283
387
  cur0.set(stack.subarray(h * N).subarray(0, N));
284
- cur1.set(context.thashN(2, current, treeAddr));
388
+ cur1.set(rawContext.thashN(2, current, treeAddr));
285
389
  }
286
390
  stack.subarray(h * N).set(cur1); // stack.push(cur1)
287
391
  }
@@ -295,41 +399,53 @@ function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
295
399
  leafAddr: ADRS;
296
400
  pkAddr: ADRS;
297
401
  };
298
- const wotsTreehash = treehash(TREE_HEIGHT, (leafIdx, addrOffset, context, info: LeafInfo) => {
299
- const wotsPk = new Uint8Array(WOTS_LEN * N);
300
- const wotsKmask = addrOffset === leafIdx ? 0 : ~0 >>> 0;
301
- setAddr({ keypair: addrOffset }, info.leafAddr);
302
- setAddr({ keypair: addrOffset }, info.pkAddr);
303
- for (let i = 0; i < WOTS_LEN; i++) {
304
- const wotsK = info.wotsSteps[i] | wotsKmask;
305
- const pk = wotsPk.subarray(i * N, (i + 1) * N);
306
- setAddr({ chain: i, hash: 0, type: AddressType.WOTSPRF }, info.leafAddr);
307
- pk.set(context.PRFaddr(info.leafAddr));
308
- setAddr({ type: AddressType.WOTS }, info.leafAddr);
309
- for (let k = 0; ; k++) {
310
- if (k === wotsK) info.wotsSig.subarray(i * N).set(pk); //wotsSig.push()
311
- if (k === W - 1) break;
312
- setAddr({ hash: k }, info.leafAddr);
313
- pk.set(context.thash1(pk, info.leafAddr));
402
+ const wotsTreehash = treehash(
403
+ TREE_HEIGHT,
404
+ (leafIdx: number, addrOffset: number, context: TArg<Context>, info: TArg<LeafInfo>) => {
405
+ const rawContext = context as Context;
406
+ const wotsPk = new Uint8Array(WOTS_LEN * N);
407
+ // `keygen()` passes `leafIdx = ~0 >>> 0`, so no real XMSS leaf matches and this suppresses
408
+ // WOTS signature capture while still hashing every chain to its public-key endpoint.
409
+ const wotsKmask = addrOffset === leafIdx ? 0 : ~0 >>> 0;
410
+ setAddr({ keypair: addrOffset }, info.leafAddr);
411
+ setAddr({ keypair: addrOffset }, info.pkAddr);
412
+ for (let i = 0; i < WOTS_LEN; i++) {
413
+ const wotsK = info.wotsSteps[i] | wotsKmask;
414
+ const pk = wotsPk.subarray(i * N, (i + 1) * N);
415
+ setAddr({ chain: i, hash: 0, type: AddressType.WOTSPRF }, info.leafAddr);
416
+ pk.set(rawContext.PRFaddr(info.leafAddr));
417
+ setAddr({ type: AddressType.WOTS }, info.leafAddr);
418
+ for (let k = 0; ; k++) {
419
+ if (k === wotsK) info.wotsSig.subarray(i * N).set(pk); //wotsSig.push()
420
+ if (k === W - 1) break;
421
+ setAddr({ hash: k }, info.leafAddr);
422
+ pk.set(rawContext.thash1(pk, info.leafAddr));
423
+ }
314
424
  }
425
+ return rawContext.thashN(WOTS_LEN, wotsPk, info.pkAddr);
315
426
  }
316
- return context.thashN(WOTS_LEN, wotsPk, info.pkAddr);
317
- });
427
+ );
318
428
 
319
- const forsTreehash = treehash(A, (_, addrOffset, context, forsLeafAddr: ForsLeafInfo) => {
320
- setAddr({ type: AddressType.FORSPRF, index: addrOffset }, forsLeafAddr);
321
- const prf = context.PRFaddr(forsLeafAddr);
322
- setAddr({ type: AddressType.FORSTREE }, forsLeafAddr);
323
- return context.thash1(prf, forsLeafAddr);
324
- });
429
+ const forsTreehash = treehash(
430
+ A,
431
+ (_: number, addrOffset: number, context: TArg<Context>, forsLeafAddr: TArg<ForsLeafInfo>) => {
432
+ const rawContext = context as Context;
433
+ setAddr({ type: AddressType.FORSPRF, index: addrOffset }, forsLeafAddr);
434
+ const prf = rawContext.PRFaddr(forsLeafAddr);
435
+ setAddr({ type: AddressType.FORSTREE }, forsLeafAddr);
436
+ return rawContext.thash1(prf, forsLeafAddr);
437
+ }
438
+ );
325
439
 
440
+ // Fuse `xmss_sign` with the subtree-root computation needed by `ht_sign`, so one tree walk
441
+ // yields both the WOTS/auth-path signature and the root that the next hypertree layer signs.
326
442
  const merkleSign = (
327
- context: Context,
328
- wotsAddr: ADRS,
329
- treeAddr: ADRS,
443
+ context: TArg<Context>,
444
+ wotsAddr: TArg<ADRS>,
445
+ treeAddr: TArg<ADRS>,
330
446
  leafIdx: number,
331
- prevRoot: Uint8Array = new Uint8Array(N)
332
- ) => {
447
+ prevRoot: TArg<Uint8Array> = new Uint8Array(N)
448
+ ): TRet<{ root: Uint8Array; sigWots: Uint8Array; sigAuth: Uint8Array }> => {
333
449
  setAddr({ type: AddressType.HASHTREE }, treeAddr);
334
450
  // State variables
335
451
  const info = {
@@ -343,23 +459,28 @@ function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
343
459
  root,
344
460
  sigWots: info.wotsSig.subarray(0, WOTS_LEN * N),
345
461
  sigAuth: authPath,
346
- };
462
+ } as TRet<{ root: Uint8Array; sigWots: Uint8Array; sigAuth: Uint8Array }>;
347
463
  };
348
464
 
349
465
  type ForsLeafInfo = ADRS;
350
466
 
351
467
  const computeRoot = (
352
- leaf: Uint8Array,
468
+ leaf: TArg<Uint8Array>,
353
469
  leafIdx: number,
354
470
  idxOffset: number,
355
- authPath: Uint8Array,
471
+ authPath: TArg<Uint8Array>,
356
472
  treeHeight: number,
357
- context: Context,
358
- addr: ADRS
473
+ context: TArg<Context>,
474
+ addr: TArg<ADRS>
359
475
  ) => {
476
+ const rawContext = context as Context;
360
477
  const buffer = new Uint8Array(2 * N);
361
478
  const b0 = buffer.subarray(0, N);
362
479
  const b1 = buffer.subarray(N, 2 * N);
480
+ // Algorithm 11 hashes `node || AUTH[k]` for even nodes and `AUTH[k] || node` for odd ones,
481
+ // so reuse one `2N` buffer and just swap which half receives the sibling at each level.
482
+ // `idxOffset` carries the subtree base for the shared FORS path, so `leafIdx + idxOffset`
483
+ // tracks the same tree-global index updates that Algorithms 11 and 17 apply to ADRS.
363
484
  // First iter
364
485
  if ((leafIdx & 1) !== 0) {
365
486
  b1.set(leaf.subarray(0, N));
@@ -375,16 +496,16 @@ function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
375
496
  setAddr({ height: i + 1, index: leafIdx + idxOffset }, addr);
376
497
  const a = authPath.subarray((i + 1) * N, (i + 2) * N);
377
498
  if ((leafIdx & 1) !== 0) {
378
- b1.set(context.thashN(2, buffer, addr));
499
+ b1.set(rawContext.thashN(2, buffer, addr));
379
500
  b0.set(a);
380
501
  } else {
381
- buffer.set(context.thashN(2, buffer, addr));
502
+ buffer.set(rawContext.thashN(2, buffer, addr));
382
503
  b1.set(a);
383
504
  }
384
505
  }
385
506
  // Root
386
507
  setAddr({ height: treeHeight, index: leafIdx + idxOffset }, addr);
387
- return context.thashN(2, buffer, addr);
508
+ return rawContext.thashN(2, buffer, addr);
388
509
  };
389
510
 
390
511
  const seedCoder = splitCoder('seed', N, N, N);
@@ -393,16 +514,16 @@ function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
393
514
  const forsCoder = vecCoder(splitCoder('fors', N, N * A), K);
394
515
  const wotsCoder = vecCoder(splitCoder('wots', WOTS_LEN * N, TREE_HEIGHT * N), D);
395
516
  const sigCoder = splitCoder('signature', N, forsCoder, wotsCoder); // random || fors || wots
396
- const internal: Signer = {
397
- info: { type: 'internal-slh-dsa' },
398
- lengths: {
517
+ const internal: TRet<Signer> = Object.freeze({
518
+ info: Object.freeze({ type: 'internal-slh-dsa' }),
519
+ lengths: Object.freeze({
399
520
  publicKey: publicCoder.bytesLen,
400
521
  secretKey: secretCoder.bytesLen,
401
522
  signature: sigCoder.bytesLen,
402
523
  seed: seedCoder.bytesLen,
403
524
  signRand: N,
404
- },
405
- keygen(seed?: Uint8Array) {
525
+ }),
526
+ keygen(seed?: TArg<Uint8Array>) {
406
527
  if (seed !== undefined) abytes(seed, seedCoder.bytesLen, 'seed');
407
528
  seed = seed === undefined ? randomBytes(seedCoder.bytesLen) : copyBytes(seed);
408
529
  // Set SK.seed, SK.prf, and PK.seed to random n-byte
@@ -417,13 +538,16 @@ function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
417
538
  const secretKey = secretCoder.encode([secretSeed, secretPRF, publicKey]);
418
539
  context.clean();
419
540
  cleanBytes(secretSeed, secretPRF, root, wotsAddr, topTreeAddr);
420
- return { publicKey, secretKey };
541
+ return {
542
+ publicKey: publicKey as TRet<Uint8Array>,
543
+ secretKey: secretKey as TRet<Uint8Array>,
544
+ };
421
545
  },
422
- getPublicKey: (secretKey: Uint8Array) => {
546
+ getPublicKey: (secretKey: TArg<Uint8Array>): TRet<Uint8Array> => {
423
547
  const [_skSeed, _skPRF, pk] = secretCoder.decode(secretKey);
424
- return Uint8Array.from(pk);
548
+ return Uint8Array.from(pk) as TRet<Uint8Array>;
425
549
  },
426
- sign: (msg: Uint8Array, sk: Uint8Array, opts: SigOpts = {}) => {
550
+ sign: (msg: TArg<Uint8Array>, sk: TArg<Uint8Array>, opts: TArg<SigOpts> = {}) => {
427
551
  validateSigOpts(opts);
428
552
  let { extraEntropy: random } = opts;
429
553
  const [skSeed, skPRF, pk] = secretCoder.decode(sk); // todo: fix
@@ -494,9 +618,9 @@ function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
494
618
  context.clean();
495
619
  const SIG = sigCoder.encode([R, fors, wots]);
496
620
  cleanBytes(R, random, treeAddr, wotsAddr, forsLeaf, forsTreeAddr, indices, roots);
497
- return SIG;
621
+ return SIG as TRet<Uint8Array>;
498
622
  },
499
- verify: (sig: Uint8Array, msg: Uint8Array, publicKey: Uint8Array) => {
623
+ verify: (sig: TArg<Uint8Array>, msg: TArg<Uint8Array>, publicKey: TArg<Uint8Array>) => {
500
624
  const [pkSeed, pubRoot] = publicCoder.decode(publicKey);
501
625
  const [random, forsVec, wotsVec] = sigCoder.decode(sig);
502
626
  const pk = publicKey;
@@ -555,74 +679,106 @@ function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
555
679
  }
556
680
  return equalBytes(root, pubRoot);
557
681
  },
558
- };
559
- return {
560
- info: { type: 'slh-dsa' },
682
+ });
683
+ return Object.freeze({
684
+ info: Object.freeze({ type: 'slh-dsa' }),
561
685
  internal,
562
686
  securityLevel: securityLevel,
563
687
  lengths: internal.lengths,
564
688
  keygen: internal.keygen,
565
689
  getPublicKey: internal.getPublicKey,
566
- sign: (msg: Uint8Array, secretKey: Uint8Array, opts: SigOpts = {}) => {
690
+ sign: (msg: TArg<Uint8Array>, secretKey: TArg<Uint8Array>, opts: TArg<SigOpts> = {}) => {
567
691
  validateSigOpts(opts);
568
692
  const M = getMessage(msg, opts.context);
569
693
  const res = internal.sign(M, secretKey, opts);
570
694
  cleanBytes(M);
571
- return res;
695
+ return res as TRet<Uint8Array>;
572
696
  },
573
- verify: (sig: Uint8Array, msg: Uint8Array, publicKey: Uint8Array, opts: VerOpts = {}) => {
697
+ verify: (
698
+ sig: TArg<Uint8Array>,
699
+ msg: TArg<Uint8Array>,
700
+ publicKey: TArg<Uint8Array>,
701
+ opts: TArg<VerOpts> = {}
702
+ ) => {
574
703
  validateVerOpts(opts);
575
704
  return internal.verify(sig, getMessage(msg, opts.context), publicKey);
576
705
  },
577
- prehash: (hash: CHash) => {
578
- checkHash(hash, securityLevel);
579
- return {
580
- info: { type: 'hashslh-dsa' },
706
+ prehash: (hash: TArg<CHash>): TRet<Signer> => {
707
+ checkHash(hash as CHash, securityLevel);
708
+ const rawHash = hash as CHash;
709
+ return Object.freeze({
710
+ info: Object.freeze({ type: 'hashslh-dsa' }),
581
711
  lengths: internal.lengths,
582
712
  keygen: internal.keygen,
583
713
  getPublicKey: internal.getPublicKey,
584
- sign: (msg: Uint8Array, secretKey: Uint8Array, opts: SigOpts = {}) => {
714
+ sign: (msg: TArg<Uint8Array>, secretKey: TArg<Uint8Array>, opts: TArg<SigOpts> = {}) => {
585
715
  validateSigOpts(opts);
586
- const M = getMessagePrehash(hash, msg, opts.context);
716
+ const M = getMessagePrehash(rawHash, msg, opts.context);
587
717
  const res = internal.sign(M, secretKey, opts);
588
718
  cleanBytes(M);
589
- return res;
719
+ return res as TRet<Uint8Array>;
590
720
  },
591
- verify: (sig: Uint8Array, msg: Uint8Array, publicKey: Uint8Array, opts: VerOpts = {}) => {
721
+ verify: (
722
+ sig: TArg<Uint8Array>,
723
+ msg: TArg<Uint8Array>,
724
+ publicKey: TArg<Uint8Array>,
725
+ opts: TArg<VerOpts> = {}
726
+ ) => {
592
727
  validateVerOpts(opts);
593
- return internal.verify(sig, getMessagePrehash(hash, msg, opts.context), publicKey);
728
+ return internal.verify(sig, getMessagePrehash(rawHash, msg, opts.context), publicKey);
594
729
  },
595
- };
730
+ });
596
731
  },
597
- };
732
+ });
598
733
  }
599
734
 
735
+ // FIPS 205 §11.1 SHAKE instantiation: this path hashes the full uncompressed address bytes,
736
+ // unlike the compressed 22-byte SHA2 path in §11.2.
600
737
  const genShake =
601
- (): GetContext => (opts: SphincsOpts) => (pubSeed: Uint8Array, skSeed?: Uint8Array) => {
738
+ (): TRet<GetContext> =>
739
+ (opts: SphincsOpts) =>
740
+ (pubSeed: TArg<Uint8Array>, skSeed?: TArg<Uint8Array>): TRet<Context> => {
602
741
  const { N } = opts;
603
742
  const stats = { prf: 0, thash: 0, hmsg: 0, gen_message_random: 0 };
743
+ // §11.1 prefixes PRF/F/H/T_l with `PK.seed`, so cache that absorbed prefix once and clone it
744
+ // for each address-bound call instead of reabsorbing the same seed every time.
604
745
  const h0 = shake256.create({}).update(pubSeed);
605
746
  const h0tmp = h0.clone();
606
- const thash = (blocks: number, input: Uint8Array, addr: ADRS) => {
747
+ const thash = (blocks: number, input: TArg<Uint8Array>, addr: TArg<ADRS>): TRet<Uint8Array> => {
607
748
  stats.thash++;
608
749
  return h0
609
750
  ._cloneInto(h0tmp)
610
751
  .update(addr)
611
752
  .update(input.subarray(0, blocks * N))
612
- .xof(N);
753
+ .xof(N) as TRet<Uint8Array>;
613
754
  };
614
755
  return {
615
- PRFaddr: (addr: ADRS) => {
756
+ PRFaddr: (addr: TArg<ADRS>): TRet<Uint8Array> => {
616
757
  if (!skSeed) throw new Error('no sk seed');
617
758
  stats.prf++;
618
759
  const res = h0._cloneInto(h0tmp).update(addr).update(skSeed).xof(N);
619
- return res;
760
+ return res as TRet<Uint8Array>;
620
761
  },
621
- PRFmsg: (skPRF: Uint8Array, random: Uint8Array, msg: Uint8Array) => {
762
+ PRFmsg: (
763
+ skPRF: TArg<Uint8Array>,
764
+ random: TArg<Uint8Array>,
765
+ msg: TArg<Uint8Array>
766
+ ): TRet<Uint8Array> => {
622
767
  stats.gen_message_random++;
623
- return shake256.create({}).update(skPRF).update(random).update(msg).digest().subarray(0, N);
768
+ return shake256
769
+ .create({})
770
+ .update(skPRF)
771
+ .update(random)
772
+ .update(msg)
773
+ .digest()
774
+ .subarray(0, N) as TRet<Uint8Array>;
624
775
  },
625
- Hmsg: (R: Uint8Array, pk: Uint8Array, m: Uint8Array, outLen) => {
776
+ Hmsg: (
777
+ R: TArg<Uint8Array>,
778
+ pk: TArg<Uint8Array>,
779
+ m: TArg<Uint8Array>,
780
+ outLen
781
+ ): TRet<Uint8Array> => {
626
782
  stats.hmsg++;
627
783
  return shake256.create({}).update(R.subarray(0, N)).update(pk).update(m).xof(outLen);
628
784
  },
@@ -633,29 +789,62 @@ const genShake =
633
789
  h0tmp.destroy();
634
790
  //console.log(stats);
635
791
  },
636
- };
792
+ } as TRet<Context>;
637
793
  };
638
794
 
639
- const SHAKE_SIMPLE = { getContext: genShake() };
640
-
641
- /** SLH-DSA: 128-bit fast SHAKE version. */
642
- export const slh_dsa_shake_128f: SphincsSigner = /* @__PURE__ */ gen(PARAMS['128f'], SHAKE_SIMPLE);
643
- /** SLH-DSA: 128-bit short SHAKE version. */
644
- export const slh_dsa_shake_128s: SphincsSigner = /* @__PURE__ */ gen(PARAMS['128s'], SHAKE_SIMPLE);
645
- /** SLH-DSA: 192-bit fast SHAKE version. */
646
- export const slh_dsa_shake_192f: SphincsSigner = /* @__PURE__ */ gen(PARAMS['192f'], SHAKE_SIMPLE);
647
- /** SLH-DSA: 192-bit short SHAKE version. */
648
- export const slh_dsa_shake_192s: SphincsSigner = /* @__PURE__ */ gen(PARAMS['192s'], SHAKE_SIMPLE);
649
- /** SLH-DSA: 256-bit fast SHAKE version. */
650
- export const slh_dsa_shake_256f: SphincsSigner = /* @__PURE__ */ gen(PARAMS['256f'], SHAKE_SIMPLE);
651
- /** SLH-DSA: 256-bit short SHAKE version. */
652
- export const slh_dsa_shake_256s: SphincsSigner = /* @__PURE__ */ gen(PARAMS['256s'], SHAKE_SIMPLE);
795
+ const SHAKE_SIMPLE = /* @__PURE__ */ (() => ({ getContext: genShake() }))();
796
+
797
+ /**
798
+ * SLH-DSA-SHAKE-128f: Table 2 row `n=16, h=66, d=22, h'=3, a=6, k=33, lg w=4, m=34`;
799
+ * lengths `publicKey=32`, `secretKey=64`, `signature=17088`, `seed=48`, `signRand=16`.
800
+ * Also exposes `.prehash(...)`.
801
+ */
802
+ export const slh_dsa_shake_128f: TRet<SphincsSigner> = /* @__PURE__ */ (() =>
803
+ gen(PARAMS['128f'], SHAKE_SIMPLE))();
804
+ /**
805
+ * SLH-DSA-SHAKE-128s: Table 2 row `n=16, h=63, d=7, h'=9, a=12, k=14, lg w=4, m=30`;
806
+ * lengths `publicKey=32`, `secretKey=64`, `signature=7856`, `seed=48`, `signRand=16`.
807
+ * Also exposes `.prehash(...)`.
808
+ */
809
+ export const slh_dsa_shake_128s: TRet<SphincsSigner> = /* @__PURE__ */ (() =>
810
+ gen(PARAMS['128s'], SHAKE_SIMPLE))();
811
+ /**
812
+ * SLH-DSA-SHAKE-192f: Table 2 row `n=24, h=66, d=22, h'=3, a=8, k=33, lg w=4, m=42`;
813
+ * lengths `publicKey=48`, `secretKey=96`, `signature=35664`, `seed=72`, `signRand=24`.
814
+ * Also exposes `.prehash(...)`.
815
+ */
816
+ export const slh_dsa_shake_192f: TRet<SphincsSigner> = /* @__PURE__ */ (() =>
817
+ gen(PARAMS['192f'], SHAKE_SIMPLE))();
818
+ /**
819
+ * SLH-DSA-SHAKE-192s: Table 2 row `n=24, h=63, d=7, h'=9, a=14, k=17, lg w=4, m=39`;
820
+ * lengths `publicKey=48`, `secretKey=96`, `signature=16224`, `seed=72`, `signRand=24`.
821
+ * Also exposes `.prehash(...)`.
822
+ */
823
+ export const slh_dsa_shake_192s: TRet<SphincsSigner> = /* @__PURE__ */ (() =>
824
+ gen(PARAMS['192s'], SHAKE_SIMPLE))();
825
+ /**
826
+ * SLH-DSA-SHAKE-256f: Table 2 row `n=32, h=68, d=17, h'=4, a=9, k=35, lg w=4, m=49`;
827
+ * lengths `publicKey=64`, `secretKey=128`, `signature=49856`, `seed=96`, `signRand=32`.
828
+ * Also exposes `.prehash(...)`.
829
+ */
830
+ export const slh_dsa_shake_256f: TRet<SphincsSigner> = /* @__PURE__ */ (() =>
831
+ gen(PARAMS['256f'], SHAKE_SIMPLE))();
832
+ /**
833
+ * SLH-DSA-SHAKE-256s: Table 2 row `n=32, h=64, d=8, h'=8, a=14, k=22, lg w=4, m=47`;
834
+ * lengths `publicKey=64`, `secretKey=128`, `signature=29792`, `seed=96`, `signRand=32`.
835
+ * Also exposes `.prehash(...)`.
836
+ */
837
+ export const slh_dsa_shake_256s: TRet<SphincsSigner> = /* @__PURE__ */ (() =>
838
+ gen(PARAMS['256s'], SHAKE_SIMPLE))();
653
839
 
654
840
  type ShaType = typeof sha256 | typeof sha512;
841
+ // FIPS 205 §11.2 SHA2 instantiation. The `h0` / `h1` split is intentional:
842
+ // category-1 keeps everything on SHA-256, while category-3/5 keep `PRFaddr` / `thash1`
843
+ // on SHA-256 but switch `PRFmsg`, `Hmsg`, and multi-block `thashN` to SHA-512.
655
844
  const genSha =
656
- (h0: ShaType, h1: ShaType): GetContext =>
845
+ (h0: ShaType, h1: ShaType): TRet<GetContext> =>
657
846
  (opts) =>
658
- (pub_seed, sk_seed?) => {
847
+ (pub_seed: TArg<Uint8Array>, sk_seed?: TArg<Uint8Array>): TRet<Context> => {
659
848
  const { N } = opts;
660
849
  /*
661
850
  Perf debug stats, how much hashes we call?
@@ -667,6 +856,8 @@ const genSha =
667
856
 
668
857
  const counterB = new Uint8Array(4);
669
858
  const counterV = createView(counterB);
859
+ // §11.2 prefixes SHA2 PRF/F/H/T_l with `PK.seed || toByte(0, blockLen-N)`, so cache the
860
+ // zero-padded seed block once for the SHA-256 lane and once for the SHA-512 lane.
670
861
  const h0ps = h0
671
862
  .create()
672
863
  .update(pub_seed)
@@ -680,7 +871,10 @@ const genSha =
680
871
  const h1tmp = h1ps.clone();
681
872
 
682
873
  // https://www.rfc-editor.org/rfc/rfc8017.html#appendix-B.2.1
683
- function mgf1(seed: Uint8Array, length: number, hash: ShaType) {
874
+ // This local helper is intentionally stricter than generic MGF1 reuse: current SLH-DSA callers
875
+ // only request tiny `m`-byte outputs, but the guard below rejects `length > 2^32` instead of
876
+ // RFC 8017's broader `maskLen > 2^32 * hLen` bound.
877
+ function mgf1(seed: TArg<Uint8Array>, length: number, hash: ShaType): TRet<Uint8Array> {
684
878
  stats.mgf1++;
685
879
  const out = new Uint8Array(Math.ceil(length / hash.outputLen) * hash.outputLen);
686
880
  // NOT 2^32-1
@@ -691,22 +885,22 @@ const genSha =
691
885
  o = o.subarray(hash.outputLen);
692
886
  }
693
887
  cleanBytes(out.subarray(length));
694
- return out.subarray(0, length);
888
+ return out.subarray(0, length) as TRet<Uint8Array>;
695
889
  }
696
890
 
697
891
  const thash =
698
892
  (_: ShaType, h: typeof h0ps, hTmp: typeof h0ps) =>
699
- (blocks: number, input: Uint8Array, addr: ADRS) => {
893
+ (blocks: number, input: TArg<Uint8Array>, addr: TArg<ADRS>): TRet<Uint8Array> => {
700
894
  stats.thash++;
701
895
  const d = h
702
896
  ._cloneInto(hTmp as any)
703
897
  .update(addr)
704
898
  .update(input.subarray(0, blocks * N))
705
899
  .digest();
706
- return d.subarray(0, N);
900
+ return d.subarray(0, N) as TRet<Uint8Array>;
707
901
  };
708
902
  return {
709
- PRFaddr: (addr: ADRS) => {
903
+ PRFaddr: (addr: TArg<ADRS>): TRet<Uint8Array> => {
710
904
  if (!sk_seed) throw new Error('No sk seed');
711
905
  stats.prf++;
712
906
  const res = h0ps
@@ -715,13 +909,27 @@ const genSha =
715
909
  .update(sk_seed)
716
910
  .digest()
717
911
  .subarray(0, N);
718
- return res;
912
+ return res as TRet<Uint8Array>;
719
913
  },
720
- PRFmsg: (skPRF: Uint8Array, random: Uint8Array, msg: Uint8Array) => {
914
+ PRFmsg: (
915
+ skPRF: TArg<Uint8Array>,
916
+ random: TArg<Uint8Array>,
917
+ msg: TArg<Uint8Array>
918
+ ): TRet<Uint8Array> => {
721
919
  stats.gen_message_random++;
722
- return hmac.create(h1, skPRF).update(random).update(msg).digest().subarray(0, N);
920
+ return hmac
921
+ .create(h1, skPRF)
922
+ .update(random)
923
+ .update(msg)
924
+ .digest()
925
+ .subarray(0, N) as TRet<Uint8Array>;
723
926
  },
724
- Hmsg: (R: Uint8Array, pk: Uint8Array, m: Uint8Array, outLen) => {
927
+ Hmsg: (
928
+ R: TArg<Uint8Array>,
929
+ pk: TArg<Uint8Array>,
930
+ m: TArg<Uint8Array>,
931
+ outLen
932
+ ): TRet<Uint8Array> => {
725
933
  stats.hmsg++;
726
934
  const seed = concatBytes(
727
935
  R.subarray(0, N),
@@ -739,27 +947,57 @@ const genSha =
739
947
  h1tmp.destroy();
740
948
  //console.log(stats);
741
949
  },
742
- };
950
+ } as TRet<Context>;
743
951
  };
744
952
 
745
- const SHA256_SIMPLE = {
953
+ const SHA256_SIMPLE = /* @__PURE__ */ (() => ({
746
954
  isCompressed: true,
747
955
  getContext: genSha(sha256, sha256),
748
- };
749
- const SHA512_SIMPLE = {
956
+ }))();
957
+ const SHA512_SIMPLE = /* @__PURE__ */ (() => ({
750
958
  isCompressed: true,
751
959
  getContext: genSha(sha256, sha512),
752
- };
960
+ }))();
753
961
 
754
- /** SLH-DSA: 128-bit fast SHA2 version. */
755
- export const slh_dsa_sha2_128f: SphincsSigner = /* @__PURE__ */ gen(PARAMS['128f'], SHA256_SIMPLE);
756
- /** SLH-DSA: 128-bit small SHA2 version. */
757
- export const slh_dsa_sha2_128s: SphincsSigner = /* @__PURE__ */ gen(PARAMS['128s'], SHA256_SIMPLE);
758
- /** SLH-DSA: 192-bit fast SHA2 version. */
759
- export const slh_dsa_sha2_192f: SphincsSigner = /* @__PURE__ */ gen(PARAMS['192f'], SHA512_SIMPLE);
760
- /** SLH-DSA: 192-bit small SHA2 version. */
761
- export const slh_dsa_sha2_192s: SphincsSigner = /* @__PURE__ */ gen(PARAMS['192s'], SHA512_SIMPLE);
762
- /** SLH-DSA: 256-bit fast SHA2 version. */
763
- export const slh_dsa_sha2_256f: SphincsSigner = /* @__PURE__ */ gen(PARAMS['256f'], SHA512_SIMPLE);
764
- /** SLH-DSA: 256-bit small SHA2 version. */
765
- export const slh_dsa_sha2_256s: SphincsSigner = /* @__PURE__ */ gen(PARAMS['256s'], SHA512_SIMPLE);
962
+ /**
963
+ * SLH-DSA-SHA2-128f: Table 2 row `n=16, h=66, d=22, h'=3, a=6, k=33, lg w=4, m=34`;
964
+ * lengths `publicKey=32`, `secretKey=64`, `signature=17088`, `seed=48`, `signRand=16`.
965
+ * Also exposes `.prehash(...)`.
966
+ */
967
+ export const slh_dsa_sha2_128f: TRet<SphincsSigner> = /* @__PURE__ */ (() =>
968
+ gen(PARAMS['128f'], SHA256_SIMPLE))();
969
+ /**
970
+ * SLH-DSA-SHA2-128s: Table 2 row `n=16, h=63, d=7, h'=9, a=12, k=14, lg w=4, m=30`;
971
+ * lengths `publicKey=32`, `secretKey=64`, `signature=7856`, `seed=48`, `signRand=16`.
972
+ * Also exposes `.prehash(...)`.
973
+ */
974
+ export const slh_dsa_sha2_128s: TRet<SphincsSigner> = /* @__PURE__ */ (() =>
975
+ gen(PARAMS['128s'], SHA256_SIMPLE))();
976
+ /**
977
+ * SLH-DSA-SHA2-192f: Table 2 row `n=24, h=66, d=22, h'=3, a=8, k=33, lg w=4, m=42`;
978
+ * lengths `publicKey=48`, `secretKey=96`, `signature=35664`, `seed=72`, `signRand=24`.
979
+ * Also exposes `.prehash(...)`.
980
+ */
981
+ export const slh_dsa_sha2_192f: TRet<SphincsSigner> = /* @__PURE__ */ (() =>
982
+ gen(PARAMS['192f'], SHA512_SIMPLE))();
983
+ /**
984
+ * SLH-DSA-SHA2-192s: Table 2 row `n=24, h=63, d=7, h'=9, a=14, k=17, lg w=4, m=39`;
985
+ * lengths `publicKey=48`, `secretKey=96`, `signature=16224`, `seed=72`, `signRand=24`.
986
+ * Also exposes `.prehash(...)`.
987
+ */
988
+ export const slh_dsa_sha2_192s: TRet<SphincsSigner> = /* @__PURE__ */ (() =>
989
+ gen(PARAMS['192s'], SHA512_SIMPLE))();
990
+ /**
991
+ * SLH-DSA-SHA2-256f: Table 2 row `n=32, h=68, d=17, h'=4, a=9, k=35, lg w=4, m=49`;
992
+ * lengths `publicKey=64`, `secretKey=128`, `signature=49856`, `seed=96`, `signRand=32`.
993
+ * Also exposes `.prehash(...)`.
994
+ */
995
+ export const slh_dsa_sha2_256f: TRet<SphincsSigner> = /* @__PURE__ */ (() =>
996
+ gen(PARAMS['256f'], SHA512_SIMPLE))();
997
+ /**
998
+ * SLH-DSA-SHA2-256s: Table 2 row `n=32, h=64, d=8, h'=8, a=14, k=22, lg w=4, m=47`;
999
+ * lengths `publicKey=64`, `secretKey=128`, `signature=29792`, `seed=96`, `signRand=32`.
1000
+ * Also exposes `.prehash(...)`.
1001
+ */
1002
+ export const slh_dsa_sha2_256s: TRet<SphincsSigner> = /* @__PURE__ */ (() =>
1003
+ gen(PARAMS['256s'], SHA512_SIMPLE))();