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
@@ -1,1011 +0,0 @@
1
-
2
- ### Architecture
3
-
4
- Overview of Leviathan Crypto's architecture design, comprising six independent WASM modules unified by a misuse-resistant TypeScript API, which delivers both Serpent's paranoia and ChaCha's elegance as a zero-dependency, tree-shakable, post-quantum library.
5
-
6
- > ### Table of Contents
7
- >
8
- > - [Vision](#vision)
9
- > - [Scope](#scope)
10
- > - [Repository Structure](#repository-structure)
11
- > - [Architecture: TypeScript over WASM](#architecture-typescript-over-wasm)
12
- > - [Six Independent WASM Modules](#six-independent-wasm-modules)
13
- > - [init() API](#init-api)
14
- > - [Public API Classes](#public-api-classes)
15
- > - [Build Pipeline](#build-pipeline)
16
- > - [Module Relationships](#module-relationships)
17
- > - [npm Package](#npm-package)
18
- > - [Buffer Layouts](#buffer-layouts)
19
- > - [Test Suite](#test-suite)
20
- > - [Correctness Contract](#correctness-contract)
21
- > - [Known Limitations](#known-limitations)
22
-
23
- ## Vision
24
-
25
- `leviathan-crypto` is a post-quantum WASM cryptography library with zero dependencies, tree-shakable, and side-effect free.
26
-
27
- **JS is the problem, SIMD WASM is the solution.** JavaScript engines offer no formal constant-time guarantees. JIT compilers optimize based on runtime patterns, which leak secrets through cache access and instruction timing. By contrast, [WebAssembly](https://github.com/xero/leviathan-crypto/wiki/wasm) executes outside the JIT entirely, running compiled bytecode with linear memory you control. No speculative optimization, no value-dependent branches between source and execution.
28
-
29
- **WebAssembly is the correctness layer.** All algorithm logic lives in WASM. Six AssemblyScript modules ([`serpent`](https://github.com/xero/leviathan-crypto/wiki/asm_serpent), [`chacha20`](https://github.com/xero/leviathan-crypto/wiki/asm_chacha), [`sha2`](https://github.com/xero/leviathan-crypto/wiki/asm_sha2), [`sha3`](https://github.com/xero/leviathan-crypto/wiki/asm_sha3), [`kyber`](https://github.com/xero/leviathan-crypto/wiki/asm_kyber), and [`ct`](https://github.com/xero/leviathan-crypto/wiki/asm_ct)) compile independently to WASM with SIMD where it pays off. Each module is its own instance with its own linear memory. Within a module, stateful primitives share the instance, and a runtime exclusivity model keeps them from interfering with each other.
30
-
31
- **TypeScript is the ergonomics layer.** The strongly-typed public API covers [`Seal`](https://github.com/xero/leviathan-crypto/wiki/aead#seal), [`SealStream`](https://github.com/xero/leviathan-crypto/wiki/aead#sealstream), [`SealStreamPool`](https://github.com/xero/leviathan-crypto/wiki/aead#sealstreampool), [`Fortuna`](https://github.com/xero/leviathan-crypto/wiki/fortuna), [`HKDF`](https://github.com/xero/leviathan-crypto/wiki/sha2#hkdf_sha256), [`SkippedKeyStore`](https://github.com/xero/leviathan-crypto/wiki/ratchet#skippedkeystore), and others. The design is misuse-resistant by default. Authentication is verify-then-decrypt; key material wipes on dispose; validation runs before any crypto path; one-shot AEADs lock on first call. TypeScript never implements cryptographic algorithms. It orchestrates the WASM layer and enforces best practice through API shape, not convention.
32
-
33
- **[Serpent-256](https://github.com/xero/leviathan-crypto/wiki/serpent_reference): maximum paranoia.** 32 rounds of S-boxes in pure Boolean logic with no table lookups. An ouroboros devouring every bit, in every block, through every round.
34
-
35
- **[XChaCha20-Poly1305](https://github.com/xero/leviathan-crypto/wiki/chacha_reference): precise elegance.** 20 rounds of add-rotate-XOR, choreography without S-boxes or cache-timing leakage. A dance closing with Poly1305's unconditional forgery bound.
36
-
37
- **Two ciphers, one interface.** Both share the [`CipherSuite`](https://github.com/xero/leviathan-crypto/wiki/ciphersuite) shape and slot into [`Seal`](https://github.com/xero/leviathan-crypto/wiki/aead#seal), [`SealStream`](https://github.com/xero/leviathan-crypto/wiki/aead#sealstream), and [`SealStreamPool`](https://github.com/xero/leviathan-crypto/wiki/aead#sealstreampool) interchangeably. Post-quantum extends the same model, [`KyberSuite`](https://github.com/xero/leviathan-crypto/wiki/ciphersuite#kybersuite) wraps [`MlKem512`](https://github.com/xero/leviathan-crypto/wiki/kyber#parameter-sets), [`MlKem768`](https://github.com/xero/leviathan-crypto/wiki/kyber#parameter-sets), or [`MlKem1024`](https://github.com/xero/leviathan-crypto/wiki/kyber#parameter-sets) around either cipher, and the [SPQR ratchet](https://github.com/xero/leviathan-crypto/wiki/ratchet) builds forward-secret sessions on top.
38
-
39
- ---
40
-
41
- ## Scope
42
-
43
- **Primitives.** WASM algorithms with their TypeScript wrapper classes.
44
-
45
- | Module | Algorithms | TypeScript API |
46
- | ----------------------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
47
- | [`serpent`](./asm_serpent.md) | Serpent-256 block cipher: ECB, CTR, CBC | [`Serpent`](./serpent.md) (cipher class for mode operations) |
48
- | [`chacha20`](./asm_chacha.md) | ChaCha20, Poly1305, ChaCha20-Poly1305, XChaCha20-Poly1305 | [`ChaCha20`](./chacha20.md#chacha20), [`Poly1305`](./chacha20.md#poly1305), [`XChaCha20Poly1305`](./chacha20.md#xchacha20poly1305) |
49
- | [`sha2`](./asm_sha2.md) | SHA-256, SHA-384, SHA-512, HMAC variants, HKDF variants | [`SHA256`](./sha2.md#sha256), [`SHA384`](./sha2.md#sha384), [`SHA512`](./sha2.md#sha512), [`HMAC_SHA256`](./sha2.md#hmac_sha256), [`HMAC_SHA384`](./sha2.md#hmac_sha384), [`HMAC_SHA512`](./sha2.md#hmac_sha512), [`HKDF_SHA256`](./sha2.md#hkdf_sha256), [`HKDF_SHA512`](./sha2.md#hkdf_sha512) |
50
- | [`sha3`](./asm_sha3.md) | SHA3-224/256/384/512, SHAKE128, SHAKE256 (XOFs) | [`SHA3_256`](./sha3.md#sha3_256), [`SHA3_512`](./sha3.md#sha3_512), [`SHAKE256`](./sha3.md#shake256) |
51
- | [`kyber`](./asm_kyber.md) | MlKem512, MlKem768, MlKem1024 | [`MlKem512`](./kyber.md#parameter-sets), [`MlKem768`](./kyber.md#parameter-sets), [`MlKem1024`](./kyber.md#parameter-sets) |
52
- | [`ct`](./asm_ct.md) | Constant-time comparison primitives | [`constantTimeEqual`](./utils.md#constanttimeequal) |
53
-
54
-
55
- **Cipher Suites.** Composition of WASM modules into complete cipher packages.
56
-
57
- | Suite | Composition | Use case |
58
- | ------------------------------------- | ------------------------------------ | ----------------------------------- |
59
- | [`SerpentCipher`](./ciphersuite.md#serpentcipher) | `serpent` + `sha2` (CBC+HMAC-SHA256) | Authenticated encryption via STREAM |
60
- | [`XChaCha20Cipher`](./ciphersuite.md#xchacha20cipher) | `chacha20` (XChaCha20-Poly1305 AEAD) | Streaming authenticated encryption |
61
- | [`KyberSuite`](./ciphersuite.md#kybersuite) | `kyber` + (any cipher) | Post-quantum key encapsulation |
62
-
63
- **High-Level Constructs.** Pure TypeScript abstractions over cipher suites.
64
-
65
- | API | Dependencies | Purpose |
66
- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------- | -------------------------------------------- |
67
- | [`Seal`](./aead.md#seal) / [`SealStream`](./aead.md#sealstream) / [`SealStreamPool`](./aead.md#sealstreampool) | Any CipherSuite | One-shot, streaming, and parallel encryption |
68
- | [`ratchetInit`](./ratchet.md#ratchetinitsk-context), [`KDFChain`](./ratchet.md#kdfchain), [`kemRatchetEncap`](./ratchet.md#kemratchetencapkem-rk-peerek-context)/[`kemRatchetDecap`](./ratchet.md#kemratchetdecapkem-rk-dk-kemct-ownek-context) | `sha2`; `kyber` + `sha3` for KEM | Forward-secret session ratcheting (SPQR) |
69
- | [`Fortuna`](./fortuna.md) | Cipher PRF + HashFn | Cryptographically-secure RNG |
70
-
71
- **Utilities.** Pure TypeScript helpers, no `init()` dependency.
72
-
73
- | Utility | Purpose |
74
- | --------------------------------------------------- | ------------------------------------------------ |
75
- | [`hexToBytes`](./utils.md#hextobytes), [`bytesToHex`](./utils.md#bytestohex) | Hex/byte conversions |
76
- | [`wipe`](./utils.md#wipe) | Secure memory zeroing |
77
- | [`xor`](./utils.md#xor), [`concat`](./utils.md#concat) | Byte operations |
78
- | [`randomBytes`](./utils.md#randombytes) | One-off random byte generation |
79
- | [`constantTimeEqual`](./utils.md#constanttimeequal) | Timing-attack resistant comparison (WASM-backed) |
80
-
81
- ---
82
-
83
- ## Repository Structure
84
-
85
-
86
- Source lives under `src/`, split between AssemblyScript primitives in `src/asm/` and the TypeScript API in `src/ts/`. Tests are in `test/`. Build, codegen, and tooling scripts go in `scripts/`. CI/CD configuration sits in `.github/`. The repository root holds project documentation, package metadata, and tool configs. Each subsection below shows the relevant tree and notes the conventions that apply across files in that tier.
87
-
88
- ### CI/CD
89
-
90
- `.github/` holds GitHub-specific repository configuration: workflow definitions, the CI image build context, and platform metadata. Workflows split along functional lines.
91
-
92
- **Merge gate.** `build.yml`, `lint.yml`, `e2e.yml`, `test-suite.yml`. `test-suite.yml` orchestrates the per-domain unit runners (`unit-*.yml`) plus `verify-vectors.yml` for parallel execution and per-domain failure isolation.
93
-
94
- **Test vectors.** `verify-vectors.yml` validates the corpus against `SHA256SUMS`.
95
-
96
- **Release flow.** Manual `release.yml` bumps the version and creates the tag; the resulting `v*` tag push triggers `publish.yml`, which runs the npm publish with provenance attestations. `npm-remove.yml` is the manual deprecate/unpublish escape hatch.
97
-
98
- **Wiki.** `wiki.yml` syncs `docs/` to the GitHub Wiki on every merge to main.
99
-
100
- **CI image.** `ci-image.yml` rebuilds the test-runner container from `ci.Dockerfile` whenever the Dockerfile changes.
101
-
102
- ```
103
- .github/
104
- ├── ci.Dockerfile
105
- └── workflows/
106
- ├── build.yml
107
- ├── ci-image.yml
108
- ├── e2e.yml
109
- ├── lint.yml
110
- ├── npm-remove.yml
111
- ├── publish.yml
112
- ├── release.yml
113
- ├── test-suite.yml
114
- ├── unit-chacha20.yml
115
- ├── unit-core.yml
116
- ├── unit-hashing.yml
117
- ├── unit-kyber.yml
118
- ├── unit-montecarlo-cbc.yml
119
- ├── unit-montecarlo-ecb.yml
120
- ├── unit-nessie.yml
121
- ├── unit-ratchet.yml
122
- ├── unit-serpent.yml
123
- ├── unit-stream.yml
124
- ├── verify-vectors.yml
125
- └── wiki.yml
126
- ```
127
-
128
- ### Build pipeline
129
-
130
- `scripts/` holds the build, codegen, and tooling scripts that produce `dist/` and the test-vector corpus. Three categories.
131
-
132
- **Build orchestration.** `build-asm.js` drives AssemblyScript compilation across the six modules. `embed-wasm.ts` produces the gzip+base64 blob for each `.wasm`. `embed-workers.ts` bundles each pool worker into a self-contained IIFE via esbuild. `copy-docs.ts` ships the consumer doc subset into `dist/`. See [Build Pipeline](#build-pipeline) for the full sequence.
133
-
134
- **Codegen.** `generate_simd.ts` produces `src/asm/serpent/serpent_simd.ts` from a template by translating S-box gate logic into v128 ops; the generator and its output are both committed and the output is never edited by hand. `gen-seal-vectors.ts`, `gen-sealstream-vectors.ts`, `gen-fortuna-vectors.ts`, and `gen-ratchet-vectors.ts` produce known-answer-test vectors for their respective primitives.
135
-
136
- **Tooling.** `gen-changelog.ts` generates `CHANGELOG` entries. `lint-asm.js` lints the AssemblyScript sources. `pin-actions.ts` pins every GitHub Action reference to a SHA, run via `bun pin` after workflow changes.
137
-
138
- ```
139
- scripts/
140
- ├── build-asm.js
141
- ├── copy-docs.ts
142
- ├── embed-wasm.ts
143
- ├── embed-workers.ts
144
- ├── gen-changelog.ts
145
- ├── gen-fortuna-vectors.ts
146
- ├── gen-ratchet-vectors.ts
147
- ├── gen-seal-vectors.ts
148
- ├── gen-sealstream-vectors.ts
149
- ├── generate_simd.ts
150
- ├── lint-asm.js
151
- └── pin-actions.ts
152
- ```
153
-
154
- ### AssemblyScript layer
155
-
156
- `src/asm/` holds the AssemblyScript sources for each WASM binary. Every subdirectory compiles to its own `.wasm` with fully independent linear memory and no cross-module imports.
157
-
158
- **Per-module conventions.** Every module exposes an `index.ts` as the asc entry point; it re-exports the public surface that becomes the WASM exports. Every module except `ct/` has a `buffers.ts` that defines the static memory layout and the offset getters that all other files in that module import. The `ct/` module is intentionally minimal: a single `index.ts` whose layout is implicit in its single 64 KB page.
159
-
160
- ```
161
- src/asm/
162
- ├── chacha20/
163
- │ ├── index.ts
164
- │ ├── chacha20.ts ← block function (RFC 8439)
165
- │ ├── chacha20_simd_4x.ts ← SIMD 4-wide inter-block keystream
166
- │ ├── poly1305.ts ← one-time MAC
167
- │ ├── wipe.ts ← module-wide buffer zeroizer
168
- │ └── buffers.ts
169
- ├── ct/
170
- │ └── index.ts ← v128 XOR-accumulate constant-time compare
171
- ├── kyber/
172
- │ ├── index.ts
173
- │ ├── ntt.ts ← scalar NTT/invNTT + zetas table
174
- │ ├── ntt_simd.ts ← v128 NTT butterflies, fqmul_8x, barrett_reduce_8x
175
- │ ├── reduce.ts ← Montgomery/Barrett reduction, fqmul
176
- │ ├── poly.ts ← polynomial serialization, compression, basemul
177
- │ ├── poly_simd.ts ← SIMD poly add/sub/reduce/ntt wrappers
178
- │ ├── polyvec.ts ← k-wide polyvec operations
179
- │ ├── cbd.ts ← centered binomial distribution (η=2, η=3)
180
- │ ├── sampling.ts ← uniform rejection sampling
181
- │ ├── verify.ts ← constant-time compare and conditional move
182
- │ ├── params.ts ← Q, QINV, MONT, Barrett/compression constants
183
- │ └── buffers.ts
184
- ├── serpent/
185
- │ ├── index.ts
186
- │ ├── serpent.ts ← block function + key schedule
187
- │ ├── serpent_unrolled.ts ← unrolled S-boxes and round functions
188
- │ ├── serpent_simd.ts ← SIMD bitsliced block operations
189
- │ ├── cbc.ts ← CBC mode
190
- │ ├── cbc_simd.ts ← SIMD CBC decrypt
191
- │ ├── ctr.ts ← CTR mode
192
- │ ├── ctr_simd.ts ← SIMD CTR 4-wide inter-block
193
- │ └── buffers.ts
194
- ├── sha2/
195
- │ ├── index.ts
196
- │ ├── sha256.ts
197
- │ ├── sha512.ts ← shared by SHA-512 and SHA-384
198
- │ ├── hmac.ts ← HMAC-SHA256
199
- │ ├── hmac512.ts ← HMAC-SHA512 and HMAC-SHA384
200
- │ └── buffers.ts
201
- └── sha3/
202
- ├── index.ts
203
- ├── keccak.ts ← Keccak-f[1600] permutation, sponge absorb/squeeze
204
- └── buffers.ts
205
- ```
206
-
207
- ### TypeScript layer
208
-
209
- `src/ts/` is the public API layer. Each subdirectory is a published npm subpath; top-level files cover cross-cutting concerns and standalone utilities.
210
-
211
- **Subpath conventions.** Every cipher and hash module has an `index.ts` barrel, a `types.ts` for TypeScript-only declarations, and an `embedded.ts` that re-exports its gzip+base64 WASM blob from `src/ts/embedded/`. The `keccak/` alias subpath omits `types.ts` and re-exports sha3's instead. The `ratchet/` and `stream/` modules have no `embedded.ts` because they compose other modules and ship no WASM of their own.
212
-
213
- **Cipher modules** (`serpent/`, `chacha20/`) add a `cipher-suite.ts` (the `CipherSuite` implementation for STREAM), a `pool-worker.ts` (Web Worker source for `SealStreamPool`), a `generator.ts` (Fortuna `Generator`), and a `shared-ops.ts` (serpent) or `ops.ts` (chacha20) holding pure primitive functions shared between the cipher-suite and the pool worker.
214
-
215
- **Hash modules** (`sha2/`, `sha3/`) add a `hash.ts` (the stateless Fortuna `HashFn`).
216
-
217
- **Build artifacts.** `ct-wasm.ts` and the `embedded/` directory hold auto-generated outputs that only exist after `bun run build`. Both are gitignored. `ct-wasm.ts` is the inline raw byte array of the ct WASM module. `embedded/` holds gzip+base64 blobs of each WASM binary (from `scripts/embed-wasm.ts`) and IIFE source strings for each pool worker (from `scripts/embed-workers.ts`).
218
-
219
- ```
220
- src/ts/
221
- ├── chacha20/
222
- │ ├── cipher-suite.ts
223
- │ ├── embedded.ts
224
- │ ├── generator.ts
225
- │ ├── index.ts
226
- │ ├── ops.ts
227
- │ ├── pool-worker.ts
228
- │ └── types.ts
229
- ├── ct-wasm.ts ← gitignored build artifact: raw ct WASM bytes
230
- ├── embedded/ ← gitignored build artifacts
231
- │ ├── chacha20-pool-worker.ts ← ChaCha20 pool-worker IIFE source string
232
- │ ├── chacha20.ts ← chacha20.wasm gzip+base64 blob
233
- │ ├── kyber.ts ← kyber.wasm gzip+base64 blob
234
- │ ├── serpent-pool-worker.ts ← Serpent pool-worker IIFE source string
235
- │ ├── serpent.ts ← serpent.wasm gzip+base64 blob
236
- │ ├── sha2.ts ← sha2.wasm gzip+base64 blob
237
- │ └── sha3.ts ← sha3.wasm gzip+base64 blob
238
- ├── errors.ts ← AuthenticationError
239
- ├── fortuna.ts ← Fortuna CSPRNG (composes pluggable Generator + HashFn)
240
- ├── index.ts ← root barrel + dispatching init()
241
- ├── init.ts ← initModule(), module cache, isInitialized
242
- ├── keccak/ ← alias subpath; same WASM and instance slot as sha3
243
- │ ├── embedded.ts
244
- │ └── index.ts
245
- ├── kyber/
246
- │ ├── embedded.ts
247
- │ ├── indcpa.ts ← IND-CPA encrypt/decrypt + matrix generation
248
- │ ├── index.ts
249
- │ ├── kem.ts ← Fujisaki-Okamoto transform (keygen, encaps, decaps)
250
- │ ├── params.ts ← MLKEM512, MLKEM768, MLKEM1024 parameter sets
251
- │ ├── suite.ts ← KyberSuite (hybrid KEM+AEAD CipherSuite factory)
252
- │ ├── types.ts
253
- │ └── validate.ts ← key validation (FIPS 203 §7.2, §7.3)
254
- ├── loader.ts ← loadWasm()/compileWasm() WasmSource dispatch
255
- ├── ratchet/
256
- │ ├── index.ts
257
- │ ├── kdf-chain.ts ← KDFChain (per-message KDF chain, DR §5.2)
258
- │ ├── ratchet-keypair.ts ← RatchetKeypair (single-use ek/dk wrapper)
259
- │ ├── root-kdf.ts ← ratchetInit, kemRatchetEncap, kemRatchetDecap (DR §7.2)
260
- │ ├── skipped-key-store.ts ← SkippedKeyStore (MKSKIPPED cache, DR §3.2/§3.5)
261
- │ └── types.ts
262
- ├── serpent/
263
- │ ├── cipher-suite.ts
264
- │ ├── embedded.ts
265
- │ ├── generator.ts
266
- │ ├── index.ts
267
- │ ├── pool-worker.ts
268
- │ ├── serpent-cbc.ts ← SerpentCbc (broken out to avoid circular import)
269
- │ ├── shared-ops.ts
270
- │ └── types.ts
271
- ├── sha2/
272
- │ ├── embedded.ts
273
- │ ├── hash.ts
274
- │ ├── hkdf.ts ← HKDF_SHA256, HKDF_SHA512 (pure TS over HMAC)
275
- │ ├── index.ts
276
- │ └── types.ts
277
- ├── sha3/
278
- │ ├── embedded.ts
279
- │ ├── hash.ts
280
- │ ├── index.ts
281
- │ └── types.ts
282
- ├── stream/
283
- │ ├── constants.ts ← HEADER_SIZE, CHUNK_MIN/MAX, TAG_DATA/FINAL, FLAG_FRAMED
284
- │ ├── header.ts ← wire format header encode/decode, counter nonce
285
- │ ├── index.ts
286
- │ ├── open-stream.ts ← OpenStream (cipher-agnostic streaming decryption)
287
- │ ├── seal-stream-pool.ts ← SealStreamPool (worker-based parallel batch)
288
- │ ├── seal-stream.ts ← SealStream (cipher-agnostic streaming encryption)
289
- │ ├── seal.ts ← Seal (static one-shot AEAD)
290
- │ └── types.ts
291
- ├── types.ts ← shared interfaces: Hash, KeyedHash, Blockcipher, Streamcipher, AEAD, Generator, HashFn
292
- ├── utils.ts ← encoding, wipe, randomBytes, constantTimeEqual, CT_MAX_BYTES, hasSIMD
293
- └── wasm-source.ts ← WasmSource union type
294
- ```
295
-
296
- ### Tests
297
-
298
- `test/` holds three independent categories of files, used by separate workflows.
299
-
300
- **Unit tests** (`unit/`) are Vitest suites that compile to a JS target for fast local iteration. The directory mirrors `src/ts/` structure with one folder per module, plus a handful of top-level `.test.ts` files for cross-cutting concerns (init, errors, utils, fortuna). CI splits these by domain via `unit-*.yml` for parallel execution.
301
-
302
- **End-to-end tests** (`e2e/`) are Playwright suites that exercise the actual WASM artifacts across V8, SpiderMonkey, and JavaScriptCore. They run after the full build, including pool-worker bundling.
303
-
304
- **Test vectors** (`vectors/`) is the immutable known-answer-test corpus. Files are read-only reference data. Some come from authoritative specifications (FIPS, RFCs, ACVP, NIST CAVP); others are self generated as regression vectors by `scripts/gen-*-vectors.ts`. CI validates KAT file integrity against `SHA256SUMS`.
305
-
306
- See [test-suite.md](./test-suite.md) for full testing methodology, vector corpus inventory with provenance, and gate discipline.
307
-
308
- ```
309
- test/
310
- ├── e2e/ ← Playwright suites against built WASM in V8, SpiderMonkey, JSC
311
- ├── unit/
312
- │ ├── chacha20/
313
- │ ├── ct/
314
- │ ├── errors.test.ts
315
- │ ├── fortuna/
316
- │ ├── fortuna.test.ts
317
- │ ├── helpers.ts
318
- │ ├── init/
319
- │ ├── init.test.ts
320
- │ ├── kyber/
321
- │ ├── loader/
322
- │ ├── ratchet/
323
- │ ├── serpent/
324
- │ ├── sha2/
325
- │ ├── sha3/
326
- │ ├── stream/
327
- │ └── utils.test.ts
328
- └── vectors/ ← KAT corpus; integrity verified against SHA256SUMS
329
- ```
330
-
331
- ### Project files
332
-
333
- The repository root holds project documentation, package metadata, and tool configuration. Build artifacts that only exist after `bun run build` are listed at the end.
334
-
335
- **Documentation.** `README.md` is the entry point. `SECURITY.md` covers the vulnerability disclosure policy. `AGENTS.md` is the agent contract that governs how AI agents work in the repo. `CHANGELOG` tracks release history and `LICENSE` is MIT. The `docs/` directory holds the full API reference, audits, benchmarks, and architecture notes (this file lives there).
336
-
337
- **Package metadata.** `package.json` declares the npm manifest, subpath exports, and scripts. `package-lock.json` and `bun.lock` are the lockfiles for npm and bun respectively; both ship checked in so either tool can install reproducibly.
338
-
339
- **Tool configs.** `asconfig.json` configures AssemblyScript compilation. `eslint.config.ts` is the active linter, run via `bun fix`. `playwright.config.ts` and `vitest.config.ts` configure the e2e and unit test runners. `tsconfig.json` is the base TypeScript config; `tsconfig.test.json` and `tsconfig.e2e.json` extend it for the test targets. `tslint.json` is a TSLint config (older format).
340
-
341
- **Build artifacts** (gitignored; only exist after `bun run build`). `build/` holds the raw `.wasm` outputs from AssemblyScript compilation. `dist/` is the published npm package contents (compiled JS, declarations, copied WASM, embedded blobs, doc subset).
342
-
343
- ```
344
- .
345
- ├── build/ ← gitignored: .wasm outputs from AS compilation
346
- ├── dist/ ← gitignored: published npm package contents
347
- ├── docs/ ← API reference, audits, benchmarks (this file lives here)
348
- ├── README.md
349
- ├── SECURITY.md
350
- ├── AGENTS.md
351
- ├── CHANGELOG
352
- ├── LICENSE
353
- ├── package.json
354
- ├── package-lock.json
355
- ├── bun.lock
356
- ├── asconfig.json
357
- ├── eslint.config.ts
358
- ├── playwright.config.ts
359
- ├── tsconfig.json
360
- ├── tsconfig.e2e.json
361
- ├── tsconfig.test.json
362
- ├── tslint.json
363
- └── vitest.config.ts
364
- ```
365
-
366
- ---
367
-
368
- ## Architecture: TypeScript over WASM
369
-
370
-
371
- The TypeScript layer never implements cryptographic algorithms. It manages the boundary between JavaScript and WebAssembly by writing inputs into WASM linear memory, calling exported functions, and reading back outputs. All algorithm logic resides within AssemblyScript.
372
-
373
- Higher-level classes like `Seal`, `SealStream`, and `SealStreamPool` are pure TypeScript, but they compose WASM-backed primitives (Serpent-CBC, HMAC-SHA256, ChaCha20-Poly1305, and HKDF-SHA256) rather than implementing new cryptographic logic. TypeScript orchestrates, while WASM computes. Pool workers instantiate their own WASM modules and directly call primitives, bypassing the main-thread module cache.
374
-
375
- ---
376
-
377
- ## Six Independent WASM Modules
378
-
379
- Each primitive family compiles to its own `.wasm` binary with fully independent linear memory and buffer layouts. No shared state, no cross-module interference. Five of the six modules load through `init()`. The sixth, `ct`, sits outside the public `Module` union and the `init()` gate; it occupies a single 64 KB memory page and lazy-loads on the first call to `constantTimeEqual`. The ct module backs the public `constantTimeEqual` and `CT_MAX_BYTES` exports from the root barrel; neither requires an `init()` call.
380
-
381
- |Module|Binary|Primitives|
382
- |---|---|---|
383
- |`serpent`|`serpent.wasm`|Serpent-256 block cipher: ECB, CTR mode, CBC mode|
384
- |`chacha20`|`chacha20.wasm`|ChaCha20, Poly1305, ChaCha20-Poly1305 AEAD, XChaCha20-Poly1305 AEAD|
385
- |`sha2`|`sha2.wasm`|SHA-256, SHA-384, SHA-512, HMAC-SHA256, HMAC-SHA384, HMAC-SHA512|
386
- |`sha3`|`sha3.wasm`|SHA3-224, SHA3-256, SHA3-384, SHA3-512, SHAKE128, SHAKE256|
387
- |`kyber`|`kyber.wasm`|ML-KEM polynomial arithmetic: SIMD NTT/invNTT (v128 butterflies with scalar tail), basemul, Montgomery/Barrett, CBD, compress, CT verify/cmov|
388
- |`ct`|`ct.wasm`|SIMD constant-time byte comparison. Backs `constantTimeEqual` and `CT_MAX_BYTES`, lazy-loaded outside `init()`. Single 64 KB page.|
389
-
390
- **Size.** Consumers who only use Serpent don't load the SHA-3 binary.
391
-
392
- **Isolation.** Key material in `serpent.wasm` memory cannot bleed into `sha3.wasm` memory even in theory.
393
-
394
- Each module's buffer layout starts at offset 0 and is defined in its own `buffers.ts`. Buffer layouts are fully independent across modules.
395
-
396
- ### Module contents
397
-
398
- **`serpent.wasm`** implements Serpent-256, a 128-bit block cipher. It handles key scheduling, block encryption and decryption, and both CTR and CBC streaming modes with SIMD variants for inter-block parallelism. See: [Serpent-256 WASM Module Reference](./asm_serpent.md)
399
-
400
- [The TypeScript module](./serpent.md) wraps this with `SerpentCipher`, a CipherSuite that combines Serpent-CBC with HMAC-SHA256 and HKDF key derivation for the STREAM construction. Primitive operations (HMAC, CBC, PKCS7 padding) live in `serpent/shared-ops.ts` and are reused by both the main thread and pool workers, guaranteeing byte-identical output and consistent Vaudenay 2002 padding normalization. Requires `serpent` and `sha2` to be initialized.
401
-
402
- **`chacha20.wasm`** implements the full ChaCha20-Poly1305 AEAD family per RFC 8439 and draft-irtf-cfrg-xchacha. It includes ChaCha20 stream cipher, Poly1305 one-time MAC, the AEAD construction, HChaCha20 for nonce extension, and SIMD 4-wide inter-block parallelism. See: [ChaCha20/Poly1305 WASM Reference](./asm_chacha.md)
403
-
404
- [The TypeScript module](./chacha20.md) exports `XChaCha20Cipher`, a CipherSuite implementation for STREAM using XChaCha20-Poly1305 with HKDF key derivation. Pool workers load internally via `SealStreamPool` at runtime and don't appear in the package exports map.
405
-
406
- **`sha2.wasm`** implements SHA-256 and SHA-512 per FIPS 180-4, plus SHA-384 (which reuses SHA-512's buffer and compress function with different IVs and truncation). It also provides HMAC per RFC 2104 for all three variants. HKDF-SHA256 and HKDF-SHA512 (RFC 5869) are pure TypeScript compositions over HMAC with no new WASM logic. See: [SHA-2 WASM Reference](./asm_sha2.md)
407
-
408
- **`sha3.wasm`** implements the Keccak-f[1600] permutation per FIPS 202. All SHA3 variants (SHA3-224, SHA3-256, SHA3-384, SHA3-512) and XOF variants (SHAKE128, SHAKE256) share a single permutation, differing only in rate, domain separation byte, and output length. SHAKE supports unbounded multi-squeeze output. See: [SHA-3 WASM Reference](./asm_sha3.md)
409
-
410
- **`kyber.wasm`** implements ML-KEM polynomial arithmetic per FIPS 203. It includes Montgomery and Barrett reduction, 7-layer NTT and inverse NTT with SIMD butterflies, basemul in Z_q[X]/(X²-ζ), centered binomial distribution sampling (η=2 and η=3), compression and decompression across all five bit-width paths, rejection sampling for matrix generation, and constant-time byte comparison and conditional move. Requires WebAssembly SIMD (`v128` instructions). Uses 3 memory pages (192 KB) with 10 polynomial slots, 8 polynomial vector slots, and dedicated buffers for keys and ciphertexts. See: [Kyber WASM Reference](./asm_kyber.md)
411
-
412
- [The TypeScript module](./kyber.md) exports `MlKem512`, `MlKem768`, and `MlKem1024`—KEM classes implementing the Fujisaki-Okamoto transform. All three require both `kyber` and `sha3` to be initialized; the sha3 module provides the Keccak sponge for matrix generation (SHAKE128), noise sampling (SHAKE256), and finalization (SHA3-256 for H, SHA3-512 for G).
413
-
414
- **`ct.wasm`** implements constant-time byte array equality with a single SIMD-only primitive. The module exports `compare(aOff, bOff, len)`, which reads both arrays directly from caller-specified offsets in linear memory and returns 1 if all bytes match, 0 otherwise. Comparison is zero-copy: no internal staging buffers, no buffer slots, no `wipeBuffers` export. The implementation is structurally branch-free. A `v128.xor`/`v128.or` accumulator processes 16-byte blocks, a scalar tail handles any remainder, and the final zero-test is an arithmetic shift, not a conditional. Requires WebAssembly SIMD (`v128` instructions); if the runtime lacks SIMD or compilation fails, the first call throws a branded error. See: [Constant-Time WASM Reference](asm_ct.md)
415
-
416
- [The TypeScript module](./utils.md#constanttimeequal) exports `constantTimeEqual` and `CT_MAX_BYTES` from the root barrel. The wrapper instantiates the WASM synchronously on first call and caches it for subsequent calls. It writes both arrays into linear memory, calls `compare`, and zeroes both regions in a `finally` block before returning. `CT_MAX_BYTES` is 32 KB per side; the 64 KB page holds two equal-length inputs.
417
-
418
- ---
419
-
420
- ## `init()` API
421
-
422
- WASM instantiation is async. [`init()`](./init.md) is the initialization gate, call it once before using any cryptographic class. The cost is explicit and the developer controls when it is paid.
423
-
424
- ### Signature
425
-
426
- ```typescript
427
- type Module = 'serpent' | 'chacha20' | 'sha2' | 'sha3' | 'keccak' | 'kyber'
428
-
429
- type WasmSource =
430
- | string // gzip+base64 embedded blob
431
- | URL // fetch + compileStreaming
432
- | ArrayBuffer // compile from raw bytes
433
- | Uint8Array // compile from raw bytes
434
- | WebAssembly.Module // pre-compiled (edge runtimes)
435
- | Response // instantiateStreaming
436
- | Promise<Response> // deferred fetch
437
-
438
- async function init(
439
- sources: Partial<Record<Module, WasmSource>>,
440
- ): Promise<void>
441
- ```
442
-
443
- The loading strategy is inferred from the source type, so there is no need for a mode string. Each module also exports its own init function, such as `serpentInit(source)`, `chacha20Init(source)`, `sha2Init(source)`, `sha3Init(source)`, `keccakInit(source)`, and `kyberInit(source)`, enabling tree-shakeable imports.
444
-
445
- > [!NOTE]
446
- > **`keccak` is an alias for `sha3`.** Both names are accepted by `init()`, `initModule()`, `getInstance()`, and `isInitialized()`. They share the same WASM binary and the same instance slot. The alias exists so Kyber/ML-KEM consumers can write `init({ keccak: keccakWasm })` using the semantically correct name for the underlying sponge primitive.
447
-
448
- ### Embedded subpath exports
449
-
450
- Each module provides a `/embedded` subpath that exports the gzip+base64 blob as a ready-to-use `WasmSource`:
451
-
452
- ```typescript
453
- import { init } from 'leviathan-crypto'
454
- import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
455
- import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
456
-
457
- await init({ serpent: serpentWasm, sha2: sha2Wasm })
458
- ```
459
-
460
- ### Behavioral contracts
461
-
462
- **Idempotent initialization.** Calling `init()` on an already initialized module is a no-op. It is safe to call `init()` from multiple locations within the codebase.
463
-
464
- **Module-scope cache.** Each `WebAssembly.Instance` is cached at module scope after initial instantiation. All subsequent class constructions use the cached instance with no recompilation.
465
-
466
- **Error before initialization.** Invoking any cryptographic class before calling `init()` throws a clear error prompting the developer to call `init({ <module>: ... })` first.
467
-
468
- **No implicit initialization.** Classes never call `init()` automatically on first use. Explicit initialization is preferable to hidden costs.
469
-
470
- **Thread safety.** The main thread uses a single WASM instance per module. `SealStreamPool` provides worker-based parallelism. Each pool worker is spawned from an IIFE bundled at build time and instantiates its own WASM modules with isolated linear memory, bypassing the main-thread cache entirely. For other primitives, create one instance per Worker if Workers are used.
471
-
472
- ---
473
-
474
- ## Public API Classes
475
-
476
- | Module | Classes |
477
- | ------------------------------- | ------------------------------------------------------------------------------------------------------- |
478
- | `serpent` + `sha2` | `SerpentCipher` |
479
- | `serpent` | `Serpent`, `SerpentCtr`, `SerpentCbc` |
480
- | `chacha20` | `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `XChaCha20Cipher` |
481
- | `sha2` | `SHA256`, `SHA384`, `SHA512`, `HMAC_SHA256`, `HMAC_SHA384`, `HMAC_SHA512`, `HKDF_SHA256`, `HKDF_SHA512` |
482
- | `sha3` | `SHA3_224`, `SHA3_256`, `SHA3_384`, `SHA3_512`, `SHAKE128`, `SHAKE256` |
483
- | `kyber` + `sha3` | `MlKem512`, `MlKem768`, `MlKem1024` |
484
- | `kyber` + `sha3` + inner cipher | `KyberSuite` (hybrid KEM+AEAD factory) |
485
- | `sha2` | `ratchetInit`, `KDFChain`, `SkippedKeyStore` |
486
- | `kyber` + `sha3` + `sha2` | `kemRatchetEncap`, `kemRatchetDecap`, `RatchetKeypair` |
487
- | `stream` | `Seal`, `SealStream`, `OpenStream`, `SealStreamPool` |
488
- | `serpent` + `sha2` | `Fortuna` with `SerpentGenerator` + `SHA256Hash` |
489
- | `serpent` + `sha3` | `Fortuna` with `SerpentGenerator` + `SHA3_256Hash` |
490
- | `chacha20` + `sha2` | `Fortuna` with `ChaCha20Generator` + `SHA256Hash` |
491
- | `chacha20` + `sha3` | `Fortuna` with `ChaCha20Generator` + `SHA3_256Hash` |
492
-
493
- >[!NOTE]
494
- > Class Names match conventional cryptographic notation.
495
-
496
- - HMAC names use underscore separator (`HMAC_SHA256`) matching RFC convention.
497
- - SHA-3 names use underscore separator (`SHA3_256`) for readability.
498
- - Ratchet exports are KDF primitives from Signal's Sparse Post-Quantum Ratchet spec; session state, message ordering, and header format remain application concerns.
499
- - **`Fortuna`** requires `await Fortuna.create({ generator, hash })` rather than `new Fortuna()`. Required modules depend on the generator and hash you pass. See [fortuna.md](./fortuna.md) for valid combinations.
500
- - `SealStream`, `OpenStream`, and `SealStreamPool` are cipher-agnostic; you select the cipher by passing `XChaCha20Cipher` or `SerpentCipher` at construction.
501
-
502
- ### Usage pattern
503
-
504
- All WASM-backed classes follow the same pattern:
505
-
506
- ```typescript
507
- import { init, Seal, SerpentCipher, SHA3_256 } from 'leviathan-crypto'
508
- import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
509
- import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
510
- import { sha3Wasm } from 'leviathan-crypto/sha3/embedded'
511
-
512
- await init({ serpent: serpentWasm, sha2: sha2Wasm, sha3: sha3Wasm })
513
-
514
- const key = SerpentCipher.keygen()
515
- const blob = Seal.encrypt(SerpentCipher, key, plaintext)
516
-
517
- const hasher = new SHA3_256()
518
- const digest = hasher.hash(message)
519
- ```
520
-
521
- ### Utility exports (no `init()` required)
522
-
523
- Pure TypeScript utilities ship alongside the WASM-backed primitives:
524
-
525
- |Category|Exports|
526
- |---|---|
527
- |Encoding|`hexToBytes`, `bytesToHex`, `utf8ToBytes`, `bytesToUtf8`, `base64ToBytes`, `bytesToBase64`|
528
- |Security|`constantTimeEqual`, `CT_MAX_BYTES`, `wipe`, `xor`|
529
- |Helpers|`concat`, `randomBytes`, `hasSIMD`|
530
- |Types|`Hash`, `KeyedHash`, `Blockcipher`, `Streamcipher`, `AEAD`|
531
-
532
- ---
533
-
534
- ## Build Pipeline
535
-
536
-
537
- 1. `npm run build:asm`: AssemblyScript compiler reads `src/asm/*/index.ts`, emits `build/*.wasm`
538
- 2. `npm run build:embed`: `scripts/embed-wasm.ts` reads each `.wasm`, gzip compresses, base64 encodes, writes to `src/ts/embedded/*.ts` and per-module `src/ts/*/embedded.ts`
539
- 3. `npm run build:embed-workers`: `scripts/embed-workers.ts` bundles each pool worker into a self-contained IIFE via esbuild and writes the source to `src/ts/embedded/<cipher>-pool-worker.ts` as a string export
540
- 4. `npm run build:ts`: TypeScript compiler emits `dist/`
541
- 5. `cp build/*.wasm dist/`: WASM binaries copied for URL-based consumers
542
- 6. At runtime (subpath): `serpentInit(serpentWasm)` → `initModule()` → `loadWasm(source)` → decode gzip+base64 → `WebAssembly.instantiate` → cache in `init.ts`
543
- 7. At runtime (root): `init({ serpent: serpentWasm, sha2: sha2Wasm })` → dispatches to each module's init function via `Promise.all` → same path as step 6 per module
544
-
545
- `src/ts/embedded/` is gitignored; these files are build artifacts. The WASM blobs (`<module>.ts`) derive from the AssemblyScript source in `src/asm/`. The pool-worker bundles (`<cipher>-pool-worker.ts`) derive from the worker source in `src/ts/<cipher>/pool-worker.ts`, bundled as a self-contained IIFE by `scripts/embed-workers.ts`.
546
-
547
- ---
548
-
549
- ## Module Relationships
550
-
551
- ### ASM layer: internal import graph
552
-
553
- Each WASM module is fully independent. No cross-module imports exist.
554
-
555
- **Serpent (`src/asm/serpent/`)**
556
-
557
- ```
558
- buffers.ts
559
- <- serpent.ts (offsets for key, block, subkey, work, CBC IV)
560
- <- serpent_unrolled.ts (block offsets, subkey, work)
561
- <- serpent_simd.ts (SIMD bitsliced block operations)
562
- <- cbc.ts (IV, block, chunk offsets)
563
- <- cbc_simd.ts (SIMD CBC decrypt)
564
- <- ctr.ts (nonce, counter, block, chunk offsets)
565
- <- ctr_simd.ts (SIMD CTR 4-wide inter-block)
566
-
567
- serpent.ts
568
- <- serpent_unrolled.ts (S-boxes sb0-sb7, si0-si7, lk, kl, keyXor)
569
-
570
- serpent_unrolled.ts
571
- <- cbc.ts (encryptBlock_unrolled, decryptBlock_unrolled)
572
- <- ctr.ts (encryptBlock_unrolled)
573
-
574
- serpent_simd.ts
575
- <- cbc_simd.ts (SIMD block operations)
576
- <- ctr_simd.ts (SIMD block operations)
577
-
578
- index.ts
579
- re-exports: buffers + serpent + serpent_unrolled + serpent_simd + cbc + cbc_simd + ctr + ctr_simd
580
- ```
581
-
582
- **ChaCha (`src/asm/chacha20/`)**
583
-
584
- ```
585
- buffers.ts
586
- <- chacha20.ts (key, nonce, counter, block, state, poly key, xchacha offsets)
587
- <- chacha20_simd_4x.ts (SIMD work buffer, chunk offsets)
588
- <- poly1305.ts (poly key, msg, buf, tag, h, r, rs, s offsets)
589
- <- wipe.ts (all buffer offsets, zeroes everything)
590
-
591
- index.ts
592
- re-exports: buffers + chacha20 + chacha20_simd_4x + poly1305 + wipe
593
- ```
594
-
595
- **SHA-2 (`src/asm/sha2/`)**
596
-
597
- ```
598
- buffers.ts
599
- <- sha256.ts (H, block, W, out, input, partial, total offsets)
600
- <- sha512.ts (H, block, W, out, input, partial, total offsets)
601
- <- hmac.ts (SHA-256 input, out, ipad, opad, inner offsets)
602
- <- hmac512.ts (SHA-512 input, out, ipad, opad, inner offsets)
603
-
604
- sha256.ts
605
- <- hmac.ts (sha256Init, sha256Update, sha256Final)
606
-
607
- sha512.ts
608
- <- hmac512.ts (sha512Init, sha384Init, sha512Update, sha512Final, sha384Final)
609
-
610
- index.ts
611
- re-exports: buffers + sha256 + sha512 + hmac + hmac512
612
- defines: wipeBuffers() inline
613
- ```
614
-
615
- **SHA-3 (`src/asm/sha3/`)**
616
-
617
- ```
618
- buffers.ts
619
- <- keccak.ts (state, rate, absorbed, dsbyte, input, out offsets)
620
-
621
- index.ts
622
- re-exports: buffers + keccak
623
- ```
624
-
625
- **Kyber (`src/asm/kyber/`)**
626
-
627
- ```
628
- params.ts
629
- <- reduce.ts (Q, QINV, BARRETT_V, BARRETT_SHIFT)
630
- <- poly.ts (Q, POLY_BYTES, HALF_Q, compression constants)
631
- <- polyvec.ts (Q, POLY_BYTES, compression constants)
632
- <- sampling.ts (Q)
633
-
634
- buffers.ts
635
- <- polyvec.ts (POLY_ACC_OFFSET)
636
-
637
- reduce.ts
638
- <- ntt.ts (fqmul, barrett_reduce)
639
- <- ntt_simd.ts (fqmul, barrett_reduce — scalar tail)
640
- <- poly.ts (montgomery_reduce, barrett_reduce, fqmul)
641
-
642
- ntt.ts
643
- <- ntt_simd.ts (getZetasOffset — zetas table pointer)
644
- <- poly.ts (ntt, invntt, basemul, getZeta)
645
-
646
- ntt_simd.ts
647
- <- poly_simd.ts (ntt_simd, invntt_simd, barrett_reduce_8x)
648
-
649
- poly.ts
650
- <- polyvec.ts (poly_tobytes, poly_frombytes, poly_basemul_montgomery)
651
-
652
- poly_simd.ts
653
- <- polyvec.ts (poly_add_simd, poly_reduce_simd, poly_ntt_simd, poly_invntt_simd)
654
-
655
- cbd.ts
656
- <- poly.ts (cbd2, cbd3)
657
-
658
- index.ts
659
- re-exports: buffers + ntt (scalar aliases) + ntt_simd (as ntt/invntt) +
660
- reduce + poly (scalar serialization/compression/basemul) +
661
- poly_simd (as poly_add/sub/reduce/ntt/invntt) +
662
- polyvec + sampling + verify
663
- ```
664
-
665
- ---
666
-
667
- ### TS layer: internal import graph
668
-
669
-
670
- Each module's init function (`serpentInit`, `chacha20Init`, `sha2Init`, `sha3Init`, `kyberInit`) calls `initModule()` from `init.ts`, passing a `WasmSource`. `initModule()` delegates to `loadWasm(source)` in `loader.ts`. The loader infers the loading strategy from the source type, with no mode string and no knowledge of module names or embedded file paths.
671
-
672
- Pool workers (`serpent/pool-worker.ts`, `chacha20/pool-worker.ts`) instantiate their own WASM modules from pre-compiled `WebAssembly.Module` objects passed via `postMessage`. They do not use `initModule()` or the main-thread cache. Workers are spawned from blob URLs constructed in `cipher-suite.ts` over an IIFE source string built at lib build time (`src/ts/embedded/<cipher>-pool-worker.ts`). The `pool-worker.ts` file itself is the source the bundler reads, not the runtime spawn entry.
673
-
674
- ---
675
-
676
- ### TS-to-WASM mapping
677
-
678
- Each TS wrapper class maps to one WASM module and specific exported functions. Tier 2 composition classes are pure TypeScript; they call Tier 1 classes rather than WASM functions directly.
679
-
680
- **serpent/index.ts → asm/serpent/ (Tier 1: direct WASM callers)**
681
-
682
- |TS Class|WASM functions called|
683
- |---|---|
684
- |`Serpent`|`loadKey`, `encryptBlock`, `decryptBlock`, `wipeBuffers` + buffer getters|
685
- |`SerpentCtr`|`loadKey`, `resetCounter`, `setCounter`, `encryptChunk`, `encryptChunk_simd`, `wipeBuffers` + buffer getters|
686
- |`SerpentCbc`|`loadKey`, `cbcEncryptChunk`, `cbcDecryptChunk`, `cbcDecryptChunk_simd`, `wipeBuffers` + buffer getters|
687
- |`SerpentGenerator`|`loadKey`, `encryptBlock`, `wipeBuffers` + buffer getters|
688
-
689
- **chacha20/index.ts → asm/chacha20/ (Tier 1: direct WASM callers)**
690
-
691
- |TS Class|WASM functions called|
692
- |---|---|
693
- |`ChaCha20`|`chachaLoadKey`, `chachaSetCounter`, `chachaEncryptChunk`, `chachaEncryptChunk_simd`, `wipeBuffers` + buffer getters|
694
- |`Poly1305`|`polyInit`, `polyUpdate`, `polyFinal`, `wipeBuffers` + buffer getters|
695
- |`ChaCha20Poly1305`|`chachaLoadKey`, `chachaSetCounter`, `chachaGenPolyKey`, `chachaEncryptChunk`, `polyInit`, `polyUpdate`, `polyFinal`, `wipeBuffers` + buffer getters (via `ops.ts`)|
696
- |`XChaCha20Poly1305`|All of `ChaCha20Poly1305` + `hchacha20` + xchacha buffer getters (via `ops.ts`)|
697
- |`ChaCha20Generator`|`chachaLoadKey`, `chachaSetCounter`, `chachaEncryptChunk_simd`, `wipeBuffers` + buffer getters|
698
-
699
- **sha2/index.ts → asm/sha2/ (Tier 1: direct WASM callers)**
700
-
701
- |TS Class|WASM functions called|
702
- |---|---|
703
- |`SHA256`|`sha256Init`, `sha256Update`, `sha256Final`, `wipeBuffers` + buffer getters|
704
- |`SHA512`|`sha512Init`, `sha512Update`, `sha512Final`, `wipeBuffers` + buffer getters|
705
- |`SHA384`|`sha384Init`, `sha512Update`, `sha384Final`, `wipeBuffers` + buffer getters|
706
- |`HMAC_SHA256`|`hmac256Init`, `hmac256Update`, `hmac256Final`, `sha256Init`, `sha256Update`, `sha256Final`, `wipeBuffers` + buffer getters|
707
- |`HMAC_SHA512`|`hmac512Init`, `hmac512Update`, `hmac512Final`, `sha512Init`, `sha512Update`, `sha512Final`, `wipeBuffers` + buffer getters|
708
- |`HMAC_SHA384`|`hmac384Init`, `hmac384Update`, `hmac384Final`, `sha384Init`, `sha512Update`, `sha384Final`, `wipeBuffers` + buffer getters|
709
- |`SHA256Hash`|`sha256Init`, `sha256Update`, `sha256Final`, `wipeBuffers` + buffer getters|
710
-
711
- **sha3/index.ts → asm/sha3/ (Tier 1: direct WASM callers)**
712
-
713
- |TS Class|WASM functions called|
714
- |---|---|
715
- |`SHA3_224`|`sha3_224Init`, `keccakAbsorb`, `sha3_224Final`, `wipeBuffers` + buffer getters|
716
- |`SHA3_256`|`sha3_256Init`, `keccakAbsorb`, `sha3_256Final`, `wipeBuffers` + buffer getters|
717
- |`SHA3_384`|`sha3_384Init`, `keccakAbsorb`, `sha3_384Final`, `wipeBuffers` + buffer getters|
718
- |`SHA3_512`|`sha3_512Init`, `keccakAbsorb`, `sha3_512Final`, `wipeBuffers` + buffer getters|
719
- |`SHAKE128`|`shake128Init`, `keccakAbsorb`, `shakePad`, `shakeSqueezeBlock`, `wipeBuffers` + buffer getters|
720
- |`SHAKE256`|`shake256Init`, `keccakAbsorb`, `shakePad`, `shakeSqueezeBlock`, `wipeBuffers` + buffer getters|
721
- |`SHA3_256Hash`|`sha3_256Init`, `keccakAbsorb`, `sha3_256Final`, `wipeBuffers` + buffer getters|
722
-
723
- **kyber/index.ts + kyber/kem.ts + kyber/indcpa.ts → asm/kyber/ (Tier 1)**
724
-
725
- |TS Class|WASM functions called|
726
- |---|---|
727
- |`MlKem512`, `MlKem768`, `MlKem1024`|`polyvec_ntt`, `polyvec_invntt`, `polyvec_basemul_acc_montgomery`, `polyvec_add`, `polyvec_reduce`, `polyvec_tobytes`, `polyvec_frombytes`, `polyvec_compress`, `polyvec_decompress`, `poly_ntt`, `poly_invntt`, `poly_tomont`, `poly_add`, `poly_sub`, `poly_reduce`, `poly_basemul_montgomery`, `poly_frommsg`, `poly_tomsg`, `poly_compress`, `poly_decompress`, `poly_getnoise`, `rej_uniform`, `ct_verify`, `ct_cmov`, `wipeBuffers` + buffer getters|
728
-
729
- All MlKem classes also call sha3 WASM via `indcpa.ts`: `sha3_256Init`, `sha3_512Init`, `shake128Init`, `shake256Init`, `keccakAbsorb`, `sha3_256Final`, `sha3_512Final`, `shakeFinal`, `shakePad`, `shakeSqueezeBlock`.
730
-
731
- **Tier 2: pure TS composition**
732
-
733
- |TS Class / Object|Composes|
734
- |---|---|
735
- |`SerpentCipher`|`SerpentCbc` + `HMAC_SHA256` + `HKDF_SHA256`|
736
- |`XChaCha20Cipher`|`ChaCha20Poly1305` (via `ops.ts`) + `HKDF_SHA256`|
737
- |`Seal`|`SealStream` + `OpenStream` (degenerate single-chunk case)|
738
- |`SealStream`|`CipherSuite` (generic — caller provides cipher)|
739
- |`OpenStream`|`CipherSuite` (generic — caller provides cipher)|
740
- |`SealStreamPool`|`CipherSuite` + `compileWasm()` + Web Workers|
741
- |`HKDF_SHA256`|`HMAC_SHA256` (extract + expand per RFC 5869)|
742
- |`HKDF_SHA512`|`HMAC_SHA512` (extract + expand per RFC 5869)|
743
- |`Fortuna`|`Generator` + `HashFn` (any compatible pair: `SerpentGenerator`/`ChaCha20Generator` × `SHA256Hash`/`SHA3_256Hash`)|
744
-
745
- ---
746
-
747
- ### Cross-module dependencies
748
-
749
- |Relationship|Notes|
750
- |---|---|
751
- |`SerpentCipher` → `serpent` + `sha2`|Tier 2 composition: Serpent-CBC + HMAC-SHA256 + HKDF-SHA256.|
752
- |`XChaCha20Cipher` → `chacha20` + `sha2`|HKDF-SHA256 for key derivation + HChaCha20 + ChaCha20-Poly1305 for per-chunk AEAD.|
753
- |`KyberSuite` → `kyber` + `sha3` + inner cipher|KEM encaps/decaps + HKDF with kemCt binding + inner CipherSuite.|
754
- |`SealStream`, `OpenStream` → depends on cipher|Cipher-agnostic. Module requirements are determined by the `CipherSuite` passed at construction.|
755
- |`SealStreamPool` → depends on cipher|Same module requirements as the cipher, plus `WasmSource` in pool opts for worker compilation.|
756
- |`Fortuna` → cipher module + hash module|Uses `Fortuna.create({ generator, hash })` static factory instead of `new`. Required modules depend on which generator and hash you pass. See [fortuna.md](./fortuna.md).|
757
- |`MlKem512`, `MlKem768`, `MlKem1024` → `kyber` + `sha3`|Kyber module handles polynomial arithmetic; sha3 provides SHAKE128/256, SHA3-256/512 for G/H/J/matrix gen.|
758
- |`HKDF_SHA256`, `HKDF_SHA512` → `sha2`|Pure TS composition — extract and expand steps per RFC 5869.|
759
- |All other classes|Each depends on exactly **one** WASM module.|
760
-
761
- ---
762
-
763
- ### Public API barrel (`src/ts/index.ts`)
764
-
765
- The root barrel defines and exports the dispatching `init()` function. It is the only file that imports all four module-scoped init functions.
766
-
767
- |Source|Exports|
768
- |---|---|
769
- |_(barrel itself)_|`init` (dispatching function — calls per-module init functions via `Promise.all`)|
770
- |`init.ts`|`Module`, `WasmSource`, `isInitialized`|
771
- |`errors.ts`|`AuthenticationError`|
772
- |`serpent/index.ts`|`Serpent`, `SerpentCtr`, `SerpentCbc`, `SerpentCipher`, `_serpentReady`|
773
- |`chacha20/index.ts`|`ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `XChaCha20Cipher`, `_chachaReady`|
774
- |`sha2/index.ts`|`SHA256`, `SHA512`, `SHA384`, `HMAC_SHA256`, `HMAC_SHA512`, `HMAC_SHA384`, `HKDF_SHA256`, `HKDF_SHA512`, `_sha2Ready`|
775
- |`sha3/index.ts`|`SHA3_224`, `SHA3_256`, `SHA3_384`, `SHA3_512`, `SHAKE128`, `SHAKE256`, `_sha3Ready`|
776
- |`keccak/index.ts`|`keccakInit` + re-exports all sha3 classes (alias subpath)|
777
- |`kyber/index.ts`|`kyberInit`, `KyberSuite`, `MlKem512`, `MlKem768`, `MlKem1024`, `KyberKeyPair`, `KyberEncapsulation`, `KyberParams`, `MLKEM512`, `MLKEM768`, `MLKEM1024`|
778
- |`stream/index.ts`|`Seal`, `SealStream`, `OpenStream`, `SealStreamPool`, `CipherSuite`, `DerivedKeys`, `SealStreamOpts`, `PoolOpts`, `FLAG_FRAMED`, `TAG_DATA`, `TAG_FINAL`, `HEADER_SIZE`, `CHUNK_MIN`, `CHUNK_MAX`|
779
- |`fortuna.ts`|`Fortuna`|
780
- |`types.ts`|`Hash`, `KeyedHash`, `Blockcipher`, `Streamcipher`, `AEAD`, `Generator`, `HashFn`|
781
- |`utils.ts`|`hexToBytes`, `bytesToHex`, `utf8ToBytes`, `bytesToUtf8`, `base64ToBytes`, `bytesToBase64`, `constantTimeEqual`, `CT_MAX_BYTES`, `wipe`, `xor`, `concat`, `randomBytes`, `hasSIMD`|
782
-
783
- Each subpath export also exports its own module-specific init function for tree-shakeable loading: `serpentInit(source)`, `chacha20Init(source)`, `sha2Init(source)`, `sha3Init(source)`, `keccakInit(source)`.
784
-
785
- ---
786
-
787
- ## npm Package
788
-
789
- **Subpath exports:**
790
-
791
- ```json
792
- {
793
- "exports": {
794
- ".": "./dist/index.js",
795
- "./stream": "./dist/stream/index.js",
796
- "./serpent": "./dist/serpent/index.js",
797
- "./serpent/embedded": "./dist/serpent/embedded.js",
798
- "./chacha20": "./dist/chacha20/index.js",
799
- "./chacha20/embedded": "./dist/chacha20/embedded.js",
800
- "./sha2": "./dist/sha2/index.js",
801
- "./sha2/embedded": "./dist/sha2/embedded.js",
802
- "./sha3": "./dist/sha3/index.js",
803
- "./sha3/embedded": "./dist/sha3/embedded.js",
804
- "./keccak": "./dist/keccak/index.js",
805
- "./keccak/embedded": "./dist/keccak/embedded.js",
806
- "./kyber": "./dist/kyber/index.js",
807
- "./kyber/embedded": "./dist/kyber/embedded.js",
808
- "./ratchet": "./dist/ratchet/index.js"
809
- }
810
- }
811
- ```
812
-
813
- > [!NOTE]
814
- > Pool worker source files (`dist/serpent/pool-worker.js`, `dist/chacha20/pool-worker.js`) ship in the package but are not in the `exports` map. They are the build inputs from which `scripts/embed-workers.ts` produces the IIFE source strings embedded in `dist/<cipher>/cipher-suite.js` at lib build time. Workers are spawned from those embedded strings via classic blob URLs. Consumers do not import the `pool-worker.js` files directly, and bundlers do not need to chunk them. Strict-CSP consumers (`worker-src 'self'`, no `blob:`) can supply their own URL-based factory by spread-overriding `createPoolWorker` on the cipher object; see [ciphersuite.md](./ciphersuite.md).
815
-
816
- The root `.` export re-exports everything. Subpath exports allow bundlers to tree-shake at the module level; a consumer importing only `./sha3` does not include the Serpent wrapper classes or their embedded WASM binaries in their bundle.
817
-
818
- The `/embedded` subpaths provide gzip+base64 WASM blobs for zero-config usage. Consumers using URL-based or pre-compiled loading can skip the `/embedded` imports entirely, keeping them out of the bundle.
819
-
820
- **Tree-shaking:** `"sideEffects": false` is set in `package.json`. Bundlers that support tree-shaking (webpack, Rollup, esbuild) can eliminate unused modules and their embedded WASM binaries from the final bundle.
821
-
822
- **Published.** The npm package includes:
823
-
824
- - `dist/`: compiled JS, TypeScript declarations, WASM binaries, pool worker source files (build inputs, not runtime spawn entries; see the NOTE above), and a subset of consumer-facing API docs for offline use.
825
- - `CLAUDE.md`: agent-facing project context.
826
- - `SECURITY.md`: vulnerability disclosure policy.
827
-
828
- **Not published.** `src/`, `test/`, `build/`, `scripts/`, `.github/`, editor configs.
829
-
830
- ---
831
-
832
- ## Buffer Layouts
833
-
834
- All offsets start at 0 per module. Independent linear memory. No offsets are shared or coordinated across modules.
835
-
836
- ### Serpent module (3 pages, 192 KB)
837
-
838
- Source: `src/asm/serpent/buffers.ts`
839
-
840
- |Offset|Size|Name|
841
- |---|---|---|
842
- |0|32|`KEY_BUFFER` — key input (padded to 32 bytes for all key sizes)|
843
- |32|16|`BLOCK_PT_BUFFER` — single block plaintext|
844
- |48|16|`BLOCK_CT_BUFFER` — single block ciphertext|
845
- |64|16|`NONCE_BUFFER` — CTR mode nonce|
846
- |80|16|`COUNTER_BUFFER` — 128-bit little-endian counter|
847
- |96|528|`SUBKEY_BUFFER` — key schedule output (33 rounds × 4 × 4 bytes)|
848
- |624|65552|`CHUNK_PT_BUFFER` — streaming plaintext (CTR/CBC); +16 from 65536 to fit PKCS7 max overhead|
849
- |66176|65552|`CHUNK_CT_BUFFER` — streaming ciphertext (CTR/CBC)|
850
- |131728|20|`WORK_BUFFER` — 5 × i32 scratch registers (key schedule + S-box/LT rounds)|
851
- |131748|16|`CBC_IV_BUFFER` — CBC initialization vector / chaining value|
852
- |131856|—|END|
853
-
854
- `wipeBuffers()` zeroes all 10 buffers (key, block pt/ct, nonce, counter, subkeys, work, chunk pt/ct, CBC IV).
855
-
856
- ### ChaCha20 module (3 pages, 192 KB)
857
-
858
- Source: `src/asm/chacha20/buffers.ts`
859
-
860
- |Offset|Size|Name|
861
- |---|---|---|
862
- |0|32|`KEY_BUFFER` — ChaCha20 256-bit key|
863
- |32|12|`CHACHA_NONCE_BUFFER` — 96-bit nonce (3 × u32, LE)|
864
- |44|4|`CHACHA_CTR_BUFFER` — u32 block counter|
865
- |48|64|`CHACHA_BLOCK_BUFFER` — 64-byte keystream block output|
866
- |112|64|`CHACHA_STATE_BUFFER` — 16 × u32 initial state|
867
- |176|65536|`CHUNK_PT_BUFFER` — streaming plaintext|
868
- |65712|65536|`CHUNK_CT_BUFFER` — streaming ciphertext|
869
- |131248|32|`POLY_KEY_BUFFER` — one-time key r‖s|
870
- |131280|64|`POLY_MSG_BUFFER` — message staging (≤ 64 bytes per polyUpdate)|
871
- |131344|16|`POLY_BUF_BUFFER` — partial block accumulator|
872
- |131360|4|`POLY_BUF_LEN_BUFFER` — u32 bytes in partial block|
873
- |131364|16|`POLY_TAG_BUFFER` — 16-byte output MAC tag|
874
- |131380|40|`POLY_H_BUFFER` — accumulator h: 5 × u64|
875
- |131420|40|`POLY_R_BUFFER` — clamped r: 5 × u64|
876
- |131460|32|`POLY_RS_BUFFER` — precomputed 5×r[1..4]: 4 × u64|
877
- |131492|16|`POLY_S_BUFFER` — s pad: 4 × u32|
878
- |131508|24|`XCHACHA_NONCE_BUFFER` — full 24-byte XChaCha20 nonce|
879
- |131532|32|`XCHACHA_SUBKEY_BUFFER` — HChaCha20 output (key material)|
880
- |131564|4|_(padding for 16-byte SIMD alignment)_|
881
- |131568|256|`CHACHA_SIMD_WORK_BUFFER` — 4-wide inter-block keystream (4 × 64 bytes)|
882
- |131824|—|END|
883
-
884
- `wipeBuffers()` zeroes all 15 buffer regions (key, chacha nonce/ctr/block/state, chunk pt/ct, poly key/msg/buf/tag/h/r/rs/s, xchacha nonce/subkey, SIMD work).
885
-
886
- ### SHA-2 module (3 pages, 192 KB)
887
-
888
- Source: `src/asm/sha2/buffers.ts`
889
-
890
- |Offset|Size|Name|
891
- |---|---|---|
892
- |0|32|`SHA256_H` — SHA-256 hash state H0..H7 (8 × u32)|
893
- |32|64|`SHA256_BLOCK` — SHA-256 block accumulator|
894
- |96|256|`SHA256_W` — SHA-256 message schedule W[0..63] (64 × u32)|
895
- |352|32|`SHA256_OUT` — SHA-256 digest output|
896
- |384|64|`SHA256_INPUT` — SHA-256 user input staging (one block)|
897
- |448|4|`SHA256_PARTIAL` — u32 partial block length|
898
- |452|8|`SHA256_TOTAL` — u64 total bytes hashed|
899
- |460|64|`HMAC256_IPAD` — HMAC-SHA256 K' XOR ipad|
900
- |524|64|`HMAC256_OPAD` — HMAC-SHA256 K' XOR opad|
901
- |588|32|`HMAC256_INNER` — HMAC-SHA256 inner hash|
902
- |620|64|`SHA512_H` — SHA-512 hash state H0..H7 (8 × u64)|
903
- |684|128|`SHA512_BLOCK` — SHA-512 block accumulator|
904
- |812|640|`SHA512_W` — SHA-512 message schedule W[0..79] (80 × u64)|
905
- |1452|64|`SHA512_OUT` — SHA-512 digest output (SHA-384 uses first 48 bytes)|
906
- |1516|128|`SHA512_INPUT` — SHA-512 user input staging (one block)|
907
- |1644|4|`SHA512_PARTIAL` — u32 partial block length|
908
- |1648|8|`SHA512_TOTAL` — u64 total bytes hashed|
909
- |1656|128|`HMAC512_IPAD` — HMAC-SHA512 K' XOR ipad (128-byte block size)|
910
- |1784|128|`HMAC512_OPAD` — HMAC-SHA512 K' XOR opad|
911
- |1912|64|`HMAC512_INNER` — HMAC-SHA512 inner hash|
912
- |1976|—|END|
913
-
914
- `wipeBuffers()` zeroes all 20 buffer regions (SHA-256 state/block/W/out/input/partial/total, HMAC-256 ipad/opad/inner, SHA-512 state/block/W/out/input/partial/total, HMAC-512 ipad/opad/inner).
915
-
916
- ### SHA-3 module (3 pages, 192 KB)
917
-
918
- Source: `src/asm/sha3/buffers.ts`
919
-
920
- |Offset|Size|Name|
921
- |---|---|---|
922
- |0|200|`KECCAK_STATE`: 25 × u64 Keccak-f[1600] lane matrix (5×5, row-major x+5y)|
923
- |200|4|`KECCAK_RATE`: u32 rate in bytes (variant-specific: 72–168)|
924
- |204|4|`KECCAK_ABSORBED`: u32 bytes absorbed into current block|
925
- |208|1|`KECCAK_DSBYTE`: u8 domain separation byte (0x06 for SHA-3, 0x1f for SHAKE)|
926
- |209|168|`KECCAK_INPUT`: input staging buffer (max rate = SHAKE128 at 168 bytes)|
927
- |377|168|`KECCAK_OUT`: output buffer (one SHAKE128 squeeze block)|
928
- |545|—|END|
929
-
930
- `wipeBuffers()` zeroes all 6 buffer regions (state, rate, absorbed, dsbyte, input, output).
931
-
932
- ### Kyber module (3 pages, 192 KB)
933
-
934
- Source: `src/asm/kyber/`
935
-
936
- |Region|Offset|Size|Purpose|
937
- |---|---|---|---|
938
- |AS data segment|0|4096|Zetas table (128 × i16, bit-reversed Montgomery domain)|
939
- |Poly slots|4096|5120|10 × 512B scratch polynomials (256 × i16 each)|
940
- |Polyvec slots|9216|16384|8 × 2048B scratch polyvecs (k=4 max: 4 × 512B)|
941
- |SEED buffer|25600|32|Seed ρ/σ|
942
- |MSG buffer|25632|32|Message / shared secret|
943
- |PK buffer|25664|1568|Encapsulation key (max k=4)|
944
- |SK buffer|27232|1536|IND-CPA secret key (max k=4)|
945
- |CT buffer|28768|1568|Ciphertext (max k=4)|
946
- |CT_PRIME buffer|30336|1568|Decaps re-encrypt comparison (max k=4)|
947
- |XOF/PRF buffer|31904|1024|SHAKE squeeze output for rej_uniform / CBD|
948
- |Poly accumulator|32928|512|Internal scratch for polyvec_basemul_acc|
949
-
950
- Total mutable: 29344 bytes (4096–33440). End = 33440 < 192 KB.
951
-
952
- `wipeBuffers()` zeroes all mutable regions (poly slots, polyvec slots, SEED, MSG, PK, SK, CT, CT_PRIME, XOF/PRF, accumulator). The zetas data segment is read-only and is not wiped.
953
-
954
- ---
955
-
956
- ## Test Suite
957
-
958
-
959
- ### Structure
960
-
961
- For the full testing methodology and vector corpus, see: [test-suite.md](./test-suite.md)
962
-
963
- ### Gate discipline
964
-
965
- **Each primitive family has a gate test:** the simplest authoritative vector for that primitive. The gate must pass before any other tests in that family are written or run. Gate tests are annotated with a `// GATE` comment.
966
-
967
- ### `init.test.ts` contracts
968
-
969
- - `init()` with each `WasmSource` type loads and caches the module correctly
970
- - Idempotency: second `init()` call for same module is a no-op
971
- - Error before init: clear error thrown for each class before its module is loaded
972
- - Partial init: loading `{ serpent: ... }` does not make `sha3` classes available
973
-
974
- ---
975
-
976
- ## Correctness Contract
977
-
978
- leviathan-crypto must produce byte-identical output to the authoritative specification for every known test vector. Cross-checks against the leviathan TypeScript reference and external tools (OpenSSL, Python hashlib, Node.js crypto) provide additional verification layers.
979
-
980
- The vector corpus in `test/vectors/` act as a source of immutable known-answer-test truth. KAT files are reference data from authoritative specifications (FIPS, RFCs, ACVP, NIST CAVP, NESSIE) or are self generated as regression vectors by `scripts/gen-*-vectors.ts`. CI validates integrity against `SHA256SUMS`. See [test-suite.md](./test-suite.md) for the full corpus inventory, provenance, and gate discipline.
981
-
982
- ---
983
-
984
- ## Known Limitations
985
-
986
- - **`SerpentCbc` is unauthenticated**: use `Seal` with `SerpentCipher` for authenticated Serpent encryption, or pair `SerpentCbc` with `HMAC_SHA256` (Encrypt-then-MAC) if direct CBC access is required.
987
- - **Single-threaded WASM per instance**: one WASM instance per binary per thread. `SealStreamPool` provides Worker-based parallelism for both cipher families; other primitives remain single-threaded.
988
- - **Max input per WASM call**: CTR accepts at most 65536 bytes per call; CBC accepts at most 65552 bytes (65536 + 16 bytes PKCS7 maximum overhead). Wrappers handle splitting automatically for larger inputs.
989
- - **WASM side-channel posture**: WebAssembly implementations offer the best available side-channel resistance (branchless, table-free), but lack hardware-level constant-time guarantees. For applications where timing side channels are a primary threat, a native cryptographic library with verified constant-time guarantees will be more appropriate than any WASM-based implementation.
990
-
991
- ---
992
-
993
- ## Cross-References
994
-
995
- |Document|Description|
996
- |---|---|
997
- |[index](./README.md)|Project Documentation index|
998
- |[lexicon](./lexicon.md)|Glossary of cryptographic terms|
999
- |[test-suite](./test-suite.md)|testing methodology, vector corpus, and gate discipline|
1000
- |[init](./init.md)|`init()` API, `WasmSource`, and idempotent behavior|
1001
- |[loader](./loader.md)|internal WASM binary loading strategies|
1002
- |[wasm](./wasm.md)|WebAssembly primer: modules, instances, memory, and the init gate|
1003
- |[types](./types.md)|public TypeScript interfaces and `CipherSuite`|
1004
- |[utils](./utils.md)|encoding helpers, `constantTimeEqual`, `wipe`, `randomBytes`|
1005
- |[authenticated encryption](./aead.md)|`Seal`, `SealStream`, `OpenStream`: cipher-agnostic AEAD APIs using a `CipherSuite` such as `SerpentCipher` or `XChaCha20Cipher`|
1006
- |[serpent](./serpent.md)|Serpent-256 TypeScript API, SerpentCipher|
1007
- |[chacha20](./chacha20.md)|ChaCha20/Poly1305 TypeScript API, XChaCha20Cipher|
1008
- |[sha2](./sha2.md)|SHA-2 hashes, HMAC, and HKDF TypeScript API|
1009
- |[sha3](./sha3.md)|SHA-3 hashes and SHAKE XOFs TypeScript API|
1010
- |[fortuna](./fortuna.md)|Fortuna CSPRNG with forward secrecy and entropy pooling|
1011
- |[argon2id](./argon2id.md)|Argon2id password hashing and key derivation|