leviathan-crypto 1.4.0 → 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 (119) hide show
  1. package/CLAUDE.md +129 -94
  2. package/README.md +166 -223
  3. package/SECURITY.md +85 -45
  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 -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 +320 -0
  17. package/dist/docs/architecture.md +419 -285
  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 +93 -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/errors.d.ts +10 -0
  32. package/dist/errors.js +38 -0
  33. package/dist/fortuna.d.ts +0 -6
  34. package/dist/fortuna.js +5 -5
  35. package/dist/index.d.ts +25 -9
  36. package/dist/index.js +36 -7
  37. package/dist/init.d.ts +3 -7
  38. package/dist/init.js +18 -35
  39. package/dist/keccak/embedded.d.ts +1 -0
  40. package/dist/keccak/embedded.js +27 -0
  41. package/dist/keccak/index.d.ts +4 -0
  42. package/dist/keccak/index.js +31 -0
  43. package/dist/kyber/embedded.d.ts +1 -0
  44. package/dist/kyber/embedded.js +27 -0
  45. package/dist/kyber/indcpa.d.ts +49 -0
  46. package/dist/kyber/indcpa.js +352 -0
  47. package/dist/kyber/index.d.ts +38 -0
  48. package/dist/kyber/index.js +150 -0
  49. package/dist/kyber/kem.d.ts +21 -0
  50. package/dist/kyber/kem.js +160 -0
  51. package/dist/kyber/params.d.ts +14 -0
  52. package/dist/kyber/params.js +37 -0
  53. package/dist/kyber/suite.d.ts +13 -0
  54. package/dist/kyber/suite.js +93 -0
  55. package/dist/kyber/types.d.ts +98 -0
  56. package/dist/kyber/types.js +25 -0
  57. package/dist/kyber/validate.d.ts +19 -0
  58. package/dist/kyber/validate.js +68 -0
  59. package/dist/kyber.wasm +0 -0
  60. package/dist/loader.d.ts +15 -6
  61. package/dist/loader.js +65 -21
  62. package/dist/serpent/cipher-suite.d.ts +4 -0
  63. package/dist/serpent/cipher-suite.js +121 -0
  64. package/dist/serpent/embedded.d.ts +1 -0
  65. package/dist/serpent/embedded.js +27 -0
  66. package/dist/serpent/index.d.ts +6 -37
  67. package/dist/serpent/index.js +9 -118
  68. package/dist/serpent/pool-worker.d.ts +1 -0
  69. package/dist/serpent/pool-worker.js +202 -0
  70. package/dist/serpent/serpent-cbc.d.ts +30 -0
  71. package/dist/serpent/serpent-cbc.js +136 -0
  72. package/dist/sha2/embedded.d.ts +1 -0
  73. package/dist/sha2/embedded.js +27 -0
  74. package/dist/sha2/hkdf.js +6 -2
  75. package/dist/sha2/index.d.ts +3 -2
  76. package/dist/sha2/index.js +3 -4
  77. package/dist/sha3/embedded.d.ts +1 -0
  78. package/dist/sha3/embedded.js +27 -0
  79. package/dist/sha3/index.d.ts +3 -2
  80. package/dist/sha3/index.js +3 -4
  81. package/dist/stream/constants.d.ts +6 -0
  82. package/dist/stream/constants.js +30 -0
  83. package/dist/stream/header.d.ts +9 -0
  84. package/dist/stream/header.js +77 -0
  85. package/dist/stream/index.d.ts +7 -0
  86. package/dist/stream/index.js +27 -0
  87. package/dist/stream/open-stream.d.ts +21 -0
  88. package/dist/stream/open-stream.js +146 -0
  89. package/dist/stream/seal-stream-pool.d.ts +38 -0
  90. package/dist/stream/seal-stream-pool.js +391 -0
  91. package/dist/stream/seal-stream.d.ts +20 -0
  92. package/dist/stream/seal-stream.js +142 -0
  93. package/dist/stream/seal.d.ts +9 -0
  94. package/dist/stream/seal.js +75 -0
  95. package/dist/stream/types.d.ts +24 -0
  96. package/dist/stream/types.js +26 -0
  97. package/dist/utils.d.ts +7 -2
  98. package/dist/utils.js +49 -3
  99. package/dist/wasm-source.d.ts +12 -0
  100. package/dist/wasm-source.js +26 -0
  101. package/package.json +13 -5
  102. package/dist/chacha20/pool.d.ts +0 -52
  103. package/dist/chacha20/pool.js +0 -178
  104. package/dist/chacha20/pool.worker.js +0 -37
  105. package/dist/chacha20/stream-sealer.d.ts +0 -49
  106. package/dist/chacha20/stream-sealer.js +0 -327
  107. package/dist/docs/chacha20_pool.md +0 -309
  108. package/dist/docs/wasm.md +0 -194
  109. package/dist/serpent/seal.d.ts +0 -8
  110. package/dist/serpent/seal.js +0 -72
  111. package/dist/serpent/stream-pool.d.ts +0 -48
  112. package/dist/serpent/stream-pool.js +0 -275
  113. package/dist/serpent/stream-sealer.d.ts +0 -55
  114. package/dist/serpent/stream-sealer.js +0 -342
  115. package/dist/serpent/stream.d.ts +0 -28
  116. package/dist/serpent/stream.js +0 -205
  117. package/dist/serpent/stream.worker.d.ts +0 -32
  118. package/dist/serpent/stream.worker.js +0 -117
  119. /package/dist/chacha20/{pool.worker.d.ts → pool-worker.d.ts} +0 -0
