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,463 @@
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/mldsa/index.ts
23
+ //
24
+ // ML-DSA public API, MlDsa44, MlDsa65, MlDsa87 classes.
25
+ // FIPS 204, Module-Lattice-Based Digital Signature Standard.
26
+ //
27
+ // Public surface: keygen / keygenDerand, sign / verify (pure ML-DSA),
28
+ // and signHash / verifyHash (HashML-DSA). Use init({ mldsa, sha3 })
29
+ // before constructing any class, both modules are required.
30
+ import { getInstance, initModule, isInitialized, _assertNotOwned } from '../init.js';
31
+ import { randomBytes, wipe } from '../utils.js';
32
+ import { MLDSA44, MLDSA65, MLDSA87 } from './params.js';
33
+ import { mldsaKeygenInternal } from './keygen.js';
34
+ import { mldsaSignInternal, signWithPrehash } from './sign.js';
35
+ import { mldsaVerifyInternal, verifyWithPrehash } from './verify.js';
36
+ import { constructMPrime } from './format.js';
37
+ import { validateContext, validateSigningKey, validateRnd, validateMessage, validateDigest, } from './validate.js';
38
+ import { algoNeedsSha2, digestSize, preHashMessage, } from './hashvariant.js';
39
+ export async function mldsaInit(source) {
40
+ return initModule('mldsa', source);
41
+ }
42
+ export { MLDSA44, MLDSA65, MLDSA87 };
43
+ export { isInitialized };
44
+ // ── Layout assertion ────────────────────────────────────────────────────────
45
+ function assertLayout(mx, p) {
46
+ const matrix = mx.getMatrixSlot();
47
+ const matrixEnd = matrix + mx.getMatrixSlotSize();
48
+ const pvBase = mx.getPolyvecSlotBase();
49
+ const pkOff = mx.getPkOffset();
50
+ const skOff = mx.getSkOffset();
51
+ const sigOff = mx.getSigOffset();
52
+ const xofOff = mx.getXofPrfOffset();
53
+ if (matrixEnd > pvBase)
54
+ throw new Error('leviathan-crypto: mldsa MATRIX_SLOT overflows POLYVEC region');
55
+ const polyBytes = 1024;
56
+ if (p.k * p.l * polyBytes > mx.getMatrixSlotSize())
57
+ throw new Error(`leviathan-crypto: mldsa MATRIX_SLOT too small for ${p.paramSet} `
58
+ + `(needs ${p.k * p.l * polyBytes}, have ${mx.getMatrixSlotSize()})`);
59
+ if (pkOff + p.pkBytes > skOff)
60
+ throw new Error('leviathan-crypto: mldsa pk buffer overflows into sk region');
61
+ if (skOff + p.skBytes > sigOff)
62
+ throw new Error('leviathan-crypto: mldsa sk buffer overflows into sig region');
63
+ if (sigOff + p.sigBytes > xofOff)
64
+ throw new Error('leviathan-crypto: mldsa sig buffer overflows into XOF region');
65
+ }
66
+ // ── Base class ──────────────────────────────────────────────────────────────
67
+ export class MlDsaBase {
68
+ params;
69
+ constructor(params) {
70
+ if (!isInitialized('mldsa'))
71
+ throw new Error('leviathan-crypto: call init({ mldsa: ... }) before using MlDsa classes');
72
+ if (!isInitialized('sha3'))
73
+ throw new Error('leviathan-crypto: call init({ sha3: ... }) before using MlDsa classes');
74
+ this.params = params;
75
+ assertLayout(this.mx, params);
76
+ }
77
+ get mx() {
78
+ return getInstance('mldsa').exports;
79
+ }
80
+ get sx() {
81
+ return getInstance('sha3').exports;
82
+ }
83
+ get sha2x() {
84
+ return getInstance('sha2').exports;
85
+ }
86
+ /**
87
+ * Deterministic key generation, FIPS 204 §6.1 Algorithm 6.
88
+ * @param xi 32-byte seed. The sole input; ml-dsa keygen has no
89
+ * additional rejection-tied randomness.
90
+ */
91
+ keygenDerand(xi) {
92
+ _assertNotOwned('sha3');
93
+ _assertNotOwned('mldsa');
94
+ if (xi.length !== 32)
95
+ throw new RangeError(`xi seed must be 32 bytes (got ${xi.length})`);
96
+ return mldsaKeygenInternal(this.mx, this.sx, this.params, xi);
97
+ }
98
+ /** Random key generation, wraps `keygenDerand` with `randomBytes(32)`. */
99
+ keygen() {
100
+ const xi = randomBytes(32);
101
+ try {
102
+ return this.keygenDerand(xi);
103
+ }
104
+ finally {
105
+ wipe(xi);
106
+ }
107
+ }
108
+ /**
109
+ * Hedged signing, FIPS 204 §3.4 (recommended default).
110
+ * Generates a fresh 32-byte rnd via `randomBytes()` per signature; the
111
+ * rnd is mixed into ρ'' so two signatures over the same (sk, M) produce
112
+ * different bytes. Hedged signatures are recommended over deterministic
113
+ * because they remain unforgeable under fault attacks that bias the
114
+ * rejection-sampling stream (FIPS 204 §3.4 / §3.6.1).
115
+ */
116
+ sign(sk, M, ctx = new Uint8Array(0)) {
117
+ _assertNotOwned('sha3');
118
+ _assertNotOwned('mldsa');
119
+ validateSigningKey(sk, this.params);
120
+ validateMessage(M);
121
+ validateContext(ctx);
122
+ // FIPS 204 §5.2 Algorithm 2 line 10, M' = 0x00 ‖ |ctx| ‖ ctx ‖ M.
123
+ const MPrime = constructMPrime(0x00, ctx, M);
124
+ const rnd = randomBytes(32);
125
+ try {
126
+ return mldsaSignInternal(this.mx, this.sx, this.params, sk, MPrime, rnd);
127
+ }
128
+ finally {
129
+ wipe(rnd);
130
+ wipe(MPrime);
131
+ }
132
+ }
133
+ /**
134
+ * Deterministic signing, FIPS 204 §3.4. Sets rnd ← 0³² so two
135
+ * signatures over the same (sk, M) produce identical bytes. Caller
136
+ * accepts the §3.4 caveat: deterministic signatures are vulnerable to
137
+ * fault attacks that bias the SampleInBall stream, use only when no
138
+ * entropy is available or determinism is a hard protocol requirement.
139
+ */
140
+ signDeterministic(sk, M, ctx = new Uint8Array(0)) {
141
+ _assertNotOwned('sha3');
142
+ _assertNotOwned('mldsa');
143
+ validateSigningKey(sk, this.params);
144
+ validateMessage(M);
145
+ validateContext(ctx);
146
+ const MPrime = constructMPrime(0x00, ctx, M);
147
+ const rnd = new Uint8Array(32); // already zeros
148
+ try {
149
+ return mldsaSignInternal(this.mx, this.sx, this.params, sk, MPrime, rnd);
150
+ }
151
+ finally {
152
+ wipe(MPrime);
153
+ }
154
+ }
155
+ /**
156
+ * Externally-randomised signing, testing / CAVP API. Caller supplies
157
+ * the 32-byte rnd; library does not mix in additional entropy. Hard
158
+ * contract on the caller: rnd MUST come from an approved RBG and MUST
159
+ * NOT be reused across signatures. ACVP `sigGen` test vectors (with a
160
+ * supplied rnd) drive this path.
161
+ */
162
+ signDerand(sk, M, ctx, rnd) {
163
+ _assertNotOwned('sha3');
164
+ _assertNotOwned('mldsa');
165
+ validateSigningKey(sk, this.params);
166
+ validateMessage(M);
167
+ validateContext(ctx);
168
+ validateRnd(rnd);
169
+ const MPrime = constructMPrime(0x00, ctx, M);
170
+ try {
171
+ return mldsaSignInternal(this.mx, this.sx, this.params, sk, MPrime, rnd);
172
+ }
173
+ finally {
174
+ wipe(MPrime);
175
+ }
176
+ }
177
+ /**
178
+ * Pure ML-DSA verify, FIPS 204 §5.3 Algorithm 3 / §6.3 Algorithm 8.
179
+ *
180
+ * Returns boolean, `true` only if (a) the FIPS 204 norm bound on z
181
+ * holds and (b) the constant-time comparison of c̃ to the recomputed
182
+ * c̃' succeeds. Throws RangeError only on caller-side contract
183
+ * violations (`ctx.length > 255`). Wrong-length pk/sig and malformed
184
+ * hint encodings are NOT contract violations: they cause `verify` to
185
+ * return false (FIPS 204 §3.6.2 / §D.3).
186
+ */
187
+ verify(vk, M, sig, ctx = new Uint8Array(0)) {
188
+ _assertNotOwned('sha3');
189
+ _assertNotOwned('mldsa');
190
+ validateMessage(M);
191
+ // FIPS 204 §3.6.2, wrong-length pk or σ is not a caller bug; it
192
+ // is a structural mismatch that cannot verify. Return false rather
193
+ // than throw, matching how Algorithm 3 returns ⊥ on length mismatch.
194
+ if (!(vk instanceof Uint8Array) || vk.length !== this.params.pkBytes)
195
+ return false;
196
+ if (!(sig instanceof Uint8Array) || sig.length !== this.params.sigBytes)
197
+ return false;
198
+ // ctx oversize is a caller-side contract violation per Alg 3 line 1.
199
+ validateContext(ctx);
200
+ const MPrime = constructMPrime(0x00, ctx, M);
201
+ try {
202
+ return mldsaVerifyInternal(this.mx, this.sx, this.params, vk, MPrime, sig);
203
+ }
204
+ finally {
205
+ wipe(MPrime);
206
+ }
207
+ }
208
+ // ── HashML-DSA, FIPS 204 §5.4 (pre-hash variant) ──────────────────────
209
+ //
210
+ // HashML-DSA wraps the same Sign_internal / Verify_internal primitives
211
+ // pure ML-DSA uses, but pre-hashes M and builds M' with domain-sep byte
212
+ // 0x01 plus the hash function's OID DER bytes, so signatures produced
213
+ // by sign / signHash on the same key are NOT interchangeable. See
214
+ // FIPS 204 §3.6.4 for the cross-protocol attack rationale.
215
+ //
216
+ // `ph` is the LAST positional parameter on every HashML-DSA method.
217
+ // There is no sensible default, the spec lists 12 approved choices and
218
+ // none has cryptographic priority. Callers must select one explicitly.
219
+ //
220
+ // `init({ sha2: ... })` is required only when `ph` is a SHA-2 family
221
+ // algorithm. Using SHA3-* / SHAKE pre-hash needs no additional modules
222
+ // beyond the `mldsa` + `sha3` pair pure ML-DSA already requires.
223
+ _assertHashPrereqs(ph) {
224
+ // Validate ph before any other dispatch so widened-type callers
225
+ // (e.g. parsing a vector file via `as PreHashAlgorithm`) hit the
226
+ // canonical "unsupported HashML-DSA pre-hash" RangeError rather
227
+ // than a downstream sha2-not-initialized error or a fallthrough.
228
+ digestSize(ph);
229
+ if (algoNeedsSha2(ph)) {
230
+ if (!isInitialized('sha2'))
231
+ throw new Error('leviathan-crypto: call init({ sha2: ... }) before HashML-DSA with SHA-2 pre-hash');
232
+ _assertNotOwned('sha2');
233
+ }
234
+ }
235
+ /**
236
+ * Hedged HashML-DSA sign, FIPS 204 §5.4 Algorithm 4.
237
+ *
238
+ * Pre-hashes `M` with the chosen approved function `ph`, builds
239
+ * M' = 0x01 ‖ |ctx| ‖ ctx ‖ OID(ph) ‖ PH_M, then drives
240
+ * ML-DSA.Sign_internal with a fresh 32-byte rnd (FIPS 204 §3.4
241
+ * recommended default; see {@link sign} for the rationale).
242
+ */
243
+ signHash(sk, M, ph, ctx = new Uint8Array(0)) {
244
+ _assertNotOwned('sha3');
245
+ _assertNotOwned('mldsa');
246
+ this._assertHashPrereqs(ph);
247
+ validateSigningKey(sk, this.params);
248
+ validateMessage(M);
249
+ validateContext(ctx);
250
+ const sha2x = algoNeedsSha2(ph) ? this.sha2x : undefined;
251
+ const PH_M = preHashMessage(this.sx, sha2x, ph, M);
252
+ const rnd = randomBytes(32);
253
+ try {
254
+ return signWithPrehash(this.mx, this.sx, this.params, sk, PH_M, ph, ctx, rnd);
255
+ }
256
+ finally {
257
+ wipe(rnd);
258
+ // PH_M is M-derived (M is public input) so leakage is benign,
259
+ // but discipline matters, wipe it on every path.
260
+ wipe(PH_M);
261
+ // SHA-2 module's INPUT/OUT/H regions held the last block of M
262
+ // and the digest. Wipe them so secret material from any prior
263
+ // sha2 op (e.g. an HMAC) plus this PH_M digest don't linger.
264
+ if (sha2x)
265
+ sha2x.wipeBuffers();
266
+ }
267
+ }
268
+ /**
269
+ * Deterministic HashML-DSA sign, FIPS 204 §5.4 Algorithm 4 with
270
+ * rnd ← 0³². Same fault-attack caveat as {@link signDeterministic}.
271
+ */
272
+ signHashDeterministic(sk, M, ph, ctx = new Uint8Array(0)) {
273
+ _assertNotOwned('sha3');
274
+ _assertNotOwned('mldsa');
275
+ this._assertHashPrereqs(ph);
276
+ validateSigningKey(sk, this.params);
277
+ validateMessage(M);
278
+ validateContext(ctx);
279
+ const sha2x = algoNeedsSha2(ph) ? this.sha2x : undefined;
280
+ const PH_M = preHashMessage(this.sx, sha2x, ph, M);
281
+ const rnd = new Uint8Array(32); // already zeros
282
+ try {
283
+ return signWithPrehash(this.mx, this.sx, this.params, sk, PH_M, ph, ctx, rnd);
284
+ }
285
+ finally {
286
+ wipe(PH_M);
287
+ if (sha2x)
288
+ sha2x.wipeBuffers();
289
+ }
290
+ }
291
+ /**
292
+ * Externally-randomised HashML-DSA sign, testing / CAVP API. Caller
293
+ * supplies the 32-byte rnd (same contract as {@link signDerand}). Used
294
+ * to oracle ACVP HashML-DSA sigGen vectors with byte-identical output.
295
+ */
296
+ signHashDerand(sk, M, ph, ctx, rnd) {
297
+ _assertNotOwned('sha3');
298
+ _assertNotOwned('mldsa');
299
+ this._assertHashPrereqs(ph);
300
+ validateSigningKey(sk, this.params);
301
+ validateMessage(M);
302
+ validateContext(ctx);
303
+ validateRnd(rnd);
304
+ const sha2x = algoNeedsSha2(ph) ? this.sha2x : undefined;
305
+ const PH_M = preHashMessage(this.sx, sha2x, ph, M);
306
+ try {
307
+ return signWithPrehash(this.mx, this.sx, this.params, sk, PH_M, ph, ctx, rnd);
308
+ }
309
+ finally {
310
+ wipe(PH_M);
311
+ if (sha2x)
312
+ sha2x.wipeBuffers();
313
+ }
314
+ }
315
+ /**
316
+ * HashML-DSA verify, FIPS 204 §5.4 Algorithm 5.
317
+ *
318
+ * Same return / throw posture as {@link verify}: returns boolean for
319
+ * every signature outcome (including malformed-σ → false), throws
320
+ * RangeError only on caller-side contract violations such as
321
+ * `ctx.length > 255` or unsupported `ph`.
322
+ */
323
+ verifyHash(vk, M, sig, ph, ctx = new Uint8Array(0)) {
324
+ _assertNotOwned('sha3');
325
+ _assertNotOwned('mldsa');
326
+ this._assertHashPrereqs(ph);
327
+ validateMessage(M);
328
+ // FIPS 204 §3.6.2, wrong-length pk or σ is not a caller bug; it
329
+ // is a structural mismatch that cannot verify. Return false rather
330
+ // than throw, matching how Algorithm 5 returns false on length
331
+ // mismatch via Verify_internal's structural checks.
332
+ if (!(vk instanceof Uint8Array) || vk.length !== this.params.pkBytes)
333
+ return false;
334
+ if (!(sig instanceof Uint8Array) || sig.length !== this.params.sigBytes)
335
+ return false;
336
+ validateContext(ctx);
337
+ const sha2x = algoNeedsSha2(ph) ? this.sha2x : undefined;
338
+ const PH_M = preHashMessage(this.sx, sha2x, ph, M);
339
+ try {
340
+ return verifyWithPrehash(this.mx, this.sx, this.params, vk, PH_M, sig, ph, ctx);
341
+ }
342
+ finally {
343
+ wipe(PH_M);
344
+ if (sha2x)
345
+ sha2x.wipeBuffers();
346
+ }
347
+ }
348
+ // ── HashML-DSA prehashed variants, FIPS 204 §5.4 ──────────────────────
349
+ /**
350
+ * Hedged HashML-DSA sign with a caller-supplied prehash, FIPS 204
351
+ * §5.4 Algorithm 4 lines 22-24 (the post-PH path).
352
+ *
353
+ * `digest` must be exactly `digestSize(ph)` bytes (FIPS 204 §5.4.1);
354
+ * a mismatch throws `SigningError('sig-malformed-input')`. The caller
355
+ * owns `digest` and is responsible for wiping it; this method never
356
+ * mutates the buffer.
357
+ *
358
+ * Hedged variant generates a fresh 32-byte rnd internally per
359
+ * signature, see {@link sign} for the §3.4 rationale.
360
+ */
361
+ signHashPrehashed(sk, digest, ph, ctx = new Uint8Array(0)) {
362
+ _assertNotOwned('sha3');
363
+ _assertNotOwned('mldsa');
364
+ this._assertHashPrereqs(ph);
365
+ validateSigningKey(sk, this.params);
366
+ validateContext(ctx);
367
+ validateDigest(digest, ph);
368
+ const rnd = randomBytes(32);
369
+ try {
370
+ return signWithPrehash(this.mx, this.sx, this.params, sk, digest, ph, ctx, rnd);
371
+ }
372
+ finally {
373
+ wipe(rnd);
374
+ }
375
+ }
376
+ /**
377
+ * Deterministic HashML-DSA sign with a caller-supplied prehash, rnd
378
+ * ← 0³² per FIPS 204 §3.4. Same fault-attack caveat as
379
+ * {@link signDeterministic}.
380
+ */
381
+ signHashPrehashedDeterministic(sk, digest, ph, ctx = new Uint8Array(0)) {
382
+ _assertNotOwned('sha3');
383
+ _assertNotOwned('mldsa');
384
+ this._assertHashPrereqs(ph);
385
+ validateSigningKey(sk, this.params);
386
+ validateContext(ctx);
387
+ validateDigest(digest, ph);
388
+ const rnd = new Uint8Array(32); // already zeros
389
+ return signWithPrehash(this.mx, this.sx, this.params, sk, digest, ph, ctx, rnd);
390
+ }
391
+ /**
392
+ * Externally-randomised HashML-DSA sign with a caller-supplied
393
+ * prehash, testing / CAVP API. Caller supplies the 32-byte rnd (same
394
+ * contract as {@link signDerand}): rnd MUST come from an approved RBG
395
+ * and MUST NOT be reused across signatures.
396
+ */
397
+ signHashPrehashedDerand(sk, digest, ph, rnd, ctx = new Uint8Array(0)) {
398
+ _assertNotOwned('sha3');
399
+ _assertNotOwned('mldsa');
400
+ this._assertHashPrereqs(ph);
401
+ validateSigningKey(sk, this.params);
402
+ validateContext(ctx);
403
+ validateRnd(rnd);
404
+ validateDigest(digest, ph);
405
+ return signWithPrehash(this.mx, this.sx, this.params, sk, digest, ph, ctx, rnd);
406
+ }
407
+ /**
408
+ * HashML-DSA verify with a caller-supplied prehash, FIPS 204 §5.4
409
+ * Algorithm 5 lines 17-19 (the post-PH path).
410
+ *
411
+ * Returns boolean for every signature outcome. Wrong-length pk / σ
412
+ * and wrong-size `digest` all return `false` (FIPS 204 §3.6.2
413
+ * structural mismatch). Throws `RangeError` only on caller-side
414
+ * contract violations (`ctx.length > 255`, unsupported `ph`).
415
+ */
416
+ verifyHashPrehashed(vk, digest, sig, ph, ctx = new Uint8Array(0)) {
417
+ _assertNotOwned('sha3');
418
+ _assertNotOwned('mldsa');
419
+ this._assertHashPrereqs(ph);
420
+ // FIPS 204 §3.6.2, wrong-length pk / σ are not contract violations;
421
+ // they are structural mismatches that cannot verify. Wrong-size
422
+ // digest follows the same posture (the digest is an input to M',
423
+ // a wrong length means M' would have a different shape than the
424
+ // signer used). Return false rather than throw.
425
+ if (!(vk instanceof Uint8Array) || vk.length !== this.params.pkBytes)
426
+ return false;
427
+ if (!(sig instanceof Uint8Array) || sig.length !== this.params.sigBytes)
428
+ return false;
429
+ if (!(digest instanceof Uint8Array) || digest.length !== digestSize(ph))
430
+ return false;
431
+ validateContext(ctx);
432
+ return verifyWithPrehash(this.mx, this.sx, this.params, vk, digest, sig, ph, ctx);
433
+ }
434
+ dispose() {
435
+ this.mx.wipeBuffers();
436
+ // MlDsaBase does not own the sha3 module, wiping sha3 here would
437
+ // clobber any SHAKE128/SHAKE256 instance live at the time of
438
+ // dispose(). The wipe is not needed: every public mldsa op
439
+ // (keygen, sign, verify and their Hash variants) calls
440
+ // sx.wipeBuffers() before returning, under the
441
+ // _assertNotOwned('sha3') guard it holds. sha3 scratch carries no
442
+ // residue across an mldsa op boundary.
443
+ }
444
+ }
445
+ // ── Public classes ──────────────────────────────────────────────────────────
446
+ /** ML-DSA-44, FIPS 204 §4 Table 1 (NIST security category 2). */
447
+ export class MlDsa44 extends MlDsaBase {
448
+ constructor() {
449
+ super(MLDSA44);
450
+ }
451
+ }
452
+ /** ML-DSA-65, FIPS 204 §4 Table 1 (NIST security category 3). */
453
+ export class MlDsa65 extends MlDsaBase {
454
+ constructor() {
455
+ super(MLDSA65);
456
+ }
457
+ }
458
+ /** ML-DSA-87, FIPS 204 §4 Table 1 (NIST security category 5). */
459
+ export class MlDsa87 extends MlDsaBase {
460
+ constructor() {
461
+ super(MLDSA87);
462
+ }
463
+ }
@@ -0,0 +1,16 @@
1
+ import type { MlDsaExports, Sha3Exports, MlDsaKeyPair } from './types.js';
2
+ import type { MlDsaParams } from './params.js';
3
+ /**
4
+ * ML-DSA.KeyGen_internal, FIPS 204 Algorithm 6.
5
+ *
6
+ * Input ξ (32 bytes): the keygen seed. Produced internally by `keygen()`
7
+ * via `randomBytes(32)`, or supplied by `keygenDerand`.
8
+ * Output (pk, sk) byte-encoded per Alg 22 (pkEncode) and Alg 24 (skEncode).
9
+ *
10
+ * Wipe contract on return: every WASM region that held a secret or
11
+ * secret-derived intermediate is zeroed. Public regions (matrix Â, t₁,
12
+ * pk, ρ) are deliberately not wiped, they can be re-derived from the
13
+ * returned pk/sk anyway. The caller is expected to wipe the local ξ
14
+ * buffer it allocated; this function does not own that buffer.
15
+ */
16
+ export declare function mldsaKeygenInternal(mx: MlDsaExports, sx: Sha3Exports, params: MlDsaParams, xi: Uint8Array): MlDsaKeyPair;