leviathan-crypto 2.0.1 → 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 (312) hide show
  1. package/CLAUDE.md +88 -281
  2. package/LICENSE +4 -0
  3. package/README.md +275 -87
  4. package/dist/aes/aes-cbc.d.ts +40 -0
  5. package/dist/aes/aes-cbc.js +158 -0
  6. package/dist/aes/aes-ctr.d.ts +50 -0
  7. package/dist/aes/aes-ctr.js +141 -0
  8. package/dist/aes/aes-gcm-siv.d.ts +67 -0
  9. package/dist/aes/aes-gcm-siv.js +217 -0
  10. package/dist/aes/aes-gcm.d.ts +61 -0
  11. package/dist/aes/aes-gcm.js +226 -0
  12. package/dist/aes/cipher-suite.d.ts +21 -0
  13. package/dist/aes/cipher-suite.js +179 -0
  14. package/dist/aes/embedded.d.ts +1 -0
  15. package/dist/aes/embedded.js +26 -0
  16. package/dist/aes/generator.d.ts +14 -0
  17. package/dist/aes/generator.js +103 -0
  18. package/dist/aes/index.d.ts +58 -0
  19. package/dist/aes/index.js +125 -0
  20. package/dist/aes/ops.d.ts +60 -0
  21. package/dist/aes/ops.js +164 -0
  22. package/dist/aes/pool-worker.d.ts +1 -0
  23. package/dist/aes/pool-worker.js +92 -0
  24. package/dist/aes/types.d.ts +1 -0
  25. package/dist/aes/types.js +23 -0
  26. package/dist/aes.wasm +0 -0
  27. package/dist/blake3/embedded.d.ts +1 -0
  28. package/dist/blake3/embedded.js +26 -0
  29. package/dist/blake3/index.d.ts +143 -0
  30. package/dist/blake3/index.js +620 -0
  31. package/dist/blake3/types.d.ts +102 -0
  32. package/dist/blake3/types.js +31 -0
  33. package/dist/blake3/validate.d.ts +29 -0
  34. package/dist/blake3/validate.js +80 -0
  35. package/dist/blake3.wasm +0 -0
  36. package/dist/chacha20/cipher-suite.d.ts +10 -0
  37. package/dist/chacha20/cipher-suite.js +98 -13
  38. package/dist/chacha20/generator.d.ts +12 -0
  39. package/dist/chacha20/generator.js +91 -0
  40. package/dist/chacha20/index.d.ts +100 -3
  41. package/dist/chacha20/index.js +169 -35
  42. package/dist/chacha20/ops.d.ts +57 -6
  43. package/dist/chacha20/ops.js +107 -27
  44. package/dist/chacha20/pool-worker.js +14 -0
  45. package/dist/chacha20/types.d.ts +1 -32
  46. package/dist/cte-wasm.d.ts +1 -0
  47. package/dist/cte-wasm.js +3 -0
  48. package/dist/cte.wasm +0 -0
  49. package/dist/curve25519.wasm +0 -0
  50. package/dist/ecdsa/der.d.ts +23 -0
  51. package/dist/ecdsa/der.js +192 -0
  52. package/dist/ecdsa/ecprivatekey-der.d.ts +32 -0
  53. package/dist/ecdsa/ecprivatekey-der.js +230 -0
  54. package/dist/ecdsa/embedded.d.ts +1 -0
  55. package/dist/ecdsa/embedded.js +25 -0
  56. package/dist/ecdsa/index.d.ts +124 -0
  57. package/dist/ecdsa/index.js +366 -0
  58. package/dist/ecdsa/types.d.ts +31 -0
  59. package/dist/ecdsa/types.js +28 -0
  60. package/dist/ecdsa/validate.d.ts +18 -0
  61. package/dist/ecdsa/validate.js +92 -0
  62. package/dist/ed25519/embedded.d.ts +1 -0
  63. package/dist/ed25519/embedded.js +31 -0
  64. package/dist/ed25519/index.d.ts +70 -0
  65. package/dist/ed25519/index.js +308 -0
  66. package/dist/ed25519/types.d.ts +27 -0
  67. package/dist/ed25519/types.js +27 -0
  68. package/dist/ed25519/validate.d.ts +7 -0
  69. package/dist/ed25519/validate.js +77 -0
  70. package/dist/embedded/aes-pool-worker.d.ts +1 -0
  71. package/dist/embedded/aes-pool-worker.js +5 -0
  72. package/dist/embedded/aes.d.ts +1 -0
  73. package/dist/embedded/aes.js +3 -0
  74. package/dist/embedded/blake3.d.ts +1 -0
  75. package/dist/embedded/blake3.js +3 -0
  76. package/dist/embedded/chacha20-pool-worker.d.ts +1 -0
  77. package/dist/embedded/chacha20-pool-worker.js +5 -0
  78. package/dist/embedded/chacha20.d.ts +1 -1
  79. package/dist/embedded/chacha20.js +2 -2
  80. package/dist/embedded/curve25519.d.ts +1 -0
  81. package/dist/embedded/curve25519.js +3 -0
  82. package/dist/embedded/mldsa.d.ts +1 -0
  83. package/dist/embedded/mldsa.js +3 -0
  84. package/dist/embedded/mlkem.d.ts +1 -0
  85. package/dist/embedded/mlkem.js +3 -0
  86. package/dist/embedded/p256.d.ts +1 -0
  87. package/dist/embedded/p256.js +3 -0
  88. package/dist/embedded/serpent-pool-worker.d.ts +1 -0
  89. package/dist/embedded/serpent-pool-worker.js +5 -0
  90. package/dist/embedded/serpent.d.ts +1 -1
  91. package/dist/embedded/serpent.js +2 -2
  92. package/dist/embedded/sha2.d.ts +1 -1
  93. package/dist/embedded/sha2.js +2 -2
  94. package/dist/embedded/sha3.d.ts +1 -1
  95. package/dist/embedded/sha3.js +2 -2
  96. package/dist/embedded/slhdsa.d.ts +1 -0
  97. package/dist/embedded/slhdsa.js +3 -0
  98. package/dist/errors.d.ts +92 -1
  99. package/dist/errors.js +111 -1
  100. package/dist/fortuna.d.ts +18 -12
  101. package/dist/fortuna.js +166 -99
  102. package/dist/index.d.ts +42 -11
  103. package/dist/index.js +65 -20
  104. package/dist/init.d.ts +1 -3
  105. package/dist/init.js +73 -7
  106. package/dist/keccak/embedded.js +1 -1
  107. package/dist/keccak/index.d.ts +2 -0
  108. package/dist/keccak/index.js +4 -2
  109. package/dist/loader.d.ts +1 -19
  110. package/dist/loader.js +26 -32
  111. package/dist/merkle/blake3-tree.d.ts +35 -0
  112. package/dist/merkle/blake3-tree.js +187 -0
  113. package/dist/merkle/checkpoint.d.ts +58 -0
  114. package/dist/merkle/checkpoint.js +217 -0
  115. package/dist/merkle/index.d.ts +19 -0
  116. package/dist/merkle/index.js +37 -0
  117. package/dist/merkle/merkle-log.d.ts +130 -0
  118. package/dist/merkle/merkle-log.js +207 -0
  119. package/dist/merkle/merkle-verifier.d.ts +126 -0
  120. package/dist/merkle/merkle-verifier.js +296 -0
  121. package/dist/merkle/proof.d.ts +70 -0
  122. package/dist/merkle/proof.js +300 -0
  123. package/dist/merkle/sha256-tree.d.ts +33 -0
  124. package/dist/merkle/sha256-tree.js +145 -0
  125. package/dist/merkle/signed-log.d.ts +156 -0
  126. package/dist/merkle/signed-log.js +356 -0
  127. package/dist/merkle/signed-note.d.ts +309 -0
  128. package/dist/merkle/signed-note.js +648 -0
  129. package/dist/merkle/sth.d.ts +31 -0
  130. package/dist/merkle/sth.js +31 -0
  131. package/dist/merkle/storage.d.ts +40 -0
  132. package/dist/merkle/storage.js +71 -0
  133. package/dist/merkle/tree.d.ts +68 -0
  134. package/dist/merkle/tree.js +94 -0
  135. package/dist/mldsa/embedded.d.ts +1 -0
  136. package/dist/{kyber → mldsa}/embedded.js +5 -5
  137. package/dist/mldsa/expand.d.ts +53 -0
  138. package/dist/mldsa/expand.js +188 -0
  139. package/dist/mldsa/format.d.ts +16 -0
  140. package/dist/mldsa/format.js +68 -0
  141. package/dist/mldsa/hashvariant.d.ts +32 -0
  142. package/dist/mldsa/hashvariant.js +248 -0
  143. package/dist/mldsa/index.d.ts +142 -0
  144. package/dist/mldsa/index.js +463 -0
  145. package/dist/mldsa/keygen.d.ts +16 -0
  146. package/dist/mldsa/keygen.js +232 -0
  147. package/dist/mldsa/params.d.ts +21 -0
  148. package/dist/mldsa/params.js +55 -0
  149. package/dist/mldsa/sha3-helpers.d.ts +30 -0
  150. package/dist/mldsa/sha3-helpers.js +124 -0
  151. package/dist/mldsa/sign.d.ts +36 -0
  152. package/dist/mldsa/sign.js +380 -0
  153. package/dist/mldsa/types.d.ts +91 -0
  154. package/dist/mldsa/types.js +25 -0
  155. package/dist/mldsa/validate.d.ts +55 -0
  156. package/dist/mldsa/validate.js +125 -0
  157. package/dist/mldsa/verify.d.ts +29 -0
  158. package/dist/mldsa/verify.js +269 -0
  159. package/dist/mldsa.wasm +0 -0
  160. package/dist/mlkem/embedded.d.ts +1 -0
  161. package/dist/mlkem/embedded.js +27 -0
  162. package/dist/mlkem/indcpa.d.ts +49 -0
  163. package/dist/{kyber → mlkem}/indcpa.js +48 -48
  164. package/dist/mlkem/index.d.ts +37 -0
  165. package/dist/{kyber → mlkem}/index.js +41 -31
  166. package/dist/mlkem/kem.d.ts +21 -0
  167. package/dist/{kyber → mlkem}/kem.js +48 -13
  168. package/dist/{kyber → mlkem}/params.d.ts +4 -4
  169. package/dist/{kyber → mlkem}/params.js +2 -2
  170. package/dist/mlkem/suite.d.ts +12 -0
  171. package/dist/{kyber → mlkem}/suite.js +17 -12
  172. package/dist/{kyber → mlkem}/types.d.ts +4 -3
  173. package/dist/{kyber → mlkem}/types.js +1 -1
  174. package/dist/mlkem/validate.d.ts +23 -0
  175. package/dist/{kyber → mlkem}/validate.js +24 -20
  176. package/dist/{kyber.wasm → mlkem.wasm} +0 -0
  177. package/dist/p256.wasm +0 -0
  178. package/dist/ratchet/index.d.ts +8 -0
  179. package/dist/ratchet/index.js +38 -0
  180. package/dist/ratchet/kdf-chain.d.ts +13 -0
  181. package/dist/ratchet/kdf-chain.js +85 -0
  182. package/dist/ratchet/ratchet-keypair.d.ts +9 -0
  183. package/dist/ratchet/ratchet-keypair.js +61 -0
  184. package/dist/ratchet/root-kdf.d.ts +4 -0
  185. package/dist/ratchet/root-kdf.js +124 -0
  186. package/dist/ratchet/skipped-key-store.d.ts +14 -0
  187. package/dist/ratchet/skipped-key-store.js +154 -0
  188. package/dist/ratchet/types.d.ts +36 -0
  189. package/dist/ratchet/types.js +26 -0
  190. package/dist/serpent/cipher-suite.d.ts +10 -0
  191. package/dist/serpent/cipher-suite.js +144 -56
  192. package/dist/serpent/generator.d.ts +12 -0
  193. package/dist/serpent/generator.js +97 -0
  194. package/dist/serpent/index.d.ts +62 -1
  195. package/dist/serpent/index.js +97 -21
  196. package/dist/serpent/pool-worker.js +28 -102
  197. package/dist/serpent/serpent-cbc.d.ts +16 -6
  198. package/dist/serpent/serpent-cbc.js +58 -37
  199. package/dist/serpent/shared-ops.d.ts +63 -0
  200. package/dist/serpent/shared-ops.js +178 -0
  201. package/dist/serpent/types.d.ts +1 -5
  202. package/dist/serpent.wasm +0 -0
  203. package/dist/sha2/hash.d.ts +2 -0
  204. package/dist/sha2/hash.js +53 -0
  205. package/dist/sha2/hkdf.js +5 -5
  206. package/dist/sha2/index.d.ts +22 -1
  207. package/dist/sha2/index.js +80 -11
  208. package/dist/sha2/types.d.ts +41 -2
  209. package/dist/sha2.wasm +0 -0
  210. package/dist/sha3/hash.d.ts +2 -0
  211. package/dist/sha3/hash.js +53 -0
  212. package/dist/sha3/index.d.ts +87 -3
  213. package/dist/sha3/index.js +317 -19
  214. package/dist/sha3/kmac.d.ts +121 -0
  215. package/dist/sha3/kmac.js +800 -0
  216. package/dist/sha3.wasm +0 -0
  217. package/dist/shared/pkcs7.d.ts +22 -0
  218. package/dist/shared/pkcs7.js +84 -0
  219. package/dist/sign/ctx.d.ts +41 -0
  220. package/dist/sign/ctx.js +102 -0
  221. package/dist/sign/envelope.d.ts +45 -0
  222. package/dist/sign/envelope.js +152 -0
  223. package/dist/sign/hasher.d.ts +9 -0
  224. package/dist/sign/hasher.js +132 -0
  225. package/dist/sign/index.d.ts +11 -0
  226. package/dist/sign/index.js +34 -0
  227. package/dist/sign/sign-stream.d.ts +25 -0
  228. package/dist/sign/sign-stream.js +112 -0
  229. package/dist/sign/suites/ecdsa-p256.d.ts +2 -0
  230. package/dist/sign/suites/ecdsa-p256.js +120 -0
  231. package/dist/sign/suites/ed25519.d.ts +3 -0
  232. package/dist/sign/suites/ed25519.js +165 -0
  233. package/dist/sign/suites/hybrid-classical.d.ts +23 -0
  234. package/dist/sign/suites/hybrid-classical.js +526 -0
  235. package/dist/sign/suites/hybrid-pq.d.ts +4 -0
  236. package/dist/sign/suites/hybrid-pq.js +234 -0
  237. package/dist/sign/suites/mldsa.d.ts +7 -0
  238. package/dist/sign/suites/mldsa.js +161 -0
  239. package/dist/sign/suites/slhdsa.d.ts +7 -0
  240. package/dist/sign/suites/slhdsa.js +176 -0
  241. package/dist/sign/types.d.ts +106 -0
  242. package/dist/sign/types.js +28 -0
  243. package/dist/sign/verify-stream.d.ts +30 -0
  244. package/dist/sign/verify-stream.js +227 -0
  245. package/dist/slhdsa/embedded.d.ts +1 -0
  246. package/dist/slhdsa/embedded.js +26 -0
  247. package/dist/slhdsa/index.d.ts +149 -0
  248. package/dist/slhdsa/index.js +493 -0
  249. package/dist/slhdsa/params.d.ts +26 -0
  250. package/dist/slhdsa/params.js +70 -0
  251. package/dist/slhdsa/prehash.d.ts +68 -0
  252. package/dist/slhdsa/prehash.js +307 -0
  253. package/dist/slhdsa/sign.d.ts +39 -0
  254. package/dist/slhdsa/sign.js +116 -0
  255. package/dist/slhdsa/types.d.ts +129 -0
  256. package/dist/slhdsa/types.js +27 -0
  257. package/dist/slhdsa/validate.d.ts +60 -0
  258. package/dist/slhdsa/validate.js +127 -0
  259. package/dist/slhdsa/verify.d.ts +32 -0
  260. package/dist/slhdsa/verify.js +107 -0
  261. package/dist/slhdsa.wasm +0 -0
  262. package/dist/stream/header.js +8 -8
  263. package/dist/stream/index.d.ts +1 -0
  264. package/dist/stream/index.js +1 -0
  265. package/dist/stream/open-stream.js +65 -22
  266. package/dist/stream/seal-stream-pool.d.ts +2 -0
  267. package/dist/stream/seal-stream-pool.js +100 -33
  268. package/dist/stream/seal-stream.d.ts +1 -1
  269. package/dist/stream/seal-stream.js +48 -19
  270. package/dist/stream/seal.js +6 -6
  271. package/dist/stream/types.d.ts +3 -1
  272. package/dist/stream/types.js +1 -1
  273. package/dist/types.d.ts +22 -1
  274. package/dist/types.js +1 -1
  275. package/dist/utils.d.ts +9 -10
  276. package/dist/utils.js +84 -59
  277. package/dist/wasm-source.d.ts +9 -8
  278. package/dist/wasm-source.js +1 -1
  279. package/dist/x25519/embedded.d.ts +1 -0
  280. package/dist/x25519/embedded.js +31 -0
  281. package/dist/x25519/index.d.ts +43 -0
  282. package/dist/x25519/index.js +159 -0
  283. package/dist/x25519/types.d.ts +25 -0
  284. package/dist/x25519/types.js +27 -0
  285. package/dist/x25519/validate.d.ts +2 -0
  286. package/dist/x25519/validate.js +39 -0
  287. package/package.json +123 -64
  288. package/SECURITY.md +0 -276
  289. package/dist/ct-wasm.d.ts +0 -1
  290. package/dist/ct-wasm.js +0 -3
  291. package/dist/ct.wasm +0 -0
  292. package/dist/docs/aead.md +0 -323
  293. package/dist/docs/architecture.md +0 -932
  294. package/dist/docs/argon2id.md +0 -302
  295. package/dist/docs/chacha20.md +0 -674
  296. package/dist/docs/exports.md +0 -241
  297. package/dist/docs/fortuna.md +0 -313
  298. package/dist/docs/init.md +0 -302
  299. package/dist/docs/loader.md +0 -161
  300. package/dist/docs/serpent.md +0 -519
  301. package/dist/docs/sha2.md +0 -613
  302. package/dist/docs/sha3.md +0 -546
  303. package/dist/docs/types.md +0 -276
  304. package/dist/docs/utils.md +0 -367
  305. package/dist/embedded/kyber.d.ts +0 -1
  306. package/dist/embedded/kyber.js +0 -3
  307. package/dist/kyber/embedded.d.ts +0 -1
  308. package/dist/kyber/indcpa.d.ts +0 -49
  309. package/dist/kyber/index.d.ts +0 -38
  310. package/dist/kyber/kem.d.ts +0 -21
  311. package/dist/kyber/suite.d.ts +0 -13
  312. package/dist/kyber/validate.d.ts +0 -19
