leviathan-crypto 1.4.0 → 2.0.1

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 (122) hide show
  1. package/CLAUDE.md +129 -94
  2. package/README.md +166 -223
  3. package/SECURITY.md +90 -45
  4. package/dist/chacha20/cipher-suite.d.ts +4 -0
  5. package/dist/chacha20/cipher-suite.js +79 -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 -27
  9. package/dist/chacha20/index.js +40 -59
  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 +323 -0
  17. package/dist/docs/architecture.md +427 -292
  18. package/dist/docs/argon2id.md +42 -30
  19. package/dist/docs/chacha20.md +192 -266
  20. package/dist/docs/exports.md +241 -0
  21. package/dist/docs/fortuna.md +60 -69
  22. package/dist/docs/init.md +172 -178
  23. package/dist/docs/loader.md +87 -142
  24. package/dist/docs/serpent.md +134 -583
  25. package/dist/docs/sha2.md +91 -103
  26. package/dist/docs/sha3.md +70 -36
  27. package/dist/docs/types.md +94 -16
  28. package/dist/docs/utils.md +109 -32
  29. package/dist/embedded/kyber.d.ts +1 -0
  30. package/dist/embedded/kyber.js +3 -0
  31. package/dist/embedded/serpent.d.ts +1 -1
  32. package/dist/embedded/serpent.js +1 -1
  33. package/dist/errors.d.ts +10 -0
  34. package/dist/errors.js +38 -0
  35. package/dist/fortuna.d.ts +0 -6
  36. package/dist/fortuna.js +5 -5
  37. package/dist/index.d.ts +25 -9
  38. package/dist/index.js +36 -7
  39. package/dist/init.d.ts +3 -7
  40. package/dist/init.js +18 -35
  41. package/dist/keccak/embedded.d.ts +1 -0
  42. package/dist/keccak/embedded.js +27 -0
  43. package/dist/keccak/index.d.ts +4 -0
  44. package/dist/keccak/index.js +31 -0
  45. package/dist/kyber/embedded.d.ts +1 -0
  46. package/dist/kyber/embedded.js +27 -0
  47. package/dist/kyber/indcpa.d.ts +49 -0
  48. package/dist/kyber/indcpa.js +352 -0
  49. package/dist/kyber/index.d.ts +38 -0
  50. package/dist/kyber/index.js +150 -0
  51. package/dist/kyber/kem.d.ts +21 -0
  52. package/dist/kyber/kem.js +160 -0
  53. package/dist/kyber/params.d.ts +14 -0
  54. package/dist/kyber/params.js +37 -0
  55. package/dist/kyber/suite.d.ts +13 -0
  56. package/dist/kyber/suite.js +94 -0
  57. package/dist/kyber/types.d.ts +98 -0
  58. package/dist/kyber/types.js +25 -0
  59. package/dist/kyber/validate.d.ts +19 -0
  60. package/dist/kyber/validate.js +68 -0
  61. package/dist/kyber.wasm +0 -0
  62. package/dist/loader.d.ts +15 -6
  63. package/dist/loader.js +65 -21
  64. package/dist/serpent/cipher-suite.d.ts +4 -0
  65. package/dist/serpent/cipher-suite.js +122 -0
  66. package/dist/serpent/embedded.d.ts +1 -0
  67. package/dist/serpent/embedded.js +27 -0
  68. package/dist/serpent/index.d.ts +6 -37
  69. package/dist/serpent/index.js +9 -118
  70. package/dist/serpent/pool-worker.d.ts +1 -0
  71. package/dist/serpent/pool-worker.js +208 -0
  72. package/dist/serpent/serpent-cbc.d.ts +30 -0
  73. package/dist/serpent/serpent-cbc.js +142 -0
  74. package/dist/serpent.wasm +0 -0
  75. package/dist/sha2/embedded.d.ts +1 -0
  76. package/dist/sha2/embedded.js +27 -0
  77. package/dist/sha2/hkdf.js +6 -2
  78. package/dist/sha2/index.d.ts +3 -2
  79. package/dist/sha2/index.js +3 -4
  80. package/dist/sha3/embedded.d.ts +1 -0
  81. package/dist/sha3/embedded.js +27 -0
  82. package/dist/sha3/index.d.ts +3 -2
  83. package/dist/sha3/index.js +3 -4
  84. package/dist/stream/constants.d.ts +6 -0
  85. package/dist/stream/constants.js +30 -0
  86. package/dist/stream/header.d.ts +9 -0
  87. package/dist/stream/header.js +77 -0
  88. package/dist/stream/index.d.ts +7 -0
  89. package/dist/stream/index.js +27 -0
  90. package/dist/stream/open-stream.d.ts +21 -0
  91. package/dist/stream/open-stream.js +146 -0
  92. package/dist/stream/seal-stream-pool.d.ts +38 -0
  93. package/dist/stream/seal-stream-pool.js +400 -0
  94. package/dist/stream/seal-stream.d.ts +20 -0
  95. package/dist/stream/seal-stream.js +142 -0
  96. package/dist/stream/seal.d.ts +9 -0
  97. package/dist/stream/seal.js +75 -0
  98. package/dist/stream/types.d.ts +25 -0
  99. package/dist/stream/types.js +26 -0
  100. package/dist/utils.d.ts +7 -2
  101. package/dist/utils.js +49 -3
  102. package/dist/wasm-source.d.ts +12 -0
  103. package/dist/wasm-source.js +26 -0
  104. package/package.json +13 -5
  105. package/dist/chacha20/pool.d.ts +0 -52
  106. package/dist/chacha20/pool.js +0 -178
  107. package/dist/chacha20/pool.worker.js +0 -37
  108. package/dist/chacha20/stream-sealer.d.ts +0 -49
  109. package/dist/chacha20/stream-sealer.js +0 -327
  110. package/dist/docs/chacha20_pool.md +0 -309
  111. package/dist/docs/wasm.md +0 -194
  112. package/dist/serpent/seal.d.ts +0 -8
  113. package/dist/serpent/seal.js +0 -72
  114. package/dist/serpent/stream-pool.d.ts +0 -48
  115. package/dist/serpent/stream-pool.js +0 -275
  116. package/dist/serpent/stream-sealer.d.ts +0 -55
  117. package/dist/serpent/stream-sealer.js +0 -342
  118. package/dist/serpent/stream.d.ts +0 -28
  119. package/dist/serpent/stream.js +0 -205
  120. package/dist/serpent/stream.worker.d.ts +0 -32
  121. package/dist/serpent/stream.worker.js +0 -117
  122. /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`, `XChaCha20Seal`, `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,16 +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
