leviathan-crypto 2.1.0 → 3.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 (296) hide show
  1. package/CLAUDE.md +86 -443
  2. package/README.md +198 -65
  3. package/dist/aes/aes-cbc.d.ts +40 -0
  4. package/dist/aes/aes-cbc.js +158 -0
  5. package/dist/aes/aes-ctr.d.ts +50 -0
  6. package/dist/aes/aes-ctr.js +141 -0
  7. package/dist/aes/aes-gcm-siv.d.ts +67 -0
  8. package/dist/aes/aes-gcm-siv.js +217 -0
  9. package/dist/aes/aes-gcm.d.ts +61 -0
  10. package/dist/aes/aes-gcm.js +226 -0
  11. package/dist/aes/cipher-suite.d.ts +21 -0
  12. package/dist/aes/cipher-suite.js +179 -0
  13. package/dist/aes/embedded.d.ts +1 -0
  14. package/dist/aes/embedded.js +26 -0
  15. package/dist/aes/generator.d.ts +14 -0
  16. package/dist/aes/generator.js +103 -0
  17. package/dist/aes/index.d.ts +58 -0
  18. package/dist/aes/index.js +125 -0
  19. package/dist/aes/ops.d.ts +60 -0
  20. package/dist/aes/ops.js +164 -0
  21. package/dist/aes/pool-worker.d.ts +1 -0
  22. package/dist/aes/pool-worker.js +92 -0
  23. package/dist/aes/types.d.ts +1 -0
  24. package/dist/aes/types.js +23 -0
  25. package/dist/aes.wasm +0 -0
  26. package/dist/blake3/embedded.d.ts +1 -0
  27. package/dist/blake3/embedded.js +26 -0
  28. package/dist/blake3/index.d.ts +143 -0
  29. package/dist/blake3/index.js +620 -0
  30. package/dist/blake3/types.d.ts +102 -0
  31. package/dist/blake3/types.js +31 -0
  32. package/dist/blake3/validate.d.ts +29 -0
  33. package/dist/blake3/validate.js +80 -0
  34. package/dist/blake3.wasm +0 -0
  35. package/dist/chacha20/cipher-suite.js +47 -25
  36. package/dist/chacha20/generator.d.ts +2 -2
  37. package/dist/chacha20/generator.js +4 -4
  38. package/dist/chacha20/index.d.ts +16 -15
  39. package/dist/chacha20/index.js +52 -46
  40. package/dist/chacha20/ops.d.ts +7 -7
  41. package/dist/chacha20/ops.js +34 -34
  42. package/dist/chacha20/pool-worker.js +5 -3
  43. package/dist/cte-wasm.d.ts +1 -0
  44. package/dist/cte-wasm.js +3 -0
  45. package/dist/curve25519.wasm +0 -0
  46. package/dist/ecdsa/der.d.ts +23 -0
  47. package/dist/ecdsa/der.js +192 -0
  48. package/dist/ecdsa/ecprivatekey-der.d.ts +32 -0
  49. package/dist/ecdsa/ecprivatekey-der.js +230 -0
  50. package/dist/ecdsa/embedded.d.ts +1 -0
  51. package/dist/ecdsa/embedded.js +25 -0
  52. package/dist/ecdsa/index.d.ts +124 -0
  53. package/dist/ecdsa/index.js +366 -0
  54. package/dist/ecdsa/types.d.ts +31 -0
  55. package/dist/ecdsa/types.js +28 -0
  56. package/dist/ecdsa/validate.d.ts +18 -0
  57. package/dist/ecdsa/validate.js +92 -0
  58. package/dist/ed25519/embedded.d.ts +1 -0
  59. package/dist/ed25519/embedded.js +31 -0
  60. package/dist/ed25519/index.d.ts +70 -0
  61. package/dist/ed25519/index.js +308 -0
  62. package/dist/ed25519/types.d.ts +27 -0
  63. package/dist/ed25519/types.js +27 -0
  64. package/dist/ed25519/validate.d.ts +7 -0
  65. package/dist/ed25519/validate.js +77 -0
  66. package/dist/embedded/aes-pool-worker.d.ts +1 -0
  67. package/dist/embedded/aes-pool-worker.js +5 -0
  68. package/dist/embedded/aes.d.ts +1 -0
  69. package/dist/embedded/aes.js +3 -0
  70. package/dist/embedded/blake3.d.ts +1 -0
  71. package/dist/embedded/blake3.js +3 -0
  72. package/dist/embedded/chacha20-pool-worker.d.ts +1 -1
  73. package/dist/embedded/chacha20-pool-worker.js +2 -2
  74. package/dist/embedded/chacha20.d.ts +1 -1
  75. package/dist/embedded/chacha20.js +2 -2
  76. package/dist/embedded/curve25519.d.ts +1 -0
  77. package/dist/embedded/curve25519.js +3 -0
  78. package/dist/embedded/mldsa.d.ts +1 -0
  79. package/dist/embedded/mldsa.js +3 -0
  80. package/dist/embedded/mlkem.d.ts +1 -0
  81. package/dist/embedded/mlkem.js +3 -0
  82. package/dist/embedded/p256.d.ts +1 -0
  83. package/dist/embedded/p256.js +3 -0
  84. package/dist/embedded/serpent-pool-worker.d.ts +1 -1
  85. package/dist/embedded/serpent-pool-worker.js +2 -2
  86. package/dist/embedded/serpent.d.ts +1 -1
  87. package/dist/embedded/serpent.js +2 -2
  88. package/dist/embedded/sha2.d.ts +1 -1
  89. package/dist/embedded/sha2.js +2 -2
  90. package/dist/embedded/sha3.d.ts +1 -1
  91. package/dist/embedded/sha3.js +2 -2
  92. package/dist/embedded/slhdsa.d.ts +1 -0
  93. package/dist/embedded/slhdsa.js +3 -0
  94. package/dist/errors.d.ts +92 -1
  95. package/dist/errors.js +111 -1
  96. package/dist/fortuna.d.ts +5 -5
  97. package/dist/fortuna.js +37 -64
  98. package/dist/index.d.ts +38 -9
  99. package/dist/index.js +63 -19
  100. package/dist/init.d.ts +1 -1
  101. package/dist/init.js +11 -25
  102. package/dist/keccak/embedded.js +1 -1
  103. package/dist/keccak/index.d.ts +2 -0
  104. package/dist/keccak/index.js +4 -2
  105. package/dist/loader.d.ts +1 -24
  106. package/dist/loader.js +13 -16
  107. package/dist/merkle/blake3-tree.d.ts +35 -0
  108. package/dist/merkle/blake3-tree.js +187 -0
  109. package/dist/merkle/checkpoint.d.ts +58 -0
  110. package/dist/merkle/checkpoint.js +217 -0
  111. package/dist/merkle/index.d.ts +19 -0
  112. package/dist/merkle/index.js +37 -0
  113. package/dist/merkle/merkle-log.d.ts +130 -0
  114. package/dist/merkle/merkle-log.js +207 -0
  115. package/dist/merkle/merkle-verifier.d.ts +126 -0
  116. package/dist/merkle/merkle-verifier.js +296 -0
  117. package/dist/merkle/proof.d.ts +70 -0
  118. package/dist/merkle/proof.js +300 -0
  119. package/dist/merkle/sha256-tree.d.ts +33 -0
  120. package/dist/merkle/sha256-tree.js +145 -0
  121. package/dist/merkle/signed-log.d.ts +156 -0
  122. package/dist/merkle/signed-log.js +356 -0
  123. package/dist/merkle/signed-note.d.ts +309 -0
  124. package/dist/merkle/signed-note.js +648 -0
  125. package/dist/merkle/sth.d.ts +31 -0
  126. package/dist/merkle/sth.js +31 -0
  127. package/dist/merkle/storage.d.ts +40 -0
  128. package/dist/merkle/storage.js +71 -0
  129. package/dist/merkle/tree.d.ts +68 -0
  130. package/dist/merkle/tree.js +94 -0
  131. package/dist/mldsa/embedded.d.ts +1 -0
  132. package/dist/{kyber → mldsa}/embedded.js +5 -5
  133. package/dist/mldsa/expand.d.ts +53 -0
  134. package/dist/mldsa/expand.js +188 -0
  135. package/dist/mldsa/format.d.ts +16 -0
  136. package/dist/mldsa/format.js +68 -0
  137. package/dist/mldsa/hashvariant.d.ts +32 -0
  138. package/dist/mldsa/hashvariant.js +248 -0
  139. package/dist/mldsa/index.d.ts +142 -0
  140. package/dist/mldsa/index.js +463 -0
  141. package/dist/mldsa/keygen.d.ts +16 -0
  142. package/dist/mldsa/keygen.js +232 -0
  143. package/dist/mldsa/params.d.ts +21 -0
  144. package/dist/mldsa/params.js +55 -0
  145. package/dist/mldsa/sha3-helpers.d.ts +30 -0
  146. package/dist/mldsa/sha3-helpers.js +124 -0
  147. package/dist/mldsa/sign.d.ts +36 -0
  148. package/dist/mldsa/sign.js +380 -0
  149. package/dist/mldsa/types.d.ts +91 -0
  150. package/dist/mldsa/types.js +25 -0
  151. package/dist/mldsa/validate.d.ts +55 -0
  152. package/dist/mldsa/validate.js +125 -0
  153. package/dist/mldsa/verify.d.ts +29 -0
  154. package/dist/mldsa/verify.js +269 -0
  155. package/dist/mldsa.wasm +0 -0
  156. package/dist/mlkem/embedded.d.ts +1 -0
  157. package/dist/mlkem/embedded.js +27 -0
  158. package/dist/mlkem/indcpa.d.ts +49 -0
  159. package/dist/{kyber → mlkem}/indcpa.js +44 -44
  160. package/dist/mlkem/index.d.ts +37 -0
  161. package/dist/{kyber → mlkem}/index.js +24 -34
  162. package/dist/mlkem/kem.d.ts +21 -0
  163. package/dist/{kyber → mlkem}/kem.js +44 -64
  164. package/dist/{kyber → mlkem}/params.d.ts +4 -4
  165. package/dist/{kyber → mlkem}/params.js +2 -2
  166. package/dist/mlkem/suite.d.ts +12 -0
  167. package/dist/{kyber → mlkem}/suite.js +17 -12
  168. package/dist/{kyber → mlkem}/types.d.ts +3 -3
  169. package/dist/{kyber → mlkem}/types.js +1 -1
  170. package/dist/{kyber → mlkem}/validate.d.ts +7 -7
  171. package/dist/{kyber → mlkem}/validate.js +7 -7
  172. package/dist/{kyber.wasm → mlkem.wasm} +0 -0
  173. package/dist/p256.wasm +0 -0
  174. package/dist/ratchet/index.d.ts +2 -0
  175. package/dist/ratchet/index.js +1 -0
  176. package/dist/ratchet/kdf-chain.js +3 -3
  177. package/dist/ratchet/ratchet-keypair.js +2 -2
  178. package/dist/ratchet/root-kdf.js +7 -7
  179. package/dist/ratchet/skipped-key-store.js +4 -4
  180. package/dist/ratchet/types.d.ts +1 -1
  181. package/dist/serpent/cipher-suite.js +20 -17
  182. package/dist/serpent/generator.d.ts +1 -1
  183. package/dist/serpent/generator.js +2 -2
  184. package/dist/serpent/index.d.ts +8 -7
  185. package/dist/serpent/index.js +18 -27
  186. package/dist/serpent/pool-worker.js +7 -5
  187. package/dist/serpent/serpent-cbc.d.ts +4 -4
  188. package/dist/serpent/serpent-cbc.js +11 -8
  189. package/dist/serpent/shared-ops.d.ts +3 -23
  190. package/dist/serpent/shared-ops.js +50 -85
  191. package/dist/serpent.wasm +0 -0
  192. package/dist/sha2/hkdf.js +5 -5
  193. package/dist/sha2/index.d.ts +21 -1
  194. package/dist/sha2/index.js +65 -10
  195. package/dist/sha2/types.d.ts +41 -2
  196. package/dist/sha2.wasm +0 -0
  197. package/dist/sha3/index.d.ts +72 -3
  198. package/dist/sha3/index.js +240 -14
  199. package/dist/sha3/kmac.d.ts +121 -0
  200. package/dist/sha3/kmac.js +800 -0
  201. package/dist/sha3.wasm +0 -0
  202. package/dist/shared/pkcs7.d.ts +22 -0
  203. package/dist/shared/pkcs7.js +84 -0
  204. package/dist/sign/ctx.d.ts +41 -0
  205. package/dist/sign/ctx.js +102 -0
  206. package/dist/sign/envelope.d.ts +45 -0
  207. package/dist/sign/envelope.js +152 -0
  208. package/dist/sign/hasher.d.ts +9 -0
  209. package/dist/sign/hasher.js +132 -0
  210. package/dist/sign/index.d.ts +11 -0
  211. package/dist/sign/index.js +34 -0
  212. package/dist/sign/sign-stream.d.ts +25 -0
  213. package/dist/sign/sign-stream.js +112 -0
  214. package/dist/sign/suites/ecdsa-p256.d.ts +2 -0
  215. package/dist/sign/suites/ecdsa-p256.js +120 -0
  216. package/dist/sign/suites/ed25519.d.ts +3 -0
  217. package/dist/sign/suites/ed25519.js +165 -0
  218. package/dist/sign/suites/hybrid-classical.d.ts +23 -0
  219. package/dist/sign/suites/hybrid-classical.js +526 -0
  220. package/dist/sign/suites/hybrid-pq.d.ts +4 -0
  221. package/dist/sign/suites/hybrid-pq.js +234 -0
  222. package/dist/sign/suites/mldsa.d.ts +7 -0
  223. package/dist/sign/suites/mldsa.js +161 -0
  224. package/dist/sign/suites/slhdsa.d.ts +7 -0
  225. package/dist/sign/suites/slhdsa.js +176 -0
  226. package/dist/sign/types.d.ts +106 -0
  227. package/dist/sign/types.js +28 -0
  228. package/dist/sign/verify-stream.d.ts +30 -0
  229. package/dist/sign/verify-stream.js +227 -0
  230. package/dist/slhdsa/embedded.d.ts +1 -0
  231. package/dist/slhdsa/embedded.js +26 -0
  232. package/dist/slhdsa/index.d.ts +149 -0
  233. package/dist/slhdsa/index.js +493 -0
  234. package/dist/slhdsa/params.d.ts +26 -0
  235. package/dist/slhdsa/params.js +70 -0
  236. package/dist/slhdsa/prehash.d.ts +68 -0
  237. package/dist/slhdsa/prehash.js +307 -0
  238. package/dist/slhdsa/sign.d.ts +39 -0
  239. package/dist/slhdsa/sign.js +116 -0
  240. package/dist/slhdsa/types.d.ts +129 -0
  241. package/dist/slhdsa/types.js +27 -0
  242. package/dist/slhdsa/validate.d.ts +60 -0
  243. package/dist/slhdsa/validate.js +127 -0
  244. package/dist/slhdsa/verify.d.ts +32 -0
  245. package/dist/slhdsa/verify.js +107 -0
  246. package/dist/slhdsa.wasm +0 -0
  247. package/dist/stream/header.js +3 -3
  248. package/dist/stream/index.d.ts +1 -0
  249. package/dist/stream/index.js +1 -0
  250. package/dist/stream/open-stream.js +31 -10
  251. package/dist/stream/seal-stream-pool.d.ts +1 -0
  252. package/dist/stream/seal-stream-pool.js +63 -26
  253. package/dist/stream/seal-stream.d.ts +1 -1
  254. package/dist/stream/seal-stream.js +20 -9
  255. package/dist/stream/seal.js +6 -6
  256. package/dist/stream/types.d.ts +3 -1
  257. package/dist/stream/types.js +1 -1
  258. package/dist/types.d.ts +1 -1
  259. package/dist/types.js +1 -1
  260. package/dist/utils.d.ts +3 -3
  261. package/dist/utils.js +46 -54
  262. package/dist/wasm-source.d.ts +7 -7
  263. package/dist/wasm-source.js +1 -1
  264. package/dist/x25519/embedded.d.ts +1 -0
  265. package/dist/x25519/embedded.js +31 -0
  266. package/dist/x25519/index.d.ts +43 -0
  267. package/dist/x25519/index.js +159 -0
  268. package/dist/x25519/types.d.ts +25 -0
  269. package/dist/x25519/types.js +27 -0
  270. package/dist/x25519/validate.d.ts +2 -0
  271. package/dist/x25519/validate.js +39 -0
  272. package/package.json +70 -26
  273. package/SECURITY.md +0 -163
  274. package/dist/ct-wasm.d.ts +0 -1
  275. package/dist/ct-wasm.js +0 -3
  276. package/dist/docs/aead.md +0 -363
  277. package/dist/docs/architecture.md +0 -1011
  278. package/dist/docs/argon2id.md +0 -305
  279. package/dist/docs/chacha20.md +0 -781
  280. package/dist/docs/exports.md +0 -277
  281. package/dist/docs/fortuna.md +0 -530
  282. package/dist/docs/init.md +0 -301
  283. package/dist/docs/loader.md +0 -256
  284. package/dist/docs/serpent.md +0 -617
  285. package/dist/docs/sha2.md +0 -671
  286. package/dist/docs/sha3.md +0 -612
  287. package/dist/docs/types.md +0 -416
  288. package/dist/docs/utils.md +0 -457
  289. package/dist/embedded/kyber.d.ts +0 -1
  290. package/dist/embedded/kyber.js +0 -3
  291. package/dist/kyber/embedded.d.ts +0 -1
  292. package/dist/kyber/indcpa.d.ts +0 -49
  293. package/dist/kyber/index.d.ts +0 -38
  294. package/dist/kyber/kem.d.ts +0 -21
  295. package/dist/kyber/suite.d.ts +0 -12
  296. /package/dist/{ct.wasm → cte.wasm} +0 -0
