leviathan-crypto 2.1.0 → 3.0.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.
Files changed (296) hide show
  1. package/CLAUDE.md +86 -443
  2. package/README.md +198 -65
  3. package/dist/aes/aes-cbc.d.ts +40 -0
  4. package/dist/aes/aes-cbc.js +158 -0
  5. package/dist/aes/aes-ctr.d.ts +50 -0
  6. package/dist/aes/aes-ctr.js +141 -0
  7. package/dist/aes/aes-gcm-siv.d.ts +67 -0
  8. package/dist/aes/aes-gcm-siv.js +217 -0
  9. package/dist/aes/aes-gcm.d.ts +61 -0
  10. package/dist/aes/aes-gcm.js +226 -0
  11. package/dist/aes/cipher-suite.d.ts +21 -0
  12. package/dist/aes/cipher-suite.js +179 -0
  13. package/dist/aes/embedded.d.ts +1 -0
  14. package/dist/aes/embedded.js +26 -0
  15. package/dist/aes/generator.d.ts +14 -0
  16. package/dist/aes/generator.js +103 -0
  17. package/dist/aes/index.d.ts +58 -0
  18. package/dist/aes/index.js +125 -0
  19. package/dist/aes/ops.d.ts +60 -0
  20. package/dist/aes/ops.js +164 -0
  21. package/dist/aes/pool-worker.d.ts +1 -0
  22. package/dist/aes/pool-worker.js +92 -0
  23. package/dist/aes/types.d.ts +1 -0
  24. package/dist/aes/types.js +23 -0
  25. package/dist/aes.wasm +0 -0
  26. package/dist/blake3/embedded.d.ts +1 -0
  27. package/dist/blake3/embedded.js +26 -0
  28. package/dist/blake3/index.d.ts +143 -0
  29. package/dist/blake3/index.js +620 -0
  30. package/dist/blake3/types.d.ts +102 -0
  31. package/dist/blake3/types.js +31 -0
  32. package/dist/blake3/validate.d.ts +29 -0
  33. package/dist/blake3/validate.js +80 -0
  34. package/dist/blake3.wasm +0 -0
  35. package/dist/chacha20/cipher-suite.js +47 -25
  36. package/dist/chacha20/generator.d.ts +2 -2
  37. package/dist/chacha20/generator.js +4 -4
  38. package/dist/chacha20/index.d.ts +16 -15
  39. package/dist/chacha20/index.js +52 -46
  40. package/dist/chacha20/ops.d.ts +7 -7
  41. package/dist/chacha20/ops.js +34 -34
  42. package/dist/chacha20/pool-worker.js +5 -3
  43. package/dist/cte-wasm.d.ts +1 -0
  44. package/dist/cte-wasm.js +3 -0
  45. package/dist/curve25519.wasm +0 -0
  46. package/dist/ecdsa/der.d.ts +23 -0
  47. package/dist/ecdsa/der.js +192 -0
  48. package/dist/ecdsa/ecprivatekey-der.d.ts +32 -0
  49. package/dist/ecdsa/ecprivatekey-der.js +230 -0
  50. package/dist/ecdsa/embedded.d.ts +1 -0
  51. package/dist/ecdsa/embedded.js +25 -0
  52. package/dist/ecdsa/index.d.ts +124 -0
  53. package/dist/ecdsa/index.js +366 -0
  54. package/dist/ecdsa/types.d.ts +31 -0
  55. package/dist/ecdsa/types.js +28 -0
  56. package/dist/ecdsa/validate.d.ts +18 -0
  57. package/dist/ecdsa/validate.js +92 -0
  58. package/dist/ed25519/embedded.d.ts +1 -0
  59. package/dist/ed25519/embedded.js +31 -0
  60. package/dist/ed25519/index.d.ts +70 -0
  61. package/dist/ed25519/index.js +308 -0
  62. package/dist/ed25519/types.d.ts +27 -0
  63. package/dist/ed25519/types.js +27 -0
  64. package/dist/ed25519/validate.d.ts +7 -0
  65. package/dist/ed25519/validate.js +77 -0
  66. package/dist/embedded/aes-pool-worker.d.ts +1 -0
  67. package/dist/embedded/aes-pool-worker.js +5 -0
  68. package/dist/embedded/aes.d.ts +1 -0
  69. package/dist/embedded/aes.js +3 -0
  70. package/dist/embedded/blake3.d.ts +1 -0
  71. package/dist/embedded/blake3.js +3 -0
  72. package/dist/embedded/chacha20-pool-worker.d.ts +1 -1
  73. package/dist/embedded/chacha20-pool-worker.js +2 -2
  74. package/dist/embedded/chacha20.d.ts +1 -1
  75. package/dist/embedded/chacha20.js +2 -2
  76. package/dist/embedded/curve25519.d.ts +1 -0
  77. package/dist/embedded/curve25519.js +3 -0
  78. package/dist/embedded/mldsa.d.ts +1 -0
  79. package/dist/embedded/mldsa.js +3 -0
  80. package/dist/embedded/mlkem.d.ts +1 -0
  81. package/dist/embedded/mlkem.js +3 -0
  82. package/dist/embedded/p256.d.ts +1 -0
  83. package/dist/embedded/p256.js +3 -0
  84. package/dist/embedded/serpent-pool-worker.d.ts +1 -1
  85. package/dist/embedded/serpent-pool-worker.js +2 -2
  86. package/dist/embedded/serpent.d.ts +1 -1
  87. package/dist/embedded/serpent.js +2 -2
  88. package/dist/embedded/sha2.d.ts +1 -1
  89. package/dist/embedded/sha2.js +2 -2
  90. package/dist/embedded/sha3.d.ts +1 -1
  91. package/dist/embedded/sha3.js +2 -2
  92. package/dist/embedded/slhdsa.d.ts +1 -0
  93. package/dist/embedded/slhdsa.js +3 -0
  94. package/dist/errors.d.ts +92 -1
  95. package/dist/errors.js +111 -1
  96. package/dist/fortuna.d.ts +5 -5
  97. package/dist/fortuna.js +37 -64
  98. package/dist/index.d.ts +38 -9
  99. package/dist/index.js +63 -19
  100. package/dist/init.d.ts +1 -1
  101. package/dist/init.js +11 -25
  102. package/dist/keccak/embedded.js +1 -1
  103. package/dist/keccak/index.d.ts +2 -0
  104. package/dist/keccak/index.js +4 -2
  105. package/dist/loader.d.ts +1 -24
  106. package/dist/loader.js +13 -16
  107. package/dist/merkle/blake3-tree.d.ts +35 -0
  108. package/dist/merkle/blake3-tree.js +187 -0
  109. package/dist/merkle/checkpoint.d.ts +58 -0
  110. package/dist/merkle/checkpoint.js +217 -0
  111. package/dist/merkle/index.d.ts +19 -0
  112. package/dist/merkle/index.js +37 -0
  113. package/dist/merkle/merkle-log.d.ts +130 -0
  114. package/dist/merkle/merkle-log.js +207 -0
  115. package/dist/merkle/merkle-verifier.d.ts +126 -0
  116. package/dist/merkle/merkle-verifier.js +296 -0
  117. package/dist/merkle/proof.d.ts +70 -0
  118. package/dist/merkle/proof.js +300 -0
  119. package/dist/merkle/sha256-tree.d.ts +33 -0
  120. package/dist/merkle/sha256-tree.js +145 -0
  121. package/dist/merkle/signed-log.d.ts +156 -0
  122. package/dist/merkle/signed-log.js +356 -0
  123. package/dist/merkle/signed-note.d.ts +309 -0
  124. package/dist/merkle/signed-note.js +648 -0
  125. package/dist/merkle/sth.d.ts +31 -0
  126. package/dist/merkle/sth.js +31 -0
  127. package/dist/merkle/storage.d.ts +40 -0
  128. package/dist/merkle/storage.js +71 -0
  129. package/dist/merkle/tree.d.ts +68 -0
  130. package/dist/merkle/tree.js +94 -0
  131. package/dist/mldsa/embedded.d.ts +1 -0
  132. package/dist/{kyber → mldsa}/embedded.js +5 -5
  133. package/dist/mldsa/expand.d.ts +53 -0
  134. package/dist/mldsa/expand.js +188 -0
  135. package/dist/mldsa/format.d.ts +16 -0
  136. package/dist/mldsa/format.js +68 -0
  137. package/dist/mldsa/hashvariant.d.ts +32 -0
  138. package/dist/mldsa/hashvariant.js +248 -0
  139. package/dist/mldsa/index.d.ts +142 -0
  140. package/dist/mldsa/index.js +463 -0
  141. package/dist/mldsa/keygen.d.ts +16 -0
  142. package/dist/mldsa/keygen.js +232 -0
  143. package/dist/mldsa/params.d.ts +21 -0
  144. package/dist/mldsa/params.js +55 -0
  145. package/dist/mldsa/sha3-helpers.d.ts +30 -0
  146. package/dist/mldsa/sha3-helpers.js +124 -0
  147. package/dist/mldsa/sign.d.ts +36 -0
  148. package/dist/mldsa/sign.js +380 -0
  149. package/dist/mldsa/types.d.ts +91 -0
  150. package/dist/mldsa/types.js +25 -0
  151. package/dist/mldsa/validate.d.ts +55 -0
  152. package/dist/mldsa/validate.js +125 -0
  153. package/dist/mldsa/verify.d.ts +29 -0
  154. package/dist/mldsa/verify.js +269 -0
  155. package/dist/mldsa.wasm +0 -0
  156. package/dist/mlkem/embedded.d.ts +1 -0
  157. package/dist/mlkem/embedded.js +27 -0
  158. package/dist/mlkem/indcpa.d.ts +49 -0
  159. package/dist/{kyber → mlkem}/indcpa.js +44 -44
  160. package/dist/mlkem/index.d.ts +37 -0
  161. package/dist/{kyber → mlkem}/index.js +24 -34
  162. package/dist/mlkem/kem.d.ts +21 -0
  163. package/dist/{kyber → mlkem}/kem.js +44 -64
  164. package/dist/{kyber → mlkem}/params.d.ts +4 -4
  165. package/dist/{kyber → mlkem}/params.js +2 -2
  166. package/dist/mlkem/suite.d.ts +12 -0
  167. package/dist/{kyber → mlkem}/suite.js +17 -12
  168. package/dist/{kyber → mlkem}/types.d.ts +3 -3
  169. package/dist/{kyber → mlkem}/types.js +1 -1
  170. package/dist/{kyber → mlkem}/validate.d.ts +7 -7
  171. package/dist/{kyber → mlkem}/validate.js +7 -7
  172. package/dist/{kyber.wasm → mlkem.wasm} +0 -0
  173. package/dist/p256.wasm +0 -0
  174. package/dist/ratchet/index.d.ts +2 -0
  175. package/dist/ratchet/index.js +1 -0
  176. package/dist/ratchet/kdf-chain.js +3 -3
  177. package/dist/ratchet/ratchet-keypair.js +2 -2
  178. package/dist/ratchet/root-kdf.js +7 -7
  179. package/dist/ratchet/skipped-key-store.js +4 -4
  180. package/dist/ratchet/types.d.ts +1 -1
  181. package/dist/serpent/cipher-suite.js +20 -17
  182. package/dist/serpent/generator.d.ts +1 -1
  183. package/dist/serpent/generator.js +2 -2
  184. package/dist/serpent/index.d.ts +8 -7
  185. package/dist/serpent/index.js +18 -27
  186. package/dist/serpent/pool-worker.js +7 -5
  187. package/dist/serpent/serpent-cbc.d.ts +4 -4
  188. package/dist/serpent/serpent-cbc.js +11 -8
  189. package/dist/serpent/shared-ops.d.ts +3 -23
  190. package/dist/serpent/shared-ops.js +50 -85
  191. package/dist/serpent.wasm +0 -0
  192. package/dist/sha2/hkdf.js +5 -5
  193. package/dist/sha2/index.d.ts +21 -1
  194. package/dist/sha2/index.js +65 -10
  195. package/dist/sha2/types.d.ts +41 -2
  196. package/dist/sha2.wasm +0 -0
  197. package/dist/sha3/index.d.ts +72 -3
  198. package/dist/sha3/index.js +240 -14
  199. package/dist/sha3/kmac.d.ts +121 -0
  200. package/dist/sha3/kmac.js +800 -0
  201. package/dist/sha3.wasm +0 -0
  202. package/dist/shared/pkcs7.d.ts +22 -0
  203. package/dist/shared/pkcs7.js +84 -0
  204. package/dist/sign/ctx.d.ts +41 -0
  205. package/dist/sign/ctx.js +102 -0
  206. package/dist/sign/envelope.d.ts +45 -0
  207. package/dist/sign/envelope.js +152 -0
  208. package/dist/sign/hasher.d.ts +9 -0
  209. package/dist/sign/hasher.js +132 -0
  210. package/dist/sign/index.d.ts +11 -0
  211. package/dist/sign/index.js +34 -0
  212. package/dist/sign/sign-stream.d.ts +25 -0
  213. package/dist/sign/sign-stream.js +112 -0
  214. package/dist/sign/suites/ecdsa-p256.d.ts +2 -0
  215. package/dist/sign/suites/ecdsa-p256.js +120 -0
  216. package/dist/sign/suites/ed25519.d.ts +3 -0
  217. package/dist/sign/suites/ed25519.js +165 -0
  218. package/dist/sign/suites/hybrid-classical.d.ts +23 -0
  219. package/dist/sign/suites/hybrid-classical.js +526 -0
  220. package/dist/sign/suites/hybrid-pq.d.ts +4 -0
  221. package/dist/sign/suites/hybrid-pq.js +234 -0
  222. package/dist/sign/suites/mldsa.d.ts +7 -0
  223. package/dist/sign/suites/mldsa.js +161 -0
  224. package/dist/sign/suites/slhdsa.d.ts +7 -0
  225. package/dist/sign/suites/slhdsa.js +176 -0
  226. package/dist/sign/types.d.ts +106 -0
  227. package/dist/sign/types.js +28 -0
  228. package/dist/sign/verify-stream.d.ts +30 -0
  229. package/dist/sign/verify-stream.js +227 -0
  230. package/dist/slhdsa/embedded.d.ts +1 -0
  231. package/dist/slhdsa/embedded.js +26 -0
  232. package/dist/slhdsa/index.d.ts +149 -0
  233. package/dist/slhdsa/index.js +493 -0
  234. package/dist/slhdsa/params.d.ts +26 -0
  235. package/dist/slhdsa/params.js +70 -0
  236. package/dist/slhdsa/prehash.d.ts +68 -0
  237. package/dist/slhdsa/prehash.js +307 -0
  238. package/dist/slhdsa/sign.d.ts +39 -0
  239. package/dist/slhdsa/sign.js +116 -0
  240. package/dist/slhdsa/types.d.ts +129 -0
  241. package/dist/slhdsa/types.js +27 -0
  242. package/dist/slhdsa/validate.d.ts +60 -0
  243. package/dist/slhdsa/validate.js +127 -0
  244. package/dist/slhdsa/verify.d.ts +32 -0
  245. package/dist/slhdsa/verify.js +107 -0
  246. package/dist/slhdsa.wasm +0 -0
  247. package/dist/stream/header.js +3 -3
  248. package/dist/stream/index.d.ts +1 -0
  249. package/dist/stream/index.js +1 -0
  250. package/dist/stream/open-stream.js +31 -10
  251. package/dist/stream/seal-stream-pool.d.ts +1 -0
  252. package/dist/stream/seal-stream-pool.js +63 -26
  253. package/dist/stream/seal-stream.d.ts +1 -1
  254. package/dist/stream/seal-stream.js +20 -9
  255. package/dist/stream/seal.js +6 -6
  256. package/dist/stream/types.d.ts +3 -1
  257. package/dist/stream/types.js +1 -1
  258. package/dist/types.d.ts +1 -1
  259. package/dist/types.js +1 -1
  260. package/dist/utils.d.ts +3 -3
  261. package/dist/utils.js +46 -54
  262. package/dist/wasm-source.d.ts +7 -7
  263. package/dist/wasm-source.js +1 -1
  264. package/dist/x25519/embedded.d.ts +1 -0
  265. package/dist/x25519/embedded.js +31 -0
  266. package/dist/x25519/index.d.ts +43 -0
  267. package/dist/x25519/index.js +159 -0
  268. package/dist/x25519/types.d.ts +25 -0
  269. package/dist/x25519/types.js +27 -0
  270. package/dist/x25519/validate.d.ts +2 -0
  271. package/dist/x25519/validate.js +39 -0
  272. package/package.json +70 -26
  273. package/SECURITY.md +0 -163
  274. package/dist/ct-wasm.d.ts +0 -1
  275. package/dist/ct-wasm.js +0 -3
  276. package/dist/docs/aead.md +0 -363
  277. package/dist/docs/architecture.md +0 -1011
  278. package/dist/docs/argon2id.md +0 -305
  279. package/dist/docs/chacha20.md +0 -781
  280. package/dist/docs/exports.md +0 -277
  281. package/dist/docs/fortuna.md +0 -530
  282. package/dist/docs/init.md +0 -301
  283. package/dist/docs/loader.md +0 -256
  284. package/dist/docs/serpent.md +0 -617
  285. package/dist/docs/sha2.md +0 -671
  286. package/dist/docs/sha3.md +0 -612
  287. package/dist/docs/types.md +0 -416
  288. package/dist/docs/utils.md +0 -457
  289. package/dist/embedded/kyber.d.ts +0 -1
  290. package/dist/embedded/kyber.js +0 -3
  291. package/dist/kyber/embedded.d.ts +0 -1
  292. package/dist/kyber/indcpa.d.ts +0 -49
  293. package/dist/kyber/index.d.ts +0 -38
  294. package/dist/kyber/kem.d.ts +0 -21
  295. package/dist/kyber/suite.d.ts +0 -12
  296. /package/dist/{ct.wasm → cte.wasm} +0 -0