- // Optional AAD: seal.encrypt(key, plaintext, aad) / seal.decrypt(key, ciphertext, aad)
112
- seal.dispose()
138
+ const key = SerpentCipher.keygen()
139
+ const blob = Seal.encrypt(SerpentCipher, key, plaintext)
140
+ const decrypted = Seal.decrypt(SerpentCipher, key, blob)
113
141
  ```
114
142
 
115
143
  ### Incremental streaming AEAD
@@ -117,67 +145,42 @@ seal.dispose()
117
145
  Use when you cannot buffer the full message before encrypting.
118
146
 
119
147
  ```typescript
120
- import { init, SerpentStreamSealer, SerpentStreamOpener, randomBytes } from 'leviathan-crypto'
121
-
122
- await init(['serpent', 'sha2'])
123
-
124
- const key = randomBytes(64)
125
- const sealer = new SerpentStreamSealer(key, 65536)
126
- const header = sealer.header() // send to opener before any chunks
127
-
128
- const chunk0 = sealer.seal(data0) // exactly chunkSize bytes
129
- const last = sealer.final(tail) // any size up to chunkSize; wipes key
130
-
131
- const opener = new SerpentStreamOpener(key, header)
132
- const pt0 = opener.open(chunk0) // throws on auth failure
133
- 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)
134
165
  ```
135
166
 
136
167
  ### Length-prefixed streaming (for files and buffered transports)
137
168
 
138
- Pass `{ framed: true }` to `SerpentStreamSealer`/`SerpentStreamOpener` for self-delimiting
139
- `u32be` length-prefixed framing. Use when chunks will be concatenated into a flat byte
140
- 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).
141
172
 
142
173
  ```typescript
143
- import { init, SerpentStreamSealer, SerpentStreamOpener, randomBytes } from 'leviathan-crypto'
144
-
145
- await init(['serpent', 'sha2'])
146
-
147
- const key = randomBytes(64)
148
- const sealer = new SerpentStreamSealer(key, 65536, { framed: true })
149
- const header = sealer.header()
150
-
151
- const frame0 = sealer.seal(data0) // u32be(len) || sealed chunk
152
- const last = sealer.final(tail)
153
-
154
- const opener = new SerpentStreamOpener(key, header, { framed: true })
155
- const chunks = opener.feed(frame0) // Uint8Array[] — throws on auth failure
174
+ const sealer = new SealStream(SerpentCipher, key, { framed: true })
156
175
  ```
157
176
 
158
- ### XChaCha20Seal (recommended)
159
-
160
- ```typescript
161
- import { init, XChaCha20Seal, randomBytes } from 'leviathan-crypto'
162
-
163
- await init(['chacha20'])
164
-
165
- const seal = new XChaCha20Seal(randomBytes(32)) // 32-byte key
166
- const ct = seal.encrypt(plaintext) // nonce(24) || ct || tag(16)
167
- const pt = seal.decrypt(ct) // throws on tamper
168
- seal.dispose()
169
- ```
170
-
171
- Binds key at construction, generates a fresh nonce per `encrypt()` call. No nonce
172
- management needed. For protocol interop requiring explicit nonces, use
173
- `XChaCha20Poly1305` directly.
174
-
175
177
  ### XChaCha20-Poly1305
176
178
 
177
179
  ```typescript
178
180
  import { init, XChaCha20Poly1305, randomBytes } from 'leviathan-crypto'
