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
@@ -5,205 +5,150 @@
5
5
  > instantiation of WebAssembly binaries. You normally do not interact
6
6
  > with this module directly.
7
7
 
8
+ > ### Table of Contents
9
+ > - [Overview](#overview)
10
+ > - [Security Notes](#security-notes)
11
+ > - [API Reference](#api-reference)
12
+ > - [Internal Details](#internal-details)
13
+
14
+ ---
15
+
8
16
  ## Overview
9
17
 
10
- When you call `init()`, it delegates the work of obtaining and compiling the
11
- WASM binary to the loader. The loader supports three strategies:
18
+ When you call [`init()`](./init.md), it delegates the work of obtaining and compiling the
19
+ WASM binary to the loader. The loading strategy is inferred from the
20
+ `WasmSource` type, so no mode string is required:
21
+
22
+ **Embedded string.** gzip-compressed, base64-encoded WASM bundled in the package. Decoded and decompressed at [`init()`](./init.md) time using `DecompressionStream`. No network requests. This is the default and simplest option.
23
+
24
+ **URL.** Fetches the `.wasm` file and uses the browser's streaming compilation API. The browser can start compiling while still downloading.
25
+
26
+ **ArrayBuffer / Uint8Array.** Raw WASM bytes, compiled directly.
27
+
28
+ **WebAssembly.Module.** Already compiled. Instantiated immediately. Useful for edge runtimes and KV-cached modules.
12
29
 
13
- - **Embedded** -- The WASM binary is bundled in the package as a
14
- gzip-compressed, base64-encoded string. The loader decodes and decompresses
15
- it at `init()` time using `DecompressionStream`. No network requests are
16
- made. This is the default and simplest option.
17
- - **Streaming** -- The loader fetches the `.wasm` file from a URL you provide
18
- and uses the browser's streaming compilation API. The browser can start
19
- compiling the binary while it is still downloading, which can improve
20
- load times for larger modules.
21
- - **Manual** -- You provide the raw binary data (as a `Uint8Array` or
22
- `ArrayBuffer`) and the loader instantiates it directly. This gives you
23
- full control over how the binary is obtained.
30
+ **Response / Promise\<Response\>.** Streaming compilation from an in-flight or deferred fetch.
24
31
 
25
- All three strategies produce the same result: a `WebAssembly.Instance` that
26
- the wrapper classes use to perform cryptographic operations.
32
+ All strategies produce the same result: a `WebAssembly.Instance` that the
33
+ wrapper classes use to perform cryptographic operations.
27
34
 
28
35
  ---
29
36
 
30
37
  ## Security Notes
31
38
 
32
- - **Embedded mode requires no network access.** The WASM binary is part of
33
- the installed package. This eliminates the risk of a compromised CDN or
34
- man-in-the-middle attack altering the binary at load time.
35
- - **Streaming mode requires correct MIME type.** The `.wasm` files must be
36
- served with `Content-Type: application/wasm`. This is a browser requirement
37
- for `WebAssembly.instantiateStreaming`. If the header is missing or wrong,
38
- the browser will reject the response.
39
- - **Manual mode places integrity responsibility on you.** The loader
40
- instantiates whatever binary you provide. If you use manual mode, you are
41
- responsible for verifying that the binary is authentic and unmodified.
42
- - **Each module gets its own memory.** Every instantiation creates a fresh
43
- `WebAssembly.Memory` with 3 pages (192 KB). Modules cannot share or
44
- access each other's memory. This means key material loaded into one
45
- module's memory space is isolated from all other modules.
39
+ **Embedded mode requires no network access.** The WASM binary is part of the installed package. This eliminates the risk of a compromised CDN or man-in-the-middle attack altering the binary at load time.
40
+
41
+ **URL-based loading requires correct MIME type.** The `.wasm` files must be served with `Content-Type: application/wasm`. This is a browser requirement for `WebAssembly.instantiateStreaming`. If the header is missing or wrong, the browser will reject the response.
42
+
43
+ **Raw binary / Module sources place integrity responsibility on you.** The loader instantiates whatever binary you provide. If you supply your own bytes or pre-compiled Module, you are responsible for verifying authenticity.
44
+
45
+ **Each module gets its own memory.** Every instantiation creates a fresh `WebAssembly.Memory` with 3 pages (192 KB). Modules cannot share or access each other's memory. Key material in one module's memory space is isolated from all other modules.
46
46
 
47
47
  ---
48
48
 
49
49
  ## API Reference
50
50
 
51
51
  These functions are exported from `loader.ts` and called by `init.ts`. They
52
- are not part of the public API -- they are documented here for completeness
53
- and for contributors working on the internals.
54
-
55
- ### `loadEmbedded(thunk)`
52
+ are not part of the public API. They are documented here for completeness and for contributors working on the internals.
56
53
 
54
+ ### `loadWasm(source)`
57
55
  ```typescript
58
- async function loadEmbedded(
59
- thunk: () => Promise<string>,
60
- ): Promise<WebAssembly.Instance>
56
+ async function loadWasm(source: WasmSource): Promise<WebAssembly.Instance>
61
57
  ```
62
58
 
63
- Loads a WASM module from an embedded base64-encoded string obtained by calling
64
- the provided thunk.
65
-
66
- **How it works:**
67
-
68
- 1. Calls the thunk, which dynamically imports the embedded binary file and
69
- returns the gzip-compressed, base64-encoded WASM string.
70
- 2. Decodes the base64 string and decompresses the result using
71
- `DecompressionStream('gzip')`.
72
- 3. Instantiates the WASM module with a fresh 3-page `WebAssembly.Memory`.
73
-
74
- The thunk is provided by each module's own `init()` function (e.g.
75
- `serpent/index.ts` passes `() => import('../embedded/serpent.js').then(m => m.WASM_GZ_BASE64)`).
76
- This design means `loader.ts` has no knowledge of module names or embedded file
77
- paths -- each module owns its own embedded import, enabling tree-shaking.
59
+ Loads and instantiates a WASM module from any accepted source type. Each
60
+ instance receives a fresh 3-page `WebAssembly.Memory`.
78
61
 
79
- **Parameters:**
62
+ **Source type handling:**
80
63
 
81
- - `thunk` -- A function that returns a Promise resolving to a gzip-compressed,
82
- base64-encoded WASM binary string. Each module's `index.ts` defines its own
83
- thunk pointing to its own embedded file.
64
+ | Source type | Loading path |
65
+ |--------------------------------|----------------------------------------------------------------------|
66
+ | `string` | Decoded from gzip+base64 via `decodeWasm()`, then `WebAssembly.instantiate()`. |
67
+ | `URL` | `WebAssembly.instantiateStreaming(fetch(url))`. |
68
+ | `ArrayBuffer` | `WebAssembly.instantiate()`. |
69
+ | `Uint8Array` | `WebAssembly.instantiate()`. |
70
+ | `WebAssembly.Module` | `WebAssembly.instantiate(module, imports)`. |
71
+ | `Response` / `Promise<Response>` | `WebAssembly.instantiateStreaming()`. |
84
72
 
85
- **Returns:** A Promise that resolves to a `WebAssembly.Instance`.
73
+ **Throws:**
86
74
 
87
- **Error conditions:**
75
+ - `TypeError` if `source` is null, numeric, or otherwise unrecognised.
76
+ - `TypeError` with `"empty string"` if `source` is an empty string.
88
77
 
89
- - If the embedded binary file does not exist, this means the build step
90
- (`scripts/embed-wasm.ts`) has not been run. Run `npm run build` to
91
- generate the embedded files.
78
+ **Runtime guards:** `Response` and `Promise` checks are guarded with
79
+ `typeof Response !== 'undefined'` to avoid `ReferenceError` in runtimes
80
+ where these globals do not exist (Node < 18).
92
81
 
93
82
  ---
94
83
 
95
- ### `loadStreaming(mod, baseUrl, filename)`
96
-
84
+ ### `compileWasm(source)`
97
85
  ```typescript
98
- async function loadStreaming(
99
- _mod: Module,
100
- baseUrl: URL | string,
101
- filename: string,
102
- ): Promise<WebAssembly.Instance>
86
+ async function compileWasm(source: WasmSource): Promise<WebAssembly.Module>
103
87
  ```
104
88
 
105
- Loads a WASM module by fetching it from a URL and using streaming compilation.
106
-
107
- **How it works:**
108
-
109
- 1. Constructs the full URL by combining `baseUrl` and `filename`
110
- (e.g. `https://example.com/wasm/` + `serpent.wasm`).
111
- 2. Calls `WebAssembly.instantiateStreaming(fetch(url), imports)`, which
112
- allows the browser to compile the module while it downloads.
113
- 3. Creates a fresh 3-page `WebAssembly.Memory` for the instance.
114
-
115
- **Parameters:**
89
+ Compiles a `WasmSource` to a `WebAssembly.Module` without instantiating it.
90
+ Used by pool infrastructure to send a compiled module to workers. Each worker receives the `Module` and instantiates it with their own isolated memory.
116
91
 
117
- - `_mod` -- The module name (currently unused in the function body, but
118
- passed for consistency).
119
- - `baseUrl` -- The base URL where `.wasm` files are hosted. Can be a
120
- `URL` object or a string.
121
- - `filename` -- The `.wasm` filename (e.g. `'serpent.wasm'`). This is
122
- determined by `init.ts` using its internal filename mapping.
92
+ **Source type handling:** Same dispatch table as `loadWasm()`, but calls
93
+ `WebAssembly.compile()` / `WebAssembly.compileStreaming()` instead of the
94
+ `instantiate` variants. `WebAssembly.Module` sources are returned as-is.
123
95
 
124
- **Returns:** A Promise that resolves to a `WebAssembly.Instance`.
125
-
126
- **Error conditions:**
127
-
128
- - Network failure (server unreachable, 404, etc.) will cause the Promise
129
- to reject.
130
- - If the server does not respond with `Content-Type: application/wasm`,
131
- the browser will reject the streaming compilation. This is a common
132
- issue with misconfigured web servers -- ensure your server is configured
133
- to serve `.wasm` files with the correct MIME type.
96
+ **Throws:** Same as `loadWasm()`.
134
97
 
135
98
  ---
136
99
 
137
- ### `loadManual(binary)`
138
-
100
+ ### `decodeWasm(b64)`
139
101
  ```typescript
140
- async function loadManual(
141
- binary: Uint8Array | ArrayBuffer,
142
- ): Promise<WebAssembly.Instance>
102
+ async function decodeWasm(b64: string): Promise<Uint8Array>
143
103
  ```
144
104
 
145
- Loads a WASM module from a raw binary you provide directly.
105
+ Decodes a gzip-compressed, base64-encoded WASM string to raw bytes.
146
106
 
147
- **How it works:**
107
+ 1. Base64-decodes the string using the shared `base64ToBytes` utility.
108
+ 2. Decompresses the result using `DecompressionStream('gzip')`.
148
109
 
149
- 1. Converts `ArrayBuffer` to `Uint8Array` if needed.
150
- 2. Instantiates the WASM module with a fresh 3-page `WebAssembly.Memory`.
110
+ **Throws:**
151
111
 
152
- **Parameters:**
112
+ - `Error` if `DecompressionStream` is not available in the runtime.
113
+ The error message directs the user to provide a URL, ArrayBuffer, or
114
+ WebAssembly.Module source instead.
115
+ - `Error` if base64 decoding fails (corrupt embedded blob).
153
116
 
154
- - `binary` -- The raw WASM binary as a `Uint8Array` or `ArrayBuffer`.
155
-
156
- **Returns:** A Promise that resolves to a `WebAssembly.Instance`.
157
-
158
- **Error conditions:**
159
-
160
- - If the binary is not a valid WASM module, `WebAssembly.instantiate` will
161
- throw. The error message will come from the browser's WASM engine and
162
- will typically mention a validation or compilation failure.
117
+ Exported for use by pool worker launchers that need to decode blobs
118
+ before spawning threads.
163
119
 
164
120
  ---
165
121
 
166
122
  ## Internal Details
167
123
 
168
- ### Embedded binary ownership
124
+ ### Embedded binary structure
169
125
 
170
- Each module's `index.ts` owns the dynamic import to its own embedded binary
171
- file. The loader has no knowledge of module names or file paths -- it receives
172
- a thunk from `initModule()` and calls it. This means `loader.ts` has no
173
- dependency on any embedded file, which enables bundlers to tree-shake unused
174
- modules.
126
+ Each module provides two paths to its embedded blob:
175
127
 
176
- | Module | Thunk defined in | Embedded file path |
177
- |-------------|-------------------------------|----------------------------|
178
- | `serpent` | `serpent/index.ts` | `./embedded/serpent.js` |
179
- | `chacha20` | `chacha20/index.ts` | `./embedded/chacha20.js` |
180
- | `sha2` | `sha2/index.ts` | `./embedded/sha2.js` |
181
- | `sha3` | `sha3/index.ts` | `./embedded/sha3.js` |
128
+ | Path | Export | Used by |
129
+ |----------------------------------------|-----------------|-----------------------------|
130
+ | `src/ts/embedded/serpent.ts` | (raw blob) | Build artifact, gitignored |
131
+ | `src/ts/serpent/embedded.ts` | `serpentWasm` | Consumer import |
182
132
 
183
- The embedded `.js` files are generated by the build script
184
- (`scripts/embed-wasm.ts`) and are gitignored. They are not meant to be
185
- created or edited by hand.
186
-
187
- ### Embedded binary decoding
133
+ The per-module `embedded.ts` re-exports the generated blob as a named
134
+ export. Consumers import from the `/embedded` subpath:
135
+ ```typescript
136
+ import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
137
+ ```
188
138
 
189
- The `decodeWasm()` helper handles both decoding and decompression:
139
+ The `src/ts/embedded/` directory is generated by `scripts/embed-wasm.ts`
140
+ and is gitignored. These files are not meant to be created or edited by hand.
190
141
 
191
- 1. Base64-decodes the string using the shared `base64ToBytes` utility
192
- (handles both browser `atob` and Node.js `Buffer` paths).
193
- 2. Decompresses the result using the platform `DecompressionStream('gzip')`.
194
- This API is available in all runtimes that support WASM SIMD, which is
195
- the library's minimum requirement.
142
+ ### Embedded compression
196
143
 
197
- The embedded files export `WASM_GZ_BASE64` — a gzip-compressed WASM binary
198
- encoded as base64. Compression reduces the embedded footprint from ~198 KB
199
- to ~33 KB across all four modules, with Serpent alone shrinking from 167 KB
200
- to ~20 KB.
144
+ The embedded files contain gzip-compressed WASM encoded as base64.
145
+ Compression reduces the embedded footprint from ~198 KB to ~33 KB across
146
+ all four modules, with Serpent alone shrinking from ~167 KB to ~20 KB.
201
147
 
202
148
  ### Memory allocation
203
149
 
204
150
  Every WASM instance receives a `WebAssembly.Memory` with exactly 3 pages
205
- (192 KB total). The memory size is fixed -- modules do not grow their memory
206
- at runtime. This is a deliberate design choice: fixed memory prevents
151
+ (192 KB total). The memory size is fixed; modules do not grow their memory at runtime. This is a deliberate design choice: fixed memory prevents
207
152
  unexpected allocations and makes the memory layout predictable and auditable.
208
153
 
209
154
  ---