leviathan-crypto 1.3.1 → 2.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 (124) hide show
  1. package/CLAUDE.md +129 -76
  2. package/README.md +166 -221
  3. package/SECURITY.md +89 -37
  4. package/dist/chacha20/cipher-suite.d.ts +4 -0
  5. package/dist/chacha20/cipher-suite.js +78 -0
  6. package/dist/chacha20/embedded.d.ts +1 -0
  7. package/dist/chacha20/embedded.js +27 -0
  8. package/dist/chacha20/index.d.ts +20 -7
  9. package/dist/chacha20/index.js +41 -14
  10. package/dist/chacha20/ops.d.ts +1 -1
  11. package/dist/chacha20/ops.js +19 -18
  12. package/dist/chacha20/pool-worker.js +77 -0
  13. package/dist/ct-wasm.d.ts +1 -0
  14. package/dist/ct-wasm.js +3 -0
  15. package/dist/ct.wasm +0 -0
  16. package/dist/docs/aead.md +320 -0
  17. package/dist/docs/architecture.md +419 -285
  18. package/dist/docs/argon2id.md +42 -30
  19. package/dist/docs/chacha20.md +218 -150
  20. package/dist/docs/exports.md +241 -0
  21. package/dist/docs/fortuna.md +65 -74
  22. package/dist/docs/init.md +172 -178
  23. package/dist/docs/loader.md +87 -132
  24. package/dist/docs/serpent.md +134 -565
  25. package/dist/docs/sha2.md +91 -103
  26. package/dist/docs/sha3.md +70 -36
  27. package/dist/docs/types.md +93 -16
  28. package/dist/docs/utils.md +114 -41
  29. package/dist/embedded/chacha20.d.ts +1 -1
  30. package/dist/embedded/chacha20.js +2 -1
  31. package/dist/embedded/kyber.d.ts +1 -0
  32. package/dist/embedded/kyber.js +3 -0
  33. package/dist/embedded/serpent.d.ts +1 -1
  34. package/dist/embedded/serpent.js +2 -1
  35. package/dist/embedded/sha2.d.ts +1 -1
  36. package/dist/embedded/sha2.js +2 -1
  37. package/dist/embedded/sha3.d.ts +1 -1
  38. package/dist/embedded/sha3.js +2 -1
  39. package/dist/errors.d.ts +10 -0
  40. package/dist/{serpent/seal.js → errors.js} +14 -46
  41. package/dist/fortuna.d.ts +2 -8
  42. package/dist/fortuna.js +11 -9
  43. package/dist/index.d.ts +25 -9
  44. package/dist/index.js +36 -7
  45. package/dist/init.d.ts +3 -7
  46. package/dist/init.js +18 -35
  47. package/dist/keccak/embedded.d.ts +1 -0
  48. package/dist/keccak/embedded.js +27 -0
  49. package/dist/keccak/index.d.ts +4 -0
  50. package/dist/keccak/index.js +31 -0
  51. package/dist/kyber/embedded.d.ts +1 -0
  52. package/dist/kyber/embedded.js +27 -0
  53. package/dist/kyber/indcpa.d.ts +49 -0
  54. package/dist/kyber/indcpa.js +352 -0
  55. package/dist/kyber/index.d.ts +38 -0
  56. package/dist/kyber/index.js +150 -0
  57. package/dist/kyber/kem.d.ts +21 -0
  58. package/dist/kyber/kem.js +160 -0
  59. package/dist/kyber/params.d.ts +14 -0
  60. package/dist/kyber/params.js +37 -0
  61. package/dist/kyber/suite.d.ts +13 -0
  62. package/dist/kyber/suite.js +93 -0
  63. package/dist/kyber/types.d.ts +98 -0
  64. package/dist/kyber/types.js +25 -0
  65. package/dist/kyber/validate.d.ts +19 -0
  66. package/dist/kyber/validate.js +68 -0
  67. package/dist/kyber.wasm +0 -0
  68. package/dist/loader.d.ts +19 -4
  69. package/dist/loader.js +91 -25
  70. package/dist/serpent/cipher-suite.d.ts +4 -0
  71. package/dist/serpent/cipher-suite.js +121 -0
  72. package/dist/serpent/embedded.d.ts +1 -0
  73. package/dist/serpent/embedded.js +27 -0
  74. package/dist/serpent/index.d.ts +6 -37
  75. package/dist/serpent/index.js +9 -118
  76. package/dist/serpent/pool-worker.d.ts +1 -0
  77. package/dist/serpent/pool-worker.js +202 -0
  78. package/dist/serpent/serpent-cbc.d.ts +30 -0
  79. package/dist/serpent/serpent-cbc.js +136 -0
  80. package/dist/sha2/embedded.d.ts +1 -0
  81. package/dist/sha2/embedded.js +27 -0
  82. package/dist/sha2/hkdf.js +6 -2
  83. package/dist/sha2/index.d.ts +3 -2
  84. package/dist/sha2/index.js +3 -4
  85. package/dist/sha3/embedded.d.ts +1 -0
  86. package/dist/sha3/embedded.js +27 -0
  87. package/dist/sha3/index.d.ts +3 -2
  88. package/dist/sha3/index.js +3 -4
  89. package/dist/stream/constants.d.ts +6 -0
  90. package/dist/stream/constants.js +30 -0
  91. package/dist/stream/header.d.ts +9 -0
  92. package/dist/stream/header.js +77 -0
  93. package/dist/stream/index.d.ts +7 -0
  94. package/dist/stream/index.js +27 -0
  95. package/dist/stream/open-stream.d.ts +21 -0
  96. package/dist/stream/open-stream.js +146 -0
  97. package/dist/stream/seal-stream-pool.d.ts +38 -0
  98. package/dist/stream/seal-stream-pool.js +391 -0
  99. package/dist/stream/seal-stream.d.ts +20 -0
  100. package/dist/stream/seal-stream.js +142 -0
  101. package/dist/stream/seal.d.ts +9 -0
  102. package/dist/stream/seal.js +75 -0
  103. package/dist/stream/types.d.ts +24 -0
  104. package/dist/stream/types.js +26 -0
  105. package/dist/utils.d.ts +12 -7
  106. package/dist/utils.js +75 -19
  107. package/dist/wasm-source.d.ts +12 -0
  108. package/dist/wasm-source.js +26 -0
  109. package/package.json +13 -5
  110. package/dist/chacha20/pool.d.ts +0 -52
  111. package/dist/chacha20/pool.js +0 -188
  112. package/dist/chacha20/pool.worker.js +0 -37
  113. package/dist/docs/chacha20_pool.md +0 -309
  114. package/dist/docs/wasm.md +0 -194
  115. package/dist/serpent/seal.d.ts +0 -8
  116. package/dist/serpent/stream-pool.d.ts +0 -48
  117. package/dist/serpent/stream-pool.js +0 -285
  118. package/dist/serpent/stream-sealer.d.ts +0 -50
  119. package/dist/serpent/stream-sealer.js +0 -341
  120. package/dist/serpent/stream.d.ts +0 -28
  121. package/dist/serpent/stream.js +0 -205
  122. package/dist/serpent/stream.worker.d.ts +0 -32
  123. package/dist/serpent/stream.worker.js +0 -117
  124. /package/dist/chacha20/{pool.worker.d.ts → pool-worker.d.ts} +0 -0
@@ -1,22 +1,34 @@
1
- # Leviathan Crypto Library Architecture
1
+ # Architecture
2
2
 
3
3
  > [!NOTE]