@@ -19,12 +19,12 @@
19
19
  // ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
20
20
  // ▀█████▀▀
21
21
  //
22
- // src/ts/kyber/indcpa.ts
22
+ // src/ts/mlkem/indcpa.ts
23
23
  //
24
- // ML-KEM IND-CPA PKE scheme FIPS 203 Algorithms 12, 13, 14 (K-PKE).
25
- // Orchestrates kyber WASM (polynomial math) and sha3 WASM (Keccak sponge).
24
+ // ML-KEM IND-CPA PKE scheme, FIPS 203 Algorithms 12, 13, 14 (K-PKE).
25
+ // Orchestrates mlkem WASM (polynomial math) and sha3 WASM (Keccak sponge).
26
26
  import { wipe } from '../utils.js';
27
- // ── SHA3 helpers ──────────────────────────────────────────────────────────────
27
+ // ── SHA3 helpers ────────────────────────────────────────────────────────────
28
28
  // All operate directly on raw Sha3Exports, no init() system involved.
29
29
  /** Absorb msg into the sha3 sponge in 168-byte chunks (max rate). */
30
30
  function sha3Absorb(sx, msg) {
@@ -77,12 +77,12 @@ export function shake256Hash(sx, msg, n) {
77
77
  }
78
78
  return out;
79
79
  }