181
+ import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
179
182
 
180
- await init(['chacha20'])
183
+ await init({ chacha20: chacha20Wasm })
181
184
 
182
185
  const aead = new XChaCha20Poly1305()
183
186
  const key = randomBytes(32)
@@ -188,14 +191,15 @@ aead.dispose()
188
191
  ```
189
192
 
190
193
  Note: `encrypt()` returns ciphertext with the 16-byte Poly1305 tag appended.
191
- `decrypt()` expects the same concatenated format not separate ciphertext and tag.
194
+ `decrypt()` expects the same concatenated format, not separate ciphertext and tag.
192
195
 
193
196
  ### Hashing
194
197
 
195
198
  ```typescript
196
199
  import { init, SHA256, HMAC_SHA256 } from 'leviathan-crypto'
200
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
197
201
 
198
- await init(['sha2'])
202
+ await init({ sha2: sha2Wasm })
199
203
 
200
204
  const hasher = new SHA256()
201
205
  const digest = hasher.hash(data) // returns Uint8Array
@@ -210,8 +214,9 @@ mac.dispose()
210
214
 
211
215
  ```typescript
212
216
  import { init, SHAKE128 } from 'leviathan-crypto'
217
+ import { sha3Wasm } from 'leviathan-crypto/sha3/embedded'
213
218
 
214
- await init(['sha3'])
219
+ await init({ sha3: sha3Wasm })
215
220
 
216
221
  const xof = new SHAKE128()
217
222
  xof.absorb(data)
@@ -220,12 +225,39 @@ const out2 = xof.squeeze(32) // next 32 bytes — contiguous XOF stream
220
225
  xof.dispose()
221
226
  ```
222
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
+
223
253
  ### Fortuna CSPRNG
224
254
 
225
255
  ```typescript
226
256
  import { init, Fortuna } from 'leviathan-crypto'
257
+ import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
258
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
227
259
 
228
- await init(['serpent', 'sha2'])
260
+ await init({ serpent: serpentWasm, sha2: sha2Wasm })
229
261
 
230
262
  const fortuna = await Fortuna.create() // static factory — not new Fortuna()
231
263
  const bytes = fortuna.get(32)
@@ -244,7 +276,7 @@ cipher.decrypt(key, iv, ciphertext) // correct
244
276
  ```
245
277
 
246
278
  `SerpentCbc` is unauthenticated. Always pair with `HMAC_SHA256`
247
- (Encrypt-then-MAC) or use `SerpentSeal` instead.
279
+ (Encrypt-then-MAC) or use `Seal` with `SerpentCipher` instead.
248
280
 
249
281
  ---
250
282
 
@@ -261,10 +293,11 @@ const safe = constantTimeEqual(a, b) // constant-time equality — never use =
261
293
  wipe(key) // zero a Uint8Array in place
262
294
  ```
263
295
 
264
- `hasSIMD()` returns `true` if the runtime supports WebAssembly SIMD. It is used
265
- internally you do not need to call it. SIMD acceleration is fully transparent:
266
- `SerpentCtr.encryptChunk`, `SerpentCbc.decrypt`, and `ChaCha20.encryptChunk` all
267
- 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.
268
301
 
269
302
  ---
270
303
 
@@ -274,12 +307,14 @@ The complete API reference ships in `docs/` alongside this file:
274
307
 
275
308
  | File | Contents |
276
309
  |------|----------|
277
- | `docs/serpent.md` | `SerpentSeal`, `SerpentStream`, `SerpentStreamPool`, `SerpentStreamSealer`, `SerpentStreamOpener`, `Serpent`, `SerpentCtr`, `SerpentCbc` |
278
- | `docs/chacha20.md` | `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `XChaCha20Seal`, `XChaCha20Poly1305Pool` |
310
+ | `docs/serpent.md` | `SerpentCipher`, `Serpent`, `SerpentCtr`, `SerpentCbc` |
311
+ | `docs/chacha20.md` | `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `XChaCha20Cipher` |
279
312
  | `docs/sha2.md` | `SHA256`, `SHA384`, `SHA512`, `HMAC_SHA256`, `HMAC_SHA384`, `HMAC_SHA512`, `HKDF_SHA256`, `HKDF_SHA512` |
280
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 |
281
316
  | `docs/fortuna.md` | `Fortuna` CSPRNG |
282
317
  | `docs/init.md` | `init()` API, loading modes, subpath imports |
283
318
  | `docs/utils.md` | Encoding helpers, `constantTimeEqual`, `wipe`, `randomBytes` |
284
- | `docs/types.md` | `Hash`, `KeyedHash`, `Blockcipher`, `Streamcipher`, `AEAD` interfaces |
319
+ | `docs/types.md` | `Hash`, `KeyedHash`, `Blockcipher`, `Streamcipher`, `AEAD` interfaces; `CipherSuite`, `DerivedKeys`, `SealStreamOpts`, `PoolOpts`, `WasmSource` |
285
320
  | `docs/architecture.md` | Module structure, WASM layer, three-tier design |