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
@@ -5,195 +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 already bundled in the package as a
14
- base64-encoded string. The loader decodes it and instantiates the module.
15
- No network requests are made. This is the default and simplest option.
16
- - **Streaming** -- The loader fetches the `.wasm` file from a URL you provide
17
- and uses the browser's streaming compilation API. The browser can start
18
- compiling the binary while it is still downloading, which can improve
19
- load times for larger modules.
20
- - **Manual** -- You provide the raw binary data (as a `Uint8Array` or
21
- `ArrayBuffer`) and the loader instantiates it directly. This gives you
22
- full control over how the binary is obtained.
30
+ **Response / Promise\<Response\>.** Streaming compilation from an in-flight or deferred fetch.
23
31
 
24
- All three strategies produce the same result: a `WebAssembly.Instance` that
25
- 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.
26
34
 
27
35
  ---
28
36
 
29
37
  ## Security Notes
30
38
 
31
- - **Embedded mode requires no network access.** The WASM binary is part of
32
- the installed package. This eliminates the risk of a compromised CDN or
33
- man-in-the-middle attack altering the binary at load time.
34
- - **Streaming mode requires correct MIME type.** The `.wasm` files must be
35
- served with `Content-Type: application/wasm`. This is a browser requirement
36
- for `WebAssembly.instantiateStreaming`. If the header is missing or wrong,
37
- the browser will reject the response.
38
- - **Manual mode places integrity responsibility on you.** The loader
39
- instantiates whatever binary you provide. If you use manual mode, you are
40
- responsible for verifying that the binary is authentic and unmodified.
41
- - **Each module gets its own memory.** Every instantiation creates a fresh
42
- `WebAssembly.Memory` with 3 pages (192 KB). Modules cannot share or
43
- access each other's memory. This means key material loaded into one
44
- 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.
45
46
 
46
47
  ---
47
48
 
48
49
  ## API Reference
49
50
 
50
51
  These functions are exported from `loader.ts` and called by `init.ts`. They
51
- are not part of the public API -- they are documented here for completeness
52
- and for contributors working on the internals.
53
-
54
- ### `loadEmbedded(thunk)`
52
+ are not part of the public API. They are documented here for completeness and for contributors working on the internals.
55
53
 
54
+ ### `loadWasm(source)`
56
55
  ```typescript
57
- async function loadEmbedded(
58
- thunk: () => Promise<string>,
59
- ): Promise<WebAssembly.Instance>
56
+ async function loadWasm(source: WasmSource): Promise<WebAssembly.Instance>
60
57
  ```
61
58
 
62
- Loads a WASM module from an embedded base64-encoded string obtained by calling
63
- the provided thunk.
64
-
65
- **How it works:**
66
-
67
- 1. Calls the thunk, which dynamically imports the embedded binary file and
68
- returns the base64-encoded WASM string.
69
- 2. Decodes the base64 string to raw bytes.
70
- 3. Instantiates the WASM module with a fresh 3-page `WebAssembly.Memory`.
59
+ Loads and instantiates a WASM module from any accepted source type. Each
60
+ instance receives a fresh 3-page `WebAssembly.Memory`.
71
61
 
72
- The thunk is provided by each module's own `init()` function (e.g.
73
- `serpent/index.ts` passes `() => import('../embedded/serpent.js').then(m => m.WASM_BASE64)`).
74
- This design means `loader.ts` has no knowledge of module names or embedded file
75
- paths -- each module owns its own embedded import, enabling tree-shaking.
62
+ **Source type handling:**
76
63
 
77
- **Parameters:**
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()`. |
78
72
 
79
- - `thunk` -- A function that returns a Promise resolving to a base64-encoded
80
- WASM binary string. Each module's `index.ts` defines its own thunk pointing
81
- to its own embedded file.
73
+ **Throws:**
82
74
 
83
- **Returns:** A Promise that resolves to a `WebAssembly.Instance`.
75
+ - `TypeError` if `source` is null, numeric, or otherwise unrecognised.
76
+ - `TypeError` with `"empty string"` if `source` is an empty string.
84
77
 
85
- **Error conditions:**
86
-
87
- - If the embedded binary file does not exist, this means the build step
88
- (`scripts/embed-wasm.ts`) has not been run. Run `npm run build` to
89
- 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).
90
81
 
91
82
  ---
92
83
 
93
- ### `loadStreaming(mod, baseUrl, filename)`
94
-
84
+ ### `compileWasm(source)`
95
85
  ```typescript