80
- // ── Matrix generation ─────────────────────────────────────────────────────────
80
+ // ── Matrix generation ───────────────────────────────────────────────────────
81
81
  /**
82
82
  * Generate row `rowI` of matrix  (or Â^T) into polyvec slot `pvecSlot`.
83
83
  *
84
84
  * FIPS 203 Algorithm 6 (SampleNTT): each entry Â[i][j] = XOF(ρ, j, i).
85
- * The matrix entries are already in NTT domain by construction no separate
85
+ * The matrix entries are already in NTT domain by construction, no separate
86
86
  * NTT call is needed after rej_uniform.
87
87
  *
88
88
  * transposed=false (keygen): XOF input = ρ || j || i → Â[i][j]
@@ -90,7 +90,7 @@ export function shake256Hash(sx, msg, n) {
90
90
  */
91
91
  function genMatrixRow(kx, sx, k, rho, transposed, pvecSlot, rowI) {
92
92
  const xofPrfOff = kx.getXofPrfOffset();
93
- const kyberMem = new Uint8Array(kx.memory.buffer);
93
+ const mlkemMem = new Uint8Array(kx.memory.buffer);
94
94
  const sha3Mem = new Uint8Array(sx.memory.buffer);
95
95
  const outOff = sx.getOutOffset();
96
96
  // XOF seed buffer: ρ(32) || byte0 || byte1
@@ -115,19 +115,19 @@ function genMatrixRow(kx, sx, k, rho, transposed, pvecSlot, rowI) {
115
115
  let ctr = 0;
116
116
  while (ctr < 256) {
117
117
  sx.shakeSqueezeBlock(); // 168 bytes at sha3 OUT_OFFSET
118
- kyberMem.set(sha3Mem.subarray(outOff, outOff + 168), xofPrfOff);
118
+ mlkemMem.set(sha3Mem.subarray(outOff, outOff + 168), xofPrfOff);
119
119
  ctr += kx.rej_uniform(polyOff, ctr, xofPrfOff, 168);
120
120
  }
121
121
  }
122
122
  }
