@noble/post-quantum 0.5.3 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -39
- package/_crystals.d.ts +84 -0
- package/_crystals.d.ts.map +1 -1
- package/_crystals.js +64 -3
- package/_crystals.js.map +1 -1
- package/falcon.d.ts +84 -0
- package/falcon.d.ts.map +1 -0
- package/falcon.js +2378 -0
- package/falcon.js.map +1 -0
- package/hybrid.d.ts +181 -5
- package/hybrid.d.ts.map +1 -1
- package/hybrid.js +375 -53
- package/hybrid.js.map +1 -1
- package/ml-dsa.d.ts +22 -1
- package/ml-dsa.d.ts.map +1 -1
- package/ml-dsa.js +101 -51
- package/ml-dsa.js.map +1 -1
- package/ml-kem.d.ts +27 -3
- package/ml-kem.d.ts.map +1 -1
- package/ml-kem.js +154 -52
- package/ml-kem.js.map +1 -1
- package/package.json +12 -5
- package/slh-dsa.d.ts +116 -13
- package/slh-dsa.d.ts.map +1 -1
- package/slh-dsa.js +134 -35
- package/slh-dsa.js.map +1 -1
- package/src/_crystals.ts +101 -7
- package/src/falcon.ts +2470 -0
- package/src/hybrid.ts +406 -72
- package/src/ml-dsa.ts +144 -74
- package/src/ml-kem.ts +168 -54
- package/src/slh-dsa.ts +203 -44
- package/src/utils.ts +320 -15
- package/utils.d.ts +283 -4
- package/utils.d.ts.map +1 -1
- package/utils.js +245 -14
- package/utils.js.map +1 -1
package/src/slh-dsa.ts
CHANGED
|
@@ -62,30 +62,52 @@ import {
|
|
|
62
62
|
* * K: FORS trees numbers. A: FORS trees height
|
|
63
63
|
*/
|
|
64
64
|
export type SphincsOpts = {
|
|
65
|
+
/** Security parameter in bytes. */
|
|
65
66
|
N: number;
|
|
67
|
+
/** Winternitz parameter. */
|
|
66
68
|
W: number;
|
|
69
|
+
/** Total hypertree height. */
|
|
67
70
|
H: number;
|
|
71
|
+
/** Number of hypertree layers. */
|
|
68
72
|
D: number;
|
|
73
|
+
/** Number of FORS trees. */
|
|
69
74
|
K: number;
|
|
75
|
+
/** Height of each FORS tree. */
|
|
70
76
|
A: number;
|
|
77
|
+
/** Target security level in bits. */
|
|
71
78
|
securityLevel: number;
|
|
72
79
|
};
|
|
73
80
|
|
|
81
|
+
/** Hash customization options for SLH-DSA context creation. */
|
|
74
82
|
export type SphincsHashOpts = {
|
|
83
|
+
/** Whether to use the compressed-address variant from the standard. */
|
|
75
84
|
isCompressed?: boolean;
|
|
85
|
+
/** Factory that binds one parameter set to one per-key hash context generator. */
|
|
76
86
|
getContext: GetContext;
|
|
77
87
|
};
|
|
78
88
|
|
|
79
89
|
/** Winternitz signature params. */
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Built-in SLH-DSA Table 2 subset keyed by strength/profile.
|
|
92
|
+
* SHA2 and SHAKE pairs share the same numeric rows here, so the hash family is chosen separately.
|
|
93
|
+
* `securityLevel` stores 128/192/256-bit strengths for `checkHash(...)`,
|
|
94
|
+
* not Table 2's category labels 1/3/5.
|
|
95
|
+
* Other Table 2 columns such as `m`, public-key bytes, and signature bytes
|
|
96
|
+
* stay derived at the export layer.
|
|
97
|
+
*/
|
|
98
|
+
export const PARAMS: Record<string, SphincsOpts> = /* @__PURE__ */ (() =>
|
|
99
|
+
({
|
|
100
|
+
'128f': { W: 16, N: 16, H: 66, D: 22, K: 33, A: 6, securityLevel: 128 },
|
|
101
|
+
'128s': { W: 16, N: 16, H: 63, D: 7, K: 14, A: 12, securityLevel: 128 },
|
|
102
|
+
'192f': { W: 16, N: 24, H: 66, D: 22, K: 33, A: 8, securityLevel: 192 },
|
|
103
|
+
'192s': { W: 16, N: 24, H: 63, D: 7, K: 17, A: 14, securityLevel: 192 },
|
|
104
|
+
'256f': { W: 16, N: 32, H: 68, D: 17, K: 35, A: 9, securityLevel: 256 },
|
|
105
|
+
'256s': { W: 16, N: 32, H: 64, D: 8, K: 22, A: 14, securityLevel: 256 },
|
|
106
|
+
}) as const)();
|
|
107
|
+
|
|
108
|
+
// FIPS 205 `ADRS.setTypeAndClear(...)` selectors. Local names shorten the spec labels
|
|
109
|
+
// (`WOTS_HASH` -> `WOTS`, `TREE` -> `HASHTREE`, `FORS_ROOTS` -> `FORSPK`), and `setAddr({ type })`
|
|
110
|
+
// below only writes the type word; callers still need to preserve or overwrite the trailing words.
|
|
89
111
|
const AddressType = {
|
|
90
112
|
WOTS: 0,
|
|
91
113
|
WOTSPK: 1,
|
|
@@ -96,17 +118,53 @@ const AddressType = {
|
|
|
96
118
|
FORSPRF: 6,
|
|
97
119
|
} as const;
|
|
98
120
|
|
|
99
|
-
/** Address
|
|
121
|
+
/** Address byte array of size `ADDR_BYTES`. */
|
|
100
122
|
export type ADRS = Uint8Array;
|
|
101
123
|
|
|
124
|
+
/** Hash and tweakable-hash callbacks bound to one SLH-DSA keypair context. */
|
|
102
125
|
export type Context = {
|
|
126
|
+
/**
|
|
127
|
+
* Derive a PRF output for one address.
|
|
128
|
+
* @param addr - Address bytes.
|
|
129
|
+
* @returns PRF output bytes.
|
|
130
|
+
*/
|
|
103
131
|
PRFaddr: (addr: ADRS) => Uint8Array;
|
|
132
|
+
/**
|
|
133
|
+
* Derive the randomized message hash prefix.
|
|
134
|
+
* @param skPRF - Secret PRF seed.
|
|
135
|
+
* @param random - Per-signature randomness.
|
|
136
|
+
* @param msg - Message bytes.
|
|
137
|
+
* @returns PRF output bytes.
|
|
138
|
+
*/
|
|
104
139
|
PRFmsg: (skPRF: Uint8Array, random: Uint8Array, msg: Uint8Array) => Uint8Array;
|
|
140
|
+
/**
|
|
141
|
+
* Hash one randomized message transcript.
|
|
142
|
+
* @param R - Randomized message prefix.
|
|
143
|
+
* @param pk - Public key bytes.
|
|
144
|
+
* @param m - Message bytes.
|
|
145
|
+
* @param outLen - Output length in bytes.
|
|
146
|
+
* @returns Transcript hash bytes.
|
|
147
|
+
*/
|
|
105
148
|
Hmsg: (R: Uint8Array, pk: Uint8Array, m: Uint8Array, outLen: number) => Uint8Array;
|
|
149
|
+
/**
|
|
150
|
+
* Tweakable hash over one input block.
|
|
151
|
+
* @param input - Input block.
|
|
152
|
+
* @param addr - Address bytes.
|
|
153
|
+
* @returns Hash output bytes.
|
|
154
|
+
*/
|
|
106
155
|
thash1: (input: Uint8Array, addr: ADRS) => Uint8Array;
|
|
156
|
+
/**
|
|
157
|
+
* Tweakable hash over multiple input blocks.
|
|
158
|
+
* @param blocks - Number of input blocks.
|
|
159
|
+
* @param input - Concatenated input bytes.
|
|
160
|
+
* @param addr - Address bytes.
|
|
161
|
+
* @returns Hash output bytes.
|
|
162
|
+
*/
|
|
107
163
|
thashN: (blocks: number, input: Uint8Array, addr: ADRS) => Uint8Array;
|
|
164
|
+
/** Wipe any buffered hash state for the current context. */
|
|
108
165
|
clean: () => void;
|
|
109
166
|
};
|
|
167
|
+
/** Factory that creates a context generator for one SLH-DSA parameter set. */
|
|
110
168
|
export type GetContext = (
|
|
111
169
|
opts: SphincsOpts
|
|
112
170
|
) => (pub_seed: Uint8Array, sk_seed?: Uint8Array) => Context;
|
|
@@ -116,16 +174,19 @@ function hexToNumber(hex: string): bigint {
|
|
|
116
174
|
return BigInt(hex === '' ? '0' : '0x' + hex); // Big Endian
|
|
117
175
|
}
|
|
118
176
|
|
|
119
|
-
// BE: Big Endian, LE: Little Endian
|
|
177
|
+
// BE: Big Endian, LE: Little Endian. This is the local FIPS 205 `toInt(...)` equivalent.
|
|
120
178
|
function bytesToNumberBE(bytes: Uint8Array): bigint {
|
|
121
179
|
return hexToNumber(bytesToHex(bytes));
|
|
122
180
|
}
|
|
123
181
|
|
|
182
|
+
// Local in-range FIPS 205 `toByte(x, n)` equivalent; callers must keep `n < 256^len`.
|
|
124
183
|
function numberToBytesBE(n: number | bigint, len: number): Uint8Array {
|
|
125
184
|
return hexToBytes(n.toString(16).padStart(len * 2, '0'));
|
|
126
185
|
}
|
|
127
186
|
|
|
128
|
-
//
|
|
187
|
+
// Local FIPS 205 Algorithm 4 `base_2^b(...)` implementation. Bits are consumed in big-endian
|
|
188
|
+
// order within each input byte, and callers must provide at least `ceil(outLen * b / 8)` bytes;
|
|
189
|
+
// short inputs are not rejected and would zero-extend implicitly.
|
|
129
190
|
const base2b = (outLen: number, b: number) => {
|
|
130
191
|
const mask = getMask(b);
|
|
131
192
|
return (bytes: Uint8Array) => {
|
|
@@ -146,12 +207,18 @@ function getMaskBig(bits: number) {
|
|
|
146
207
|
return (1n << BigInt(bits)) - 1n; // 4 -> 0b1111
|
|
147
208
|
}
|
|
148
209
|
|
|
210
|
+
/** Public SLH-DSA signer with prehash customization. */
|
|
149
211
|
export type SphincsSigner = Signer & {
|
|
150
212
|
internal: Signer;
|
|
151
213
|
securityLevel: number;
|
|
152
214
|
prehash: (hash: CHash) => Signer;
|
|
153
215
|
};
|
|
154
216
|
|
|
217
|
+
/** One parameter/hash instantiation of the public SLH-DSA API.
|
|
218
|
+
* `keygen(seed)` is a deterministic 3N-byte library hook around the internal keygen flow,
|
|
219
|
+
* and `getPublicKey(secretKey)` only extracts the embedded public key
|
|
220
|
+
* instead of recomputing `PK.root`.
|
|
221
|
+
*/
|
|
155
222
|
function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
|
|
156
223
|
const { N, W, H, D, K, A, securityLevel: securityLevel } = opts;
|
|
157
224
|
const getContext = hashOpts.getContext(opts);
|
|
@@ -183,6 +250,11 @@ function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
|
|
|
183
250
|
OFFSET_HASH_ADDR += 10;
|
|
184
251
|
}
|
|
185
252
|
|
|
253
|
+
// Mutates and returns `addr` in place. For the built-in parameter sets, the layer / chain /
|
|
254
|
+
// hash / height / keypair values fit in the low byte(s), and the tree value fits in 64 bits,
|
|
255
|
+
// so the untouched leading bytes in the wider FIPS 205 ADRS / ADRS_c fields stay zero.
|
|
256
|
+
// `height` / `chain` and `index` / `hash` share the same spec words, so callers must use the
|
|
257
|
+
// address-type-specific combinations instead of mixing both meanings in one call.
|
|
186
258
|
const setAddr = (
|
|
187
259
|
opts: {
|
|
188
260
|
type?: (typeof AddressType)[keyof typeof AddressType];
|
|
@@ -227,7 +299,8 @@ function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
|
|
|
227
299
|
const W1 = base2b(WOTS_LEN1, WOTS_LOGW)(msg);
|
|
228
300
|
let csum = 0;
|
|
229
301
|
for (let i = 0; i < W1.length; i++) csum += W - 1 - W1[i]; // ▷ Compute checksum
|
|
230
|
-
|
|
302
|
+
// csum ← csum ≪ ((8 − ((len2 · lg(w)) mod 8)) mod 8
|
|
303
|
+
csum <<= (8 - ((WOTS_LEN2 * WOTS_LOGW) % 8)) % 8;
|
|
231
304
|
// Checksum to base(LOG_W)
|
|
232
305
|
const W2 = chainCoder(numberToBytesBE(csum, Math.ceil((WOTS_LEN2 * WOTS_LOGW) / 8)));
|
|
233
306
|
// W1 || W2 (concatBytes cannot concat TypedArrays)
|
|
@@ -246,14 +319,20 @@ function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
|
|
|
246
319
|
Math.ceil(TREE_BITS / 8),
|
|
247
320
|
Math.ceil(TREE_HEIGHT / 8)
|
|
248
321
|
);
|
|
322
|
+
// `pkSeed` is the full public key byte string `PK.seed || PK.root`; after splitting `Hmsg`,
|
|
323
|
+
// mask away any spare high bits so `idx_tree` / `idx_leaf` match the spec's final mod-2^k steps.
|
|
249
324
|
const hashMessage = (R: Uint8Array, pkSeed: Uint8Array, msg: Uint8Array, context: Context) => {
|
|
250
|
-
|
|
325
|
+
// digest ← Hmsg(R, PK.seed, PK.root, M)
|
|
326
|
+
const digest = context.Hmsg(R, pkSeed, msg, hashMsgCoder.bytesLen);
|
|
251
327
|
const [md, tmpIdxTree, tmpIdxLeaf] = hashMsgCoder.decode(digest);
|
|
252
328
|
const tree = bytesToNumberBE(tmpIdxTree) & getMaskBig(TREE_BITS);
|
|
253
329
|
const leafIdx = Number(bytesToNumberBE(tmpIdxLeaf)) & getMask(LEAF_BITS);
|
|
254
330
|
return { tree, leafIdx, md };
|
|
255
331
|
};
|
|
256
332
|
|
|
333
|
+
// Iterative `xmss_node` / `xmss_sign` core: mutate `treeAddr` in place, collapse completed
|
|
334
|
+
// sibling pairs on `stack`, and record the sibling whenever the current subtree is the auth-path
|
|
335
|
+
// neighbor of the target leaf at that height.
|
|
257
336
|
const treehash = <T>(
|
|
258
337
|
height: number,
|
|
259
338
|
fn: (leafIdx: number, addrOffset: number, context: Context, info: T) => Uint8Array
|
|
@@ -297,6 +376,8 @@ function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
|
|
|
297
376
|
};
|
|
298
377
|
const wotsTreehash = treehash(TREE_HEIGHT, (leafIdx, addrOffset, context, info: LeafInfo) => {
|
|
299
378
|
const wotsPk = new Uint8Array(WOTS_LEN * N);
|
|
379
|
+
// `keygen()` passes `leafIdx = ~0 >>> 0`, so no real XMSS leaf matches and this suppresses
|
|
380
|
+
// WOTS signature capture while still hashing every chain to its public-key endpoint.
|
|
300
381
|
const wotsKmask = addrOffset === leafIdx ? 0 : ~0 >>> 0;
|
|
301
382
|
setAddr({ keypair: addrOffset }, info.leafAddr);
|
|
302
383
|
setAddr({ keypair: addrOffset }, info.pkAddr);
|
|
@@ -323,6 +404,8 @@ function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
|
|
|
323
404
|
return context.thash1(prf, forsLeafAddr);
|
|
324
405
|
});
|
|
325
406
|
|
|
407
|
+
// Fuse `xmss_sign` with the subtree-root computation needed by `ht_sign`, so one tree walk
|
|
408
|
+
// yields both the WOTS/auth-path signature and the root that the next hypertree layer signs.
|
|
326
409
|
const merkleSign = (
|
|
327
410
|
context: Context,
|
|
328
411
|
wotsAddr: ADRS,
|
|
@@ -360,6 +443,10 @@ function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
|
|
|
360
443
|
const buffer = new Uint8Array(2 * N);
|
|
361
444
|
const b0 = buffer.subarray(0, N);
|
|
362
445
|
const b1 = buffer.subarray(N, 2 * N);
|
|
446
|
+
// Algorithm 11 hashes `node || AUTH[k]` for even nodes and `AUTH[k] || node` for odd ones,
|
|
447
|
+
// so reuse one `2N` buffer and just swap which half receives the sibling at each level.
|
|
448
|
+
// `idxOffset` carries the subtree base for the shared FORS path, so `leafIdx + idxOffset`
|
|
449
|
+
// tracks the same tree-global index updates that Algorithms 11 and 17 apply to ADRS.
|
|
363
450
|
// First iter
|
|
364
451
|
if ((leafIdx & 1) !== 0) {
|
|
365
452
|
b1.set(leaf.subarray(0, N));
|
|
@@ -597,10 +684,14 @@ function gen(opts: SphincsOpts, hashOpts: SphincsHashOpts): SphincsSigner {
|
|
|
597
684
|
};
|
|
598
685
|
}
|
|
599
686
|
|
|
687
|
+
// FIPS 205 §11.1 SHAKE instantiation: this path hashes the full uncompressed address bytes,
|
|
688
|
+
// unlike the compressed 22-byte SHA2 path in §11.2.
|
|
600
689
|
const genShake =
|
|
601
690
|
(): GetContext => (opts: SphincsOpts) => (pubSeed: Uint8Array, skSeed?: Uint8Array) => {
|
|
602
691
|
const { N } = opts;
|
|
603
692
|
const stats = { prf: 0, thash: 0, hmsg: 0, gen_message_random: 0 };
|
|
693
|
+
// §11.1 prefixes PRF/F/H/T_l with `PK.seed`, so cache that absorbed prefix once and clone it
|
|
694
|
+
// for each address-bound call instead of reabsorbing the same seed every time.
|
|
604
695
|
const h0 = shake256.create({}).update(pubSeed);
|
|
605
696
|
const h0tmp = h0.clone();
|
|
606
697
|
const thash = (blocks: number, input: Uint8Array, addr: ADRS) => {
|
|
@@ -636,22 +727,55 @@ const genShake =
|
|
|
636
727
|
};
|
|
637
728
|
};
|
|
638
729
|
|
|
639
|
-
const SHAKE_SIMPLE = { getContext: genShake() };
|
|
640
|
-
|
|
641
|
-
/**
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
export const
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
730
|
+
const SHAKE_SIMPLE = /* @__PURE__ */ (() => ({ getContext: genShake() }))();
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* SLH-DSA-SHAKE-128f: Table 2 row `n=16, h=66, d=22, h'=3, a=6, k=33, lg w=4, m=34`;
|
|
734
|
+
* lengths `publicKey=32`, `secretKey=64`, `signature=17088`, `seed=48`, `signRand=16`.
|
|
735
|
+
* Also exposes `.prehash(...)`.
|
|
736
|
+
*/
|
|
737
|
+
export const slh_dsa_shake_128f: SphincsSigner = /* @__PURE__ */ (() =>
|
|
738
|
+
gen(PARAMS['128f'], SHAKE_SIMPLE))();
|
|
739
|
+
/**
|
|
740
|
+
* SLH-DSA-SHAKE-128s: Table 2 row `n=16, h=63, d=7, h'=9, a=12, k=14, lg w=4, m=30`;
|
|
741
|
+
* lengths `publicKey=32`, `secretKey=64`, `signature=7856`, `seed=48`, `signRand=16`.
|
|
742
|
+
* Also exposes `.prehash(...)`.
|
|
743
|
+
*/
|
|
744
|
+
export const slh_dsa_shake_128s: SphincsSigner = /* @__PURE__ */ (() =>
|
|
745
|
+
gen(PARAMS['128s'], SHAKE_SIMPLE))();
|
|
746
|
+
/**
|
|
747
|
+
* SLH-DSA-SHAKE-192f: Table 2 row `n=24, h=66, d=22, h'=3, a=8, k=33, lg w=4, m=42`;
|
|
748
|
+
* lengths `publicKey=48`, `secretKey=96`, `signature=35664`, `seed=72`, `signRand=24`.
|
|
749
|
+
* Also exposes `.prehash(...)`.
|
|
750
|
+
*/
|
|
751
|
+
export const slh_dsa_shake_192f: SphincsSigner = /* @__PURE__ */ (() =>
|
|
752
|
+
gen(PARAMS['192f'], SHAKE_SIMPLE))();
|
|
753
|
+
/**
|
|
754
|
+
* SLH-DSA-SHAKE-192s: Table 2 row `n=24, h=63, d=7, h'=9, a=14, k=17, lg w=4, m=39`;
|
|
755
|
+
* lengths `publicKey=48`, `secretKey=96`, `signature=16224`, `seed=72`, `signRand=24`.
|
|
756
|
+
* Also exposes `.prehash(...)`.
|
|
757
|
+
*/
|
|
758
|
+
export const slh_dsa_shake_192s: SphincsSigner = /* @__PURE__ */ (() =>
|
|
759
|
+
gen(PARAMS['192s'], SHAKE_SIMPLE))();
|
|
760
|
+
/**
|
|
761
|
+
* SLH-DSA-SHAKE-256f: Table 2 row `n=32, h=68, d=17, h'=4, a=9, k=35, lg w=4, m=49`;
|
|
762
|
+
* lengths `publicKey=64`, `secretKey=128`, `signature=49856`, `seed=96`, `signRand=32`.
|
|
763
|
+
* Also exposes `.prehash(...)`.
|
|
764
|
+
*/
|
|
765
|
+
export const slh_dsa_shake_256f: SphincsSigner = /* @__PURE__ */ (() =>
|
|
766
|
+
gen(PARAMS['256f'], SHAKE_SIMPLE))();
|
|
767
|
+
/**
|
|
768
|
+
* SLH-DSA-SHAKE-256s: Table 2 row `n=32, h=64, d=8, h'=8, a=14, k=22, lg w=4, m=47`;
|
|
769
|
+
* lengths `publicKey=64`, `secretKey=128`, `signature=29792`, `seed=96`, `signRand=32`.
|
|
770
|
+
* Also exposes `.prehash(...)`.
|
|
771
|
+
*/
|
|
772
|
+
export const slh_dsa_shake_256s: SphincsSigner = /* @__PURE__ */ (() =>
|
|
773
|
+
gen(PARAMS['256s'], SHAKE_SIMPLE))();
|
|
653
774
|
|
|
654
775
|
type ShaType = typeof sha256 | typeof sha512;
|
|
776
|
+
// FIPS 205 §11.2 SHA2 instantiation. The `h0` / `h1` split is intentional:
|
|
777
|
+
// category-1 keeps everything on SHA-256, while category-3/5 keep `PRFaddr` / `thash1`
|
|
778
|
+
// on SHA-256 but switch `PRFmsg`, `Hmsg`, and multi-block `thashN` to SHA-512.
|
|
655
779
|
const genSha =
|
|
656
780
|
(h0: ShaType, h1: ShaType): GetContext =>
|
|
657
781
|
(opts) =>
|
|
@@ -667,6 +791,8 @@ const genSha =
|
|
|
667
791
|
|
|
668
792
|
const counterB = new Uint8Array(4);
|
|
669
793
|
const counterV = createView(counterB);
|
|
794
|
+
// §11.2 prefixes SHA2 PRF/F/H/T_l with `PK.seed || toByte(0, blockLen-N)`, so cache the
|
|
795
|
+
// zero-padded seed block once for the SHA-256 lane and once for the SHA-512 lane.
|
|
670
796
|
const h0ps = h0
|
|
671
797
|
.create()
|
|
672
798
|
.update(pub_seed)
|
|
@@ -680,6 +806,9 @@ const genSha =
|
|
|
680
806
|
const h1tmp = h1ps.clone();
|
|
681
807
|
|
|
682
808
|
// https://www.rfc-editor.org/rfc/rfc8017.html#appendix-B.2.1
|
|
809
|
+
// This local helper is intentionally stricter than generic MGF1 reuse: current SLH-DSA callers
|
|
810
|
+
// only request tiny `m`-byte outputs, but the guard below rejects `length > 2^32` instead of
|
|
811
|
+
// RFC 8017's broader `maskLen > 2^32 * hLen` bound.
|
|
683
812
|
function mgf1(seed: Uint8Array, length: number, hash: ShaType) {
|
|
684
813
|
stats.mgf1++;
|
|
685
814
|
const out = new Uint8Array(Math.ceil(length / hash.outputLen) * hash.outputLen);
|
|
@@ -742,24 +871,54 @@ const genSha =
|
|
|
742
871
|
};
|
|
743
872
|
};
|
|
744
873
|
|
|
745
|
-
const SHA256_SIMPLE = {
|
|
874
|
+
const SHA256_SIMPLE = /* @__PURE__ */ (() => ({
|
|
746
875
|
isCompressed: true,
|
|
747
876
|
getContext: genSha(sha256, sha256),
|
|
748
|
-
};
|
|
749
|
-
const SHA512_SIMPLE = {
|
|
877
|
+
}))();
|
|
878
|
+
const SHA512_SIMPLE = /* @__PURE__ */ (() => ({
|
|
750
879
|
isCompressed: true,
|
|
751
880
|
getContext: genSha(sha256, sha512),
|
|
752
|
-
};
|
|
881
|
+
}))();
|
|
753
882
|
|
|
754
|
-
/**
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
export const
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
883
|
+
/**
|
|
884
|
+
* SLH-DSA-SHA2-128f: Table 2 row `n=16, h=66, d=22, h'=3, a=6, k=33, lg w=4, m=34`;
|
|
885
|
+
* lengths `publicKey=32`, `secretKey=64`, `signature=17088`, `seed=48`, `signRand=16`.
|
|
886
|
+
* Also exposes `.prehash(...)`.
|
|
887
|
+
*/
|
|
888
|
+
export const slh_dsa_sha2_128f: SphincsSigner = /* @__PURE__ */ (() =>
|
|
889
|
+
gen(PARAMS['128f'], SHA256_SIMPLE))();
|
|
890
|
+
/**
|
|
891
|
+
* SLH-DSA-SHA2-128s: Table 2 row `n=16, h=63, d=7, h'=9, a=12, k=14, lg w=4, m=30`;
|
|
892
|
+
* lengths `publicKey=32`, `secretKey=64`, `signature=7856`, `seed=48`, `signRand=16`.
|
|
893
|
+
* Also exposes `.prehash(...)`.
|
|
894
|
+
*/
|
|
895
|
+
export const slh_dsa_sha2_128s: SphincsSigner = /* @__PURE__ */ (() =>
|
|
896
|
+
gen(PARAMS['128s'], SHA256_SIMPLE))();
|
|
897
|
+
/**
|
|
898
|
+
* SLH-DSA-SHA2-192f: Table 2 row `n=24, h=66, d=22, h'=3, a=8, k=33, lg w=4, m=42`;
|
|
899
|
+
* lengths `publicKey=48`, `secretKey=96`, `signature=35664`, `seed=72`, `signRand=24`.
|
|
900
|
+
* Also exposes `.prehash(...)`.
|
|
901
|
+
*/
|
|
902
|
+
export const slh_dsa_sha2_192f: SphincsSigner = /* @__PURE__ */ (() =>
|
|
903
|
+
gen(PARAMS['192f'], SHA512_SIMPLE))();
|
|
904
|
+
/**
|
|
905
|
+
* SLH-DSA-SHA2-192s: Table 2 row `n=24, h=63, d=7, h'=9, a=14, k=17, lg w=4, m=39`;
|
|
906
|
+
* lengths `publicKey=48`, `secretKey=96`, `signature=16224`, `seed=72`, `signRand=24`.
|
|
907
|
+
* Also exposes `.prehash(...)`.
|
|
908
|
+
*/
|
|
909
|
+
export const slh_dsa_sha2_192s: SphincsSigner = /* @__PURE__ */ (() =>
|
|
910
|
+
gen(PARAMS['192s'], SHA512_SIMPLE))();
|
|
911
|
+
/**
|
|
912
|
+
* SLH-DSA-SHA2-256f: Table 2 row `n=32, h=68, d=17, h'=4, a=9, k=35, lg w=4, m=49`;
|
|
913
|
+
* lengths `publicKey=64`, `secretKey=128`, `signature=49856`, `seed=96`, `signRand=32`.
|
|
914
|
+
* Also exposes `.prehash(...)`.
|
|
915
|
+
*/
|
|
916
|
+
export const slh_dsa_sha2_256f: SphincsSigner = /* @__PURE__ */ (() =>
|
|
917
|
+
gen(PARAMS['256f'], SHA512_SIMPLE))();
|
|
918
|
+
/**
|
|
919
|
+
* SLH-DSA-SHA2-256s: Table 2 row `n=32, h=64, d=8, h'=8, a=14, k=22, lg w=4, m=47`;
|
|
920
|
+
* lengths `publicKey=64`, `secretKey=128`, `signature=29792`, `seed=96`, `signRand=32`.
|
|
921
|
+
* Also exposes `.prehash(...)`.
|
|
922
|
+
*/
|
|
923
|
+
export const slh_dsa_sha2_256s: SphincsSigner = /* @__PURE__ */ (() =>
|
|
924
|
+
gen(PARAMS['256s'], SHA512_SIMPLE))();
|