package/dist/docs/init.md DELETED
@@ -1,301 +0,0 @@
1
- <img src="https://github.com/xero/leviathan-crypto/raw/main/docs/logo.svg" alt="logo" width="120" align="left" margin="10">
2
-
3
- ### Module Initialization and WASM Loading
4
-
5
- Call `init()` before using any cryptographic class. It loads the WebAssembly modules that perform cryptographic work, caches them in memory, and makes them available to all wrapper classes.
6
-
7
- > ### Table of Contents
8
- > - [Overview](#overview)
9
- > - [Security Notes](#security-notes)
10
- > - [API Reference](#api-reference)
11
- > - [Usage Examples](#usage-examples)
12
- > - [Error Conditions](#error-conditions)
13
-
14
- ---
15
-
16
- ## Overview
17
-
18
- leviathan-crypto runs all cryptographic computation inside WebAssembly modules.
19
- These modules are not loaded automatically. You tell `init()` which modules you
20
- need and provide a source for each one. After that, every class backed by those
21
- modules is ready to use.
22
-
23
- `init()` is idempotent. Calling it multiple times with the same module is safe.
24
- It skips modules already loaded, so you can call `init()` in multiple places
25
- without redundant work. It returns a Promise. Always `await` it before
26
- constructing any class.
27
-
28
- If you try to use a cryptographic class before calling `init()`, you get a clear
29
- error telling you exactly which module to load.
30
-
31
- ---
32
-
33
- ## Security Notes
34
-
35
- **WASM runs outside the JavaScript JIT.** Cryptographic code executes in
36
- WebAssembly, which provides more predictable execution timing than optimized
37
- JavaScript. This reduces the risk of timing side-channels introduced by the
38
- JIT compiler.
39
-
40
- **Each module gets its own linear memory.** Every WASM module receives 3 pages
41
- (192 KB) of independent memory. Key material in one module cannot be read by
42
- another. There is no shared memory between modules.
43
-
44
- **No silent auto-initialization.** Every wrapper class checks that its backing
45
- module has been initialized. If it has not, the class throws immediately rather
46
- than loading the module in the background. Initialization is explicit and
47
- auditable.
48
-
49
- ---
50
-
51
- ## API Reference
52
-
53
- ### Types
54
-
55
- ```typescript
56
- type Module = 'serpent' | 'chacha20' | 'sha2' | 'sha3' | 'keccak' | 'kyber'
57
- ```
58
-
59
- The WASM module families. Each one backs a group of related classes.
60
- `'keccak'` is an alias for `'sha3'` — same WASM binary, same instance slot.
61
-
62
- | Module | Classes it enables |
63
- |---|---|
64
- | `'serpent'` | `Serpent`, `SerpentCbc`, `SerpentCtr` |
65
- | `'serpent'` + `'sha2'` | `SerpentCipher`, `Seal` (with `SerpentCipher`), `SealStream`, `OpenStream` — see [aead.md](./aead.md) |
66
- | `Fortuna` combinations | `Fortuna` accepts a `Generator` + `HashFn` pair. Valid module combinations: `'serpent' + 'sha2'`, `'serpent' + 'sha3'`, `'chacha20' + 'sha2'`, `'chacha20' + 'sha3'`. See [fortuna.md](./fortuna.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) |
73
-
74
- ```typescript
75
- type WasmSource = string | URL | ArrayBuffer | Uint8Array
76
- | WebAssembly.Module | Response | PromiseLike<WasmSource>
77
- ```
78
-
79
- A value that resolves to a WASM binary. The loading strategy is inferred from
80
- the type:
81
-
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` | Streaming compilation via `WebAssembly.instantiateStreaming`. |
90
- | `PromiseLike<WasmSource>` | Awaited and re-dispatched by the resolved runtime type. |
91
-
92
- Any `PromiseLike<WasmSource>` is accepted — `Promise<ArrayBuffer>`,
93
- `Promise<Uint8Array>`, `Promise<string>`, a `fetch()` response promise, and
94
- nested thenables up to depth 3 all resolve transparently. See
95
- [loader.md](./loader.md) for details.
96
-
97
- ---
98
-
99
- ### Functions
100
-
101
- #### init()
102
-
103
- ```typescript
104
- async function init(
105
- sources: Partial<Record<Module, WasmSource>>,
106
- ): Promise<void>
107
- ```
108
-
109
- Initializes one or more WASM modules. Pass an object mapping module names to
110
- their `WasmSource`. Only modules present in the object are loaded. Others are
111
- left untouched.
112
-
113
- ---
114
-
115
- #### Per-module init functions
116
-
117
- Each module subpath exports its own init function for tree-shakeable imports.
118
- These take a single `WasmSource` argument.
119
-
120
- ```typescript
121
- async function serpentInit(source: WasmSource): Promise<void>
122
- async function chacha20Init(source: WasmSource): Promise<void>
123
- async function sha2Init(source: WasmSource): Promise<void>
124
- async function sha3Init(source: WasmSource): Promise<void>
125
- async function keccakInit(source: WasmSource): Promise<void>
126
- async function kyberInit(source: WasmSource): Promise<void>
127
- ```
128
-
129
- Each function initializes only its own WASM module, keeping other modules out
130
- of your bundle.
131
-
132
- ---
133
-
134
- #### Embedded subpath exports
135
-
136
- The `/embedded` subpath for each module provides the gzip+base64 blob as a
137
- ready-to-use `WasmSource`:
138
-
139
- | Subpath | Export |
140
- |---|---|
141
- | `leviathan-crypto/serpent/embedded` | `serpentWasm` |
142
- | `leviathan-crypto/chacha20/embedded` | `chacha20Wasm` |
143
- | `leviathan-crypto/sha2/embedded` | `sha2Wasm` |
144
- | `leviathan-crypto/sha3/embedded` | `sha3Wasm` |
145
- | `leviathan-crypto/keccak/embedded` | `keccakWasm` |
146
- | `leviathan-crypto/kyber/embedded` | `kyberWasm` |
147
-
148
- `keccakWasm` and `sha3Wasm` are the same gzip+base64 blob. Both point to `sha3.wasm`.
149
-
150
- > [!NOTE]
151
- > `MlKem512`, `MlKem768`, and `MlKem1024` require both `kyber` and `sha3`
152
- > (or `keccak`) to be initialized. The kyber module handles polynomial arithmetic.
153
- > The sha3 module provides the Keccak sponge operations used for key generation
154
- > and encapsulation.
155
-
156
- ---
157
-
158
- #### isInitialized()
159
-
160
- ```typescript
161
- function isInitialized(mod: Module): boolean
162
- ```
163
-
164
- Returns `true` if the given module has been loaded and cached. Exported from
165
- both `init.ts` and the root barrel.
166
-
167
- > [!NOTE]
168
- > `isInitialized` is a diagnostic indicator, not a control mechanism. Use
169
- > `init()` to load modules. Do not guard calls on this value.
170
-
171
- ---
172
-
173
- #### getInstance()
174
-
175
- ```typescript
176
- function getInstance(mod: Module): WebAssembly.Instance
177
- ```
178
-
179
- Returns the cached WebAssembly instance for a module. Used internally by
180
- wrapper classes. You do not normally need to call this yourself.
181
-
182
- Throws `'leviathan-crypto: call init({ <mod>: ... }) before using this class'`
183
- if the module has not been initialized.
184
-
185
- ---
186
-
187
- #### compileWasm()
188
-
189
- ```typescript
190
- async function compileWasm(source: WasmSource): Promise<WebAssembly.Module>
191
- ```
192
-
193
- Compiles a `WasmSource` to a `WebAssembly.Module` without instantiating it.
194
- Used by pool infrastructure to send compiled modules to workers. See
195
- [loader.md](./loader.md) for details.
196
-
197
- ---
198
-
199
- ## Usage Examples
200
-
201
- ### Embedded init (most common)
202
-
203
- The WASM binaries are bundled inside the package as gzip+base64 strings. Import
204
- the blob from the module's `/embedded` subpath and pass it to `init()`.
205
-
206
- ```typescript
207
- import { init } from 'leviathan-crypto'
208
- import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
209
- import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
210
-
211
- await init({ serpent: serpentWasm, sha2: sha2Wasm })
212
- ```
213
-
214
- ### Per-module init (tree-shaking)
215
-
216
- Use the subpath init function when you need one module and want the smallest
217
- possible bundle:
218
-
219
- ```typescript
220
- import { serpentInit } from 'leviathan-crypto/serpent'
221
- import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
222
-
223
- await serpentInit(serpentWasm)
224
- ```
225
-
226
- ### Keccak alias (for ML-KEM)
227
-
228
- `'keccak'` is an alias for `'sha3'`. Both resolve to the same WASM binary and
229
- the same instance slot. Use it when you want the semantically correct primitive
230
- name for ML-KEM consumers:
231
-
232
- ```typescript
233
- import { init } from 'leviathan-crypto'
234
- import { keccakWasm } from 'leviathan-crypto/keccak/embedded'
235
-
236
- await init({ keccak: keccakWasm })
237
- // isInitialized('sha3') === true — same slot
238
- // isInitialized('keccak') === true — alias resolves symmetrically
239
- ```
240
-
241
- Or via the subpath directly:
242
-
243
- ```typescript
244
- import { keccakInit, SHAKE128, SHA3_256 } from 'leviathan-crypto/keccak'
245
- import { keccakWasm } from 'leviathan-crypto/keccak/embedded'
246
-
247
- await keccakInit(keccakWasm)
248
- ```
249
-
250
- ### URL-based loading (CDN)
251
-
252
- Pass a `URL` to fetch and compile the `.wasm` file via streaming compilation.
253
- The server must respond with `Content-Type: application/wasm`.
254
-
255
- ```typescript
256
- await init({ serpent: new URL('https://unpkg.com/leviathan-crypto/dist/serpent.wasm') })
257
- ```
258
-
259
- ### Pre-compiled module (edge runtimes)
260
-
261
- If you have already compiled the binary, for example from a KV cache, pass the
262
- `WebAssembly.Module` directly:
263
-
264
- ```typescript
265
- const mod = await WebAssembly.compile(bytes)
266
- await init({ serpent: mod })
267
- ```
268
-
269
- ### Checking initialization state
270
-
271
- ```typescript
272
- import { isInitialized } from 'leviathan-crypto'
273
-
274
- if (!isInitialized('sha2')) {
275
- // handle accordingly
276
- }
277
- ```
278
-
279
- ---
280
-
281
- ## Error Conditions
282
-
283
- | Situation | What happens |
284
- |---|---|
285
- | Using a class before calling `init()` | Throws: `"leviathan-crypto: call init({ ${mod}: ... }) before using this class"` |
286
- | Invalid `WasmSource` (null, number, etc.) | Throws: `TypeError` with a descriptive message |
287
- | Empty string source | Throws: `"leviathan-crypto: invalid WasmSource — empty string"` |
288
- | Calling `init()` for an already-loaded module | No error. Module is silently skipped. |
289
-
290
- ---
291
-
292
-
293
- ## Cross-References
294
-
295
- | Document | Description |
296
- | -------- | ----------- |
297
- | [index](./README.md) | Project Documentation index |
298
- | [architecture](./architecture.md) | architecture overview, module relationships, buffer layouts, and build pipeline |
299
- | [loader](./loader.md) | WASM binary loading strategies (internal details) |
300
- | [wasm](./wasm.md) | WebAssembly primer: modules, instances, memory, and the init gate |
301
-
@@ -1,256 +0,0 @@
1
- <img src="https://github.com/xero/leviathan-crypto/raw/main/docs/logo.svg" alt="logo" width="120" align="left" margin="10">
2
-
3
- ### WASM Binary Loading Strategies
4
-
5
- Internal module used by `init()` that handles the actual loading and instantiation of WebAssembly binaries. You normally do not interact with this module directly.
6
-
7
- > ### Table of Contents
8
- > - [Overview](#overview)
9
- > - [Security Notes](#security-notes)
10
- > - [API Reference](#api-reference)
11
- > - [Examples](#examples)
12
- > - [Internal Details](#internal-details)
13
-
14
- ---
15
-
16
- ## Overview
17
-
18
- When you call [`init()`](./init.md), it delegates the work of obtaining and compiling the WASM binary to the loader. The loading strategy is inferred from the `WasmSource` type, so no mode string is required:
19
-
20
- **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.
21
-
22
- **URL.** Fetches the `.wasm` file and uses the browser's streaming compilation API. The browser can start compiling while still downloading.
23
-
24
- **ArrayBuffer / Uint8Array.** Raw WASM bytes, compiled directly.
25
-
26
- **WebAssembly.Module.** Already compiled. Instantiated immediately. Useful for edge runtimes and KV-cached modules.
27
-
28
- **Response / Promise\<Response\>.** Streaming compilation from an in-flight or deferred fetch.
29
-
30
- **Any other thenable (`PromiseLike<WasmSource>`).** The loader `await`s the thenable and recursively re-dispatches by the runtime type of the resolved value. `Promise<ArrayBuffer>`, `Promise<Uint8Array>`, `Promise<string>` (gzip+base64 blob), and even nested `Promise<Promise<Response>>` all work. Nesting is capped at depth 3 — deeper chains throw a `TypeError: thenable nesting too deep (max 3)`.
31
-
32
- All strategies produce the same result: a `WebAssembly.Instance` that the wrapper classes use to perform cryptographic operations.
33
-
34
- ---
35
-
36
- ## Security Notes
37
-
38
- **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.
39
-
40
- **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.
41
-
42
- **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.
43
-
44
- **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
- ---
47
-
48
- ## API Reference
49
-
50
- These functions are exported from `loader.ts` and called by `init.ts`. They are not part of the public API. They are documented here for completeness and for contributors working on the internals.
51
-
52
- ### `loadWasm(source)`
53
- ```typescript
54
- async function loadWasm(source: WasmSource): Promise<WebAssembly.Instance>
55
- ```
56
-
57
- Loads and instantiates a WASM module from any accepted source type. Each
58
- instance receives a fresh 3-page `WebAssembly.Memory`.
59
-
60
- **Source type handling:**
61
-
62
- | Source type | Loading path |
63
- |--------------------------------|----------------------------------------------------------------------|
64
- | `string` | Decoded from gzip+base64 via `decodeWasm()`, then `WebAssembly.instantiate()`. |
65
- | `URL` | `WebAssembly.instantiateStreaming(fetch(url))`. |
66
- | `ArrayBuffer` | `WebAssembly.instantiate()`. |
67
- | `Uint8Array` | `WebAssembly.instantiate()`. |
68
- | `WebAssembly.Module` | `WebAssembly.instantiate(module, imports)`. |
69
- | `Response` | `WebAssembly.instantiateStreaming()`. |
70
- | `PromiseLike<WasmSource>` | `await` the thenable, re-dispatch by resolved type (recursive, max depth 3). |
71
-
72
- **Throws:**
73
-
74
- - `TypeError` if `source` is null, numeric, or otherwise unrecognised.
75
- - `TypeError` with `"empty string"` if `source` is an empty string.
76
- - `TypeError` with `"thenable nesting too deep (max 3)"` if a thenable source resolves to another thenable more than three levels deep.
77
-
78
- **Runtime guards:** `Response` and `Promise` checks are guarded with `typeof Response !== 'undefined'` to avoid `ReferenceError` in runtimes where these globals do not exist (Node < 18).
79
-
80
- ---
81
-
82
- ### `compileWasm(source)`
83
- ```typescript
84
- async function compileWasm(source: WasmSource): Promise<WebAssembly.Module>
85
- ```
86
-
87
- Compiles a `WasmSource` to a `WebAssembly.Module` without instantiating it. Used by pool infrastructure to send a compiled module to workers. Each worker receives the `Module` and instantiates it with their own isolated memory.
88
-
89
- **Source type handling:** Same dispatch table as `loadWasm()`, but calls `WebAssembly.compile()` / `WebAssembly.compileStreaming()` instead of the `instantiate` variants. `WebAssembly.Module` sources are returned as-is.
90
-
91
- **Throws:** Same as `loadWasm()`.
92
-
93
- ---
94
-
95
- ### `decodeWasm(b64)`
96
- ```typescript
97
- async function decodeWasm(b64: string): Promise<Uint8Array>
98
- ```
99
-
100
- Decodes a gzip-compressed, base64-encoded WASM string to raw bytes.
101
-
102
- 1. Base64-decodes the string using the shared `base64ToBytes` utility.
103
- 2. Decompresses the result using `DecompressionStream('gzip')`.
104
-
105
- **Throws:**
106
-
107
- - `Error` if `DecompressionStream` is not available in the runtime. The error message directs the user to provide a URL, ArrayBuffer, or WebAssembly.Module source instead.
108
- - `RangeError` with message `'base64ToBytes: invalid base64 input'` if the input is not valid base64 (corrupt embedded blob, wrong-length string, or non-base64 characters). Propagated unchanged from the shared `base64ToBytes` utility.
109
-
110
- Exported for use by the cipher-suite spawn paths that may need to decode blobs before passing pre-compiled modules to pool workers.
111
-
112
- ---
113
-
114
- # Examples
115
-
116
- These examples show how to pass different source types to `init()` depending on your runtime, bundler, and deployment target.
117
-
118
- ## Embedded (default, bundled)
119
-
120
- Use the compressed WASM blobs included in the package. No network call. Ideal for browser bundles and Node servers.
121
-
122
- ```typescript
123
- import { init } from 'leviathan-crypto'
124
- import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
125
- import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
126
- import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
127
- import { kyberWasm } from 'leviathan-crypto/kyber/embedded'
128
-
129
- await init({
130
- serpent: serpentWasm,
131
- sha2: sha2Wasm,
132
- chacha20: chacha20Wasm,
133
- kyber: kyberWasm
134
- })
135
- ```
136
-
137
- ## URL-based (streaming from CDN or public folder)
138
-
139
- Fetch the `.wasm` file from a served asset. The browser streams and compiles while downloading. Requires `Content-Type: application/wasm`.
140
-
141
- ```typescript
142
- import { init } from 'leviathan-crypto'
143
-
144
- // From a CDN
145
- await init({
146
- serpent: new URL('https://cdn.example.com/wasm/serpent.wasm'),
147
- sha2: new URL('https://cdn.example.com/wasm/sha2.wasm'),
148
- chacha20: new URL('https://cdn.example.com/wasm/chacha20.wasm'),
149
- kyber: new URL('https://cdn.example.com/wasm/kyber.wasm')
150
- })
151
-
152
- // From a local /public folder
153
- await init({
154
- serpent: new URL('/assets/wasm/serpent.wasm', import.meta.url),
155
- sha2: new URL('/assets/wasm/sha2.wasm', import.meta.url)
156
- })
157
- ```
158
-
159
- ## Pre-compiled Module (edge runtimes, KV cache)
160
-
161
- Compile the WASM once, cache the `WebAssembly.Module`, and reuse it across requests. Each instantiation gets its own isolated memory.
162
-
163
- ```typescript
164
- import { init, compileWasm } from 'leviathan-crypto/loader'
165
- import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
166
-
167
- // Compile once (at module load time or in an init handler)
168
- const serpentModule = await compileWasm(serpentWasm)
169
-
170
- // Reuse across requests
171
- async function handleRequest(req) {
172
- await init({ serpent: serpentModule })
173
- // Each request gets a fresh instance with isolated memory
174
- // ...
175
- }
176
- ```
177
-
178
- ## Deferred loading (lazy initialization)
179
-
180
- Load the WASM only when needed. Pass a Promise that resolves to any valid source.
181
-
182
- ```typescript
183
- import { init } from 'leviathan-crypto'
184
-
185
- const serpentPromise = fetch('/assets/wasm/serpent.wasm')
186
- .then(res => res.arrayBuffer())
187
-
188
- await init({
189
- serpent: serpentPromise,
190
- sha2: new URL('/assets/wasm/sha2.wasm', import.meta.url)
191
- })
192
- ```
193
-
194
- ## Worker pool with pre-compiled modules
195
-
196
- Pass a compiled `WebAssembly.Module` to workers so each worker instantiates it with isolated memory. Used internally by `SealStreamPool` for parallel encryption.
197
-
198
- ```typescript
199
- import { compileWasm } from 'leviathan-crypto/loader'
200
- import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
201
-
202
- // Main thread: compile once
203
- const serpentModule = await compileWasm(serpentWasm)
204
-
205
- // Pass to worker via postMessage
206
- worker.postMessage({ type: 'init', module: serpentModule })
207
-
208
- // Inside worker: instantiate with isolated memory
209
- self.onmessage = async (ev) => {
210
- if (ev.data.type === 'init') {
211
- await init({ serpent: ev.data.module })
212
- }
213
- }
214
- ```
215
-
216
- ---
217
-
218
- ## Internal Details
219
-
220
- ### Embedded binary structure
221
-
222
- Each module provides two paths to its embedded blob:
223
-
224
- | Path | Export | Used by |
225
- |--------------------------------------------|--------------------|-------------------------------------------|
226
- | `src/ts/embedded/serpent.ts` | (raw blob) | Build artifact, gitignored |
227
- | `src/ts/serpent/embedded.ts` | `serpentWasm` | Consumer import |
228
- | `src/ts/embedded/serpent-pool-worker.ts` | `WORKER_SOURCE` | Internal, consumed by `cipher-suite.ts` |
229
-
230
- The per-module `embedded.ts` re-exports the generated blob as a named export. Consumers import from the `/embedded` subpath:
231
- ```typescript
232
- import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
233
- ```
234
-
235
- The `src/ts/embedded/` directory is generated by `scripts/embed-wasm.ts` (WASM blobs) and `scripts/embed-workers.ts` (pool-worker IIFE bundles), and is gitignored. These files are not meant to be created or edited by hand.
236
-
237
- ### Embedded compression
238
-
239
- The embedded files contain gzip-compressed WASM encoded as base64. Compression reduces the embedded footprint from ~198 KB to ~33 KB across all four modules, with Serpent alone shrinking from \~167 KB to \~20 KB.
240
-
241
- ### Memory allocation
242
-
243
- Every WASM instance receives a `WebAssembly.Memory` with exactly 3 pages (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 unexpected allocations and makes the memory layout predictable and auditable.
244
-
245
- ---
246
-
247
-
248
- ## Cross-References
249
-
250
- | Document | Description |
251
- | -------- | ----------- |
252
- | [index](./README.md) | Project Documentation index |
253
- | [architecture](./architecture.md) | architecture overview, module relationships, buffer layouts, and build pipeline |
254
- | [init](./init.md) | the public `init()` API that uses this loader |
255
- | [wasm](./wasm.md) | WebAssembly primer: modules, instances, and memory model |
256
-