123
- // ── Noise generation ──────────────────────────────────────────────────────────
123
+ // ── Noise generation ────────────────────────────────────────────────────────
124
124
  /**
125
125
  * CBD noise polyvec: SHAKE256(σ || nonce) for each entry.
126
126
  * FIPS 203 Algorithm 7: PRF_η(σ, N) = SHAKE256(σ || N)[0..64η-1].
127
127
  */
128
128
  function noisePolyvec(kx, sx, pvSlot, k, sigma, nonceStart, eta) {
129
129
  const xofPrfOff = kx.getXofPrfOffset();
130
- const kyberMem = new Uint8Array(kx.memory.buffer);
130
+ const mlkemMem = new Uint8Array(kx.memory.buffer);
131
131
  const sha3Mem = new Uint8Array(sx.memory.buffer);
132
132
  const outOff = sx.getOutOffset();
133
133
  const prfLen = eta * 64; // 128 for η=2, 192 for η=3
@@ -145,7 +145,7 @@ function noisePolyvec(kx, sx, pvSlot, k, sigma, nonceStart, eta) {
145
145
  while (pos < prfLen) {
146
146
  sx.shakeSqueezeBlock();
147
147
  const take = Math.min(prfLen - pos, rate);
148
- kyberMem.set(sha3Mem.subarray(outOff, outOff + take), xofPrfOff + pos);
148
+ mlkemMem.set(sha3Mem.subarray(outOff, outOff + take), xofPrfOff + pos);
149
149
  pos += take;
150
150
  }
151
151
  kx.poly_getnoise(pvSlot + i * 512, xofPrfOff, eta);
@@ -160,7 +160,7 @@ function noisePolyvec(kx, sx, pvSlot, k, sigma, nonceStart, eta) {
160
160
  */
161
161
  function noisePoly(kx, sx, polyOff, sigma, nonce, eta) {
162
162
  const xofPrfOff = kx.getXofPrfOffset();
163
- const kyberMem = new Uint8Array(kx.memory.buffer);
163
+ const mlkemMem = new Uint8Array(kx.memory.buffer);
164
164
  const sha3Mem = new Uint8Array(sx.memory.buffer);
165
165
  const outOff = sx.getOutOffset();
166
166
  const prfLen = eta * 64;
@@ -176,7 +176,7 @@ function noisePoly(kx, sx, polyOff, sigma, nonce, eta) {
176
176
  while (pos < prfLen) {
177
177
  sx.shakeSqueezeBlock();
178
178
  const take = Math.min(prfLen - pos, rate);
179
- kyberMem.set(sha3Mem.subarray(outOff, outOff + take), xofPrfOff + pos);
179
+ mlkemMem.set(sha3Mem.subarray(outOff, outOff + take), xofPrfOff + pos);
180
180
  pos += take;
181
181
  }
182
182
  kx.poly_getnoise(polyOff, xofPrfOff, eta);
@@ -185,15 +185,15 @@ function noisePoly(kx, sx, polyOff, sigma, nonce, eta) {
185
185
  wipe(prfInput);
186
186
  }
187
187
  }
188
- // ── IND-CPA functions ─────────────────────────────────────────────────────────
188
+ // ── IND-CPA functions ───────────────────────────────────────────────────────
189
189
  /**
190
- * K-PKE.KeyGen (FIPS 203 Algorithm 12) deterministic.
190
+ * K-PKE.KeyGen (FIPS 203 Algorithm 12), deterministic.
191
191
  *
192
192
  * Slot map:
193
- * pvec0 current row of  (overwritten per row)
194
- * pvec1 ŝ (noise, persistent through dot products)
195
- * pvec2 ê (noise)
196
- * pvec3 t̂ = ·ŝ + ê (output)
193
+ * pvec0, current row of  (overwritten per row)
194
+ * pvec1, ŝ (noise, persistent through dot products)
195
+ * pvec2, ê (noise)
196
+ * pvec3, t̂ = ·ŝ + ê (output)
197
197
  */
198
198
  export function indcpaKeypairDerand(kx, sx, params, d) {
199
199
  const { k, eta1 } = params;
@@ -219,7 +219,7 @@ export function indcpaKeypairDerand(kx, sx, params, d) {
219
219
  kx.polyvec_ntt(pvec1, k);
220
220
  kx.polyvec_ntt(pvec2, k);
221
221
  // Step 5: For each row i, t̂[i] = Â[i] · ŝ
222
- const kyberMem = new Uint8Array(kx.memory.buffer);
222
+ const mlkemMem = new Uint8Array(kx.memory.buffer);
223
223
  for (let i = 0; i < k; i++) {
224
224
  genMatrixRow(kx, sx, k, rho, false, pvec0, i);
225
225
  kx.polyvec_basemul_acc_montgomery(pvec3 + i * 512, pvec0, pvec1, k);
@@ -230,12 +230,12 @@ export function indcpaKeypairDerand(kx, sx, params, d) {
230
230
  kx.polyvec_reduce(pvec3, k);
231
231
  // Step 8: ek = polyvec_tobytes(t̂) || ρ
232
232
  kx.polyvec_tobytes(pkOff, pvec3, k);
233
- kyberMem.set(rho, pkOff + k * 384);
233
+ mlkemMem.set(rho, pkOff + k * 384);
234
234
  // Step 9: sk = polyvec_tobytes(ŝ)
235
235
  kx.polyvec_tobytes(skOff, pvec1, k);
236
236
  return {
237
- ekCpa: kyberMem.slice(pkOff, pkOff + params.ekBytes),
238
- skCpa: kyberMem.slice(skOff, skOff + params.skCpaBytes),
237
+ ekCpa: mlkemMem.slice(pkOff, pkOff + params.ekBytes),
238
+ skCpa: mlkemMem.slice(skOff, skOff + params.skCpaBytes),
239
239
  };
240
240
  }
241
241
  finally {
@@ -244,21 +244,21 @@ export function indcpaKeypairDerand(kx, sx, params, d) {
244
244
  }
245
245
  }
246
246
  /**
247
- * K-PKE.Encrypt (FIPS 203 Algorithm 13) deterministic.
247
+ * K-PKE.Encrypt (FIPS 203 Algorithm 13), deterministic.
248
248
  *
249
249
  * Slot map:
250
- * pvec0 current row of Â^T (transposed, overwritten per row)
251
- * pvec1 r̂ = NTT(r)
252
- * pvec2 e₁ (noise)
253
- * pvec3 u = invNTT(Â^T · r̂) + e₁
254
- * pvec4 t̂ (unpacked from ek)
255
- * poly1 e₂ (noise)
256
- * poly2 v = invNTT(t̂^T · r̂) + e₂ + msg
257
- * poly3 message polynomial
250
+ * pvec0, current row of Â^T (transposed, overwritten per row)
251
+ * pvec1, r̂ = NTT(r)
252
+ * pvec2, e₁ (noise)
253
+ * pvec3, u = invNTT(Â^T · r̂) + e₁
254
+ * pvec4, t̂ (unpacked from ek)
255
+ * poly1 , e₂ (noise)
256
+ * poly2 , v = invNTT(t̂^T · r̂) + e₂ + msg
257
+ * poly3 , message polynomial
258
258
  */
259
259
  export function indcpaEncrypt(kx, sx, params, ek, m, coins) {
260
260
  const { k, eta1, eta2, du, dv } = params;
261
- const kyberMem = new Uint8Array(kx.memory.buffer);
261
+ const mlkemMem = new Uint8Array(kx.memory.buffer);
262
262
  const pvec0 = kx.getPolyvecSlot0();
263
263
  const pvec1 = kx.getPolyvecSlot1();
264
264
  const pvec2 = kx.getPolyvecSlot2();
@@ -270,8 +270,8 @@ export function indcpaEncrypt(kx, sx, params, ek, m, coins) {
270
270
  const pkOff = kx.getPkOffset();
271
271
  const ctOff = kx.getCtOffset();
272
272
  const msgOff = kx.getMsgOffset();
273
- // Step 1: Unpack ek t̂ → pvec4, ρ from ek tail
274
- kyberMem.set(ek, pkOff);
273
+ // Step 1: Unpack ek, t̂ → pvec4, ρ from ek tail
274
+ mlkemMem.set(ek, pkOff);
275
275
  kx.polyvec_frombytes(pvec4, pkOff, k);
276
276
  const rho = ek.slice(k * 384, k * 384 + 32);
277
277
  // Steps 2-4: Generate noise r, e₁, e₂ from coins
@@ -296,31 +296,31 @@ export function indcpaEncrypt(kx, sx, params, ek, m, coins) {
296
296
  kx.polyvec_basemul_acc_montgomery(poly2, pvec4, pvec1, k);
297
297
  kx.poly_invntt(poly2);
298
298
  // Step 12: decode message
299
- kyberMem.set(m, msgOff);
299
+ mlkemMem.set(m, msgOff);
300
300
  kx.poly_frommsg(poly3, msgOff);
301
301
  // Steps 13-15: v = v + e₂ + msg, reduce
302
302
  kx.poly_add(poly2, poly2, poly1);
303
303
  kx.poly_add(poly2, poly2, poly3);
304
304
  kx.poly_reduce(poly2);
305
- // Step 16: pack ciphertext Compress_du(u) || Compress_dv(v)
305
+ // Step 16: pack ciphertext, Compress_du(u) || Compress_dv(v)
306
306
  const pvecCompBytes = k * du * 32;
307
307
  kx.polyvec_compress(ctOff, pvec3, k, du);
308
308
  kx.poly_compress(ctOff + pvecCompBytes, poly2, dv);
309
- return kyberMem.slice(ctOff, ctOff + params.ctBytes);
309
+ return mlkemMem.slice(ctOff, ctOff + params.ctBytes);
310
310
  }
311
311
  /**
312
312
  * K-PKE.Decrypt (FIPS 203 Algorithm 14).
313
313
  *
314
314
  * Slot map:
315
- * pvec0 û (decompressed from ct)
316
- * pvec1 ŝ (from sk)
317
- * poly0 v (decompressed from ct)
318
- * poly1 w = invNTT(ŝ^T · NTT(û))
319
- * poly2 m' = v - w
315
+ * pvec0, û (decompressed from ct)
316
+ * pvec1, ŝ (from sk)
317
+ * poly0 , v (decompressed from ct)
318
+ * poly1 , w = invNTT(ŝ^T · NTT(û))
319
+ * poly2 , m' = v - w
320
320
  */
321
321
  export function indcpaDecrypt(kx, params, skCpa, ct) {
322
322
  const { k, du, dv } = params;
323
- const kyberMem = new Uint8Array(kx.memory.buffer);
323
+ const mlkemMem = new Uint8Array(kx.memory.buffer);
324
324
  const pvec0 = kx.getPolyvecSlot0();
325
325
  const pvec1 = kx.getPolyvecSlot1();
326
326
  const poly0 = kx.getPolySlot0();
@@ -329,9 +329,9 @@ export function indcpaDecrypt(kx, params, skCpa, ct) {
329
329
  const ctOff = kx.getCtOffset();
330
330
  const skOff = kx.getSkOffset();
331
331
  const msgOff = kx.getMsgOffset();
332
- // Load ct and sk into kyber memory
333
- kyberMem.set(ct, ctOff);
334
- kyberMem.set(skCpa, skOff);
332
+ // Load ct and sk into mlkem memory
333
+ mlkemMem.set(ct, ctOff);
334
+ mlkemMem.set(skCpa, skOff);
335
335
  // Steps 1-2: Decompress û and v
336
336
  const pvecCompBytes = k * du * 32;
337
337
  kx.polyvec_decompress(pvec0, ctOff, k, du);
@@ -348,5 +348,5 @@ export function indcpaDecrypt(kx, params, skCpa, ct) {
348
348
  kx.poly_reduce(poly2);
349
349
  // Step 9-10: poly_tomsg → 32-byte message
350
350
  kx.poly_tomsg(msgOff, poly2);
351
- return kyberMem.slice(msgOff, msgOff + 32);
351
+ return mlkemMem.slice(msgOff, msgOff + 32);
352
352
  }
@@ -0,0 +1,37 @@
1
+ import { isInitialized } from '../init.js';
2
+ import type { WasmSource } from '../wasm-source.js';
3
+ import type { MlKemExports, Sha3Exports, MlKemKeyPair, MlKemEncapsulation } from './types.js';
4
+ import { MlKemParams, MLKEM512, MLKEM768, MLKEM1024 } from './params.js';
5
+ export declare function mlkemInit(source: WasmSource): Promise<void>;
6
+ export type { WasmSource };
7
+ export type { MlKemKeyPair, MlKemEncapsulation, MlKemExports, Sha3Exports };
8
+ export { MLKEM512, MLKEM768, MLKEM1024 };
9
+ export type { MlKemParams };
10
+ export { isInitialized };
11
+ export { MlKemSuite } from './suite.js';
12
+ export declare class MlKemBase {
13
+ readonly params: MlKemParams;
14
+ constructor(params: MlKemParams);
15
+ private get kx();
16
+ private get sx();
17
+ keygenDerand(d: Uint8Array, z: Uint8Array): MlKemKeyPair;
18
+ keygen(): MlKemKeyPair;
19
+ encapsulateDerand(ek: Uint8Array, m: Uint8Array): MlKemEncapsulation;
20
+ encapsulate(ek: Uint8Array): MlKemEncapsulation;
21
+ decapsulate(dk: Uint8Array, c: Uint8Array): Uint8Array;
22
+ checkEncapsulationKey(ek: Uint8Array): boolean;
23
+ checkDecapsulationKey(dk: Uint8Array): boolean;
24
+ dispose(): void;
25
+ }
26
+ /** ML-KEM-512, k=2, η₁=3, η₂=2, dᵤ=10, dᵥ=4. */
27
+ export declare class MlKem512 extends MlKemBase {
28
+ constructor();
29
+ }
30
+ /** ML-KEM-768, k=3, η₁=2, η₂=2, dᵤ=10, dᵥ=4. */
31
+ export declare class MlKem768 extends MlKemBase {
32
+ constructor();
33
+ }
34
+ /** ML-KEM-1024, k=4, η₁=2, η₂=2, dᵤ=11, dᵥ=5. */
35
+ export declare class MlKem1024 extends MlKemBase {
36
+ constructor();
37
+ }
@@ -19,32 +19,22 @@
19
19
  // ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
20
20
  // ▀█████▀▀
21
21
  //
22
- // src/ts/kyber/index.ts
22
+ // src/ts/mlkem/index.ts
23
23
  //
24
- // ML-KEM public API MlKem512, MlKem768, MlKem1024 classes.
25
- // Uses the init() module cache call init({ kyber: ..., sha3: ... }) before constructing.
26
- import { getInstance, initModule, isInitialized } from '../init.js';
24
+ // ML-KEM public API, MlKem512, MlKem768, MlKem1024 classes.
25
+ // Uses the init() module cache, call init({ mlkem: ..., sha3: ... }) before constructing.
26
+ import { getInstance, initModule, isInitialized, _assertNotOwned } from '../init.js';
27
27
  import { randomBytes, wipe } from '../utils.js';
28
28
  import { MLKEM512, MLKEM768, MLKEM1024 } from './params.js';
29
29
  import { kemKeypairDerand, kemEncapsulateDerand, kemDecapsulate } from './kem.js';
30
30
  import { checkEncapsulationKey, checkDecapsulationKey } from './validate.js';
31
- export async function kyberInit(source) {
32
- return initModule('kyber', source);
33
- }
34
- export function _kyberReady() {
35
- try {
36
- getInstance('kyber');
37
- getInstance('sha3');
38
- return true;
39
- }
40
- catch {
41
- return false;
42
- }
31
+ export async function mlkemInit(source) {
32
+ return initModule('mlkem', source);
43
33
  }
44
34
  export { MLKEM512, MLKEM768, MLKEM1024 };
45
35
  export { isInitialized };
46
- export { KyberSuite } from './suite.js';
47
- // ── Layout assertion ──────────────────────────────────────────────────────────
36
+ export { MlKemSuite } from './suite.js';
37
+ // ── Layout assertion ────────────────────────────────────────────────────────
48
38
  function assertLayout(kx, p) {
49
39
  const pk = kx.getPkOffset();
50
40
  const sk = kx.getSkOffset();
@@ -52,32 +42,34 @@ function assertLayout(kx, p) {
52
42
  const ctPrime = kx.getCtPrimeOffset();
53
43
  const xof = kx.getXofPrfOffset();
54
44
  if (pk + p.ekBytes > sk)
55
- throw new Error('leviathan-crypto: kyber buffer overflow ek overflows into SK region');
45
+ throw new Error('leviathan-crypto: mlkem buffer overflow, ek overflows into SK region');
56
46
  if (sk + p.skCpaBytes > ct)
57
- throw new Error('leviathan-crypto: kyber buffer overflow sk overflows into CT region');
47
+ throw new Error('leviathan-crypto: mlkem buffer overflow, sk overflows into CT region');
58
48
  if (ct + p.ctBytes > ctPrime)
59
- throw new Error('leviathan-crypto: kyber buffer overflow ct overflows into CT_PRIME region');
49
+ throw new Error('leviathan-crypto: mlkem buffer overflow, ct overflows into CT_PRIME region');
60
50
  if (ctPrime + p.ctBytes > xof)
61
- throw new Error('leviathan-crypto: kyber buffer overflow ct_prime overflows into XOF region');
51
+ throw new Error('leviathan-crypto: mlkem buffer overflow, ct_prime overflows into XOF region');
62
52
  }
63
- // ── Base class ────────────────────────────────────────────────────────────────
53
+ // ── Base class ──────────────────────────────────────────────────────────────
64
54
  export class MlKemBase {
65
55
  params;
66
56
  constructor(params) {
67
- if (!isInitialized('kyber'))
68
- throw new Error('leviathan-crypto: call init({ kyber: ... }) before using MlKem classes');
57
+ if (!isInitialized('mlkem'))
58
+ throw new Error('leviathan-crypto: call init({ mlkem: ... }) before using MlKem classes');
69
59
  if (!isInitialized('sha3'))
70
60
  throw new Error('leviathan-crypto: call init({ sha3: ... }) before using MlKem classes');
71
61
  this.params = params;
72
62
  assertLayout(this.kx, params);
73
63
  }
74
64
  get kx() {
75
- return getInstance('kyber').exports;
65
+ return getInstance('mlkem').exports;
76
66
  }
77
67
  get sx() {
78
68
  return getInstance('sha3').exports;
79
69
  }
80
70
  keygenDerand(d, z) {
71
+ _assertNotOwned('sha3');
72
+ _assertNotOwned('mlkem');
81
73
  if (d.length !== 32)
82
74
  throw new RangeError(`d seed must be 32 bytes (got ${d.length})`);
83
75
  if (z.length !== 32)
@@ -96,10 +88,14 @@ export class MlKemBase {
96
88
  }
97
89
  }
98
90
  encapsulateDerand(ek, m) {
91
+ _assertNotOwned('sha3');
92
+ _assertNotOwned('mlkem');
99
93
  if (ek.length !== this.params.ekBytes)
100
94
  throw new RangeError(`encapsulation key must be ${this.params.ekBytes} bytes (got ${ek.length})`);
101
95
  if (m.length !== 32)
102
96
  throw new RangeError(`randomness m must be 32 bytes (got ${m.length})`);
97
+ if (!checkEncapsulationKey(this.kx, this.params, ek))
98
+ throw new RangeError('leviathan-crypto: encapsulation key failed FIPS 203 §7.2 validity check');
103
99
  return kemEncapsulateDerand(this.kx, this.sx, this.params, ek, m);
104
100
  }
105
101
  encapsulate(ek) {
@@ -112,37 +108,51 @@ export class MlKemBase {
112
108
  }
113
109
  }
114
110
  decapsulate(dk, c) {
111
+ _assertNotOwned('sha3');
112
+ _assertNotOwned('mlkem');
115
113
  if (dk.length !== this.params.dkBytes)
116
114
  throw new RangeError(`decapsulation key must be ${this.params.dkBytes} bytes (got ${dk.length})`);
117
115
  if (c.length !== this.params.ctBytes)
118
116
  throw new RangeError(`ciphertext must be ${this.params.ctBytes} bytes (got ${c.length})`);
117
+ if (!checkDecapsulationKey(this.kx, this.sx, this.params, dk))
118
+ throw new RangeError('leviathan-crypto: decapsulation key failed FIPS 203 §7.3 validity check');
119
119
  return kemDecapsulate(this.kx, this.sx, this.params, dk, c);
120
120
  }
121
121
  checkEncapsulationKey(ek) {
122
+ _assertNotOwned('sha3');
123
+ _assertNotOwned('mlkem');
122
124
  return checkEncapsulationKey(this.kx, this.params, ek);
123
125
  }
124
126
  checkDecapsulationKey(dk) {
127
+ _assertNotOwned('sha3');
128
+ _assertNotOwned('mlkem');
125
129
  return checkDecapsulationKey(this.kx, this.sx, this.params, dk);
126
130
  }
127
131
  dispose() {
128
132
  this.kx.wipeBuffers();
129
- this.sx.wipeBuffers();
133
+ // MlKemBase does not own the sha3 module, wiping this.sx.wipeBuffers()
134
+ // here would clobber any SHAKE128/SHAKE256 instance live at the time
135
+ // of dispose(). The wipe is not needed: every public mlkem op
136
+ // (keygen, encapsulate, decapsulate, checkDecapsulationKey) calls
137
+ // sx.wipeBuffers() before returning, under the _assertNotOwned('sha3')
138
+ // guard it holds for the duration. sha3 scratch therefore carries no
139
+ // residue across a mlkem op boundary, secret-derived or otherwise.
130
140
  }
131
141
  }
132
- // ── Public classes ────────────────────────────────────────────────────────────
133
- /** ML-KEM-512 k=2, η₁=3, η₂=2, dᵤ=10, dᵥ=4. */
142
+ // ── Public classes ──────────────────────────────────────────────────────────
143
+ /** ML-KEM-512, k=2, η₁=3, η₂=2, dᵤ=10, dᵥ=4. */
134
144
  export class MlKem512 extends MlKemBase {
135
145
  constructor() {
136
146
  super(MLKEM512);
137
147
  }
138
148
  }
139
- /** ML-KEM-768 k=3, η₁=2, η₂=2, dᵤ=10, dᵥ=4. */
149
+ /** ML-KEM-768, k=3, η₁=2, η₂=2, dᵤ=10, dᵥ=4. */
140
150
  export class MlKem768 extends MlKemBase {
141
151
  constructor() {
142
152
  super(MLKEM768);
143
153
  }
144
154
  }
145
- /** ML-KEM-1024 k=4, η₁=2, η₂=2, dᵤ=11, dᵥ=5. */
155
+ /** ML-KEM-1024, k=4, η₁=2, η₂=2, dᵤ=11, dᵥ=5. */
146
156
  export class MlKem1024 extends MlKemBase {
147
157
  constructor() {
148
158
  super(MLKEM1024);
@@ -0,0 +1,21 @@
1
+ import type { MlKemExports, Sha3Exports, MlKemKeyPair, MlKemEncapsulation } from './types.js';
2
+ import type { MlKemParams } from './params.js';
3
+ /**
4
+ * ML-KEM.KeyGen_internal (FIPS 203 Algorithm 15).
5
+ *
6
+ * dk = skCpa || ek || H(ek) || z
7
+ */
8
+ export declare function kemKeypairDerand(kx: MlKemExports, sx: Sha3Exports, params: MlKemParams, d: Uint8Array, z: Uint8Array): MlKemKeyPair;
9
+ /**
10
+ * ML-KEM.Encaps_internal (FIPS 203 Algorithm 16).
11
+ *
12
+ * (K, r) = G(m || H(ek)), c = K-PKE.Encrypt(ek, m, r)
13
+ */
14
+ export declare function kemEncapsulateDerand(kx: MlKemExports, sx: Sha3Exports, params: MlKemParams, ek: Uint8Array, m: Uint8Array): MlKemEncapsulation;
15
+ /**
16
+ * ML-KEM.Decaps_internal (FIPS 203 Algorithm 17).
17
+ *
18
+ * Constant-time: uses ct_verify and ct_cmov from mlkem WASM.
19
+ * MUST NOT branch on secret data in JS, all comparison via WASM primitives.
20
+ */
21
+ export declare function kemDecapsulate(kx: MlKemExports, sx: Sha3Exports, params: MlKemParams, dk: Uint8Array, c: Uint8Array): Uint8Array;
@@ -19,9 +19,9 @@
19
19
  // ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
20
20
  // ▀█████▀▀
21
21
  //
22
- // src/ts/kyber/kem.ts
22
+ // src/ts/mlkem/kem.ts
23
23
  //
24
- // ML-KEM KEM layer Fujisaki-Okamoto transform.
24
+ // ML-KEM KEM layer, Fujisaki-Okamoto transform.
25
25
  // FIPS 203 Algorithms 15, 16, 17 (ML-KEM internal).
26
26
  import { indcpaKeypairDerand, indcpaEncrypt, indcpaDecrypt, sha3_256Hash, sha3_512Hash, shake256Hash, } from './indcpa.js';
27
27
  import { wipe } from '../utils.js';
@@ -33,6 +33,13 @@ import { wipe } from '../utils.js';
33
33
  export function kemKeypairDerand(kx, sx, params, d, z) {
34
34
  // indcpaKeypairDerand handles its own sigma wipe
35
35
  const { ekCpa, skCpa } = indcpaKeypairDerand(kx, sx, params, d);
36
+ // Wipe CPA sk + keygen noise + PRF scratch. See
37
+ // docs/mlkem.md#wipe-discipline.
38
+ const mlkemMem = new Uint8Array(kx.memory.buffer);
39
+ mlkemMem.fill(0, kx.getSkOffset(), kx.getSkOffset() + params.skCpaBytes);
40
+ mlkemMem.fill(0, kx.getPolyvecSlot1(), kx.getPolyvecSlot1() + 2048);
41
+ mlkemMem.fill(0, kx.getPolyvecSlot2(), kx.getPolyvecSlot2() + 2048);
42
+ mlkemMem.fill(0, kx.getXofPrfOffset(), kx.getXofPrfOffset() + 1024);
36
43
  const h = sha3_256Hash(sx, ekCpa);
37
44
  try {
38
45
  const dk = new Uint8Array(params.dkBytes);
@@ -40,6 +47,7 @@ export function kemKeypairDerand(kx, sx, params, d, z) {
40
47
  dk.set(ekCpa, params.skCpaBytes);
41
48
  dk.set(h, params.skCpaBytes + params.ekBytes);
42
49
  dk.set(z, params.skCpaBytes + params.ekBytes + 32);
50
+ sx.wipeBuffers();
43
51
  return {
44
52
  encapsulationKey: ekCpa,
45
53
  decapsulationKey: dk,
@@ -68,6 +76,18 @@ export function kemEncapsulateDerand(kx, sx, params, ek, m) {
68
76
  const K = gOut.slice(0, 32);
69
77
  r = gOut.slice(32, 64);
70
78
  const c = indcpaEncrypt(kx, sx, params, ek, m, r);
79
+ // Wipe m + r + e1/e2/u/v + m-poly + PRF scratch. See
80
+ // docs/mlkem.md#wipe-discipline.
81
+ const mlkemMem = new Uint8Array(kx.memory.buffer);
82
+ mlkemMem.fill(0, kx.getMsgOffset(), kx.getMsgOffset() + 32);
83
+ mlkemMem.fill(0, kx.getPolyvecSlot1(), kx.getPolyvecSlot1() + 2048);
84
+ mlkemMem.fill(0, kx.getPolyvecSlot2(), kx.getPolyvecSlot2() + 2048);
85
+ mlkemMem.fill(0, kx.getPolyvecSlot3(), kx.getPolyvecSlot3() + 2048);
86
+ mlkemMem.fill(0, kx.getPolySlot1(), kx.getPolySlot1() + 512);
87
+ mlkemMem.fill(0, kx.getPolySlot2(), kx.getPolySlot2() + 512);
88
+ mlkemMem.fill(0, kx.getPolySlot3(), kx.getPolySlot3() + 512);
89
+ mlkemMem.fill(0, kx.getXofPrfOffset(), kx.getXofPrfOffset() + 1024);
90
+ sx.wipeBuffers();
71
91
  return { ciphertext: c, sharedSecret: K };
72
92
  }
73
93
  finally {
@@ -82,8 +102,8 @@ export function kemEncapsulateDerand(kx, sx, params, ek, m) {
82
102
  /**
83
103
  * ML-KEM.Decaps_internal (FIPS 203 Algorithm 17).
84
104
  *
85
- * Constant-time: uses ct_verify and ct_cmov from kyber WASM.
86
- * MUST NOT branch on secret data in JS all comparison via WASM primitives.
105
+ * Constant-time: uses ct_verify and ct_cmov from mlkem WASM.
106
+ * MUST NOT branch on secret data in JS, all comparison via WASM primitives.
87
107
  */
88
108
  export function kemDecapsulate(kx, sx, params, dk, c) {
89
109
  const { skCpaBytes, ekBytes, ctBytes } = params;
@@ -115,25 +135,40 @@ export function kemDecapsulate(kx, sx, params, dk, c) {
115
135
  jInput.set(z, 0);
116
136
  jInput.set(c, 32);
117
137
  kBar = shake256Hash(sx, jInput, 32);
118
- // Re-encrypt c' = K-PKE.Encrypt(ek, m', r') indcpaEncrypt handles its own prfInput wipe
138
+ // Re-encrypt c' = K-PKE.Encrypt(ek, m', r'), indcpaEncrypt handles its own prfInput wipe
119
139
  cPrime = indcpaEncrypt(kx, sx, params, ek, mPrime, rPrime);
120
- // Constant-time comparison and conditional select via kyber WASM
121
- const kyberMem = new Uint8Array(kx.memory.buffer);
140
+ // Constant-time comparison and conditional select via mlkem WASM
141
+ const mlkemMem = new Uint8Array(kx.memory.buffer);
122
142
  const ctOff = kx.getCtOffset();
123
143
  const ctPrimeOff = kx.getCtPrimeOffset();
124
- // Write c and c' into named kyber memory regions
125
- kyberMem.set(c, ctOff);
126
- kyberMem.set(cPrime, ctPrimeOff);
144
+ // Write c and c' into named mlkem memory regions
145
+ mlkemMem.set(c, ctOff);
146
+ mlkemMem.set(cPrime, ctPrimeOff);
127
147
  // Write K' and K̄ into poly slots (512B each, 32B needed)
128
148
  const kPrimeOff = kx.getPolySlot0();
129
149
  const kBarOff = kx.getPolySlot1();
130
- kyberMem.set(kPrime, kPrimeOff);
131
- kyberMem.set(kBar, kBarOff);
150
+ mlkemMem.set(kPrime, kPrimeOff);
151
+ mlkemMem.set(kBar, kBarOff);
132
152
  // fail = 0 if c == c', non-zero if different
133
153
  const fail = kx.ct_verify(ctOff, ctPrimeOff, ctBytes);
134
154
  // If fail != 0 (mismatch): K' ← K̄
135
155
  kx.ct_cmov(kPrimeOff, kBarOff, 32, fail);
136
- return kyberMem.slice(kPrimeOff, kPrimeOff + 32);
156
+ const sharedSecret = mlkemMem.slice(kPrimeOff, kPrimeOff + 32);
157
+ // Wipe CPA sk + m' + K'/K_bar + noise + PRF scratch. skCpa is the
158
+ // highest-severity residual (compromises every ciphertext under the
159
+ // corresponding ek). See docs/mlkem.md#wipe-discipline.
160
+ mlkemMem.fill(0, kx.getMsgOffset(), kx.getMsgOffset() + 32); // m' (bytes)
161
+ mlkemMem.fill(0, kPrimeOff, kPrimeOff + 32); // K' (final shared secret)
162
+ mlkemMem.fill(0, kBarOff, kBarOff + 512); // K̄ (first 32B) + e₂ poly tail
163
+ mlkemMem.fill(0, kx.getPolySlot2(), kx.getPolySlot2() + 512); // m'-poly / v residual
164
+ mlkemMem.fill(0, kx.getPolySlot3(), kx.getPolySlot3() + 512); // indcpa message poly
165
+ mlkemMem.fill(0, kx.getPolyvecSlot1(), kx.getPolyvecSlot1() + 2048); // r (NTT-domain noise polyvec)
166
+ mlkemMem.fill(0, kx.getPolyvecSlot2(), kx.getPolyvecSlot2() + 2048); // e₁ (noise polyvec for u)
167
+ mlkemMem.fill(0, kx.getPolyvecSlot3(), kx.getPolyvecSlot3() + 2048); // uncompressed u polyvec from FO re-encryption
168
+ mlkemMem.fill(0, kx.getXofPrfOffset(), kx.getXofPrfOffset() + 1024); // last PRF output block
169
+ mlkemMem.fill(0, kx.getSkOffset(), kx.getSkOffset() + skCpaBytes); // CPA secret key (long-lived, highest severity residual)
170
+ sx.wipeBuffers();
171
+ return sharedSecret;
137
172
  }
138
173
  finally {
139
174
  if (mPrime)
@@ -1,4 +1,4 @@
1
- export interface KyberParams {
1
+ export interface MlKemParams {
2
2
  k: number;
3
3
  eta1: number;
4
4
  eta2: number;
@@ -9,6 +9,6 @@ export interface KyberParams {
9
9
  ctBytes: number;
10
10
  skCpaBytes: number;
11
11
  }
12
- export declare const MLKEM512: KyberParams;
13
- export declare const MLKEM768: KyberParams;
14
- export declare const MLKEM1024: KyberParams;
12
+ export declare const MLKEM512: MlKemParams;
13
+ export declare const MLKEM768: MlKemParams;
14
+ export declare const MLKEM1024: MlKemParams;
@@ -19,10 +19,10 @@
19
19
  // ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
20
20
  // ▀█████▀▀
21
21
  //
22
- // src/ts/kyber/params.ts
22
+ // src/ts/mlkem/params.ts
23
23
  //
24
24
  // ML-KEM (FIPS 203) parameter set configurations.
25
- // FIPS 203 Table 2 Parameter sets for ML-KEM.
25
+ // FIPS 203 Table 2, Parameter sets for ML-KEM.
26
26
  export const MLKEM512 = {
27
27
  k: 2, eta1: 3, eta2: 2, du: 10, dv: 4,
28
28
  ekBytes: 800, dkBytes: 1632, ctBytes: 768, skCpaBytes: 768,
@@ -0,0 +1,12 @@
1
+ import type { CipherSuite } from '../stream/types.js';
2
+ import type { MlKemKeyPair, MlKemEncapsulation } from './types.js';
3
+ import type { MlKemParams } from './params.js';
4
+ export interface MlKemLike {
5
+ readonly params: MlKemParams;
6
+ encapsulate(ek: Uint8Array): MlKemEncapsulation;
7
+ decapsulate(dk: Uint8Array, c: Uint8Array): Uint8Array;
8
+ keygen(): MlKemKeyPair;
9
+ }
10
+ export declare function MlKemSuite(kem: MlKemLike, inner: CipherSuite): CipherSuite & {
11
+ keygen(): MlKemKeyPair;
12
+ };