96
- async function loadStreaming(
97
- _mod: Module,
98
- baseUrl: URL | string,
99
- filename: string,
100
- ): Promise<WebAssembly.Instance>
86
+ async function compileWasm(source: WasmSource): Promise<WebAssembly.Module>
101
87
  ```
102
88
 
103
- Loads a WASM module by fetching it from a URL and using streaming compilation.
104
-
105
- **How it works:**
106
-
107
- 1. Constructs the full URL by combining `baseUrl` and `filename`
108
- (e.g. `https://example.com/wasm/` + `serpent.wasm`).
109
- 2. Calls `WebAssembly.instantiateStreaming(fetch(url), imports)`, which
110
- allows the browser to compile the module while it downloads.
111
- 3. Creates a fresh 3-page `WebAssembly.Memory` for the instance.
112
-
113
- **Parameters:**
114
-
115
- - `_mod` -- The module name (currently unused in the function body, but
116
- passed for consistency).
117
- - `baseUrl` -- The base URL where `.wasm` files are hosted. Can be a
118
- `URL` object or a string.
119
- - `filename` -- The `.wasm` filename (e.g. `'serpent.wasm'`). This is
120
- determined by `init.ts` using its internal filename mapping.
121
-
122
- **Returns:** A Promise that resolves to a `WebAssembly.Instance`.
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.
123
91
 
124
- **Error conditions:**
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.
125
95
 
126
- - Network failure (server unreachable, 404, etc.) will cause the Promise
127
- to reject.
128
- - If the server does not respond with `Content-Type: application/wasm`,
129
- the browser will reject the streaming compilation. This is a common
130
- issue with misconfigured web servers -- ensure your server is configured
131
- to serve `.wasm` files with the correct MIME type.
96
+ **Throws:** Same as `loadWasm()`.
132
97
 
133
98
  ---
134
99
 
135
- ### `loadManual(binary)`
136
-
100
+ ### `decodeWasm(b64)`
137
101
  ```typescript
138
- async function loadManual(
139
- binary: Uint8Array | ArrayBuffer,
140
- ): Promise<WebAssembly.Instance>
102
+ async function decodeWasm(b64: string): Promise<Uint8Array>
141
103
  ```
142
104
 
143
- Loads a WASM module from a raw binary you provide directly.
144
-
145
- **How it works:**
146
-
147
- 1. Converts `ArrayBuffer` to `Uint8Array` if needed.
148
- 2. Instantiates the WASM module with a fresh 3-page `WebAssembly.Memory`.
105
+ Decodes a gzip-compressed, base64-encoded WASM string to raw bytes.
149
106
 
150
- **Parameters:**
107
+ 1. Base64-decodes the string using the shared `base64ToBytes` utility.
108
+ 2. Decompresses the result using `DecompressionStream('gzip')`.
151
109
 
152
- - `binary` -- The raw WASM binary as a `Uint8Array` or `ArrayBuffer`.
110
+ **Throws:**
153
111
 
154
- **Returns:** A Promise that resolves to a `WebAssembly.Instance`.
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).
155
116
 
156
- **Error conditions:**
157
-
158
- - If the binary is not a valid WASM module, `WebAssembly.instantiate` will
159
- throw. The error message will come from the browser's WASM engine and
160
- 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.
161
119
 
162
120
  ---
163
121
 
164
122
  ## Internal Details
165
123
 
166
- ### Embedded binary ownership
124
+ ### Embedded binary structure
167
125
 
168
- Each module's `index.ts` owns the dynamic import to its own embedded binary
169
- file. The loader has no knowledge of module names or file paths -- it receives
170
- a thunk from `initModule()` and calls it. This means `loader.ts` has no
171
- dependency on any embedded file, which enables bundlers to tree-shake unused
172
- modules.
126
+ Each module provides two paths to its embedded blob:
173
127
 
174
- | Module | Thunk defined in | Embedded file path |
175
- |-------------|-------------------------------|----------------------------|
176
- | `serpent` | `serpent/index.ts` | `./embedded/serpent.js` |
177
- | `chacha20` | `chacha20/index.ts` | `./embedded/chacha20.js` |
178
- | `sha2` | `sha2/index.ts` | `./embedded/sha2.js` |
179
- | `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 |
180
132
 
181
- The embedded `.js` files are generated by the build script
182
- (`scripts/embed-wasm.ts`) and are gitignored. They are not meant to be
183
- created or edited by hand.
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
+ ```
184
138
 
185
- ### Base64 decoding
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.
186
141
 
187
- The loader handles base64 decoding in both browser and Node.js environments:
142
+ ### Embedded compression
188
143
 
189
- - **Browser:** Uses the built-in `atob()` function.
190
- - **Node.js:** Falls back to `Buffer.from(b64, 'base64')`.
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.
191
147
 
192
148
  ### Memory allocation
193
149
 
194
150
  Every WASM instance receives a `WebAssembly.Memory` with exactly 3 pages
195
- (192 KB total). The memory size is fixed -- modules do not grow their memory
196
- 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
197
152
  unexpected allocations and makes the memory layout predictable and auditable.
198
153
 
199
154
  ---