package/dist/docs/init.md CHANGED
@@ -1,50 +1,51 @@
1
1
  # Module Initialization and WASM Loading
2
2
 
3
- > [!NOTE]
4
- > The `init()` function is the entry point for leviathan-crypto. You must call it
5
- > before using any cryptographic class. It loads the WebAssembly modules that
6
- > perform the actual cryptographic work, caches them in memory, and makes them
3
+ > [!IMPORTANT]
4
+ > Call `init()` before using any cryptographic class. It loads the WebAssembly
5
+ > modules that perform cryptographic work, caches them in memory, and makes them
7
6
  > available to all wrapper classes.
8
7
 
8
+ > ### Table of Contents
9
+ > - [Overview](#overview)
10
+ > - [Security Notes](#security-notes)
11
+ > - [API Reference](#api-reference)
12
+ > - [Usage Examples](#usage-examples)
13
+ > - [Error Conditions](#error-conditions)
14
+
15
+ ---
16
+
9
17
  ## Overview
10
18
 
11
19
  leviathan-crypto runs all cryptographic computation inside WebAssembly modules.
12
- These modules are not available automatically, they need to be loaded and
13
- compiled before any cryptographic class (`Serpent`, `SHA256`, `ChaCha20`, etc.)
14
- can be used.
15
-
16
- `init()` handles this process for you. You tell it which modules you need, and
17
- it loads them. After that, every class backed by those modules is ready to use.
18
-
19
- Key properties:
20
-
21
- - **Required before use.** If you try to create a cryptographic class before
22
- calling `init()`, you will get a clear error telling you what to do.
23
- - **Three loading modes.** Embedded (default, zero-config), streaming
24
- (better performance for large apps), and manual (full control over how
25
- binaries are provided).
26
- - **Idempotent.** Calling `init()` multiple times with the same module is
27
- safe, it skips modules that are already loaded. This means you can call
28
- `init()` in multiple places in your application without worrying about
29
- redundant work.
30
- - **Async.** `init()` returns a Promise. Use `await` or `.then()` before
31
- proceeding.
20
+ These modules are not loaded automatically. You tell `init()` which modules you
21
+ need and provide a source for each one. After that, every class backed by those
22
+ modules is ready to use.
23
+
24
+ `init()` is idempotent. Calling it multiple times with the same module is safe.
25
+ It skips modules already loaded, so you can call `init()` in multiple places
26
+ without redundant work. It returns a Promise. Always `await` it before
27
+ constructing any class.
28
+
29
+ If you try to use a cryptographic class before calling `init()`, you get a clear
30
+ error telling you exactly which module to load.
32
31
 
33
32
  ---
34
33
 
35
34
  ## Security Notes
36
35
 
37
- - **WASM runs outside the JavaScript JIT.** Cryptographic code executes in
38
- WebAssembly, which provides more predictable execution timing compared to
39
- optimized JavaScript. This reduces the risk of timing side-channels
40
- introduced by the JIT compiler.
41
- - **Independent memory per module.** Each WASM module gets its own linear
42
- memory (3 pages, 192 KB). Key material loaded into one module's memory
43
- cannot be read by another module. There is no shared memory between modules.
44
- - **No silent auto-initialization.** Every wrapper class checks that its
45
- backing module has been initialized. If it hasn't, the class throws an
46
- error immediately rather than silently loading the module in the
47
- background. This makes initialization explicit and auditable.
36
+ **WASM runs outside the JavaScript JIT.** Cryptographic code executes in
37
+ WebAssembly, which provides more predictable execution timing than optimized
38
+ JavaScript. This reduces the risk of timing side-channels introduced by the
39
+ JIT compiler.
40
+
41
+ **Each module gets its own linear memory.** Every WASM module receives 3 pages
42
+ (192 KB) of independent memory. Key material in one module cannot be read by
43
+ another. There is no shared memory between modules.
44
+
45
+ **No silent auto-initialization.** Every wrapper class checks that its backing
46
+ module has been initialized. If it has not, the class throws immediately rather
47
+ than loading the module in the background. Initialization is explicit and
48
+ auditable.
48
49
 
49
50
  ---
50
51
 
@@ -53,236 +54,230 @@ Key properties:
53
54
  ### Types
54
55
 
55
56
  ```typescript
56
- type Module = 'serpent' | 'chacha20' | 'sha2' | 'sha3'
57
+ type Module = 'serpent' | 'chacha20' | 'sha2' | 'sha3' | 'keccak' | 'kyber'
57
58
  ```
58
59
 
59
- The four WASM module families. Each one backs a group of related classes:
60
-
61
- | Module | Classes it enables |
62
- |-------------|----------------------------------------------------------------|
63
- | `'serpent'` | `Serpent`, `SerpentCbc`, `SerpentCtr`, `SerpentSeal`, `SerpentStream`, `SerpentStreamPool`, `Fortuna` |
64
- | `'chacha20'`| `ChaCha20`, `XChaCha20Poly1305` |
65
- | `'sha2'` | `SHA256`, `SHA384`, `SHA512`, `HMAC` (SHA-2 based), `Fortuna` |
66
- | `'sha3'` | `SHA3`, `SHAKE128`, `SHAKE256` |
67
-
68
- ```typescript
69
- type Mode = 'embedded' | 'streaming' | 'manual'
70
- ```
60
+ The WASM module families. Each one backs a group of related classes.
61
+ `'keccak'` is an alias for `'sha3'` — same WASM binary, same instance slot.
71
62
 
72
- How the WASM binary is loaded. See the [Usage Examples](#usage-examples) section
73
- for when to use each mode.
63
+ | Module | Classes it enables |
64
+ |---|---|
65
+ | `'serpent'` | `Serpent`, `SerpentCbc`, `SerpentCtr` |
66
+ | `'serpent'` + `'sha2'` | `SerpentCipher`, `Seal` (with `SerpentCipher`), `SealStream`, `OpenStream`, `Fortuna` — see [aead.md](./aead.md) |
67
+ | `'chacha20'` | `ChaCha20`, `ChaCha20Poly1305`, `XChaCha20Poly1305` |
68
+ | `'chacha20'` + `'sha2'` | `XChaCha20Cipher`, `Seal` (with `XChaCha20Cipher`), `SealStream`, `OpenStream` — see [aead.md](./aead.md) |
69
+ | `'sha2'` | `SHA256`, `SHA384`, `SHA512`, `HMAC` (SHA-2 based), `HKDF` |
70
+ | `'sha3'` / `'keccak'` | `SHA3_224`, `SHA3_256`, `SHA3_384`, `SHA3_512`, `SHAKE128`, `SHAKE256` |
71
+ | `'kyber'` | `MlKem512`, `MlKem768`, `MlKem1024` (also requires `'sha3'`) — see [kyber.md](./kyber.md) |
72
+ | `'kyber'` + `'sha3'` + inner cipher modules | `KyberSuite`, `MlKem512`, `MlKem768`, `MlKem1024` — see [kyber.md](./kyber.md) |
74
73
 
75
74
  ```typescript
76
- interface InitOpts {
77
- wasmUrl?: URL | string
78
- wasmBinary?: Partial<Record<Module, Uint8Array | ArrayBuffer>>
79
- }
75
+ type WasmSource = string | URL | ArrayBuffer | Uint8Array
76
+ | WebAssembly.Module | Response | Promise<Response>
80
77
  ```
81
78
 
82
- Optional configuration object. Which fields are required depends on the mode:
79
+ A value that resolves to a WASM binary. The loading strategy is inferred from
80
+ the type:
83
81
 
84
- | Mode | Required fields | Description |
85
- |-------------|--------------------|-------------------------------------------------|
86
- | `embedded` | (none) | Binaries are bundled in the package |
87
- | `streaming` | `wasmUrl` | Base URL where `.wasm` files are served |
88
- | `manual` | `wasmBinary` | A map of module names to raw WASM binary data |
82
+ | Source type | What happens |
83
+ |---|---|
84
+ | `string` | Treated as a gzip+base64 embedded blob. Decoded and decompressed. |
85
+ | `URL` | Fetched with streaming compilation (`WebAssembly.compileStreaming`). |
86
+ | `ArrayBuffer` | Compiled directly via `WebAssembly.instantiate`. |
87
+ | `Uint8Array` | Compiled directly via `WebAssembly.instantiate`. |
88
+ | `WebAssembly.Module` | Already compiled. Instantiated immediately. |
89
+ | `Response` / `Promise<Response>` | Streaming compilation via `WebAssembly.instantiateStreaming`. |
89
90
 
90
91
  ---
91
92
 
92
93
  ### Functions
93
94
 
94
- #### `init(modules, mode?, opts?)` — public API (exported from root barrel)
95
-
96
- > [!NOTE]
97
- > `init()` is no longer exported from `init.ts`. It is defined in the
98
- > root barrel (`src/ts/index.ts`) and dispatches to each module's own init
99
- > function (`serpentInit`, `chacha20Init`, `sha2Init`, `sha3Init`).
100
- > See [README.md](./README.md) for details.
101
-
102
- The public `init()` signature is unchanged:
95
+ #### init()
103
96
 
104
97
  ```typescript
105
98
  async function init(
106
- modules: Module | Module[],
107
- mode?: Mode, // default: 'embedded'
108
- opts?: InitOpts,
99
+ sources: Partial<Record<Module, WasmSource>>,
109
100
  ): Promise<void>
110
101
  ```
111
102
 
103
+ Initializes one or more WASM modules. Pass an object mapping module names to
104
+ their `WasmSource`. Only modules present in the object are loaded. Others are
105
+ left untouched.
106
+
112
107
  ---
113
108
 
114
- #### `initModule(mod, embeddedThunk, mode?, opts?)` — internal
109
+ #### Per-module init functions
110
+
111
+ Each module subpath exports its own init function for tree-shakeable imports.
112
+ These take a single `WasmSource` argument.
115
113
 
116
114
  ```typescript
117
- async function initModule(
118
- mod: Module,
119
- embeddedThunk: () => Promise<string>,
120
- mode?: Mode, // default: 'embedded'
121
- opts?: InitOpts,
122
- ): Promise<void>
115
+ async function serpentInit(source: WasmSource): Promise<void>
116
+ async function chacha20Init(source: WasmSource): Promise<void>
117
+ async function sha2Init(source: WasmSource): Promise<void>
118
+ async function sha3Init(source: WasmSource): Promise<void>
119
+ async function keccakInit(source: WasmSource): Promise<void>
120
+ async function kyberInit(source: WasmSource): Promise<void>
123
121
  ```
124
122
 
125
- Internal initialization function. Called by each module's own init function
126
- (`serpentInit`, `chacha20Init`, `sha2Init`, `sha3Init`), not by consumers
127
- directly. Each module passes its own embedded thunk so the
128
- dependency graph stays isolated per module, enabling tree-shaking.
123
+ Each function initializes only its own WASM module, keeping other modules out
124
+ of your bundle.
125
+
126
+ ---
127
+
128
+ #### Embedded subpath exports
129
129
 
130
- **Parameters:**
130
+ The `/embedded` subpath for each module provides the gzip+base64 blob as a
131
+ ready-to-use `WasmSource`:
131
132
 
132
- - `mod`: The module name to initialize.
133
- - `embeddedThunk`: A function that returns a Promise resolving to the
134
- base64-encoded WASM binary. Each module defines its own thunk pointing to
135
- its own embedded file.
136
- - `mode`: The loading strategy. Defaults to `'embedded'`.
137
- - `opts`: Configuration for `'streaming'` and `'manual'` modes.
133
+ | Subpath | Export |
134
+ |---|---|
135
+ | `leviathan-crypto/serpent/embedded` | `serpentWasm` |
136
+ | `leviathan-crypto/chacha20/embedded` | `chacha20Wasm` |
137
+ | `leviathan-crypto/sha2/embedded` | `sha2Wasm` |
138
+ | `leviathan-crypto/sha3/embedded` | `sha3Wasm` |
139
+ | `leviathan-crypto/keccak/embedded` | `keccakWasm` |
140
+ | `leviathan-crypto/kyber/embedded` | `kyberWasm` |
138
141
 
139
- **Returns:** A Promise that resolves when the module is loaded and cached.
142
+ `keccakWasm` and `sha3Wasm` are the same gzip+base64 blob. Both point to `sha3.wasm`.
140
143
 
141
- **Idempotent:** If the module is already initialized, returns immediately.
144
+ > [!NOTE]
145
+ > `MlKem512`, `MlKem768`, and `MlKem1024` require both `kyber` and `sha3`
146
+ > (or `keccak`) to be initialized. The kyber module handles polynomial arithmetic.
147
+ > The sha3 module provides the Keccak sponge operations used for key generation
148
+ > and encapsulation.
149
+
150
+ ---
142
151
 
143
- **Throws:**
152
+ #### isInitialized()
144
153
 
145
- - `'leviathan-crypto: streaming mode requires wasmUrl'` if mode is
146
- `'streaming'` and `opts.wasmUrl` is not provided.
147
- - `'leviathan-crypto: manual mode requires wasmBinary['<mod>']'` if mode
148
- is `'manual'` and the binary for the requested module is missing.
149
- - `'leviathan-crypto: unknown mode '<mode>''` if an invalid mode string
150
- is passed.
154
+ ```typescript
155
+ function isInitialized(mod: Module): boolean
156
+ ```
157
+
158
+ Returns `true` if the given module has been loaded and cached. Exported from
159
+ both `init.ts` and the root barrel.
151
160
 
152
161
  > [!NOTE]
153
- > The previous design exported `init()` from `init.ts`,
154
- > which contained a central `embeddedLoaders` record mapping every module name
155
- > to its embedded import. This meant any consumer importing `init()`,
156
- > even through a subpath like `leviathan-crypto/serpent`, pulled all four
157
- > embedded binaries into the bundle. Moving `init()` to the root barrel and
158
- > giving each module its own thunk isolates the dependency graph so bundlers
159
- > can tree-shake unused modules, optimizing build size.
162
+ > `isInitialized` is a diagnostic indicator, not a control mechanism. Use
163
+ > `init()` to load modules. Do not guard calls on this value.
160
164
 
161
165
  ---
162
166
 
163
- #### `getInstance(mod)`
167
+ #### getInstance()
164
168
 
165
169
  ```typescript
166
170
  function getInstance(mod: Module): WebAssembly.Instance
167
171
  ```
168
172
 
169
- Returns the cached WebAssembly instance for a module. This is used internally
170
- by wrapper classes, you do not normally need to call it yourself.
173
+ Returns the cached WebAssembly instance for a module. Used internally by
174
+ wrapper classes. You do not normally need to call this yourself.
171
175
 
172
- **Throws:**
173
- `'leviathan-crypto: call init(['<mod>']) before using this class'` if the
174
- module has not been initialized.
176
+ Throws `'leviathan-crypto: call init({ <mod>: ... }) before using this class'`
177
+ if the module has not been initialized.
175
178
 
176
179
  ---
177
180
 
178
- #### `isInitialized(mod)`
181
+ #### compileWasm()
179
182
 
180
183
  ```typescript
181
- function isInitialized(mod: Module): boolean
184
+ async function compileWasm(source: WasmSource): Promise<WebAssembly.Module>
182
185
  ```
183
186
 
184
- Returns `true` if the given module has been loaded and cached (read-only).
185
- Exported from both `init.ts` and the root barrel (`src/ts/index.ts`).
186
-
187
- > [!NOTE]
188
- > `isInitialized` is a diagnostic indicator only — not a control mechanism.
189
- > Use `init()` to initialize modules; do not guard calls on this value.
187
+ Compiles a `WasmSource` to a `WebAssembly.Module` without instantiating it.
188
+ Used by pool infrastructure to send compiled modules to workers. See
189
+ [loader.md](./loader.md) for details.
190
190
 
191
191
  ---
192
192
 
193
- #### `_resetForTesting()`
193
+ #### _resetForTesting()
194
194
 
195
195
  ```typescript
196
196
  function _resetForTesting(): void
197
197
  ```
198
198
 
199
- Clears all cached WASM instances. This is a testing utility, it allows test
200
- suites to reset the initialization state between test runs. Do not use this in
199
+ Clears all cached WASM instances. Testing utility only. Do not use in
201
200
  production code.
202
201
 
203
202
  ---
204
203
 
205
204
  ## Usage Examples
206
205
 
207
- ### Embedded mode (default: simplest)
206
+ ### Embedded init (most common)
208
207
 
209
- This is the recommended mode for most applications. The WASM binaries are
210
- bundled inside the package as base64-encoded strings, so there are no extra
211
- files to serve or fetch.
208
+ The WASM binaries are bundled inside the package as gzip+base64 strings. Import
209
+ the blob from the module's `/embedded` subpath and pass it to `init()`.
212
210
 
213
211
  ```typescript
214
- import { init, SHA256 } from 'leviathan-crypto'
215
-
216
- await init('sha2')
212
+ import { init } from 'leviathan-crypto'
213
+ import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
214
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
217
215
 
218
- const hash = new SHA256()
219
- const digest = hash.hash(myData)
216
+ await init({ serpent: serpentWasm, sha2: sha2Wasm })
220
217
  ```
221
218
 
222
- ### Initializing multiple modules at once
219
+ ### Per-module init (tree-shaking)
223
220
 
224
- Pass an array to load several modules in a single call:
221
+ Use the subpath init function when you need one module and want the smallest
222
+ possible bundle:
225
223
 
226
224
  ```typescript
227
- import { init, Serpent, SHA256 } from 'leviathan-crypto'
225
+ import { serpentInit } from 'leviathan-crypto/serpent'
226
+ import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
228
227
 
229
- await init(['serpent', 'sha2'])
230
-
231
- const cipher = new Serpent(key)
232
- const hash = new SHA256()
228
+ await serpentInit(serpentWasm)
233
229
  ```
234
230
 
235
- ### Streaming mode (better performance for large apps)
236
-
237
- Streaming mode fetches `.wasm` files from a URL and uses the browser's
238
- streaming compilation (`WebAssembly.instantiateStreaming`). This can be
239
- faster than embedded mode because the browser can begin compiling the WASM
240
- binary while it is still downloading.
231
+ ### Keccak alias (for ML-KEM)
241
232
 
242
- You must serve the `.wasm` files from your web server and provide the base
243
- URL where they are hosted. The files must be served with the
244
- `Content-Type: application/wasm` header.
233
+ `'keccak'` is an alias for `'sha3'`. Both resolve to the same WASM binary and
234
+ the same instance slot. Use it when you want the semantically correct primitive
235
+ name for ML-KEM consumers:
245
236
 
246
237
  ```typescript
247
- import { init, Serpent } from 'leviathan-crypto'
248
-
249
- await init('serpent', 'streaming', {
250
- wasmUrl: '/static/wasm/'
251
- })
238
+ import { init } from 'leviathan-crypto'
239
+ import { keccakWasm } from 'leviathan-crypto/keccak/embedded'
252
240
 
253
- const cipher = new Serpent(key)
241
+ await init({ keccak: keccakWasm })
242
+ // isInitialized('sha3') === true — same slot
243
+ // isInitialized('keccak') === true — alias resolves symmetrically
254
244
  ```
255
245
 
256
- The library knows the filename for each module (e.g. `serpent.wasm`,
257
- `sha2.wasm`). You only need to provide the directory URL.
246
+ Or via the subpath directly:
258
247
 
259
- ### Manual mode (full control)
248
+ ```typescript
249
+ import { keccakInit, SHAKE128, SHA3_256 } from 'leviathan-crypto/keccak'
250
+ import { keccakWasm } from 'leviathan-crypto/keccak/embedded'
251
+
252
+ await keccakInit(keccakWasm)
253
+ ```
260
254
 
261
- Manual mode lets you provide the raw WASM binary yourself. This is useful if
262
- you have a custom build pipeline, want to load binaries from a non-standard
263
- source, or need to verify the binary before passing it to the library.
255
+ ### URL-based loading (CDN)
256
+
257
+ Pass a `URL` to fetch and compile the `.wasm` file via streaming compilation.
258
+ The server must respond with `Content-Type: application/wasm`.
264
259
 
265
260
  ```typescript
266
- import { init, SHA256 } from 'leviathan-crypto'
261
+ await init({ serpent: new URL('https://unpkg.com/leviathan-crypto/dist/serpent.wasm') })
262
+ ```
267
263
 
268
- // Load the binary however you like
269
- const wasmBinary = await fetch('/my-custom-path/sha2.wasm')
270
- .then(r => r.arrayBuffer())
264
+ ### Pre-compiled module (edge runtimes)
271
265
 
272
- await init('sha2', 'manual', {
273
- wasmBinary: { sha2: new Uint8Array(wasmBinary) }
274
- })
266
+ If you have already compiled the binary, for example from a KV cache, pass the
267
+ `WebAssembly.Module` directly:
275
268
 
276
- const hash = new SHA256()
269
+ ```typescript
270
+ const mod = await WebAssembly.compile(bytes)
271
+ await init({ serpent: mod })
277
272
  ```
278
273
 
279
- ### Checking if a module is initialized
274
+ ### Checking initialization state
280
275
 
281
276
  ```typescript
282
277
  import { isInitialized } from 'leviathan-crypto'
283
278
 
284
279
  if (!isInitialized('sha2')) {
285
- await init('sha2')
280
+ // handle accordingly
286
281
  }
287
282
  ```
288
283
 
@@ -290,19 +285,18 @@ if (!isInitialized('sha2')) {
290
285
 
291
286
  ## Error Conditions
292
287
 
293
- | Situation | What happens |
294
- |-------------------------------------------|----------------------------------------------------------------------------------------------|
295
- | Using a class before calling `init()` | Throws: `"leviathan-crypto: call init(['<mod>']) before using this class"` |
296
- | Streaming mode without `wasmUrl` | Throws: `"leviathan-crypto: streaming mode requires wasmUrl"` |
297
- | Manual mode without the needed binary | Throws: `"leviathan-crypto: manual mode requires wasmBinary['<mod>']"` |
298
- | Unknown mode string | Throws: `"leviathan-crypto: unknown mode '<mode>'"` |
299
- | Calling `init()` for an already-loaded module | No error. Module is silently skipped (idempotent behavior) |
288
+ | Situation | What happens |
289
+ |---|---|
290
+ | Using a class before calling `init()` | Throws: `"leviathan-crypto: call init({ ${mod}: ... }) before using this class"` |
291
+ | Invalid `WasmSource` (null, number, etc.) | Throws: `TypeError` with a descriptive message |
292
+ | Empty string source | Throws: `"leviathan-crypto: invalid WasmSource empty string"` |
293
+ | Calling `init()` for an already-loaded module | No error. Module is silently skipped. |
300
294
 
301
295
  ---
302
296
 
303
297
  > ## Cross-References
304
298
  >
305
299
  > - [index](./README.md) — Project Documentation index
306
- > - [loader](./loader.md) — WASM binary loading strategies (internal details)
307
300
  > - [architecture](./architecture.md) — architecture overview, module relationships, buffer layouts, and build pipeline
301
+ > - [loader](./loader.md) — WASM binary loading strategies (internal details)
308
302
  > - [wasm](./wasm.md) — WebAssembly primer: modules, instances, memory, and the init gate