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
package/CLAUDE.md CHANGED
@@ -1,7 +1,19 @@
1
1
  # leviathan-crypto — AI Assistant Guide
2
2
 
3
- This file ships with the package to help AI assistants use this library correctly.
4
- Full API documentation is in the `docs/` directory alongside this file.
3
+ > [!NOTE]
4
+ > This file ships with the package to help AI assistants use this library correctly. Full API documentation is in the `docs/` directory alongside this file.
5
+
6
+ > ### Table of Contents
7
+ > - [What This Library Is](#what-this-library-is)
8
+ > - [Critical: `init()` is required](#critical-init-is-required)
9
+ > - [Critical: call `dispose()` after use](#critical-call-dispose-after-use)
10
+ > - [Critical: `decrypt()` throws on authentication failure](#critical-decrypt-throws-on-authentication-failure--never-returns-null)
11
+ > - [Critical: subpath init function names](#critical-subpath-init-function-names)
12
+ > - [Which module does each class require?](#which-module-does-each-class-require)
13
+ > - [Recommended patterns](#recommended-patterns)
14
+ > - [`SerpentCbc` arg order](#serpentcbc-arg-order)
15
+ > - [Utilities (no `init()` required)](#utilities-no-init-required)
16
+ > - [Full documentation](#full-documentation)
5
17
 
6
18
  ---
7
19
 
@@ -9,7 +21,7 @@ Full API documentation is in the `docs/` directory alongside this file.
9
21
 
10
22
  `leviathan-crypto` is a zero-dependency WebAssembly cryptography library for
11
23
  TypeScript and JavaScript. All cryptographic computation runs in WASM, outside
12
- the JavaScript JIT. The TypeScript layer provides the public API input
24
+ the JavaScript JIT. The TypeScript layer provides the public API: input
13
25
  validation, type safety, and ergonomics. It never implements cryptographic
14
26
  algorithms itself.
15
27
 
@@ -22,24 +34,31 @@ module is loaded throws immediately with a clear error. Call `init()` once at
22
34
  startup, before any cryptographic operations.
23
35
 
24
36
  ```typescript
25
- import { init, SerpentSeal } from 'leviathan-crypto'
37
+ import { init, Serpent } from 'leviathan-crypto'
38
+ import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
39
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
26
40
 
27
- await init(['serpent', 'sha2']) // load only the modules you need
41
+ await init({ serpent: serpentWasm, sha2: sha2Wasm })
28
42
  ```
29
43
 
30
- Available modules: `'serpent'`, `'chacha20'`, `'sha2'`, `'sha3'`
44
+ `init()` accepts a `Partial<Record<Module, WasmSource>>`. Each value is a
45
+ `WasmSource`: a gzip+base64 string, `URL`, `ArrayBuffer`, `Uint8Array`,
46
+ pre-compiled `WebAssembly.Module`, `Response`, or `Promise<Response>`.
47
+
48
+ The `/embedded` subpath exports are the simplest WasmSource: they are the
49
+ gzip+base64 blobs for each module, bundled with the package.
31
50
 
32
51
  ---
33
52
 
34
53
  ## Critical: call `dispose()` after use
35
54
 
36
55
  Every class holds WASM memory containing key material. Call `dispose()` when
37
- done it zeroes that memory. Not calling `dispose()` leaks key material.
56
+ done; it zeroes that memory. Not calling `dispose()` leaks key material.
38
57
 
39
58
  ```typescript
40
- const cipher = new SerpentSeal()
59
+ const cipher = new XChaCha20Poly1305()
41
60
  try {
42
- return cipher.encrypt(key, plaintext)
61
+ return cipher.encrypt(key, nonce, plaintext)
43
62
  } finally {
44
63
  cipher.dispose()
45
64
  }
@@ -50,7 +69,7 @@ try {
50
69
  ## Critical: `decrypt()` throws on authentication failure — never returns null
51
70
 
52
71
  All AEAD `decrypt()` methods throw if authentication fails. Do not check for a
53
- null return catch the exception.
72
+ null return; catch the exception.
54
73
 
55
74
  ```typescript
56
75
  try {
@@ -64,34 +83,44 @@ try {
64
83
 
65
84
  ## Critical: subpath init function names
66
85
 
67
- Each subpath export has its own module-specific init function not `init()`.
86
+ Each subpath export has its own module-specific init function, not `init()`.
68
87
  These are only needed for tree-shakeable imports. The root barrel `init()` is
69
88
  the normal path.
70
89
 
71
- | Subpath | Init function |
72
- |---------|---------------|
73
- | `leviathan-crypto/serpent` | `serpentInit()` |
74
- | `leviathan-crypto/chacha20` | `chacha20Init()` |
75
- | `leviathan-crypto/sha2` | `sha2Init()` |
76
- | `leviathan-crypto/sha3` | `sha3Init()` |
90
+ Each init function takes a single `WasmSource` argument. Use the module's
91
+ `/embedded` subpath to get the bundled blob as a ready-to-use WasmSource.
92
+
93
+ | Subpath | Init function | Embedded blob |
94
+ |---------|---------------|---------------|
95
+ | `leviathan-crypto/serpent` | `serpentInit(source)` | `leviathan-crypto/serpent/embedded` → `serpentWasm` |
96
+ | `leviathan-crypto/chacha20` | `chacha20Init(source)` | `leviathan-crypto/chacha20/embedded` → `chacha20Wasm` |
97
+ | `leviathan-crypto/sha2` | `sha2Init(source)` | `leviathan-crypto/sha2/embedded` → `sha2Wasm` |
98
+ | `leviathan-crypto/sha3` | `sha3Init(source)` | `leviathan-crypto/sha3/embedded` → `sha3Wasm` |
99
+ | `leviathan-crypto/keccak` | `keccakInit(source)` | `leviathan-crypto/keccak/embedded` → `keccakWasm` |
100
+ | `leviathan-crypto/kyber` | `kyberInit(source)` | `leviathan-crypto/kyber/embedded` → `kyberWasm` |
77
101
 
78
102
  ```typescript
79
103
  // Tree-shakeable — loads only serpent WASM
80
- import { serpentInit, SerpentSeal } from 'leviathan-crypto/serpent'
81
- await serpentInit()
104
+ import { serpentInit, Serpent } from 'leviathan-crypto/serpent'
105
+ import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
106
+ await serpentInit(serpentWasm)
82
107
  ```
83
108
 
84
109
  ---
85
110
 
86
111
  ## Which module does each class require?
87
112
 
88
- | Classes | `init()` call |
89
- |---------|--------------|
90
- | `SerpentSeal`, `SerpentStream`, `SerpentStreamPool`, `SerpentStreamSealer`, `SerpentStreamOpener`, `Serpent`, `SerpentCtr`, `SerpentCbc` | `init(['serpent', 'sha2'])` |
91
- | `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `XChaCha20Poly1305Pool` | `init(['chacha20'])` |
92
- | `SHA256`, `SHA384`, `SHA512`, `HMAC_SHA256`, `HMAC_SHA384`, `HMAC_SHA512`, `HKDF_SHA256`, `HKDF_SHA512` | `init(['sha2'])` |
93
- | `SHA3_224`, `SHA3_256`, `SHA3_384`, `SHA3_512`, `SHAKE128`, `SHAKE256` | `init(['sha3'])` |
94
- | `Fortuna` | `init(['serpent', 'sha2'])` |
113
+ | Classes | Required modules |
114
+ |---------|-----------------|
115
+ | `Serpent`, `SerpentCtr`, `SerpentCbc`, `SerpentCipher` | `init({ serpent: serpentWasm, sha2: sha2Wasm })` |
116
+ | `SealStream`, `OpenStream`, `SerpentCipher` (when using SerpentCipher) | `init({ serpent: serpentWasm, sha2: sha2Wasm })` |
117
+ | `SealStream`, `OpenStream`, `XChaCha20Cipher` (when using XChaCha20Cipher) | `init({ chacha20: chacha20Wasm, sha2: sha2Wasm })` |
118
+ | `SealStreamPool` | depends on cipher: same modules as the cipher suite + `sha2` |
119
+ | `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305` | `init({ chacha20: chacha20Wasm })` |
120
+ | `SHA256`, `SHA384`, `SHA512`, `HMAC_SHA256`, `HMAC_SHA384`, `HMAC_SHA512`, `HKDF_SHA256`, `HKDF_SHA512` | `init({ sha2: sha2Wasm })` |
121
+ | `SHA3_224`, `SHA3_256`, `SHA3_384`, `SHA3_512`, `SHAKE128`, `SHAKE256` | `init({ sha3: sha3Wasm })` or `init({ keccak: keccakWasm })` — `'keccak'` is an alias for `'sha3'` |
122
+ | `MlKem512`, `MlKem768`, `MlKem1024` | `init({ kyber: kyberWasm, sha3: sha3Wasm })` — both modules required |
123
+ | `Fortuna` | `init({ serpent: serpentWasm, sha2: sha2Wasm })` |
95
124
 
96
125
  ---
97
126
 
@@ -100,15 +129,15 @@ await serpentInit()
100
129
  ### Authenticated encryption (recommended default)
101
130
 
102
131
  ```typescript
103
- import { init, SerpentSeal, randomBytes } from 'leviathan-crypto'
132
+ import { init, Seal, SerpentCipher, randomBytes } from 'leviathan-crypto'
133
+ import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
134
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
104
135
 
105
- await init(['serpent', 'sha2'])
136
+ await init({ serpent: serpentWasm, sha2: sha2Wasm })
106
137
 
107
- const key = randomBytes(64) // 64-byte key (encKey + macKey)
108
- const seal = new SerpentSeal()
109
- const ciphertext = seal.encrypt(key, plaintext) // Serpent-CBC + HMAC-SHA256
110
- const decrypted = seal.decrypt(key, ciphertext) // throws on tamper
111
- seal.dispose()
138
+ const key = SerpentCipher.keygen()
139
+ const blob = Seal.encrypt(SerpentCipher, key, plaintext)
140
+ const decrypted = Seal.decrypt(SerpentCipher, key, blob)
112
141
  ```
113
142
 
114
143
  ### Incremental streaming AEAD
@@ -116,50 +145,42 @@ seal.dispose()
116
145
  Use when you cannot buffer the full message before encrypting.
117
146
 
118
147
  ```typescript
119
- import { init, SerpentStreamSealer, SerpentStreamOpener, randomBytes } from 'leviathan-crypto'
120
-
121
- await init(['serpent', 'sha2'])
122
-
123
- const key = randomBytes(64)
124
- const sealer = new SerpentStreamSealer(key, 65536)
125
- const header = sealer.header() // send to opener before any chunks
126
-
127
- const chunk0 = sealer.seal(data0) // exactly chunkSize bytes
128
- const last = sealer.final(tail) // any size up to chunkSize; wipes key
129
-
130
- const opener = new SerpentStreamOpener(key, header)
131
- const pt0 = opener.open(chunk0) // throws on auth failure
132
- const ptLast = opener.open(last)
148
+ import { init, SealStream, OpenStream, SerpentCipher, randomBytes } from 'leviathan-crypto'
149
+ import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
150
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
151
+
152
+ await init({ serpent: serpentWasm, sha2: sha2Wasm })
153
+
154
+ const key = randomBytes(32)
155
+ const sealer = new SealStream(SerpentCipher, key)
156
+ const preamble = sealer.preamble // 20 bytes — send first
157
+ const ct0 = sealer.push(chunk0)
158
+ const ct1 = sealer.push(chunk1)
159
+ const ctLast = sealer.finalize(lastChunk)
160
+
161
+ const opener = new OpenStream(SerpentCipher, key, preamble)
162
+ const pt0 = opener.pull(ct0)
163
+ const pt1 = opener.pull(ct1)
164
+ const ptLast = opener.finalize(ctLast)
133
165
  ```
134
166
 
135
167
  ### Length-prefixed streaming (for files and buffered transports)
136
168
 
137
- Pass `{ framed: true }` to `SerpentStreamSealer`/`SerpentStreamOpener` for self-delimiting
138
- `u32be` length-prefixed framing. Use when chunks will be concatenated into a flat byte
139
- stream. Omit when the transport frames messages itself (WebSocket, IPC).
169
+ Pass `{ framed: true }` to `SealStream` for self-delimiting `u32be` length-prefixed
170
+ framing. Use when chunks will be concatenated into a flat byte stream. Omit when the
171
+ transport frames messages itself (WebSocket, IPC).
140
172
 
141
173
  ```typescript
142
- import { init, SerpentStreamSealer, SerpentStreamOpener, randomBytes } from 'leviathan-crypto'
143
-
144
- await init(['serpent', 'sha2'])
145
-
146
- const key = randomBytes(64)
147
- const sealer = new SerpentStreamSealer(key, 65536, { framed: true })
148
- const header = sealer.header()
149
-
150
- const frame0 = sealer.seal(data0) // u32be(len) || sealed chunk
151
- const last = sealer.final(tail)
152
-
153
- const opener = new SerpentStreamOpener(key, header, { framed: true })
154
- const chunks = opener.feed(frame0) // Uint8Array[] — throws on auth failure
174
+ const sealer = new SealStream(SerpentCipher, key, { framed: true })
155
175
  ```
156
176
 
157
177
  ### XChaCha20-Poly1305
158
178
 
159
179
  ```typescript
160
180
  import { init, XChaCha20Poly1305, randomBytes } from 'leviathan-crypto'
181
+ import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
161
182
 
162
- await init(['chacha20'])
183
+ await init({ chacha20: chacha20Wasm })
163
184
 
164
185
  const aead = new XChaCha20Poly1305()
165
186
  const key = randomBytes(32)
@@ -170,14 +191,15 @@ aead.dispose()
170
191
  ```
171
192
 
172
193
  Note: `encrypt()` returns ciphertext with the 16-byte Poly1305 tag appended.
173
- `decrypt()` expects the same concatenated format not separate ciphertext and tag.
194
+ `decrypt()` expects the same concatenated format, not separate ciphertext and tag.
174
195
 
175
196
  ### Hashing
176
197
 
177
198
  ```typescript
178
199
  import { init, SHA256, HMAC_SHA256 } from 'leviathan-crypto'
200
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
179
201
 
180
- await init(['sha2'])
202
+ await init({ sha2: sha2Wasm })
181
203
 
182
204
  const hasher = new SHA256()
183
205
  const digest = hasher.hash(data) // returns Uint8Array
@@ -192,8 +214,9 @@ mac.dispose()
192
214
 
193
215
  ```typescript
194
216
  import { init, SHAKE128 } from 'leviathan-crypto'
217
+ import { sha3Wasm } from 'leviathan-crypto/sha3/embedded'
195
218
 
196
- await init(['sha3'])
219
+ await init({ sha3: sha3Wasm })
197
220
 
198
221
  const xof = new SHAKE128()
199
222
  xof.absorb(data)
@@ -202,12 +225,39 @@ const out2 = xof.squeeze(32) // next 32 bytes — contiguous XOF stream
202
225
  xof.dispose()
203
226
  ```
204
227
 
228
+ ### ML-KEM post-quantum key encapsulation
229
+
230
+ ```typescript
231
+ import { init, MlKem768 } from 'leviathan-crypto'
232
+ import { kyberWasm } from 'leviathan-crypto/kyber/embedded'
233
+ import { sha3Wasm } from 'leviathan-crypto/sha3/embedded'
234
+
235
+ await init({ kyber: kyberWasm, sha3: sha3Wasm })
236
+
237
+ const kem = new MlKem768()
238
+ const { encapsulationKey, decapsulationKey } = kem.keygen()
239
+
240
+ // Encapsulation (sender — public encapsulationKey only)
241
+ const { ciphertext, sharedSecret: senderSecret } = kem.encapsulate(encapsulationKey)
242
+
243
+ // Decapsulation (recipient — private decapsulationKey)
244
+ const recipientSecret = kem.decapsulate(decapsulationKey, ciphertext)
245
+
246
+ // senderSecret === recipientSecret (32 bytes)
247
+ kem.dispose()
248
+ ```
249
+
250
+ Kyber classes require **both** `kyber` and `sha3` initialized. ML-KEM produces
251
+ a 32-byte shared secret suitable for use as a symmetric key.
252
+
205
253
  ### Fortuna CSPRNG
206
254
 
207
255
  ```typescript
208
256
  import { init, Fortuna } from 'leviathan-crypto'
257
+ import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
258
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
209
259
 
210
- await init(['serpent', 'sha2'])
260
+ await init({ serpent: serpentWasm, sha2: sha2Wasm })
211
261
 
212
262
  const fortuna = await Fortuna.create() // static factory — not new Fortuna()
213
263
  const bytes = fortuna.get(32)
@@ -226,7 +276,7 @@ cipher.decrypt(key, iv, ciphertext) // correct
226
276
  ```
227
277
 
228
278
  `SerpentCbc` is unauthenticated. Always pair with `HMAC_SHA256`
229
- (Encrypt-then-MAC) or use `SerpentSeal` instead.
279
+ (Encrypt-then-MAC) or use `Seal` with `SerpentCipher` instead.
230
280
 
231
281
  ---
232
282
 
@@ -243,10 +293,11 @@ const safe = constantTimeEqual(a, b) // constant-time equality — never use =
243
293
  wipe(key) // zero a Uint8Array in place
244
294
  ```
245
295
 
246
- `hasSIMD()` returns `true` if the runtime supports WebAssembly SIMD. It is used
247
- internally you do not need to call it. SIMD acceleration is fully transparent:
248
- `SerpentCtr.encryptChunk`, `SerpentCbc.decrypt`, and `ChaCha20.encryptChunk` all
249
- auto-dispatch to the faster 4-wide SIMD path when available, with no API change.
296
+ `hasSIMD()` returns `true` if the runtime supports WebAssembly SIMD.
297
+ Serpent, ChaCha20, and Kyber modules all require SIMD; `init()` throws
298
+ a clear error on runtimes without support. SIMD has been a baseline
299
+ feature of all major browsers and runtimes since 2021. SHA-2 and SHA-3
300
+ modules run on any WASM-capable runtime.
250
301
 
251
302
  ---
252
303
 
@@ -256,12 +307,14 @@ The complete API reference ships in `docs/` alongside this file:
256
307
 
257
308
  | File | Contents |
258
309
  |------|----------|
259
- | `docs/serpent.md` | `SerpentSeal`, `SerpentStream`, `SerpentStreamPool`, `SerpentStreamSealer`, `SerpentStreamOpener`, `Serpent`, `SerpentCtr`, `SerpentCbc` |
260
- | `docs/chacha20.md` | `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `XChaCha20Poly1305Pool` |
310
+ | `docs/serpent.md` | `SerpentCipher`, `Serpent`, `SerpentCtr`, `SerpentCbc` |
311
+ | `docs/chacha20.md` | `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `XChaCha20Cipher` |
261
312
  | `docs/sha2.md` | `SHA256`, `SHA384`, `SHA512`, `HMAC_SHA256`, `HMAC_SHA384`, `HMAC_SHA512`, `HKDF_SHA256`, `HKDF_SHA512` |
262
313
  | `docs/sha3.md` | `SHA3_224`, `SHA3_256`, `SHA3_384`, `SHA3_512`, `SHAKE128`, `SHAKE256` |
314
+ | `docs/aead.md` | `Seal`, `SealStream`, `OpenStream`, `SealStreamPool`, `CipherSuite` |
315
+ | `docs/kyber.md` | `MlKem512`, `MlKem768`, `MlKem1024`, `KyberSuite` — ML-KEM (FIPS 203) API reference |
263
316
  | `docs/fortuna.md` | `Fortuna` CSPRNG |
264
317
  | `docs/init.md` | `init()` API, loading modes, subpath imports |
265
318
  | `docs/utils.md` | Encoding helpers, `constantTimeEqual`, `wipe`, `randomBytes` |
266
- | `docs/types.md` | `Hash`, `KeyedHash`, `Blockcipher`, `Streamcipher`, `AEAD` interfaces |
319
+ | `docs/types.md` | `Hash`, `KeyedHash`, `Blockcipher`, `Streamcipher`, `AEAD` interfaces; `CipherSuite`, `DerivedKeys`, `SealStreamOpts`, `PoolOpts`, `WasmSource` |
267
320
  | `docs/architecture.md` | Module structure, WASM layer, three-tier design |