@@ -0,0 +1,493 @@
1
+ // ▄▄▄▄▄▄▄▄▄▄
2
+ // ▄████████████████████▄▄ ▒ ▄▀▀ ▒ ▒ █ ▄▀▄ ▀█▀ █ ▒ ▄▀▄ █▀▄
3
+ // ▄██████████████████████ ▀████▄ ▓ ▓▀ ▓ ▓ ▓ ▓▄▓ ▓ ▓▀▓ ▓▄▓ ▓ ▓
4
+ // ▄█████████▀▀▀ ▀███████▄▄███████▌ ▀▄ ▀▄▄ ▀▄▀ ▒ ▒ ▒ ▒ ▒ █ ▒ ▒ ▒ █
5
+ // ▐████████▀ ▄▄▄▄ ▀████████▀██▀█▌
6
+ // ████████ ███▀▀ ████▀ █▀ █▀ Leviathan Crypto Library
7
+ // ███████▌ ▀██▀ ███
8
+ // ███████ ▀███ ▀██ ▀█▄ Repository & Mirror:
9
+ // ▀██████ ▄▄██ ▀▀ ██▄ github.com/xero/leviathan-crypto
10
+ // ▀█████▄ ▄██▄ ▄▀▄▀ unpkg.com/leviathan-crypto
11
+ // ▀████▄ ▄██▄
12
+ // ▐████ ▐███ Author: xero (https://x-e.ro)
13
+ // ▄▄██████████ ▐███ ▄▄ License: MIT
14
+ // ▄██▀▀▀▀▀▀▀▀▀▀ ▄████ ▄██▀
15
+ // ▄▀ ▄▄█████████▄▄ ▀▀▀▀▀ ▄███ This file is provided completely
16
+ // ▄██████▀▀▀▀▀▀██████▄ ▀▄▄▄▄████▀ free, "as is", and without
17
+ // ████▀ ▄▄▄▄▄▄▄ ▀████▄ ▀█████▀ ▄▄▄▄ warranty of any kind. The author
18
+ // █████▄▄█████▀▀▀▀▀▀▄ ▀███▄ ▄████ assumes absolutely no liability
19
+ // ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
20
+ // ▀█████▀▀
21
+ //
22
+ // src/ts/slhdsa/index.ts
23
+ //
24
+ // SLH-DSA public API, SlhDsa128f / SlhDsa192f / SlhDsa256f classes.
25
+ // FIPS 205, Stateless Hash-Based Digital Signature Standard.
26
+ //
27
+ // Public surface: keygen / sign / verify, plus the HashSLH-DSA family
28
+ // (signHash / verifyHash) and prehashed variants (signHashPrehashed /
29
+ // verifyHashPrehashed). Use init({ slhdsa, ... }) before constructing
30
+ // any class. Pure-mode usage needs only the slhdsa module; HashSLH-DSA
31
+ // with SHA-2 pre-hash adds sha2, with SHA-3 / SHAKE pre-hash adds sha3.
32
+ import { initModule, getInstance, isInitialized, _assertNotOwned } from '../init.js';
33
+ import { randomBytes, wipe } from '../utils.js';
34
+ import { SLHDSA128F, SLHDSA192F, SLHDSA256F } from './params.js';
35
+ import { slhSignInternalTs, signWithPrehash } from './sign.js';
36
+ import { slhVerifyInternalTs, verifyWithPrehash } from './verify.js';
37
+ import { constructMPrimePure } from './prehash.js';
38
+ import { validateContext, validateSigningKey, validateRnd, validateMessage, validateDigest, } from './validate.js';
39
+ import { algoNeedsSha2, algoNeedsSha3, digestSize, preHashMessage, } from './prehash.js';
40
+ export async function slhdsaInit(source) {
41
+ return initModule('slhdsa', source);
42
+ }
43
+ export { SLHDSA128F, SLHDSA192F, SLHDSA256F };
44
+ export { isInitialized };
45
+ /** Return the slhdsa WASM instance exports. Internal helper for tests that
46
+ * need raw access to the ADRS / hash / sponge primitives; consumers use
47
+ * the SlhDsa* classes below. */
48
+ export function getSlhDsaExports() {
49
+ return getInstance('slhdsa').exports;
50
+ }
51
+ // ── Base class ──────────────────────────────────────────────────────────────
52
+ export class SlhDsaBase {
53
+ params;
54
+ constructor(params) {
55
+ if (!isInitialized('slhdsa'))
56
+ throw new Error('leviathan-crypto: call init({ slhdsa: ... }) before using SlhDsa classes');
57
+ this.params = params;
58
+ }
59
+ get x() {
60
+ return getInstance('slhdsa').exports;
61
+ }
62
+ get sx() {
63
+ return getInstance('sha3').exports;
64
+ }
65
+ get sha2x() {
66
+ return getInstance('sha2').exports;
67
+ }
68
+ /**
69
+ * Deterministic key generation, FIPS 205 §9.1 Algorithm 18.
70
+ * @param seed 3n bytes laid out as `SK.seed ‖ SK.prf ‖ PK.seed`. Each
71
+ * component is `n` bytes (16 for 128f, 24 for 192f, 32 for
72
+ * 256f). The slh_keygen_internal entry consumes this layout
73
+ * directly.
74
+ */
75
+ keygenDerand(seed) {
76
+ _assertNotOwned('slhdsa');
77
+ const n = this.params.n;
78
+ if (!(seed instanceof Uint8Array))
79
+ throw new TypeError('leviathan-crypto: keygen seed must be a Uint8Array');
80
+ if (seed.length !== 3 * n)
81
+ throw new RangeError(`leviathan-crypto: keygen seed must be ${3 * n} bytes (SK.seed||SK.prf||PK.seed) for `
82
+ + `${this.params.paramSet} (got ${seed.length})`);
83
+ const x = this.x;
84
+ const mem = new Uint8Array(x.memory.buffer);
85
+ const inOff = x.getInputOffset();
86
+ const outOff = x.getOutOffset();
87
+ try {
88
+ this.params.wasmSelector();
89
+ mem.set(seed, inOff);
90
+ x.slhKeygenInternal();
91
+ const sk = mem.slice(outOff, outOff + this.params.skBytes);
92
+ const pk = mem.slice(outOff + this.params.skBytes, outOff + this.params.skBytes + this.params.pkBytes);
93
+ return { verificationKey: pk, signingKey: sk };
94
+ }
95
+ finally {
96
+ // INPUT held SK.seed ‖ SK.prf ‖ PK.seed; SK.seed and SK.prf are
97
+ // secret. Wipe the lib's staging copy unconditionally.
98
+ mem.fill(0, inOff, inOff + 3 * n);
99
+ x.wipeBuffers();
100
+ }
101
+ }
102
+ /** Random key generation, wraps `keygenDerand` with `randomBytes(3n)`. */
103
+ keygen() {
104
+ const seed = randomBytes(3 * this.params.n);
105
+ try {
106
+ return this.keygenDerand(seed);
107
+ }
108
+ finally {
109
+ wipe(seed);
110
+ }
111
+ }
112
+ /**
113
+ * Hedged signing, FIPS 205 §3.4 / §10.2.1 Algorithm 22.
114
+ * Generates a fresh n-byte addrnd (opt_rand) per signature; two
115
+ * signatures over the same (sk, M, ctx) produce different bytes.
116
+ * Hedged signing is recommended over deterministic because hedged
117
+ * signatures remain unforgeable under fault attacks that bias the
118
+ * rejection-sampling stream (FIPS 205 §3.4 / §9.2).
119
+ */
120
+ sign(sk, M, ctx = new Uint8Array(0)) {
121
+ _assertNotOwned('slhdsa');
122
+ validateSigningKey(sk, this.params);
123
+ validateMessage(M);
124
+ validateContext(ctx);
125
+ const MPrime = constructMPrimePure(M, ctx);
126
+ const optRand = randomBytes(this.params.n);
127
+ try {
128
+ return slhSignInternalTs(this.x, this.params, sk, MPrime, optRand);
129
+ }
130
+ finally {
131
+ wipe(optRand);
132
+ wipe(MPrime);
133
+ }
134
+ }
135
+ /**
136
+ * Deterministic signing, FIPS 205 §3.4. Sets opt_rand ← PK.seed so two
137
+ * signatures over the same (sk, M, ctx) produce identical bytes.
138
+ * Caller accepts the §3.4 caveat: deterministic signatures are
139
+ * vulnerable to fault attacks that bias secret-derived intermediates;
140
+ * use only when no entropy is available or determinism is a hard
141
+ * protocol requirement. PK.seed lives at sk[2n..3n] inside the
142
+ * `SK.seed ‖ SK.prf ‖ PK.seed ‖ PK.root` encoding (FIPS 205 §9.1).
143
+ */
144
+ signDeterministic(sk, M, ctx = new Uint8Array(0)) {
145
+ _assertNotOwned('slhdsa');
146
+ validateSigningKey(sk, this.params);
147
+ validateMessage(M);
148
+ validateContext(ctx);
149
+ const n = this.params.n;
150
+ // slice() keeps optRand self-contained for WASM INPUT staging.
151
+ const optRand = sk.slice(2 * n, 3 * n);
152
+ const MPrime = constructMPrimePure(M, ctx);
153
+ try {
154
+ return slhSignInternalTs(this.x, this.params, sk, MPrime, optRand);
155
+ }
156
+ finally {
157
+ wipe(MPrime);
158
+ // optRand is library scratch; wipe even though contents are sk-derivable.
159
+ wipe(optRand);
160
+ }
161
+ }
162
+ /**
163
+ * Externally-randomised signing, testing / CAVP API. Caller supplies
164
+ * the n-byte opt_rand; library does not mix in additional entropy.
165
+ * Hard contract on the caller: opt_rand MUST come from an approved
166
+ * RBG and MUST NOT be reused across signatures. ACVP SLH-DSA sigGen
167
+ * vectors (with a supplied additionalRandomness) drive this path.
168
+ */
169
+ signDerand(sk, M, optRand, ctx = new Uint8Array(0)) {
170
+ _assertNotOwned('slhdsa');
171
+ validateSigningKey(sk, this.params);
172
+ validateMessage(M);
173
+ validateContext(ctx);
174
+ validateRnd(optRand, this.params);
175
+ const MPrime = constructMPrimePure(M, ctx);
176
+ try {
177
+ return slhSignInternalTs(this.x, this.params, sk, MPrime, optRand);
178
+ }
179
+ finally {
180
+ wipe(MPrime);
181
+ }
182
+ }
183
+ /**
184
+ * Pure SLH-DSA verify, FIPS 205 §10.3 Algorithm 24 / §9.3 Algorithm 20.
185
+ *
186
+ * Returns boolean. Wrong-length pk / sig return false (FIPS 205 §3.6.2
187
+ * structural mismatch; same posture as ML-DSA verify). Throws
188
+ * `SigningError('sig-ctx-too-long')` only on the caller-side contract
189
+ * violation `ctx.length > 255`.
190
+ */
191
+ verify(pk, M, sig, ctx = new Uint8Array(0)) {
192
+ _assertNotOwned('slhdsa');
193
+ validateMessage(M);
194
+ // FIPS 205 §3.6.2 / §10.3 Algorithm 24 line 5, wrong-length pk or σ
195
+ // is not a caller bug; it is a structural mismatch that cannot
196
+ // verify. Return false rather than throw.
197
+ if (!(pk instanceof Uint8Array) || pk.length !== this.params.pkBytes)
198
+ return false;
199
+ if (!(sig instanceof Uint8Array) || sig.length !== this.params.sigBytes)
200
+ return false;
201
+ validateContext(ctx);
202
+ const MPrime = constructMPrimePure(M, ctx);
203
+ try {
204
+ return slhVerifyInternalTs(this.x, this.params, pk, MPrime, sig);
205
+ }
206
+ finally {
207
+ wipe(MPrime);
208
+ }
209
+ }
210
+ // ── HashSLH-DSA, FIPS 205 §10.2.2 / §10.3 (pre-hash variant) ─────────
211
+ //
212
+ // HashSLH-DSA wraps the same Sign_internal / Verify_internal primitives
213
+ // pure SLH-DSA uses, but pre-hashes M and builds M' with domain-sep
214
+ // byte 0x01 plus the hash function's OID DER bytes; signatures
215
+ // produced by sign / signHash on the same key are NOT interchangeable
216
+ // per FIPS 205 §10.2 narrative.
217
+ //
218
+ // `ph` is the LAST positional parameter on every HashSLH-DSA method
219
+ // (mirrors HashML-DSA's choice). There is no sensible default; callers
220
+ // must select one explicitly.
221
+ //
222
+ // `init({ sha2: ... })` is required only when `ph` is a SHA-2 family
223
+ // algorithm. `init({ sha3: ... })` is required when `ph` is a SHA-3
224
+ // or SHAKE algorithm. Pure-SLH-DSA usage needs neither (slhdsa-wasm
225
+ // has its own embedded Keccak permutation).
226
+ _assertHashPrereqs(ph) {
227
+ // Validate ph before any other dispatch so widened-type callers
228
+ // (e.g. parsing a vector file via `as PreHashAlgorithm`) hit the
229
+ // canonical "unsupported HashSLH-DSA pre-hash" RangeError rather
230
+ // than a downstream sha2-not-initialized error.
231
+ digestSize(ph);
232
+ // FIPS 205 §10.2.2: "SHA-256 and SHAKE128 are only appropriate
233
+ // for use with SLH-DSA parameter sets that are claimed to be in
234
+ // security category 1." Enforce this at the public surface: a
235
+ // category-3 or category-5 key may not be used with SHA-256 or
236
+ // SHAKE128 prehash.
237
+ if ((ph === 'SHA2-256' || ph === 'SHAKE128') && this.params.securityCategory !== 1)
238
+ throw new RangeError(`leviathan-crypto: HashSLH-DSA pre-hash '${ph}' is only appropriate for security category 1 `
239
+ + `(see FIPS 205 §10.2.2); ${this.params.paramSet} is security category ${this.params.securityCategory}`);
240
+ if (algoNeedsSha2(ph)) {
241
+ if (!isInitialized('sha2'))
242
+ throw new Error('leviathan-crypto: call init({ sha2: ... }) before HashSLH-DSA with SHA-2 pre-hash');
243
+ _assertNotOwned('sha2');
244
+ }
245
+ if (algoNeedsSha3(ph)) {
246
+ if (!isInitialized('sha3'))
247
+ throw new Error('leviathan-crypto: call init({ sha3: ... }) before HashSLH-DSA with SHA-3 / SHAKE pre-hash');
248
+ _assertNotOwned('sha3');
249
+ }
250
+ }
251
+ /**
252
+ * Hedged HashSLH-DSA sign, FIPS 205 §10.2.2 Algorithm 23.
253
+ *
254
+ * Pre-hashes `M` with the chosen approved function `ph`, builds
255
+ * M' = 0x01 ‖ |ctx| ‖ ctx ‖ OID(ph) ‖ PH_M, then drives
256
+ * slh_sign_internal with a fresh n-byte opt_rand (FIPS 205 §3.4
257
+ * recommended default; see {@link sign} for the rationale).
258
+ */
259
+ signHash(sk, M, ph, ctx = new Uint8Array(0)) {
260
+ _assertNotOwned('slhdsa');
261
+ this._assertHashPrereqs(ph);
262
+ validateSigningKey(sk, this.params);
263
+ validateMessage(M);
264
+ validateContext(ctx);
265
+ const sha2x = algoNeedsSha2(ph) ? this.sha2x : undefined;
266
+ const sha3x = algoNeedsSha3(ph) ? this.sx : undefined;
267
+ const PH_M = preHashMessage(sha3x, sha2x, ph, M);
268
+ const optRand = randomBytes(this.params.n);
269
+ try {
270
+ return signWithPrehash(this.x, this.params, sk, PH_M, ph, ctx, optRand);
271
+ }
272
+ finally {
273
+ wipe(optRand);
274
+ wipe(PH_M);
275
+ if (sha2x)
276
+ sha2x.wipeBuffers();
277
+ if (sha3x)
278
+ sha3x.wipeBuffers();
279
+ }
280
+ }
281
+ /**
282
+ * Deterministic HashSLH-DSA sign, FIPS 205 §10.2.2 Algorithm 23 with
283
+ * opt_rand ← PK.seed (the deterministic substitute per FIPS 205 §3.4).
284
+ * Same fault-attack caveat as {@link signDeterministic}.
285
+ */
286
+ signHashDeterministic(sk, M, ph, ctx = new Uint8Array(0)) {
287
+ _assertNotOwned('slhdsa');
288
+ this._assertHashPrereqs(ph);
289
+ validateSigningKey(sk, this.params);
290
+ validateMessage(M);
291
+ validateContext(ctx);
292
+ const n = this.params.n;
293
+ const optRand = sk.slice(2 * n, 3 * n);
294
+ const sha2x = algoNeedsSha2(ph) ? this.sha2x : undefined;
295
+ const sha3x = algoNeedsSha3(ph) ? this.sx : undefined;
296
+ const PH_M = preHashMessage(sha3x, sha2x, ph, M);
297
+ try {
298
+ return signWithPrehash(this.x, this.params, sk, PH_M, ph, ctx, optRand);
299
+ }
300
+ finally {
301
+ wipe(PH_M);
302
+ wipe(optRand);
303
+ if (sha2x)
304
+ sha2x.wipeBuffers();
305
+ if (sha3x)
306
+ sha3x.wipeBuffers();
307
+ }
308
+ }
309
+ /**
310
+ * Externally-randomised HashSLH-DSA sign, testing / CAVP API. Caller
311
+ * supplies the n-byte opt_rand (same contract as {@link signDerand}).
312
+ * Used to oracle ACVP HashSLH-DSA sigGen vectors with byte-identical
313
+ * output.
314
+ */
315
+ signHashDerand(sk, M, ph, optRand, ctx = new Uint8Array(0)) {
316
+ _assertNotOwned('slhdsa');
317
+ this._assertHashPrereqs(ph);
318
+ validateSigningKey(sk, this.params);
319
+ validateMessage(M);
320
+ validateContext(ctx);
321
+ validateRnd(optRand, this.params);
322
+ const sha2x = algoNeedsSha2(ph) ? this.sha2x : undefined;
323
+ const sha3x = algoNeedsSha3(ph) ? this.sx : undefined;
324
+ const PH_M = preHashMessage(sha3x, sha2x, ph, M);
325
+ try {
326
+ return signWithPrehash(this.x, this.params, sk, PH_M, ph, ctx, optRand);
327
+ }
328
+ finally {
329
+ wipe(PH_M);
330
+ if (sha2x)
331
+ sha2x.wipeBuffers();
332
+ if (sha3x)
333
+ sha3x.wipeBuffers();
334
+ }
335
+ }
336
+ /**
337
+ * HashSLH-DSA verify, FIPS 205 §10.3 Algorithm 25.
338
+ *
339
+ * Same return / throw posture as {@link verify}: returns boolean for
340
+ * every signature outcome (including malformed-σ → false), throws
341
+ * `SigningError` only on caller-side contract violations
342
+ * (`ctx.length > 255`) or `RangeError` on category violations and
343
+ * unsupported `ph`.
344
+ */
345
+ verifyHash(pk, M, sig, ph, ctx = new Uint8Array(0)) {
346
+ _assertNotOwned('slhdsa');
347
+ this._assertHashPrereqs(ph);
348
+ validateMessage(M);
349
+ if (!(pk instanceof Uint8Array) || pk.length !== this.params.pkBytes)
350
+ return false;
351
+ if (!(sig instanceof Uint8Array) || sig.length !== this.params.sigBytes)
352
+ return false;
353
+ validateContext(ctx);
354
+ const sha2x = algoNeedsSha2(ph) ? this.sha2x : undefined;
355
+ const sha3x = algoNeedsSha3(ph) ? this.sx : undefined;
356
+ const PH_M = preHashMessage(sha3x, sha2x, ph, M);
357
+ try {
358
+ return verifyWithPrehash(this.x, this.params, pk, PH_M, sig, ph, ctx);
359
+ }
360
+ finally {
361
+ wipe(PH_M);
362
+ if (sha2x)
363
+ sha2x.wipeBuffers();
364
+ if (sha3x)
365
+ sha3x.wipeBuffers();
366
+ }
367
+ }
368
+ // ── HashSLH-DSA prehashed variants, FIPS 205 §10.2.2 ──────────────────
369
+ //
370
+ // The "caller already computed PH" surface. signHash family above runs
371
+ // PH ← Hash(M, ph) internally; the prehashed family skips that step
372
+ // and accepts PH directly. Use them when M is not buffered in one
373
+ // place (streaming signers, protocols that already produced a digest
374
+ // as part of a transcript) or when a verifier prescribes a specific
375
+ // prehash and hands you the bytes.
376
+ //
377
+ // Wrong-size digest is a contract violation on the sign side (throws
378
+ // `SigningError('sig-malformed-input')`) and a structural verdict on
379
+ // the verify side (returns false, no throw), mirroring §3.6.2 for
380
+ // wrong-size pk / σ.
381
+ /**
382
+ * Hedged HashSLH-DSA sign with a caller-supplied prehash. FIPS 205
383
+ * §10.2.2 Algorithm 23 lines 18-25 (the post-PH path).
384
+ *
385
+ * `digest` must be exactly `digestSize(ph)` bytes; a mismatch throws
386
+ * `SigningError('sig-malformed-input')`. The caller owns `digest`
387
+ * and is responsible for wiping it; this method never mutates the
388
+ * buffer. Hedged variant generates a fresh n-byte opt_rand per call.
389
+ */
390
+ signHashPrehashed(sk, digest, ph, ctx = new Uint8Array(0)) {
391
+ _assertNotOwned('slhdsa');
392
+ this._assertHashPrereqs(ph);
393
+ validateSigningKey(sk, this.params);
394
+ validateContext(ctx);
395
+ validateDigest(digest, ph);
396
+ const optRand = randomBytes(this.params.n);
397
+ try {
398
+ return signWithPrehash(this.x, this.params, sk, digest, ph, ctx, optRand);
399
+ }
400
+ finally {
401
+ wipe(optRand);
402
+ }
403
+ }
404
+ /**
405
+ * Deterministic HashSLH-DSA sign with a caller-supplied prehash,
406
+ * opt_rand ← PK.seed per FIPS 205 §3.4. Same fault-attack caveat as
407
+ * {@link signDeterministic}.
408
+ */
409
+ signHashPrehashedDeterministic(sk, digest, ph, ctx = new Uint8Array(0)) {
410
+ _assertNotOwned('slhdsa');
411
+ this._assertHashPrereqs(ph);
412
+ validateSigningKey(sk, this.params);
413
+ validateContext(ctx);
414
+ validateDigest(digest, ph);
415
+ const n = this.params.n;
416
+ const optRand = sk.slice(2 * n, 3 * n);
417
+ try {
418
+ return signWithPrehash(this.x, this.params, sk, digest, ph, ctx, optRand);
419
+ }
420
+ finally {
421
+ wipe(optRand);
422
+ }
423
+ }
424
+ /**
425
+ * Externally-randomised HashSLH-DSA sign with a caller-supplied
426
+ * prehash, testing / CAVP API. Caller supplies the n-byte opt_rand:
427
+ * MUST come from an approved RBG and MUST NOT be reused across
428
+ * signatures.
429
+ */
430
+ signHashPrehashedDerand(sk, digest, ph, optRand, ctx = new Uint8Array(0)) {
431
+ _assertNotOwned('slhdsa');
432
+ this._assertHashPrereqs(ph);
433
+ validateSigningKey(sk, this.params);
434
+ validateContext(ctx);
435
+ validateRnd(optRand, this.params);
436
+ validateDigest(digest, ph);
437
+ return signWithPrehash(this.x, this.params, sk, digest, ph, ctx, optRand);
438
+ }
439
+ /**
440
+ * HashSLH-DSA verify with a caller-supplied prehash. FIPS 205 §10.3
441
+ * Algorithm 25 lines 16-19 (the post-PH path).
442
+ *
443
+ * Returns boolean for every signature outcome. Wrong-length pk / σ
444
+ * and wrong-size `digest` all return `false` (FIPS 205 §3.6.2 /
445
+ * §10.3 structural mismatch). Throws on caller-side contract
446
+ * violations only (`ctx.length > 255`, unsupported `ph`, category
447
+ * mismatch).
448
+ */
449
+ verifyHashPrehashed(pk, digest, sig, ph, ctx = new Uint8Array(0)) {
450
+ _assertNotOwned('slhdsa');
451
+ this._assertHashPrereqs(ph);
452
+ if (!(pk instanceof Uint8Array) || pk.length !== this.params.pkBytes)
453
+ return false;
454
+ if (!(sig instanceof Uint8Array) || sig.length !== this.params.sigBytes)
455
+ return false;
456
+ if (!(digest instanceof Uint8Array) || digest.length !== digestSize(ph))
457
+ return false;
458
+ validateContext(ctx);
459
+ return verifyWithPrehash(this.x, this.params, pk, digest, sig, ph, ctx);
460
+ }
461
+ dispose() {
462
+ // SlhDsaBase is atomic-only (no per-instance state beyond the
463
+ // readonly params). Every public method already runs
464
+ // wipeBuffers() in its own finally, so this dispose is just a
465
+ // final hygiene pass for defence-in-depth.
466
+ try {
467
+ this.x.wipeBuffers();
468
+ }
469
+ catch {
470
+ // dispose() is idempotent and must not throw even if the
471
+ // module was somehow torn down before the user finished.
472
+ }
473
+ }
474
+ }
475
+ // ── Public classes ──────────────────────────────────────────────────────────
476
+ /** SLH-DSA-SHAKE-128f, FIPS 205 §11.1 Table 2 (NIST security category 1). */
477
+ export class SlhDsa128f extends SlhDsaBase {
478
+ constructor() {
479
+ super(SLHDSA128F);
480
+ }
481
+ }
482
+ /** SLH-DSA-SHAKE-192f, FIPS 205 §11.1 Table 2 (NIST security category 3). */
483
+ export class SlhDsa192f extends SlhDsaBase {
484
+ constructor() {
485
+ super(SLHDSA192F);
486
+ }
487
+ }
488
+ /** SLH-DSA-SHAKE-256f, FIPS 205 §11.1 Table 2 (NIST security category 5). */
489
+ export class SlhDsa256f extends SlhDsaBase {
490
+ constructor() {
491
+ super(SLHDSA256F);
492
+ }
493
+ }
@@ -0,0 +1,26 @@
1
+ export interface SlhDsaParams {
2
+ paramSet: 'SLH-DSA-SHAKE-128f' | 'SLH-DSA-SHAKE-192f' | 'SLH-DSA-SHAKE-256f';
3
+ n: number;
4
+ h: number;
5
+ d: number;
6
+ hPrime: number;
7
+ k: number;
8
+ a: number;
9
+ m: number;
10
+ pkBytes: number;
11
+ skBytes: number;
12
+ sigBytes: number;
13
+ securityCategory: 1 | 3 | 5;
14
+ /** Bind this parameter set into the WASM PARAMS slot. Called by every
15
+ * SlhDsaBase public method before driving slh{Keygen,Sign,Verify}Internal
16
+ * so the WASM dimension lookups (slhK, slhA, slhD, slhHPrime, etc.)
17
+ * resolve to this set. Reads the slhdsa exports lazily so the function
18
+ * reference can be shared across modules without baking in an instance. */
19
+ wasmSelector: () => void;
20
+ }
21
+ /** SLH-DSA-SHAKE-128f, FIPS 205 §11.1 Table 2 (NIST security category 1). */
22
+ export declare const SLHDSA128F: SlhDsaParams;
23
+ /** SLH-DSA-SHAKE-192f, FIPS 205 §11.1 Table 2 (NIST security category 3). */
24
+ export declare const SLHDSA192F: SlhDsaParams;
25
+ /** SLH-DSA-SHAKE-256f, FIPS 205 §11.1 Table 2 (NIST security category 5). */
26
+ export declare const SLHDSA256F: SlhDsaParams;
@@ -0,0 +1,70 @@
1
+ // ▄▄▄▄▄▄▄▄▄▄
2
+ // ▄████████████████████▄▄ ▒ ▄▀▀ ▒ ▒ █ ▄▀▄ ▀█▀ █ ▒ ▄▀▄ █▀▄
3
+ // ▄██████████████████████ ▀████▄ ▓ ▓▀ ▓ ▓ ▓ ▓▄▓ ▓ ▓▀▓ ▓▄▓ ▓ ▓
4
+ // ▄█████████▀▀▀ ▀███████▄▄███████▌ ▀▄ ▀▄▄ ▀▄▀ ▒ ▒ ▒ ▒ ▒ █ ▒ ▒ ▒ █
5
+ // ▐████████▀ ▄▄▄▄ ▀████████▀██▀█▌
6
+ // ████████ ███▀▀ ████▀ █▀ █▀ Leviathan Crypto Library
7
+ // ███████▌ ▀██▀ ███
8
+ // ███████ ▀███ ▀██ ▀█▄ Repository & Mirror:
9
+ // ▀██████ ▄▄██ ▀▀ ██▄ github.com/xero/leviathan-crypto
10
+ // ▀█████▄ ▄██▄ ▄▀▄▀ unpkg.com/leviathan-crypto
11
+ // ▀████▄ ▄██▄
12
+ // ▐████ ▐███ Author: xero (https://x-e.ro)
13
+ // ▄▄██████████ ▐███ ▄▄ License: MIT
14
+ // ▄██▀▀▀▀▀▀▀▀▀▀ ▄████ ▄██▀
15
+ // ▄▀ ▄▄█████████▄▄ ▀▀▀▀▀ ▄███ This file is provided completely
16
+ // ▄██████▀▀▀▀▀▀██████▄ ▀▄▄▄▄████▀ free, "as is", and without
17
+ // ████▀ ▄▄▄▄▄▄▄ ▀████▄ ▀█████▀ ▄▄▄▄ warranty of any kind. The author
18
+ // █████▄▄█████▀▀▀▀▀▀▄ ▀███▄ ▄████ assumes absolutely no liability
19
+ // ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
20
+ // ▀█████▀▀
21
+ //
22
+ // src/ts/slhdsa/params.ts
23
+ //
24
+ // SLH-DSA (FIPS 205) parameter sets.
25
+ //
26
+ // Numeric values come from FIPS 205 §11.1 Table 2; derived sizes come from
27
+ // FIPS 205 §11.2 (pk = 2·n, sk = 4·n) and the §9 sigEncode algorithm:
28
+ //
29
+ // sigBytes = (1 + k·(a+1) + h + d·len) · n
30
+ //
31
+ // where w = 16 (the only approved Winternitz parameter in §11.1), so
32
+ // len_1 = ⌈8·n/4⌉, len_2 = 3, len = len_1 + len_2.
33
+ //
34
+ // Current scope is the SHAKE-family fast variants (128f / 192f / 256f) only;
35
+ // the slow variants and the SHA-2 family are explicitly out of scope.
36
+ //
37
+ // `securityCategory` (NIST PQC category 1 / 3 / 5) drives the per-set
38
+ // HashSLH-DSA category gate in `validate.ts` / `index.ts`. `wasmSelector`
39
+ // runs the corresponding `slhSetParams*` thunk on the WASM module so the
40
+ // PARAMS slot reflects this set before any algorithm call.
41
+ import { getInstance } from '../init.js';
42
+ function selectorFor(name) {
43
+ return () => {
44
+ getInstance('slhdsa').exports[name]();
45
+ };
46
+ }
47
+ /** SLH-DSA-SHAKE-128f, FIPS 205 §11.1 Table 2 (NIST security category 1). */
48
+ export const SLHDSA128F = {
49
+ paramSet: 'SLH-DSA-SHAKE-128f',
50
+ n: 16, h: 66, d: 22, hPrime: 3, k: 33, a: 6, m: 34,
51
+ pkBytes: 32, skBytes: 64, sigBytes: 17088,
52
+ securityCategory: 1,
53
+ wasmSelector: selectorFor('slhSetParams128f'),
54
+ };
55
+ /** SLH-DSA-SHAKE-192f, FIPS 205 §11.1 Table 2 (NIST security category 3). */
56
+ export const SLHDSA192F = {
57
+ paramSet: 'SLH-DSA-SHAKE-192f',
58
+ n: 24, h: 66, d: 22, hPrime: 3, k: 33, a: 8, m: 42,
59
+ pkBytes: 48, skBytes: 96, sigBytes: 35664,
60
+ securityCategory: 3,
61
+ wasmSelector: selectorFor('slhSetParams192f'),
62
+ };
63
+ /** SLH-DSA-SHAKE-256f, FIPS 205 §11.1 Table 2 (NIST security category 5). */
64
+ export const SLHDSA256F = {
65
+ paramSet: 'SLH-DSA-SHAKE-256f',
66
+ n: 32, h: 68, d: 17, hPrime: 4, k: 35, a: 9, m: 49,
67
+ pkBytes: 64, skBytes: 128, sigBytes: 49856,
68
+ securityCategory: 5,
69
+ wasmSelector: selectorFor('slhSetParams256f'),
70
+ };
@@ -0,0 +1,68 @@
1
+ import type { Sha3Exports } from '../mldsa/types.js';
2
+ import type { Sha2Exports } from '../sha2/types.js';
3
+ /** FIPS 205 §10.2.2 approved pre-hash functions, same surface as FIPS 204
4
+ * §5.4.1. Names follow the FIPS 204 / FIPS 205 spelling (no hyphen
5
+ * between SHAKE and the digit). The SHAKE entries are XOFs with fixed
6
+ * output lengths set by FIPS 205 §10.2.2 Algorithm 23: SHAKE128 → 256-bit
7
+ * (32-byte) output, SHAKE256 → 512-bit (64-byte). */
8
+ export type PreHashAlgorithm = 'SHA2-224' | 'SHA2-256' | 'SHA2-384' | 'SHA2-512' | 'SHA2-512/224' | 'SHA2-512/256' | 'SHA3-224' | 'SHA3-256' | 'SHA3-384' | 'SHA3-512' | 'SHAKE128' | 'SHAKE256';
9
+ /** Look up the FIPS 205 §10.2.2 OID DER bytes for `algo`. Returns a fresh
10
+ * Uint8Array each call so callers can wipe / mutate without aliasing the
11
+ * module-private constant. */
12
+ export declare function getOid(algo: PreHashAlgorithm): Uint8Array;
13
+ /** FIPS 205 §10.2.2 PH_M byte length for `algo`. SHAKE128 / SHAKE256 are
14
+ * XOFs but the spec fixes their HashSLH-DSA output to 32 / 64 bytes
15
+ * respectively; the SHA-3 and SHA-2 entries return their natural digest
16
+ * size. Used by `validateDigest` to bound the caller-supplied prehash.
17
+ *
18
+ * Duplicated from `src/ts/mldsa/hashvariant.ts:digestSize`; extraction
19
+ * is deferred until a third consumer materialises. */
20
+ export declare function digestSize(algo: PreHashAlgorithm): number;
21
+ /** True iff `algo` is one of the SHA-2 family pre-hashes (and therefore
22
+ * requires `init({ sha2: ... })`). The SHA-3 family and SHAKE variants
23
+ * use the `sha3` module. */
24
+ export declare function algoNeedsSha2(algo: PreHashAlgorithm): boolean;
25
+ /** True iff `algo` is a SHA-3 or SHAKE pre-hash (and therefore requires
26
+ * `init({ sha3: ... })`). slhdsa's own embedded Keccak permutation is
27
+ * used internally by `slh_sign_internal` / `slh_verify_internal`, but the
28
+ * HashSLH-DSA prehash dispatcher routes through the `sha3` module to keep
29
+ * the public surface byte-identical with `src/ts/mldsa/hashvariant.ts`
30
+ * (which also uses the sha3 module). */
31
+ export declare function algoNeedsSha3(algo: PreHashAlgorithm): boolean;
32
+ /**
33
+ * Build the HashSLH-DSA M' = 0x01 ‖ |ctx| ‖ ctx ‖ OID ‖ PH_M.
34
+ *
35
+ * FIPS 205 §10.2.2 Algorithm 23 lines 18-19 (sign) and §10.3 Algorithm 25
36
+ * lines 16-17 (verify). The leading byte is 0x01 (vs 0x00 for pure
37
+ * SLH-DSA), domain separation across pure / pre-hash modes per FIPS 205
38
+ * §10.2 narrative. Caller has already validated ctx.length ≤ 255.
39
+ *
40
+ * Byte-identical to FIPS 204 §5.4 Algorithm 4 M' construction; see
41
+ * src/ts/mldsa/hashvariant.ts and src/ts/mldsa/format.ts:constructMPrimeHash
42
+ * for the ML-DSA mirror. Q7 resolution: duplicate, do not extract.
43
+ */
44
+ export declare function constructMPrimeHash(digest: Uint8Array, ph: PreHashAlgorithm, ctx: Uint8Array): Uint8Array;
45
+ /**
46
+ * Build the pure-mode M' = 0x00 ‖ |ctx| ‖ ctx ‖ M for FIPS 205 §10.2.1
47
+ * Algorithm 22 line 8 (sign) and §10.3 Algorithm 24 line 8 (verify).
48
+ *
49
+ * Caller has already validated ctx.length ≤ 255. The leading byte is 0x00,
50
+ * which separates pure SLH-DSA signatures from HashSLH-DSA signatures (the
51
+ * latter prepends 0x01 via constructMPrimeHash) on the same key per the
52
+ * §10.2 narrative.
53
+ */
54
+ export declare function constructMPrimePure(M: Uint8Array, ctx: Uint8Array): Uint8Array;
55
+ /**
56
+ * Pre-hash dispatcher, applies the FIPS 205 §10.2.2 hash function `algo`
57
+ * to message `M` and returns PH_M (the bytes that go into M' alongside
58
+ * the OID).
59
+ *
60
+ * `sx` is the sha3-wasm Sha3Exports, required for SHA-3 / SHAKE prehashes.
61
+ * `sha2x` is the sha2-wasm Sha2Exports, required for SHA-2 prehashes.
62
+ * Either argument may be `undefined` when the chosen `algo` does not need
63
+ * that module; the dispatcher throws a clear error if a required module
64
+ * is missing rather than NPE'ing on a member access. Pure-SLH-DSA users
65
+ * call neither (slhdsa-wasm has its own embedded Keccak permutation), so
66
+ * both modules are strictly optional.
67
+ */
68
+ export declare function preHashMessage(sx: Sha3Exports | undefined, sha2x: Sha2Exports | undefined, algo: PreHashAlgorithm, M: Uint8Array): Uint8Array;