4
- > - Version: 1.0.0
5
- > - Package: `leviathan-crypto` (npm, unscoped)
6
- > - Status: v1.0.0 - all four WASM modules (Serpent, ChaCha20, SHA-2, SHA-3) implemented.
7
- > - Supersedes: `leviathan` (TypeScript reference) and `leviathan-wasm` (WASM primitives),
8
- > both of which remain unchanged as development references.
4
+ > `leviathan-crypto` v2.0 packages five WASM modules (Serpent, ChaCha20, SHA-2, SHA-3, Kyber), generic streaming AEAD via CipherSuite, and worker pool parallelism. It supersedes `leviathan` (TypeScript reference) and `leviathan-wasm` (WASM primitives), both of which remain unchanged as development references.
5
+
6
+ > ### Table of Contents
7
+ > - [Vision](#vision)
8
+ > - [Scope](#scope)
9
+ > - [Repository Structure](#repository-structure)
10
+ > - [Architecture: TypeScript over WASM](#architecture-typescript-over-wasm)
11
+ > - [Five Independent WASM Modules](#five-independent-wasm-modules)
12
+ > - [init() API](#init-api)
13
+ > - [Public API Classes](#public-api-classes)
14
+ > - [Build Pipeline](#build-pipeline)
15
+ > - [Module Relationship Diagrams](#module-relationship-diagrams)
16
+ > - [npm Package](#npm-package)
17
+ > - [Buffer Layouts](#buffer-layouts)
18
+ > - [Test Suite](#test-suite)
19
+ > - [Correctness Contract](#correctness-contract)
20
+ > - [Known Limitations](#known-limitations)
21
+
22
+ ---
9
23
 
10
24
  ## Vision
11
25
 
12
26
  `leviathan-crypto` is a strictly-typed, audited WebAssembly cryptography library for
13
27
  the web. It combines two previously separate efforts:
14
28
 
15
- - **leviathan:** developer-friendly TypeScript API, strict types, audited against specs
16
- and known-answer test vectors
17
- - **leviathan-wasm:** AssemblyScript WASM implementation of the same primitives,
18
- running outside the JavaScript JIT for predictable execution and practical
19
- constant-time guarantees
29
+ **leviathan.** Developer-friendly TypeScript API, strict types, audited against specs and known-answer test vectors.
30
+
31
+ **leviathan-wasm.** AssemblyScript WASM implementation of the same primitives, running outside the JavaScript JIT for predictable execution and practical constant-time guarantees.
20
32
 
21
33
  The unified library exposes the TypeScript API from leviathan, backed by the WASM
22
34
  execution engine from leviathan-wasm. Developers get ergonomic, well-typed classes.
@@ -29,17 +41,19 @@ layer handles API ergonomics; the WASM layer handles all cryptographic computati
29
41
 
30
42
  ---
31
43
 
32
- ## Scope (v1.0)
44
+ ## Scope
33
45
 
34
46
  ### In scope
35
47
 
36
- | Module | Primitives |
37
- |--------|-----------|
38
- | `serpent` | Serpent-256 block cipher: ECB, CTR mode, CBC mode |
39
- | `serpent` + `sha2` | `SerpentSeal` (Serpent-CBC + HMAC-SHA256), `SerpentStream` / `SerpentStreamPool` (chunked AEAD), `SerpentStreamSealer` / `SerpentStreamOpener` (incremental streaming AEAD) |
40
- | `chacha20` | ChaCha20, Poly1305, ChaCha20-Poly1305 AEAD, XChaCha20-Poly1305 AEAD |
41
- | `sha2` | SHA-256, SHA-384, SHA-512, HMAC-SHA256, HMAC-SHA384, HMAC-SHA512, HKDF-SHA256, HKDF-SHA512 |
42
- | `sha3` | SHA3-224, SHA3-256, SHA3-384, SHA3-512, SHAKE128, SHAKE256 (XOFs, multi-squeeze) |
48
+ | Module | Primitives |
49
+ | ------------------ | ----------------------------------------------------------------------------------------------------------------------- |
50
+ | `serpent` | Serpent-256 block cipher: ECB, CTR mode, CBC mode |
51
+ | `serpent` + `sha2` | `SerpentCipher` (CipherSuite for STREAM construction: CBC+HMAC-SHA256) |
52
+ | `chacha20` | ChaCha20, Poly1305, ChaCha20-Poly1305 AEAD, XChaCha20-Poly1305 AEAD, `XChaCha20Cipher` (CipherSuite for streaming AEAD) |
53
+ | `sha2` | SHA-256, SHA-384, SHA-512, HMAC-SHA256, HMAC-SHA384, HMAC-SHA512, HKDF-SHA256, HKDF-SHA512 |
54
+ | `sha3` / `keccak` | SHA3-224, SHA3-256, SHA3-384, SHA3-512, SHAKE128, SHAKE256 (XOFs, multi-squeeze). `'keccak'` is an alias for `'sha3'`; same binary, same instance slot. |
55
+ | `kyber` | `MlKem512`, `MlKem768`, `MlKem1024`. Requires `sha3` for Keccak sponge operations. |
56
+ | `stream` | `SealStream`, `OpenStream` (cipher-agnostic STREAM construction), `SealStreamPool` (worker-based parallelism) |
43
57
 
44
58
  Pure TypeScript utilities (encoding helpers, random generation, format converters)
45
59
  ship alongside the WASM-backed primitives with no `init()` dependency.
@@ -47,27 +61,31 @@ ship alongside the WASM-backed primitives with no `init()` dependency.
47
61
  ### Auxiliary tier (not part of `Module` union)
48
62
 
49
63
  - **`Fortuna`:** CSPRNG requiring two core modules (`serpent` + `sha2`).
50
- Initialized via the standard `init()` gate.
51
64
 
52
65
  ---
53
66
 
54
67
  ## Repository Structure
55
68
 
56
-
57
- ```
69
+ ```text
58
70
  leviathan-crypto/
71
+ ├── .github/
72
+ │ └── workflows/ ← CI: build, test-suite, e2e, publish, release, wiki
59
73
  ├── src/
60
74
  │ ├── asm/ ← AssemblyScript (compiles to .wasm)
61
75
  │ │ ├── serpent/
62
76
  │ │ │ ├── index.ts ← asc entry point → serpent.wasm
63
77
  │ │ │ ├── serpent.ts ← block function + key schedule
64
78
  │ │ │ ├── serpent_unrolled.ts ← unrolled S-boxes and round functions
79
+ │ │ │ ├── serpent_simd.ts ← SIMD bitsliced block operations
65
80
  │ │ │ ├── cbc.ts ← CBC mode
81
+ │ │ │ ├── cbc_simd.ts ← SIMD CBC decrypt
66
82
  │ │ │ ├── ctr.ts ← CTR mode
83
+ │ │ │ ├── ctr_simd.ts ← SIMD CTR 4-wide inter-block
67
84
  │ │ │ └── buffers.ts ← static buffer layout + offset getters
68
85
  │ │ ├── chacha20/
69
86
  │ │ │ ├── index.ts ← asc entry point → chacha20.wasm
70
87
  │ │ │ ├── chacha20.ts
88
+ │ │ │ ├── chacha20_simd_4x.ts ← SIMD 4-wide inter-block ChaCha20
71
89
  │ │ │ ├── poly1305.ts
72
90
  │ │ │ ├── wipe.ts
73
91
  │ │ │ └── buffers.ts
@@ -78,40 +96,78 @@ leviathan-crypto/
78
96
  │ │ │ ├── hmac.ts
79
97
  │ │ │ ├── hmac512.ts
80
98
  │ │ │ └── buffers.ts
81
- │ │ └── sha3/
99
+ │ │ ├── sha3/
82
100
  │ │ ├── index.ts ← asc entry point → sha3.wasm
83
101
  │ │ ├── keccak.ts
84
102
  │ │ └── buffers.ts
103
+ │ │ ├── ct/
104
+ │ │ │ └── index.ts ← asc entry point → ct.wasm (SIMD constant-time compare)
105
+ │ │ └── kyber/
106
+ │ │ ├── index.ts ← asc entry point → kyber.wasm
107
+ │ │ ├── ntt.ts ← NTT/invNTT (scalar reference + zetas table)
108
+ │ │ ├── ntt_simd.ts ← SIMD NTT/invNTT (v128 butterflies, fqmul_8x, barrett_reduce_8x)
109
+ │ │ ├── reduce.ts ← Montgomery/Barrett reduction, fqmul
110
+ │ │ ├── poly.ts ← polynomial serialization, compression, arithmetic, basemul
111
+ │ │ ├── poly_simd.ts ← SIMD poly add/sub/reduce/ntt wrappers
112
+ │ │ ├── polyvec.ts ← k-wide polyvec operations
113
+ │ │ ├── cbd.ts ← centered binomial distribution (η=2, η=3)
114
+ │ │ ├── sampling.ts ← uniform rejection sampling
115
+ │ │ ├── verify.ts ← constant-time compare and conditional move
116
+ │ │ ├── params.ts ← Q, QINV, MONT, Barrett/compression constants
117
+ │ │ └── buffers.ts ← static buffer layout + offset getters
85
118
  │ └── ts/ ← TypeScript (public API)
86
119
  │ ├── init.ts ← initModule() : WASM loading and module cache
87
- │ ├── loader.ts ← embedded / streaming / manual loading logic
120
+ │ ├── loader.ts ← loadWasm() / compileWasm() : polymorphic WasmSource dispatch
121
+ │ ├── wasm-source.ts ← WasmSource union type
122
+ │ ├── errors.ts ← AuthenticationError
88
123
  │ ├── types.ts ← Hash, KeyedHash, Blockcipher, Streamcipher, AEAD
89
124
  │ ├── utils.ts ← encoding, wipe, xor, concat, randomBytes
90
125
  │ ├── fortuna.ts ← Fortuna CSPRNG (requires serpent + sha2)
91
- │ ├── embedded/ ← generated base64 files (gitignored, build artifact)
126
+ │ ├── embedded/ ← generated gzip+base64 blobs (build artifact, gitignored)
92
127
  │ │ ├── serpent.ts
93
128
  │ │ ├── chacha20.ts
94
129
  │ │ ├── sha2.ts
95
130
  │ │ └── sha3.ts
96
131
  │ ├── serpent/
97
132
  │ │ ├── index.ts ← serpentInit() + Serpent, SerpentCtr, SerpentCbc
98
- │ │ ├── seal.ts SerpentSeal (Serpent-CBC + HMAC-SHA256)
99
- │ │ ├── stream.ts SerpentStream (chunked one-shot AEAD)
100
- │ │ ├── stream-pool.ts SerpentStreamPool (Worker-based parallel AEAD)
101
- │ │ ├── stream-sealer.ts ← SerpentStreamSealer / SerpentStreamOpener (incremental AEAD)
102
- │ │ ├── stream.worker.ts ← Web Worker entry point for SerpentStreamPool
133
+ │ │ ├── cipher-suite.ts SerpentCipher (CipherSuite for STREAM construction)
134
+ │ │ ├── pool-worker.ts Web Worker for SealStreamPool with SerpentCipher
135
+ │ │ ├── embedded.ts re-exports gzip+base64 blob as named export
103
136
  │ │ └── types.ts
104
137
  │ ├── chacha20/
105
- │ │ ├── index.ts ← chacha20Init() + ChaCha20, Poly1305, ChaCha20Poly1305, XChaCha20Poly1305
106
- │ │ ├── pool.ts XChaCha20Poly1305Pool + PoolOpts
107
- │ │ ├── pool.worker.ts Web Worker entry point (compiled to pool.worker.js; not a subpath export)
138
+ │ │ ├── index.ts ← chacha20Init() + ChaCha20, Poly1305, ChaCha20Poly1305, XChaCha20Poly1305, XChaCha20Cipher
139
+ │ │ ├── ops.ts raw AEAD functions shared by classes and pool worker
140
+ │ │ ├── cipher-suite.ts XChaCha20Cipher (CipherSuite for STREAM construction)
141
+ │ │ ├── pool-worker.ts ← Web Worker for SealStreamPool with XChaCha20Cipher
142
+ │ │ ├── embedded.ts ← re-exports gzip+base64 blob as named export
108
143
  │ │ └── types.ts
109
144
  │ ├── sha2/
110
- │ │ ├── index.ts ← sha2Init() + SHA256, SHA512, SHA384, HMAC_SHA256, HMAC_SHA512, HMAC_SHA384, HKDF_SHA256, HKDF_SHA512
145
+ │ │ ├── index.ts ← sha2Init() + SHA256, SHA512, SHA384, HMAC, HKDF
146
+ │ │ ├── hkdf.ts ← HKDF_SHA256, HKDF_SHA512 (pure TS over HMAC)
147
+ │ │ ├── embedded.ts ← re-exports gzip+base64 blob as named export
111
148
  │ │ └── types.ts
112
149
  │ ├── sha3/
113
- │ │ ├── index.ts ← sha3Init() + SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE128, SHAKE256
150
+ │ │ ├── index.ts ← sha3Init() + SHA3_224–512, SHAKE128, SHAKE256
151
+ │ │ ├── embedded.ts ← re-exports gzip+base64 blob as named export
114
152
  │ │ └── types.ts
153
+ │ ├── kyber/
154
+ │ │ ├── index.ts ← kyberInit() + MlKem512, MlKem768, MlKem1024, KyberSuite
155
+ │ │ ├── kem.ts ← Fujisaki-Okamoto transform (keygen, encaps, decaps)
156
+ │ │ ├── suite.ts ← KyberSuite factory (hybrid KEM+AEAD CipherSuite)
157
+ │ │ ├── indcpa.ts ← IND-CPA encrypt/decrypt + matrix generation
158
+ │ │ ├── validate.ts ← key validation (FIPS 203 §7.2, §7.3)
159
+ │ │ ├── params.ts ← parameter sets (MLKEM512, MLKEM768, MLKEM1024)
160
+ │ │ ├── types.ts ← KyberExports, Sha3Exports, KEM API types
161
+ │ │ └── embedded.ts ← re-exports gzip+base64 blob as kyberWasm
162
+ │ ├── stream/
163
+ │ │ ├── index.ts ← barrel: Seal, SealStream, OpenStream, SealStreamPool, constants
164
+ │ │ ├── seal.ts ← Seal (static one-shot AEAD)
165
+ │ │ ├── seal-stream.ts ← SealStream (cipher-agnostic streaming encryption)
166
+ │ │ ├── open-stream.ts ← OpenStream (cipher-agnostic streaming decryption)
167
+ │ │ ├── seal-stream-pool.ts ← SealStreamPool (worker-based parallel batch)
168
+ │ │ ├── header.ts ← wire format header encode/decode, counter nonce
169
+ │ │ ├── constants.ts ← HEADER_SIZE, CHUNK_MIN/MAX, TAG_DATA/FINAL, FLAG_FRAMED
170
+ │ │ └── types.ts ← CipherSuite, DerivedKeys, SealStreamOpts
115
171
  │ └── index.ts ← root barrel : dispatching init() + re-exports everything
116
172
  ├── test/
117
173
  │ ├── unit/ ← Vitest (JS target, fast iteration)
@@ -119,42 +175,44 @@ leviathan-crypto/
119
175
  │ │ ├── chacha20/
120
176
  │ │ ├── sha2/
121
177
  │ │ ├── sha3/
122
- │ │ └── init.test.ts
178
+ │ │ ├── stream/ ← SealStream, OpenStream, SealStreamPool tests
179
+ │ │ ├── loader/ ← WasmSource loading tests
180
+ │ │ ├── init.test.ts
181
+ │ │ ├── errors.test.ts
182
+ │ │ ├── fortuna.test.ts
183
+ │ │ └── utils.test.ts
123
184
  │ ├── e2e/ ← Playwright (WASM target, cross-browser)
124
185
  │ └── vectors/ ← test vector files (read-only reference data)
125
- ├── build/ ← WASM build output (gitignored)
126
- ├── dist/ ← npm publish output (gitignored)
127
- ├── docs/ ← project documentation
128
186
  ├── scripts/
129
- │ ├── embed-wasm.ts ← reads build/*.wasm, generates src/ts/embedded/*.ts
130
187
  │ ├── build-asm.js ← orchestrates AssemblyScript compilation
131
- │ ├── gen-seal-vectors.ts generates KAT vectors for SerpentSeal / SerpentStream
132
- └── gen-sealstream-vectors.ts ← generates KAT vectors for SerpentStreamSealer / Opener
188
+ │ ├── embed-wasm.ts reads build/*.wasm, generates src/ts/embedded/*.ts
189
+ ├── gen-seal-vectors.ts ← generates KAT vectors for Seal
190
+ │ ├── gen-sealstream-vectors.ts ← generates KAT vectors for SealStream
191
+ │ ├── generate_simd.ts ← generates SIMD assembly variants
192
+ │ ├── gen-changelog.ts ← changelog generation
193
+ │ ├── copy-docs.ts ← copies docs subset to dist/
194
+ │ └── pin-actions.ts ← pins GitHub Actions to SHA hashes
195
+ ├── docs/ ← project documentation + wiki source
133
196
  ├── package.json
134
197
  ├── asconfig.json ← four AssemblyScript entry points
135
198
  ├── tsconfig.json
136
199
  ├── vitest.config.ts
137
- └── playwright.config.ts
200
+ ├── playwright.config.ts
201
+ ├── AGENTS.md ← ai agent contract
202
+ └── SECURITY.md
138
203
  ```
139
204
 
140
205
  ---
141
206
 
142
207
  ## Architecture: TypeScript over WASM
143
208
 
209
+ 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.
144
210
 
145
- > [!NOTE]
146
- > The TypeScript layer never implements cryptographic algorithms. It handles the
147
- > JS/WASM boundary: writing inputs into WASM linear memory, calling exported
148
- > functions, reading outputs back. All algorithm logic lives in AssemblyScript.
149
- >
150
- > The exception is the Tier 2 composition layer: `SerpentSeal`, `SerpentStream`,
151
- > `SerpentStreamPool`, `SerpentStreamSealer`, `SerpentStreamOpener`, and `HKDF`.
152
- > These are pure TypeScript, they compose the WASM-backed Tier 1 primitives
153
- > (Serpent-CBC, HMAC-SHA256, HKDF-SHA256) without adding new algorithm logic.
211
+ 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.
154
212
 
155
213
  ---
156
214
 
157
- ## Four Independent WASM Modules
215
+ ## Five Independent WASM Modules
158
216
 
159
217
  Each primitive family compiles to its own `.wasm` binary. Modules are fully
160
218
  independent, separate linear memories, separate buffer layouts, no shared state.
@@ -165,11 +223,11 @@ independent, separate linear memories, separate buffer layouts, no shared state.
165
223
  | `chacha20` | `chacha20.wasm` | ChaCha20, Poly1305, ChaCha20-Poly1305 AEAD, XChaCha20-Poly1305 AEAD |
166
224
  | `sha2` | `sha2.wasm` | SHA-256, SHA-384, SHA-512, HMAC-SHA256, HMAC-SHA384, HMAC-SHA512 |
167
225
  | `sha3` | `sha3.wasm` | SHA3-224, SHA3-256, SHA3-384, SHA3-512, SHAKE128, SHAKE256 |
226
+ | `kyber` | `kyber.wasm` | ML-KEM polynomial arithmetic: SIMD NTT/invNTT (v128 butterflies with scalar tail), basemul, Montgomery/Barrett, CBD, compress, CT verify/cmov |
227
+
228
+ **Size.** Consumers who only use Serpent don't load the SHA-3 binary.
168
229
 
169
- **Benefits:**
170
- 1. **Size:** consumers who only use Serpent don't load the SHA-3 binary
171
- 2. **Isolation:** key material in `serpent.wasm` memory cannot bleed into
172
- `sha3.wasm` memory even in theory
230
+ **Isolation.** Key material in `serpent.wasm` memory cannot bleed into `sha3.wasm` memory even in theory.
173
231
 
174
232
  Each module's buffer layout starts at offset 0 and is defined in its own
175
233
  `buffers.ts`. Buffer layouts are fully independent across modules.
@@ -178,26 +236,23 @@ Each module's buffer layout starts at offset 0 and is defined in its own
178
236
 
179
237
  **`serpent.wasm`**
180
238
  Serpent-256 block cipher. Key schedule, block encrypt, block decrypt. CTR mode
181
- chunked streaming encrypt/decrypt. CBC mode chunked encrypt/decrypt.
239
+ chunked streaming encrypt/decrypt. CBC mode chunked encrypt/decrypt. SIMD
240
+ variants for CTR 4-wide inter-block and CBC decrypt parallelism.
182
241
  Source: `src/asm/serpent/`
183
242
 
184
- The serpent TypeScript module also includes a Tier 2 composition layer built on
185
- top of these WASM primitives: `SerpentSeal` (Serpent-CBC + HMAC-SHA256 AEAD),
186
- `SerpentStream` (chunked one-shot AEAD), `SerpentStreamPool` (Worker-based
187
- parallel AEAD), and `SerpentStreamSealer` / `SerpentStreamOpener` (incremental
188
- streaming AEAD). All Tier 2 classes use HKDF-SHA256 for per-chunk key derivation
189
- and require both `serpent` and `sha2` to be initialized.
243
+ The serpent TypeScript module includes `SerpentCipher` (CipherSuite implementation
244
+ for the STREAM construction: Serpent-CBC + HMAC-SHA256 with HKDF key derivation).
245
+ Requires `serpent` and `sha2` to be initialized.
190
246
 
191
247
  **`chacha20.wasm`**
192
248
  ChaCha20 stream cipher (RFC 8439). Poly1305 MAC (RFC 8439 §2.5). ChaCha20-Poly1305
193
249
  AEAD (RFC 8439 §2.8). XChaCha20-Poly1305 AEAD (draft-irtf-cfrg-xchacha).
194
- HChaCha20 subkey derivation.
250
+ HChaCha20 subkey derivation. SIMD 4-wide inter-block parallelism.
195
251
  Source: `src/asm/chacha20/`
196
252
 
197
- The chacha20 TypeScript module also includes `pool.ts` (`XChaCha20Poly1305Pool`)
198
- and `pool.worker.ts`. The worker file compiles to `dist/chacha20/pool.worker.js`
199
- and ships in the package, but it is **not** registered in the `exports` map, it
200
- is an internal file loaded by the pool at runtime, not a public named subpath.
253
+ The chacha20 TypeScript module includes `XChaCha20Cipher` (CipherSuite implementation
254
+ for the STREAM construction: XChaCha20-Poly1305 with HKDF key derivation).
255
+ Pool workers are internal, loaded by `SealStreamPool` at runtime, not registered in the package exports map.
201
256
 
202
257
  **`sha2.wasm`**
203
258
  SHA-256 and SHA-512 (FIPS 180-4). SHA-384 (SHA-512 with different IVs, truncated
@@ -213,90 +268,88 @@ All six variants share one permutation, differing only in rate, domain
213
268
  separation byte, and output length.
214
269
  Source: `src/asm/sha3/`
215
270
 
271
+ **`kyber.wasm`**
272
+ ML-KEM (FIPS 203) polynomial arithmetic. Montgomery and Barrett reduction,
273
+ 7-layer NTT and inverse NTT, basemul in Z_q[X]/(X²-ζ), centered binomial
274
+ distribution sampling (η=2, η=3), division-free compression/decompression
275
+ (all 5 bit-width paths: 4, 5, 10, 11, 1-bit), rejection sampling for matrix
276
+ generation, constant-time byte comparison and conditional move. Requires
277
+ WebAssembly SIMD (`v128` instructions) for NTT and polynomial arithmetic.
278
+ 3 pages (192 KB) linear memory with 10 poly slots, 8 polyvec slots, and
279
+ dedicated byte buffers for keys/ciphertexts.
280
+ Source: `src/asm/kyber/`
281
+
282
+ The kyber TypeScript module includes `MlKem512`, `MlKem768`, `MlKem1024`
283
+ (KEM classes implementing the Fujisaki-Okamoto transform). All require both
284
+ `kyber` and `sha3` to be initialized; the sha3 module provides the Keccak sponge (SHAKE128 for matrix gen, SHAKE256 for noise/J function, SHA3-256 for H, SHA3-512 for G).
285
+
216
286
  ---
217
287
 
218
288
  ## `init()` API
219
289
 
220
- WASM instantiation is async. `init()` is the explicit initialization gate,
221
- it must be called once before any cryptographic class is used. This is honest
222
- about the initialization cost and gives the developer control over when it is paid.
290
+ WASM instantiation is async. `init()` is the initialization gate, call it once before using any cryptographic class. The cost is explicit and the developer controls when it is paid.
223
291
 
224
292
  ### Signature
225
293
 
226
294
  ```typescript
227
- type Module = 'serpent' | 'chacha20' | 'sha2' | 'sha3'
228
- type Mode = 'embedded' | 'streaming' | 'manual'
295
+ type Module = 'serpent' | 'chacha20' | 'sha2' | 'sha3' | 'keccak' | 'kyber'
229
296
 
230
- interface InitOpts {
231
- wasmUrl?: URL | string
232
- wasmBinary?: Record<Module, Uint8Array | ArrayBuffer>
233
- }
297
+ type WasmSource =
298
+ | string // gzip+base64 embedded blob
299
+ | URL // fetch + compileStreaming
300
+ | ArrayBuffer // compile from raw bytes
301
+ | Uint8Array // compile from raw bytes
302
+ | WebAssembly.Module // pre-compiled (edge runtimes)
303
+ | Response // instantiateStreaming
304
+ | Promise<Response> // deferred fetch
234
305
 
235
306
  async function init(
236
- modules: Module | Module[],
237
- mode?: Mode,
238
- opts?: InitOpts
307
+ sources: Partial<Record<Module, WasmSource>>,
239
308
  ): Promise<void>
240
309
  ```
241
310
 
242
- ### Three loading modes
243
-
244
- **`'embedded'` (default: zero-config)**
245
- The `.wasm` binary is base64-encoded and inlined in the published package as a
246
- generated TypeScript file (`src/ts/embedded/*.ts`). At runtime, the base64 string
247
- is decoded and passed to `WebAssembly.instantiate()`. Works everywhere with no
248
- bundler configuration. ~33% size overhead from base64 encoding. Cannot use
249
- streaming compilation.
311
+ The loading strategy is inferred from the source type, so there is no need
312
+ for a mode string. Each module also exports its own init function, such as
313
+ `serpentInit(source)`, `chacha20Init(source)`, `sha2Init(source)`,
314
+ `sha3Init(source)`, `keccakInit(source)`, and `kyberInit(source)`,
315
+ enabling tree-shakeable imports.
250
316
 
251
- ```typescript
252
- await init(['serpent', 'sha3'])
253
- ```
317
+ > [!NOTE]
318
+ > **`'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.
254
319
 
255
- **`'streaming'` (performance path)**
256
- Uses `WebAssembly.instantiateStreaming()` for maximum load performance. The
257
- browser compiles the WASM binary while still downloading it. `wasmUrl` is a
258
- base URL, the loader appends the filename (`serpent.wasm`, `chacha20.wasm`, etc.).
259
- Requires the `.wasm` files to be served with `Content-Type: application/wasm`.
320
+ ### Embedded subpath exports
260
321
 
322
+ Each module provides a `/embedded` subpath that exports the gzip+base64
323
+ blob as a ready-to-use `WasmSource`:
261
324
  ```typescript
262
- await init(['serpent', 'sha3'], 'streaming', { wasmUrl: '/assets/wasm/' })
263
- // loads: /assets/wasm/serpent.wasm, /assets/wasm/sha3.wasm
264
- ```
325
+ import { init } from 'leviathan-crypto'
326
+ import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
327
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
265
328
 
266
- **`'manual'` (custom loading)**
267
- Caller provides the compiled binary directly as a `Uint8Array` or `ArrayBuffer`.
268
- For environments with custom loading requirements (CDN, service worker cache,
269
- non-standard fetch).
270
-
271
- ```typescript
272
- await init(['chacha20'], 'manual', {
273
- wasmBinary: { chacha20: myBuffer }
274
- })
329
+ await init({ serpent: serpentWasm, sha2: sha2Wasm })
275
330
  ```
276
331
 
277
332
  ### Behavioral contracts
278
333
 
279
- **Idempotent.** Calling `init()` for a module that is already initialized is a
280
- no-op. Safe to call from multiple modules in a codebase.
334
+ **Idempotent initialization.** Calling `init()` on an already initialized
335
+ module is a no-op. It is safe to call `init()` from multiple locations
336
+ within the codebase.
281
337
 
282
- **Module-scope cache.** The compiled `WebAssembly.Module` for each binary is
283
- cached at module scope after first compilation. All subsequent class instantiations
284
- use `WebAssembly.instantiate(cachedModule)`, fast, no recompilation.
338
+ **Module-scope cache.** Each `WebAssembly.Instance` is cached at module
339
+ scope after initial instantiation. All subsequent class constructions use
340
+ the cached instance with no recompilation.
285
341
 
286
- **Error before init.** Calling any cryptographic class before `init()` throws:
287
- ```
288
- leviathan-crypto: call init(['<module>']) before using <ClassName>
289
- ```
342
+ **Error before initialization.** Invoking any cryptographic class before
343
+ calling `init()` throws a clear error prompting the developer to call
344
+ `init({ <module>: ... })` first.
290
345
 
291
- **No lazy auto-init.** Classes never silently call `init()` on first use.
292
- Hidden initialization costs are worse than explicit ones.
346
+ **No implicit initialization.** Classes never call `init()` automatically
347
+ on first use. Explicit initialization is preferable to hidden costs.
293
348
 
294
- **Thread safety.** v1.0 uses a single WASM module instance per binary, single
295
- thread. WASM linear memory is not shared across Workers without `SharedArrayBuffer`
296
- (which requires COOP/COEP headers). Two pool classes provide Worker-based
297
- parallelism: `SerpentStreamPool` (chunked authenticated Serpent encryption) and
298
- `XChaCha20Poly1305Pool` (AEAD). Each pool worker owns its own WASM instances with
299
- isolated linear memory. For other primitives: create one instance per Worker if
349
+ **Thread safety.** The main thread uses a single WASM instance per module.
350
+ `SealStreamPool` provides worker-based parallelism. Each pool worker owns
351
+ its own WASM instances with isolated linear memory, bypassing the main-thread
352
+ cache entirely. For other primitives, create one instance per Worker if
300
353
  Workers are used.
301
354
 
302
355
  ---
@@ -307,33 +360,36 @@ Names match conventional cryptographic notation.
307
360
 
308
361
  | Module | Classes |
309
362
  |--------|---------|
310
- | `serpent` + `sha2` | `SerpentSeal`, `SerpentStream`, `SerpentStreamPool`, `SerpentStreamSealer`, `SerpentStreamOpener` |
363
+ | `serpent` + `sha2` | `SerpentCipher` |
311
364
  | `serpent` | `Serpent`, `SerpentCtr`, `SerpentCbc` |
312
- | `chacha20` | `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `XChaCha20Poly1305Pool` |
365
+ | `chacha20` | `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `XChaCha20Cipher` |
313
366
  | `sha2` | `SHA256`, `SHA384`, `SHA512`, `HMAC_SHA256`, `HMAC_SHA384`, `HMAC_SHA512`, `HKDF_SHA256`, `HKDF_SHA512` |
314
367
  | `sha3` | `SHA3_224`, `SHA3_256`, `SHA3_384`, `SHA3_512`, `SHAKE128`, `SHAKE256` |
368
+ | `kyber` + `sha3` | `MlKem512`, `MlKem768`, `MlKem1024` |
369
+ | `kyber` + `sha3` + inner cipher | `KyberSuite` (hybrid KEM+AEAD factory) |
370
+ | `stream` | `Seal`, `SealStream`, `OpenStream`, `SealStreamPool` |
315
371
  | `serpent` + `sha2` | `Fortuna` |
316
372
 
317
373
  HMAC names use underscore separator (`HMAC_SHA256`) matching RFC convention.
318
374
  SHA-3 names use underscore separator (`SHA3_256`) for readability.
375
+ `SealStream`, `OpenStream`, and `SealStreamPool` are cipher-agnostic; you select the cipher by passing `XChaCha20Cipher` or `SerpentCipher` at construction.
319
376
 
320
377
  **`Fortuna`** requires `await Fortuna.create()` rather than `new Fortuna()` due
321
- to the async `init()` dependency on two modules. It requires both `serpent` and
322
- `sha2` to be initialized. In Node.js, Fortuna collects additional entropy from
323
- `process.hrtime`, `process.cpuUsage`, `process.memoryUsage`, `os.loadavg`,
324
- and `os.freemem` in addition to `crypto.randomBytes`.
378
+ to the async `init()` dependency on two modules.
325
379
 
326
380
  ### Usage pattern
327
381
 
328
382
  All WASM-backed classes follow the same pattern:
329
-
330
383
  ```typescript
331
- import { init, SerpentSeal, SHA3_256 } from 'leviathan-crypto'
384
+ import { init, Seal, SerpentCipher, SHA3_256 } from 'leviathan-crypto'
385
+ import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
386
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
387
+ import { sha3Wasm } from 'leviathan-crypto/sha3/embedded'
332
388
 
333
- await init(['serpent', 'sha2', 'sha3'])
389
+ await init({ serpent: serpentWasm, sha2: sha2Wasm, sha3: sha3Wasm })
334
390
 
335
- const seal = new SerpentSeal()
336
- const ciphertext = seal.encrypt(key, plaintext) // throws on tamper at decrypt
391
+ const key = SerpentCipher.keygen()
392
+ const blob = Seal.encrypt(SerpentCipher, key, plaintext)
337
393
 
338
394
  const hasher = new SHA3_256()
339
395
  const digest = hasher.hash(message)
@@ -354,35 +410,33 @@ Pure TypeScript utilities ship alongside the WASM-backed primitives:
354
410
 
355
411
  ## Build Pipeline
356
412
 
413
+ 1. `npm run build:asm`: AssemblyScript compiler reads `src/asm/*/index.ts`, emits `build/*.wasm`
414
+ 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`
415
+ 3. `npm run build:ts`: TypeScript compiler emits `dist/`
416
+ 4. `cp build/*.wasm dist/`: WASM binaries copied for URL-based consumers
417
+ 5. At runtime (subpath): `serpentInit(serpentWasm)` → `initModule()` → `loadWasm(source)` → decode gzip+base64 → `WebAssembly.instantiate` → cache in `init.ts`
418
+ 6. At runtime (root): `init({ serpent: serpentWasm, sha2: sha2Wasm })` → dispatches to each module's init function via `Promise.all` → same path as step 5 per module
357
419
 
358
- **Step by step:**
359
-
360
- 1. `npm run build:asm` — AssemblyScript compiler reads `src/asm/*/index.ts`, emits `build/*.wasm`
361
- 2. `npm run build:embed` — `scripts/embed-wasm.ts` reads each `.wasm`, writes base64 to `src/ts/embedded/*.ts`
362
- 3. `npm run build:ts` — TypeScript compiler emits `dist/`
363
- 4. `cp build/*.wasm dist/` — WASM binaries copied for streaming mode consumers
364
- 5. At runtime (subpath): `serpent/index.ts:serpentInit()` → `initModule()` → `loadEmbedded(thunk)` → `thunk()` → dynamic-import `embedded/serpent.ts` → decode base64 → `WebAssembly.instantiate` → cache in `init.ts`
365
- 6. At runtime (root): `index.ts:init(['serpent', 'sha3'])` → dispatches to each module's init function (`serpentInit`, `sha3Init`) via `Promise.all` → same path as step 5 per module
366
-
367
- `src/ts/embedded/` is gitignored, these files are a build artifacts derived from the WASM
368
- binaries. There is one source of truth for each binary: the AssemblyScript source.
420
+ `src/ts/embedded/` is gitignored; these files are build artifacts derived from the WASM binaries. There is one source of truth for each binary: the AssemblyScript source.
369
421
 
370
422
  ---
371
423
 
372
424
  ## Module Relationship Diagrams
373
425
 
374
- ### ASM layer internal import graph
426
+ ### ASM layer: internal import graph
375
427
 
376
428
  Each WASM module is fully independent. No cross-module imports exist.
377
429
 
378
430
  **Serpent (`src/asm/serpent/`)**
379
-
380
431
  ```
381
432
  buffers.ts
382
433
  <- serpent.ts (offsets for key, block, subkey, work, CBC IV)
383
434
  <- serpent_unrolled.ts (block offsets, subkey, work)
435
+ <- serpent_simd.ts (SIMD bitsliced block operations)
384
436
  <- cbc.ts (IV, block, chunk offsets)
437
+ <- cbc_simd.ts (SIMD CBC decrypt)
385
438
  <- ctr.ts (nonce, counter, block, chunk offsets)
439
+ <- ctr_simd.ts (SIMD CTR 4-wide inter-block)
386
440
 
387
441
  serpent.ts
388
442
  <- serpent_unrolled.ts (S-boxes sb0-sb7, si0-si7, lk, kl, keyXor)
@@ -391,20 +445,24 @@ serpent_unrolled.ts
391
445
  <- cbc.ts (encryptBlock_unrolled, decryptBlock_unrolled)
392
446
  <- ctr.ts (encryptBlock_unrolled)
393
447
 
448
+ serpent_simd.ts
449
+ <- cbc_simd.ts (SIMD block operations)
450
+ <- ctr_simd.ts (SIMD block operations)
451
+
394
452
  index.ts
395
- re-exports: buffers + serpent + serpent_unrolled + cbc + ctr
453
+ re-exports: buffers + serpent + serpent_unrolled + serpent_simd + cbc + cbc_simd + ctr + ctr_simd
396
454
  ```
397
455
 
398
456
  **ChaCha (`src/asm/chacha20/`)**
399
-
400
457
  ```
401
458
  buffers.ts
402
459
  <- chacha20.ts (key, nonce, counter, block, state, poly key, xchacha offsets)
460
+ <- chacha20_simd_4x.ts (SIMD work buffer, chunk offsets)
403
461
  <- poly1305.ts (poly key, msg, buf, tag, h, r, rs, s offsets)
404
462
  <- wipe.ts (all buffer offsets, zeroes everything)
405
463
 
406
464
  index.ts
407
- re-exports: buffers + chacha20 + poly1305 + wipe
465
+ re-exports: buffers + chacha20 + chacha20_simd_4x + poly1305 + wipe
408
466
  ```
409
467
 
410
468
  **SHA-2 (`src/asm/sha2/`)**
@@ -437,99 +495,111 @@ index.ts
437
495
  re-exports: buffers + keccak
438
496
  ```
439
497
 
440
- ### TS layer — internal import graph
498
+ **Kyber (`src/asm/kyber/`)**
499
+
500
+ ```
501
+ params.ts
502
+ <- reduce.ts (Q, QINV, BARRETT_V, BARRETT_SHIFT)
503
+ <- poly.ts (Q, POLY_BYTES, HALF_Q, compression constants)
504
+ <- polyvec.ts (Q, POLY_BYTES, compression constants)
505
+ <- sampling.ts (Q)
506
+
507
+ buffers.ts
508
+ <- polyvec.ts (POLY_ACC_OFFSET)
509
+
510
+ reduce.ts
511
+ <- ntt.ts (fqmul, barrett_reduce)
512
+ <- ntt_simd.ts (fqmul, barrett_reduce — scalar tail)
513
+ <- poly.ts (montgomery_reduce, barrett_reduce, fqmul)
514
+
515
+ ntt.ts
516
+ <- ntt_simd.ts (getZetasOffset — zetas table pointer)
517
+ <- poly.ts (ntt, invntt, basemul, getZeta)
518
+
519
+ ntt_simd.ts
520
+ <- poly_simd.ts (ntt_simd, invntt_simd, barrett_reduce_8x)
521
+
522
+ poly.ts
523
+ <- polyvec.ts (poly_tobytes, poly_frombytes, poly_basemul_montgomery)
524
+
525
+ poly_simd.ts
526
+ <- polyvec.ts (poly_add_simd, poly_reduce_simd, poly_ntt_simd, poly_invntt_simd)
527
+
528
+ cbd.ts
529
+ <- poly.ts (cbd2, cbd3)
530
+
531
+ index.ts
532
+ re-exports: buffers + ntt (scalar aliases) + ntt_simd (as ntt/invntt) +
533
+ reduce + poly (scalar serialization/compression/basemul) +
534
+ poly_simd (as poly_add/sub/reduce/ntt/invntt) +
535
+ polyvec + sampling + verify
536
+ ```
537
+
538
+ ---
539
+
540
+ ### TS layer: internal import graph
441
541
 
442
542
  ```
443
- +---------------------+
444
- | index.ts | <- barrel: dispatching init()
445
- | (public API root) | + re-exports everything
446
- +--+--+--+--+--+--+--+
447
- | | | | | |
448
- +-------------------------+ | | | | +----------------------+
449
- | +----------------+ | | +----------+ |
450
- v v v v v v
451
- serpent/ chacha20/ sha2/ sha3/ fortuna.ts types.ts
452
- index.ts index.ts index.ts index.ts utils.ts
453
- | | | | | | | | |
454
- | | | | | | | | +-> init.ts (isInitialized)
455
- | | | +-> utils.ts | | | | +-> serpent/index.ts (Serpent)
456
- | | | | (constantTime| | | | +-> sha2/index.ts (SHA256)
457
- | | | | Equal) | | | | +-> utils.ts (wipe, concat,
458
- | | | | | | | | utf8ToBytes)
459
- | | | +-> chacha20/ | | | |
460
- | | | | types.ts | | | |
461
- | | | | | | | |
462
- | +----------+--+--+------------+--+----+--+--> init.ts <-- getInstance()
463
- | | | | | initModule()
464
- | | | | | isInitialized()
465
- v v v v v
466
- embedded/ embedded/ embedded/ embedded/
467
- serpent.ts chacha20.ts sha2.ts sha3.ts
468
- (each module owns its own embedded thunk, no cross-module imports)
543
+ +---------------------+
544
+ | index.ts | <- barrel: dispatching init()
545
+ | (public API root) | + re-exports everything
546
+ +--+--+--+--+--+--+--+
547
+ | | | | | |
548
+ +-------------------------+ | | | | +----------------------+
549
+ | +----------------+ | | +----------+ |
550
+ v v v v v v
551
+ serpent/ chacha20/ sha2/ sha3/ fortuna.ts stream/
552
+ index.ts index.ts index.ts index.ts index.ts
553
+ | | | | | | |
554
+ | | +-> ops.ts | | +-> init.ts |
555
+ | | | | +-> serpent/ |
556
+ | +-> cipher- | | +-> sha2/ +-> seal-stream.ts
557
+ | suite.ts | | +-> utils.ts +-> open-stream.ts
558
+ +-> cipher- | | | +-> seal-stream-pool.ts
559
+ suite.ts +-> pool- +-> hkdf.ts +-> header.ts
560
+ | worker.ts +-> constants.ts
561
+ +-> pool- | +-> types.ts
562
+ worker.ts |
563
+ |
564
+ All module index.ts files ──────────────────────────> init.ts <── getInstance()
565
+ initModule()
566
+ All */embedded.ts files ──────────────────────────> embedded/*.ts (gzip+base64 blobs)
469
567
  ```
470
568
 
471
569
  Each module's init function (`serpentInit`, `chacha20Init`, `sha2Init`,
472
- `sha3Init`) calls `initModule()` from `init.ts`, passing its own embedded thunk. `initModule()` delegates to `loadEmbedded(thunk)` in `loader.ts`.
473
- The loader calls the thunk, decodes base64, and instantiates the WASM binary.
474
- `loader.ts` has no knowledge of module names or embedded file paths.
475
-
476
- ### TS layer — file-by-file imports
477
-
478
- | File | Imports from | Symbols |
479
- |------|-------------|---------|
480
- | `init.ts` | *(none)* | — |
481
- | `loader.ts` | `init.ts` | `Module` (type) |
482
- | `types.ts` | *(none)* | — |
483
- | `utils.ts` | *(none)* | — |
484
- | `serpent/types.ts` | *(none)* | — |
485
- | `chacha20/types.ts` | *(none)* | — |
486
- | `sha2/types.ts` | *(none)* | — |
487
- | `sha3/types.ts` | *(none)* | — |
488
- | `serpent/index.ts` | `init.ts`, `embedded/serpent.ts` | `getInstance`, `initModule`, `Mode`, `InitOpts`, `WASM_BASE64` |
489
- | `serpent/seal.ts` | `serpent/index.ts`, `sha2/index.ts`, `utils.ts` | `SerpentCbc`, `HMAC_SHA256`, `concat`, `constantTimeEqual`, `wipe` |
490
- | `serpent/stream.ts` | `serpent/index.ts`, `sha2/index.ts`, `utils.ts` | `SerpentCtr`, `HMAC_SHA256`, `HKDF_SHA256`, `constantTimeEqual`, `concat` |
491
- | `serpent/stream-pool.ts` | `serpent/stream.ts` | `sealChunk`, `openChunk`, `chunkInfo` |
492
- | `serpent/stream-sealer.ts` | `serpent/index.ts`, `sha2/index.ts`, `utils.ts` | `SerpentCbc`, `HMAC_SHA256`, `HKDF_SHA256`, `concat`, `constantTimeEqual`, `wipe` |
493
- | `chacha20/index.ts` | `init.ts`, `utils.ts`, `chacha20/types.ts`, `embedded/chacha20.ts` | `getInstance`, `initModule`, `Mode`, `InitOpts`, `constantTimeEqual`, `ChaChaExports`, `WASM_BASE64` |
494
- | `sha2/index.ts` | `init.ts`, `embedded/sha2.ts` | `getInstance`, `initModule`, `Mode`, `InitOpts`, `WASM_BASE64` |
495
- | `sha3/index.ts` | `init.ts`, `embedded/sha3.ts` | `getInstance`, `initModule`, `Mode`, `InitOpts`, `WASM_BASE64` |
496
- | `fortuna.ts` | `init.ts`, `serpent/index.ts`, `sha2/index.ts`, `utils.ts` | `isInitialized`, `Serpent`, `SHA256`, `wipe`/`concat`/`utf8ToBytes` |
497
- | `index.ts` | `serpent/`, `chacha20/`, `sha2/`, `sha3/`, `init.ts`, `fortuna.ts`, `types.ts`, `utils.ts` | `serpentInit`, `chacha20Init`, `sha2Init`, `sha3Init` (from each module), *(all public exports)* |
570
+ `sha3Init`, `kyberInit`) calls `initModule()` from `init.ts`, passing a `WasmSource`.
571
+ `initModule()` delegates to `loadWasm(source)` in `loader.ts`. The loader
572
+ infers the loading strategy from the source type, with no mode string and no knowledge of module names or embedded file paths.
573
+
574
+ Pool workers (`serpent/pool-worker.ts`, `chacha20/pool-worker.ts`) instantiate
575
+ their own WASM modules from pre-compiled `WebAssembly.Module` objects passed
576
+ via `postMessage`. They do not use `initModule()` or the main-thread cache.
577
+
578
+ ---
498
579
 
499
580
  ### TS-to-WASM mapping
500
581
 
501
582
  Each TS wrapper class maps to one WASM module and specific exported functions.
502
- Tier 2 composition classes (`SerpentSeal`, `SerpentStream*`, `HKDF_*`) are pure
503
- TypeScript, they call Tier 1 classes rather than WASM functions directly.
583
+ Tier 2 composition classes are pure TypeScript; they call Tier 1 classes rather than WASM functions directly.
504
584
 
505
585
  **serpent/index.ts → asm/serpent/ (Tier 1: direct WASM callers)**
506
586
 
507
587
  | TS Class | WASM functions called |
508
588
  |----------|---------------------|
509
589
  | `Serpent` | `loadKey`, `encryptBlock`, `decryptBlock`, `wipeBuffers` + buffer getters |
510
- | `SerpentCtr` | `loadKey`, `resetCounter`, `setCounter`, `encryptChunk`, `decryptChunk`, `wipeBuffers` + buffer getters |
511
- | `SerpentCbc` | `loadKey`, `cbcEncryptChunk`, `cbcDecryptChunk`, `wipeBuffers` + buffer getters |
590
+ | `SerpentCtr` | `loadKey`, `resetCounter`, `setCounter`, `encryptChunk`, `encryptChunk_simd`, `wipeBuffers` + buffer getters |
591
+ | `SerpentCbc` | `loadKey`, `cbcEncryptChunk`, `cbcDecryptChunk`, `cbcDecryptChunk_simd`, `wipeBuffers` + buffer getters |
512
592
 
513
- **serpent/seal.ts, stream.ts, stream-sealer.ts (Tier 2: pure TS composition)**
514
-
515
- | TS Class | Composes |
516
- |----------|---------|
517
- | `SerpentSeal` | `SerpentCbc` + `HMAC_SHA256` |
518
- | `SerpentStream` | `SerpentCtr` + `HMAC_SHA256` + `HKDF_SHA256` |
519
- | `SerpentStreamPool` | `SerpentStream` (via worker) |
520
- | `SerpentStreamSealer` | `SerpentCbc` + `HMAC_SHA256` + `HKDF_SHA256` |
521
- | `SerpentStreamOpener` | `SerpentCbc` + `HMAC_SHA256` + `HKDF_SHA256` |
522
-
523
- **chacha20/index.ts → asm/chacha20/**
593
+ **chacha20/index.ts asm/chacha20/ (Tier 1: direct WASM callers)**
524
594
 
525
595
  | TS Class | WASM functions called |
526
596
  |----------|---------------------|
527
- | `ChaCha20` | `chachaLoadKey`, `chachaSetCounter`, `chachaResetCounter`, `chachaEncryptChunk`, `chachaDecryptChunk`, `wipeBuffers` + buffer getters |
597
+ | `ChaCha20` | `chachaLoadKey`, `chachaSetCounter`, `chachaEncryptChunk`, `chachaEncryptChunk_simd`, `wipeBuffers` + buffer getters |
528
598
  | `Poly1305` | `polyInit`, `polyUpdate`, `polyFinal`, `wipeBuffers` + buffer getters |
529
- | `ChaCha20Poly1305` | `chachaLoadKey`, `chachaResetCounter`, `chachaGenPolyKey`, `chachaEncryptChunk`, `chachaDecryptChunk`, `polyInit`, `polyUpdate`, `polyFinal`, `wipeBuffers` + buffer getters |
530
- | `XChaCha20Poly1305` | All of `ChaCha20Poly1305` + `hchacha20` + xchacha buffer getters |
599
+ | `ChaCha20Poly1305` | `chachaLoadKey`, `chachaSetCounter`, `chachaGenPolyKey`, `chachaEncryptChunk`, `polyInit`, `polyUpdate`, `polyFinal`, `wipeBuffers` + buffer getters (via `ops.ts`) |
600
+ | `XChaCha20Poly1305` | All of `ChaCha20Poly1305` + `hchacha20` + xchacha buffer getters (via `ops.ts`) |
531
601
 
532
- **sha2/index.ts → asm/sha2/**
602
+ **sha2/index.ts → asm/sha2/ (Tier 1: direct WASM callers)**
533
603
 
534
604
  | TS Class | WASM functions called |
535
605
  |----------|---------------------|
@@ -539,10 +609,8 @@ TypeScript, they call Tier 1 classes rather than WASM functions directly.
539
609
  | `HMAC_SHA256` | `hmac256Init`, `hmac256Update`, `hmac256Final`, `sha256Init`, `sha256Update`, `sha256Final`, `wipeBuffers` + buffer getters |
540
610
  | `HMAC_SHA512` | `hmac512Init`, `hmac512Update`, `hmac512Final`, `sha512Init`, `sha512Update`, `sha512Final`, `wipeBuffers` + buffer getters |
541
611
  | `HMAC_SHA384` | `hmac384Init`, `hmac384Update`, `hmac384Final`, `sha384Init`, `sha512Update`, `sha384Final`, `wipeBuffers` + buffer getters |
542
- | `HKDF_SHA256` | Pure TS composition over `HMAC_SHA256` (extract + expand per RFC 5869) |
543
- | `HKDF_SHA512` | Pure TS composition over `HMAC_SHA512` (extract + expand per RFC 5869) |
544
612
 
545
- **sha3/index.ts → asm/sha3/**
613
+ **sha3/index.ts → asm/sha3/ (Tier 1: direct WASM callers)**
546
614
 
547
615
  | TS Class | WASM functions called |
548
616
  |----------|---------------------|
@@ -553,77 +621,117 @@ TypeScript, they call Tier 1 classes rather than WASM functions directly.
553
621
  | `SHAKE128` | `shake128Init`, `keccakAbsorb`, `shakePad`, `shakeSqueezeBlock`, `wipeBuffers` + buffer getters |
554
622
  | `SHAKE256` | `shake256Init`, `keccakAbsorb`, `shakePad`, `shakeSqueezeBlock`, `wipeBuffers` + buffer getters |
555
623
 
624
+ **kyber/index.ts + kyber/kem.ts + kyber/indcpa.ts → asm/kyber/ (Tier 1)**
625
+
626
+ | TS Class | WASM functions called |
627
+ |----------|---------------------|
628
+ | `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 |
629
+
630
+ All MlKem classes also call sha3 WASM via `indcpa.ts`: `sha3_256Init`, `sha3_512Init`, `shake128Init`, `shake256Init`, `keccakAbsorb`, `sha3_256Final`, `sha3_512Final`, `shakeFinal`, `shakePad`, `shakeSqueezeBlock`.
631
+
632
+ **Tier 2: pure TS composition**
633
+
634
+ | TS Class / Object | Composes |
635
+ |--------------------|----------|
636
+ | `SerpentCipher` | `SerpentCbc` + `HMAC_SHA256` + `HKDF_SHA256` |
637
+ | `XChaCha20Cipher` | `ChaCha20Poly1305` (via `ops.ts`) + `HKDF_SHA256` |
638
+ | `Seal` | `SealStream` + `OpenStream` (degenerate single-chunk case) |
639
+ | `SealStream` | `CipherSuite` (generic — caller provides cipher) |
640
+ | `OpenStream` | `CipherSuite` (generic — caller provides cipher) |
641
+ | `SealStreamPool` | `CipherSuite` + `compileWasm()` + Web Workers |
642
+ | `HKDF_SHA256` | `HMAC_SHA256` (extract + expand per RFC 5869) |
643
+ | `HKDF_SHA512` | `HMAC_SHA512` (extract + expand per RFC 5869) |
644
+ | `Fortuna` | `Serpent` + `SHA256` |
645
+
646
+ ---
647
+
556
648
  ### Cross-module dependencies
557
649
 
558
650
  | Relationship | Notes |
559
651
  |-------------|-------|
560
- | `SerpentSeal`, `SerpentStream`, `SerpentStreamPool`, `SerpentStreamSealer`, `SerpentStreamOpener` → `serpent` + `sha2` | Tier 2 composition: Serpent-CBC/CTR + HMAC-SHA256 + HKDF-SHA256. Both modules must be initialized. |
561
- | `Fortuna` → `Serpent` + `SHA256` | Only class requiring **two** WASM modules (`serpent` + `sha2`). Uses `Fortuna.create()` static factory instead of `new`. |
562
- | `XChaCha20Poly1305` → `ChaCha20Poly1305` | Pure TS composition calls `hchacha20()` for subkey derivation, then delegates to `ChaCha20Poly1305`. |
563
- | `HKDF_SHA256`, `HKDF_SHA512` → `HMAC_SHA256`, `HMAC_SHA512` | Pure TS composition extract and expand steps per RFC 5869. |
652
+ | `SerpentCipher` → `serpent` + `sha2` | Tier 2 composition: Serpent-CBC + HMAC-SHA256 + HKDF-SHA256. |
653
+ | `XChaCha20Cipher` → `chacha20` + `sha2` | HKDF-SHA256 for key derivation + HChaCha20 + ChaCha20-Poly1305 for per-chunk AEAD. |
654
+ | `KyberSuite` → `kyber` + `sha3` + inner cipher | KEM encaps/decaps + HKDF with kemCt binding + inner CipherSuite. |
655
+ | `SealStream`, `OpenStream` → depends on cipher | Cipher-agnostic. Module requirements are determined by the `CipherSuite` passed at construction. |
656
+ | `SealStreamPool` → depends on cipher | Same module requirements as the cipher, plus `WasmSource` in pool opts for worker compilation. |
657
+ | `Fortuna` → `serpent` + `sha2` | Uses `Fortuna.create()` static factory instead of `new`. |
658
+ | `MlKem512`, `MlKem768`, `MlKem1024` → `kyber` + `sha3` | Kyber module handles polynomial arithmetic; sha3 provides SHAKE128/256, SHA3-256/512 for G/H/J/matrix gen. |
659
+ | `HKDF_SHA256`, `HKDF_SHA512` → `sha2` | Pure TS composition — extract and expand steps per RFC 5869. |
564
660
  | All other classes | Each depends on exactly **one** WASM module. |
565
661
 
662
+ ---
663
+
566
664
  ### Public API barrel (`src/ts/index.ts`)
567
665
 
568
666
  The root barrel defines and exports the dispatching `init()` function.
569
667
  It is the only file that imports all four module-scoped init functions.
570
668
 
571
669
  | Source | Exports |
572
- |--------|---------|
670
+ |--------|------------|
573
671
  | *(barrel itself)* | `init` (dispatching function — calls per-module init functions via `Promise.all`) |
574
- | `init.ts` | `Module`, `Mode`, `InitOpts`, `isInitialized`, `_resetForTesting` |
575
- | `serpent/index.ts` | `Serpent`, `SerpentCtr`, `SerpentCbc`, `SerpentSeal`, `SerpentStream`, `SerpentStreamPool`, `SerpentStreamSealer`, `SerpentStreamOpener`, `StreamPoolOpts`, `_serpentReady` |
576
- | `chacha20/index.ts` | `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `_chachaReady` |
577
- | `chacha20/pool.ts` | `XChaCha20Poly1305Pool`, `PoolOpts` |
672
+ | `init.ts` | `Module`, `WasmSource`, `isInitialized`, `_resetForTesting` |
673
+ | `errors.ts` | `AuthenticationError` |
674
+ | `serpent/index.ts` | `Serpent`, `SerpentCtr`, `SerpentCbc`, `SerpentCipher`, `_serpentReady` |
675
+ | `chacha20/index.ts` | `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `XChaCha20Cipher`, `_chachaReady` |
578
676
  | `sha2/index.ts` | `SHA256`, `SHA512`, `SHA384`, `HMAC_SHA256`, `HMAC_SHA512`, `HMAC_SHA384`, `HKDF_SHA256`, `HKDF_SHA512`, `_sha2Ready` |
579
677
  | `sha3/index.ts` | `SHA3_224`, `SHA3_256`, `SHA3_384`, `SHA3_512`, `SHAKE128`, `SHAKE256`, `_sha3Ready` |
678
+ | `keccak/index.ts` | `keccakInit` + re-exports all sha3 classes (alias subpath) |
679
+ | `kyber/index.ts` | `kyberInit`, `KyberSuite`, `MlKem512`, `MlKem768`, `MlKem1024`, `KyberKeyPair`, `KyberEncapsulation`, `KyberParams`, `MLKEM512`, `MLKEM768`, `MLKEM1024` |
680
+ | `stream/index.ts` | `Seal`, `SealStream`, `OpenStream`, `SealStreamPool`, `CipherSuite`, `DerivedKeys`, `SealStreamOpts`, `PoolOpts`, `FLAG_FRAMED`, `TAG_DATA`, `TAG_FINAL`, `HEADER_SIZE`, `CHUNK_MIN`, `CHUNK_MAX` |
580
681
  | `fortuna.ts` | `Fortuna` |
581
682
  | `types.ts` | `Hash`, `KeyedHash`, `Blockcipher`, `Streamcipher`, `AEAD` |
582
683
  | `utils.ts` | `hexToBytes`, `bytesToHex`, `utf8ToBytes`, `bytesToUtf8`, `base64ToBytes`, `bytesToBase64`, `constantTimeEqual`, `wipe`, `xor`, `concat`, `randomBytes` |
583
684
 
584
685
  Each subpath export also exports its own module-specific init function for
585
- tree-shakeable loading: `serpentInit(mode?, opts?)`, `chacha20Init(mode?, opts?)`,
586
- `sha2Init(mode?, opts?)`, `sha3Init(mode?, opts?)`.
686
+ tree-shakeable loading: `serpentInit(source)`, `chacha20Init(source)`,
687
+ `sha2Init(source)`, `sha3Init(source)`, `keccakInit(source)`.
587
688
 
588
689
  ---
589
690
 
590
691
  ## npm Package
591
692
 
592
693
  **Subpath exports:**
593
-
594
694
  ```json
595
695
  {
596
- "exports": {
597
- ".": "./dist/index.js",
598
- "./serpent": "./dist/serpent/index.js",
599
- "./chacha20": "./dist/chacha20/index.js",
600
- "./chacha20/pool": "./dist/chacha20/pool.js",
601
- "./sha2": "./dist/sha2/index.js",
602
- "./sha3": "./dist/sha3/index.js"
603
- }
696
+ "exports": {
697
+ ".": "./dist/index.js",
698
+ "./stream": "./dist/stream/index.js",
699
+ "./serpent": "./dist/serpent/index.js",
700
+ "./serpent/embedded": "./dist/serpent/embedded.js",
701
+ "./chacha20": "./dist/chacha20/index.js",
702
+ "./chacha20/embedded": "./dist/chacha20/embedded.js",
703
+ "./sha2": "./dist/sha2/index.js",
704
+ "./sha2/embedded": "./dist/sha2/embedded.js",
705
+ "./sha3": "./dist/sha3/index.js",
706
+ "./sha3/embedded": "./dist/sha3/embedded.js",
707
+ "./keccak": "./dist/keccak/index.js",
708
+ "./keccak/embedded": "./dist/keccak/embedded.js",
709
+ "./kyber": "./dist/kyber/index.js",
710
+ "./kyber/embedded": "./dist/kyber/embedded.js"
711
+ }
604
712
  }
605
713
  ```
606
714
 
607
715
  > [!NOTE]
608
- > `dist/chacha20/pool.worker.js` ships in the package but is not in the
609
- > `exports` map. It is an internal Web Worker entry point loaded by
610
- > `XChaCha20Poly1305Pool` at runtime. Do not import it as a named subpath.
716
+ > Pool worker files (`dist/serpent/pool-worker.js`, `dist/chacha20/pool-worker.js`)
717
+ > ship in the package but are not in the `exports` map. They are internal Web
718
+ > Worker entry points loaded by `SealStreamPool` at runtime via
719
+ > `new URL('./pool-worker.js', import.meta.url)`. Do not import them as named
720
+ > subpaths.
721
+
722
+ 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.
611
723
 
612
- The root `.` export re-exports everything. Subpath exports allow bundlers to
613
- tree-shake at the module level, a consumer importing only `./sha3` does not
614
- include the Serpent wrapper classes or their embedded WASM binaries in their
615
- bundle.
724
+ 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.
616
725
 
617
- **Tree-shaking:** `"sideEffects": false` is set in `package.json`. Each
618
- module's `index.ts` owns its own embedded import thunk. Bundlers that support
619
- tree-shaking (webpack, Rollup, esbuild) can eliminate unused modules and
620
- their embedded WASM binaries from the final bundle.
726
+ **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.
621
727
 
622
- **Published:** `dist/` only. Contains compiled JS, TypeScript declarations,
623
- and WASM binaries as assets for streaming mode. The embedded base64 is compiled
624
- into the JS, not a separate file.
728
+ **Published.** The npm package includes:
625
729
 
626
- **Not published:** `src/`, `test/`, `build/`, `scripts/`, `docs/`
730
+ - `dist/`: compiled JS, TypeScript declarations, WASM binaries, pool worker scripts, and a subset of consumer-facing API docs for offline use.
731
+ - `CLAUDE.md`: agent-facing project context.
732
+ - `SECURITY.md`: vulnerability disclosure policy.
733
+
734
+ **Not published.** `src/`, `test/`, `build/`, `scripts/`, `docs/`, `.github/`, editor configs.
627
735
 
628
736
  ---
629
737
 
@@ -632,7 +740,7 @@ into the JS, not a separate file.
632
740
  All offsets start at 0 per module. Independent linear memory. No offsets are
633
741
  shared or coordinated across modules.
634
742
 
635
- ### Serpent module 3 pages (192 KB)
743
+ ### Serpent module (3 pages, 192 KB)
636
744
 
637
745
  Source: `src/asm/serpent/buffers.ts`
638
746
 
@@ -652,7 +760,7 @@ Source: `src/asm/serpent/buffers.ts`
652
760
 
653
761
  `wipeBuffers()` zeroes all 10 buffers (key, block pt/ct, nonce, counter, subkeys, work, chunk pt/ct, CBC IV).
654
762
 
655
- ### ChaCha20 module 3 pages (192 KB)
763
+ ### ChaCha20 module (3 pages, 192 KB)
656
764
 
657
765
  Source: `src/asm/chacha20/buffers.ts`
658
766
 
@@ -682,7 +790,7 @@ Source: `src/asm/chacha20/buffers.ts`
682
790
 
683
791
  `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).
684
792
 
685
- ### SHA-2 module 3 pages (192 KB)
793
+ ### SHA-2 module (3 pages, 192 KB)
686
794
 
687
795
  Source: `src/asm/sha2/buffers.ts`
688
796
 
@@ -712,7 +820,7 @@ Source: `src/asm/sha2/buffers.ts`
712
820
 
713
821
  `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).
714
822
 
715
- ### SHA-3 module 3 pages (192 KB)
823
+ ### SHA-3 module (3 pages, 192 KB)
716
824
 
717
825
  Source: `src/asm/sha3/buffers.ts`
718
826
 
@@ -728,6 +836,28 @@ Source: `src/asm/sha3/buffers.ts`
728
836
 
729
837
  `wipeBuffers()` zeroes all 6 buffer regions (state, rate, absorbed, dsbyte, input, output).
730
838
 
839
+ ### Kyber module (3 pages, 192 KB)
840
+
841
+ Source: `src/asm/kyber/`
842
+
843
+ | Region | Offset | Size | Purpose |
844
+ |--------|--------|------|---------|
845
+ | AS data segment | 0 | 4096 | Zetas table (128 × i16, bit-reversed Montgomery domain) |
846
+ | Poly slots | 4096 | 5120 | 10 × 512B scratch polynomials (256 × i16 each) |
847
+ | Polyvec slots | 9216 | 16384 | 8 × 2048B scratch polyvecs (k=4 max: 4 × 512B) |
848
+ | SEED buffer | 25600 | 32 | Seed ρ/σ |
849
+ | MSG buffer | 25632 | 32 | Message / shared secret |
850
+ | PK buffer | 25664 | 1568 | Encapsulation key (max k=4) |
851
+ | SK buffer | 27232 | 1536 | IND-CPA secret key (max k=4) |
852
+ | CT buffer | 28768 | 1568 | Ciphertext (max k=4) |
853
+ | CT_PRIME buffer | 30336 | 1568 | Decaps re-encrypt comparison (max k=4) |
854
+ | XOF/PRF buffer | 31904 | 1024 | SHAKE squeeze output for rej_uniform / CBD |
855
+ | Poly accumulator | 32928 | 512 | Internal scratch for polyvec_basemul_acc |
856
+
857
+ Total mutable: 29344 bytes (4096–33440). End = 33440 < 192 KB.
858
+
859
+ `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.
860
+
731
861
  ---
732
862
 
733
863
  ## Test Suite
@@ -739,16 +869,14 @@ For the full testing methodology and vector corpus, see: [test-suite.md](./test-
739
869
 
740
870
  ### Gate discipline
741
871
 
742
- Each primitive family has a gate test, the simplest authoritative vector for
743
- that primitive. The gate must pass before any other tests in that family are
744
- written or run. Gate tests are annotated with a `// GATE` comment.
872
+ 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.
745
873
 
746
874
  ### `init.test.ts` contracts
747
875
 
748
- - `init()` with each of the three modes loads and caches the module correctly
876
+ - `init()` with each `WasmSource` type loads and caches the module correctly
749
877
  - Idempotency: second `init()` call for same module is a no-op
750
878
  - Error before init: clear error thrown for each class before its module is loaded
751
- - Partial init: loading `['serpent']` does not make `sha3` classes available
879
+ - Partial init: loading `{ serpent: ... }` does not make `sha3` classes available
752
880
 
753
881
  ---
754
882
 
@@ -765,32 +893,38 @@ They are the immutable truth, and must never be modified to make tests pass.
765
893
 
766
894
  ---
767
895
 
768
- ## Known Limitations (v1.0)
896
+ ## Known Limitations
769
897
 
770
- - **`SerpentCbc` is unauthenticated**: use `SerpentSeal` for authenticated
771
- Serpent encryption, or pair `SerpentCbc` with `HMAC_SHA256` (Encrypt-then-MAC)
772
- if direct CBC access is required.
898
+ - **`SerpentCbc` is unauthenticated**: use `Seal` with `SerpentCipher` for
899
+ authenticated Serpent encryption, or pair `SerpentCbc` with `HMAC_SHA256`
900
+ (Encrypt-then-MAC) if direct CBC access is required.
773
901
  - **Single-threaded WASM per instance**: one WASM instance per binary per thread.
774
- `SerpentStreamPool` and `XChaCha20Poly1305Pool` provide Worker-based parallelism
775
- for their respective AEAD paths; other primitive families remain single-threaded.
902
+ `SealStreamPool` provides Worker-based parallelism for both cipher families;
903
+ other primitives remain single-threaded.
776
904
  - **Max input per WASM call**: chunk-based APIs (CTR, CBC) accept at most 64KB
777
905
  per call. Wrappers handle splitting automatically for larger inputs.
778
- - **Browser WASM loading**: streaming mode requires files served with
779
- `Content-Type: application/wasm`. Embedded mode works everywhere.
906
+ - **WASM side-channel posture**: WebAssembly implementations offer the best
907
+ available side-channel resistance (branchless, table-free), but lack
908
+ hardware-level constant-time guarantees. For applications where timing
909
+ side channels are a primary threat, a native cryptographic library with
910
+ verified constant-time guarantees will be more appropriate than any
911
+ WASM-based implementation.
780
912
 
781
913
  ---
782
914
 
783
915
  > ## Cross-References
784
916
  >
785
917
  > - [index](./README.md) — Project Documentation index
918
+ > - [lexicon](./lexicon.md) — Glossary of cryptographic terms
786
919
  > - [test-suite](./test-suite.md) — testing methodology, vector corpus, and gate discipline
787
- > - [init](./init.md) — `init()` API, three loading modes, and idempotent behavior
788
- > - [loader](./loader.md) — internal WASM binary loading strategies (embedded, streaming, manual)
920
+ > - [init](./init.md) — `init()` API, `WasmSource`, and idempotent behavior
921
+ > - [loader](./loader.md) — internal WASM binary loading strategies
789
922
  > - [wasm](./wasm.md) — WebAssembly primer: modules, instances, memory, and the init gate
790
- > - [types](./types.md) — public TypeScript interfaces (`Hash`, `KeyedHash`, `Blockcipher`, `Streamcipher`, `AEAD`)
923
+ > - [types](./types.md) — public TypeScript interfaces and `CipherSuite`
791
924
  > - [utils](./utils.md) — encoding helpers, `constantTimeEqual`, `wipe`, `randomBytes`
792
- > - [serpent](./serpent.md) — Serpent-256 TypeScript API (SerpentSeal, SerpentStream, raw modes)
793
- > - [chacha20](./chacha20.md) — ChaCha20/Poly1305 TypeScript API and XChaCha20-Poly1305 AEAD
925
+ > - [authenticated encryption](./aead.md) — SealStream, OpenStream, SealStreamPool, wire format
926
+ > - [serpent](./serpent.md) — Serpent-256 TypeScript API, SerpentCipher
927
+ > - [chacha20](./chacha20.md) — ChaCha20/Poly1305 TypeScript API, XChaCha20Cipher
794
928
  > - [sha2](./sha2.md) — SHA-2 hashes, HMAC, and HKDF TypeScript API
795
929
  > - [sha3](./sha3.md) — SHA-3 hashes and SHAKE XOFs TypeScript API
796
930
  > - [fortuna](./fortuna.md) — Fortuna CSPRNG with forward secrecy and entropy pooling