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
@@ -19,10 +19,10 @@
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
27
  // ── SHA3 helpers ────────────────────────────────────────────────────────────
28
28
  // All operate directly on raw Sha3Exports, no init() system involved.
@@ -82,7 +82,7 @@ export function shake256Hash(sx, msg, n) {
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,7 +115,7 @@ 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
  }
@@ -127,7 +127,7 @@ function genMatrixRow(kx, sx, k, rho, transposed, pvecSlot, rowI) {
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);
@@ -187,13 +187,13 @@ function noisePoly(kx, sx, polyOff, sigma, nonce, eta) {
187
187
  }
188
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,31 +19,21 @@
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.
24
+ // ML-KEM public API, MlKem512, MlKem768, MlKem1024 classes.
25
+ // Uses the init() module cache, call init({ mlkem: ..., sha3: ... }) before constructing.
26
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';
36
+ export { MlKemSuite } from './suite.js';
47
37
  // ── Layout assertion ────────────────────────────────────────────────────────
48
38
  function assertLayout(kx, p) {
49
39
  const pk = kx.getPkOffset();
@@ -52,34 +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
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) {
81
71
  _assertNotOwned('sha3');
82
- _assertNotOwned('kyber');
72
+ _assertNotOwned('mlkem');
83
73
  if (d.length !== 32)
84
74
  throw new RangeError(`d seed must be 32 bytes (got ${d.length})`);
85
75
  if (z.length !== 32)
@@ -99,7 +89,7 @@ export class MlKemBase {
99
89
  }
100
90
  encapsulateDerand(ek, m) {
101
91
  _assertNotOwned('sha3');
102
- _assertNotOwned('kyber');
92
+ _assertNotOwned('mlkem');
103
93
  if (ek.length !== this.params.ekBytes)
104
94
  throw new RangeError(`encapsulation key must be ${this.params.ekBytes} bytes (got ${ek.length})`);
105
95
  if (m.length !== 32)
@@ -119,7 +109,7 @@ export class MlKemBase {
119
109
  }
120
110
  decapsulate(dk, c) {
121
111
  _assertNotOwned('sha3');
122
- _assertNotOwned('kyber');
112
+ _assertNotOwned('mlkem');
123
113
  if (dk.length !== this.params.dkBytes)
124
114
  throw new RangeError(`decapsulation key must be ${this.params.dkBytes} bytes (got ${dk.length})`);
125
115
  if (c.length !== this.params.ctBytes)
@@ -130,39 +120,39 @@ export class MlKemBase {
130
120
  }
131
121
  checkEncapsulationKey(ek) {
132
122
  _assertNotOwned('sha3');
133
- _assertNotOwned('kyber');
123
+ _assertNotOwned('mlkem');
134
124
  return checkEncapsulationKey(this.kx, this.params, ek);
135
125
  }
136
126
  checkDecapsulationKey(dk) {
137
127
  _assertNotOwned('sha3');
138
- _assertNotOwned('kyber');
128
+ _assertNotOwned('mlkem');
139
129
  return checkDecapsulationKey(this.kx, this.sx, this.params, dk);
140
130
  }
141
131
  dispose() {
142
132
  this.kx.wipeBuffers();
143
- // MlKemBase does not own the sha3 module wiping this.sx.wipeBuffers()
133
+ // MlKemBase does not own the sha3 module, wiping this.sx.wipeBuffers()
144
134
  // here would clobber any SHAKE128/SHAKE256 instance live at the time
145
- // of dispose(). The wipe is not needed: every public kyber op
135
+ // of dispose(). The wipe is not needed: every public mlkem op
146
136
  // (keygen, encapsulate, decapsulate, checkDecapsulationKey) calls
147
137
  // sx.wipeBuffers() before returning, under the _assertNotOwned('sha3')
148
138
  // guard it holds for the duration. sha3 scratch therefore carries no
149
- // residue across a kyber op boundary secret-derived or otherwise.
139
+ // residue across a mlkem op boundary, secret-derived or otherwise.
150
140
  }
151
141
  }
152
142
  // ── Public classes ──────────────────────────────────────────────────────────
153
- /** ML-KEM-512 k=2, η₁=3, η₂=2, dᵤ=10, dᵥ=4. */
143
+ /** ML-KEM-512, k=2, η₁=3, η₂=2, dᵤ=10, dᵥ=4. */
154
144
  export class MlKem512 extends MlKemBase {
155
145
  constructor() {
156
146
  super(MLKEM512);
157
147
  }
158
148
  }
159
- /** ML-KEM-768 k=3, η₁=2, η₂=2, dᵤ=10, dᵥ=4. */
149
+ /** ML-KEM-768, k=3, η₁=2, η₂=2, dᵤ=10, dᵥ=4. */
160
150
  export class MlKem768 extends MlKemBase {
161
151
  constructor() {
162
152
  super(MLKEM768);
163
153
  }
164
154
  }
165
- /** ML-KEM-1024 k=4, η₁=2, η₂=2, dᵤ=11, dᵥ=5. */
155
+ /** ML-KEM-1024, k=4, η₁=2, η₂=2, dᵤ=11, dᵥ=5. */
166
156
  export class MlKem1024 extends MlKemBase {
167
157
  constructor() {
168
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,20 +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 kyber WASM scratch regions that held the CPA secret key and the
37
- // keygen noise. After kemKeypairDerand returns, no secret or secret-
38
- // derived data persists in kyber linear memory until the next kyber op
39
- // or MlKem.dispose(). SK_OFFSET holds skCpa packed via polyvec_tobytes
40
- // same severity class as the decap-side SK_OFFSET residual (R-028):
41
- // long-lived key material whose disclosure compromises every ciphertext
42
- // under the corresponding ek. POLYVEC_SLOT_1/2 hold ŝ and ê in NTT
43
- // domain. XOF_PRF_OFFSET holds the last PRF output block. POLYVEC_SLOT_3
44
- // (t̂) and POLYVEC_SLOT_0 (Â rows) are public and intentionally skipped.
45
- const kyberMem = new Uint8Array(kx.memory.buffer);
46
- kyberMem.fill(0, kx.getSkOffset(), kx.getSkOffset() + params.skCpaBytes);
47
- kyberMem.fill(0, kx.getPolyvecSlot1(), kx.getPolyvecSlot1() + 2048);
48
- kyberMem.fill(0, kx.getPolyvecSlot2(), kx.getPolyvecSlot2() + 2048);
49
- kyberMem.fill(0, kx.getXofPrfOffset(), kx.getXofPrfOffset() + 1024);
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);
50
43
  const h = sha3_256Hash(sx, ekCpa);
51
44
  try {
52
45
  const dk = new Uint8Array(params.dkBytes);
@@ -83,26 +76,17 @@ export function kemEncapsulateDerand(kx, sx, params, ek, m) {
83
76
  const K = gOut.slice(0, 32);
84
77
  r = gOut.slice(32, 64);
85
78
  const c = indcpaEncrypt(kx, sx, params, ek, m, r);
86
- // Wipe kyber WASM scratch regions that held m / r / e₁ / e₂ / u / v /
87
- // m-poly / PRF output. After kemEncapsulateDerand returns, no secret
88
- // or secret-derived data persists in kyber linear memory until the
89
- // next kyber op or MlKem.dispose(). MSG_OFFSET holds raw m —
90
- // reproducing the shared secret K = G(m ‖ H(ek))[0..32] only needs m
91
- // plus the public ek, so this is the highest-severity encap residual.
92
- // POLYVEC_SLOT_1/2/3 hold r, e₁, and uncompressed u (u compression is
93
- // lossy for du ∈ {10,11} uncompressed u reveals low-order bits the
94
- // public ciphertext hides). POLY_SLOT_1/2/3 hold e₂ (full 512B), v,
95
- // and the m-polynomial. XOF_PRF_OFFSET holds the last PRF block.
96
- // PK_OFFSET, CT_OFFSET, POLYVEC_SLOT_0/4 are public — skipped.
97
- const kyberMem = new Uint8Array(kx.memory.buffer);
98
- kyberMem.fill(0, kx.getMsgOffset(), kx.getMsgOffset() + 32);
99
- kyberMem.fill(0, kx.getPolyvecSlot1(), kx.getPolyvecSlot1() + 2048);
100
- kyberMem.fill(0, kx.getPolyvecSlot2(), kx.getPolyvecSlot2() + 2048);
101
- kyberMem.fill(0, kx.getPolyvecSlot3(), kx.getPolyvecSlot3() + 2048);
102
- kyberMem.fill(0, kx.getPolySlot1(), kx.getPolySlot1() + 512);
103
- kyberMem.fill(0, kx.getPolySlot2(), kx.getPolySlot2() + 512);
104
- kyberMem.fill(0, kx.getPolySlot3(), kx.getPolySlot3() + 512);
105
- kyberMem.fill(0, kx.getXofPrfOffset(), kx.getXofPrfOffset() + 1024);
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);
106
90
  sx.wipeBuffers();
107
91
  return { ciphertext: c, sharedSecret: K };
108
92
  }
@@ -118,8 +102,8 @@ export function kemEncapsulateDerand(kx, sx, params, ek, m) {
118
102
  /**
119
103
  * ML-KEM.Decaps_internal (FIPS 203 Algorithm 17).
120
104
  *
121
- * Constant-time: uses ct_verify and ct_cmov from kyber WASM.
122
- * 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.
123
107
  */
124
108
  export function kemDecapsulate(kx, sx, params, dk, c) {
125
109
  const { skCpaBytes, ekBytes, ctBytes } = params;
@@ -151,42 +135,38 @@ export function kemDecapsulate(kx, sx, params, dk, c) {
151
135
  jInput.set(z, 0);
152
136
  jInput.set(c, 32);
153
137
  kBar = shake256Hash(sx, jInput, 32);
154
- // 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
155
139
  cPrime = indcpaEncrypt(kx, sx, params, ek, mPrime, rPrime);
156
- // Constant-time comparison and conditional select via kyber WASM
157
- 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);
158
142
  const ctOff = kx.getCtOffset();
159
143
  const ctPrimeOff = kx.getCtPrimeOffset();
160
- // Write c and c' into named kyber memory regions
161
- kyberMem.set(c, ctOff);
162
- kyberMem.set(cPrime, ctPrimeOff);
144
+ // Write c and c' into named mlkem memory regions
145
+ mlkemMem.set(c, ctOff);
146
+ mlkemMem.set(cPrime, ctPrimeOff);
163
147
  // Write K' and K̄ into poly slots (512B each, 32B needed)
164
148
  const kPrimeOff = kx.getPolySlot0();
165
149
  const kBarOff = kx.getPolySlot1();
166
- kyberMem.set(kPrime, kPrimeOff);
167
- kyberMem.set(kBar, kBarOff);
150
+ mlkemMem.set(kPrime, kPrimeOff);
151
+ mlkemMem.set(kBar, kBarOff);
168
152
  // fail = 0 if c == c', non-zero if different
169
153
  const fail = kx.ct_verify(ctOff, ctPrimeOff, ctBytes);
170
154
  // If fail != 0 (mismatch): K' ← K̄
171
155
  kx.ct_cmov(kPrimeOff, kBarOff, 32, fail);
172
- const sharedSecret = kyberMem.slice(kPrimeOff, kPrimeOff + 32);
173
- // Wipe kyber WASM scratch regions that held the CPA secret key (skCpa),
174
- // m' / K' / / e₂ / r / e₁ / u, and the PRF output buffer. Without
175
- // this, residual secret and secret-derived bytes persist in linear
176
- // memory until the next kyber op or MlKem.dispose() — a window during
177
- // which any other code with a handle to the kyber exports could read
178
- // them. skCpa is the highest-severity residual: it compromises every
179
- // ciphertext under the corresponding ek, not just this message.
180
- kyberMem.fill(0, kx.getMsgOffset(), kx.getMsgOffset() + 32); // m' (bytes)
181
- kyberMem.fill(0, kPrimeOff, kPrimeOff + 32); // K' (final shared secret)
182
- kyberMem.fill(0, kBarOff, kBarOff + 512); // (first 32B) + e₂ poly tail
183
- kyberMem.fill(0, kx.getPolySlot2(), kx.getPolySlot2() + 512); // m'-poly / v residual
184
- kyberMem.fill(0, kx.getPolySlot3(), kx.getPolySlot3() + 512); // indcpa message poly
185
- kyberMem.fill(0, kx.getPolyvecSlot1(), kx.getPolyvecSlot1() + 2048); // r (NTT-domain noise polyvec)
186
- kyberMem.fill(0, kx.getPolyvecSlot2(), kx.getPolyvecSlot2() + 2048); // e₁ (noise polyvec for u)
187
- kyberMem.fill(0, kx.getPolyvecSlot3(), kx.getPolyvecSlot3() + 2048); // uncompressed u polyvec from FO re-encryption
188
- kyberMem.fill(0, kx.getXofPrfOffset(), kx.getXofPrfOffset() + 1024); // last PRF output block
189
- kyberMem.fill(0, kx.getSkOffset(), kx.getSkOffset() + skCpaBytes); // CPA secret key (long-lived — highest severity residual)
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); // (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)
190
170
  sx.wipeBuffers();
191
171
  return sharedSecret;
192
172
  }
@@ -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
+ };