leviathan-crypto 1.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.
- package/CLAUDE.md +265 -0
- package/LICENSE +21 -0
- package/README.md +322 -0
- package/SECURITY.md +174 -0
- package/dist/chacha.wasm +0 -0
- package/dist/chacha20/index.d.ts +49 -0
- package/dist/chacha20/index.js +177 -0
- package/dist/chacha20/ops.d.ts +16 -0
- package/dist/chacha20/ops.js +146 -0
- package/dist/chacha20/pool.d.ts +52 -0
- package/dist/chacha20/pool.js +188 -0
- package/dist/chacha20/pool.worker.d.ts +1 -0
- package/dist/chacha20/pool.worker.js +37 -0
- package/dist/chacha20/types.d.ts +30 -0
- package/dist/chacha20/types.js +1 -0
- package/dist/docs/architecture.md +795 -0
- package/dist/docs/argon2id.md +290 -0
- package/dist/docs/chacha20.md +602 -0
- package/dist/docs/chacha20_pool.md +306 -0
- package/dist/docs/fortuna.md +322 -0
- package/dist/docs/init.md +308 -0
- package/dist/docs/loader.md +206 -0
- package/dist/docs/serpent.md +914 -0
- package/dist/docs/sha2.md +620 -0
- package/dist/docs/sha3.md +509 -0
- package/dist/docs/types.md +198 -0
- package/dist/docs/utils.md +273 -0
- package/dist/docs/wasm.md +193 -0
- package/dist/embedded/chacha.d.ts +1 -0
- package/dist/embedded/chacha.js +2 -0
- package/dist/embedded/serpent.d.ts +1 -0
- package/dist/embedded/serpent.js +2 -0
- package/dist/embedded/sha2.d.ts +1 -0
- package/dist/embedded/sha2.js +2 -0
- package/dist/embedded/sha3.d.ts +1 -0
- package/dist/embedded/sha3.js +2 -0
- package/dist/fortuna.d.ts +72 -0
- package/dist/fortuna.js +445 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +44 -0
- package/dist/init.d.ts +11 -0
- package/dist/init.js +49 -0
- package/dist/loader.d.ts +4 -0
- package/dist/loader.js +30 -0
- package/dist/serpent/index.d.ts +65 -0
- package/dist/serpent/index.js +242 -0
- package/dist/serpent/seal.d.ts +8 -0
- package/dist/serpent/seal.js +70 -0
- package/dist/serpent/stream-encoder.d.ts +20 -0
- package/dist/serpent/stream-encoder.js +167 -0
- package/dist/serpent/stream-pool.d.ts +48 -0
- package/dist/serpent/stream-pool.js +285 -0
- package/dist/serpent/stream-sealer.d.ts +34 -0
- package/dist/serpent/stream-sealer.js +223 -0
- package/dist/serpent/stream.d.ts +28 -0
- package/dist/serpent/stream.js +205 -0
- package/dist/serpent/stream.worker.d.ts +32 -0
- package/dist/serpent/stream.worker.js +117 -0
- package/dist/serpent/types.d.ts +5 -0
- package/dist/serpent/types.js +1 -0
- package/dist/serpent.wasm +0 -0
- package/dist/sha2/hkdf.d.ts +16 -0
- package/dist/sha2/hkdf.js +108 -0
- package/dist/sha2/index.d.ts +40 -0
- package/dist/sha2/index.js +190 -0
- package/dist/sha2/types.d.ts +5 -0
- package/dist/sha2/types.js +1 -0
- package/dist/sha2.wasm +0 -0
- package/dist/sha3/index.d.ts +55 -0
- package/dist/sha3/index.js +246 -0
- package/dist/sha3/types.d.ts +5 -0
- package/dist/sha3/types.js +1 -0
- package/dist/sha3.wasm +0 -0
- package/dist/types.d.ts +24 -0
- package/dist/types.js +26 -0
- package/dist/utils.d.ts +26 -0
- package/dist/utils.js +169 -0
- package/package.json +90 -0
|
@@ -0,0 +1,795 @@
|
|
|
1
|
+
# Leviathan Crypto Library Architecture
|
|
2
|
+
|
|
3
|
+
> [!NOTE]
|
|
4
|
+
> - Version: 1.0.0
|
|
5
|
+
> - Package: `leviathan-crypto` (npm, unscoped)
|
|
6
|
+
> - Status: v1.0.0 - all four WASM modules (Serpent, ChaCha20, SHA-2, SHA-3) implemented.
|
|
7
|
+
> - Supersedes: `leviathan` (TypeScript reference) and `leviathan-wasm` (WASM primitives),
|
|
8
|
+
> both of which remain unchanged as development references.
|
|
9
|
+
|
|
10
|
+
## Vision
|
|
11
|
+
|
|
12
|
+
`leviathan-crypto` is a strictly-typed, audited WebAssembly cryptography library for
|
|
13
|
+
the web. It combines two previously separate efforts:
|
|
14
|
+
|
|
15
|
+
- **leviathan:** developer-friendly TypeScript API, strict types, audited against specs
|
|
16
|
+
and known-answer test vectors
|
|
17
|
+
- **leviathan-wasm:** AssemblyScript WASM implementation of the same primitives,
|
|
18
|
+
running outside the JavaScript JIT for predictable execution and practical
|
|
19
|
+
constant-time guarantees
|
|
20
|
+
|
|
21
|
+
The unified library exposes the TypeScript API from leviathan, backed by the WASM
|
|
22
|
+
execution engine from leviathan-wasm. Developers get ergonomic, well-typed classes.
|
|
23
|
+
The runtime gets deterministic cryptographic computation outside the JIT.
|
|
24
|
+
|
|
25
|
+
**The fundamental insight:** JavaScript engines provide no formal constant-time
|
|
26
|
+
guarantees for arbitrary code. WASM execution is deterministic and not subject to
|
|
27
|
+
JIT speculation. For a cryptography library, this distinction matters. The TypeScript
|
|
28
|
+
layer handles API ergonomics; the WASM layer handles all cryptographic computation.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Scope (v1.0)
|
|
33
|
+
|
|
34
|
+
### In scope
|
|
35
|
+
|
|
36
|
+
| Module | Primitives |
|
|
37
|
+
|--------|-----------|
|
|
38
|
+
| `serpent` | Serpent-256 block cipher: ECB, CTR mode, CBC mode |
|
|
39
|
+
| `serpent` + `sha2` | `SerpentSeal` (Serpent-CBC + HMAC-SHA256), `SerpentStream` / `SerpentStreamPool` (chunked AEAD), `SerpentStreamSealer` / `SerpentStreamOpener` (incremental streaming AEAD) |
|
|
40
|
+
| `chacha20` | ChaCha20, Poly1305, ChaCha20-Poly1305 AEAD, XChaCha20-Poly1305 AEAD |
|
|
41
|
+
| `sha2` | SHA-256, SHA-384, SHA-512, HMAC-SHA256, HMAC-SHA384, HMAC-SHA512, HKDF-SHA256, HKDF-SHA512 |
|
|
42
|
+
| `sha3` | SHA3-224, SHA3-256, SHA3-384, SHA3-512, SHAKE128, SHAKE256 (XOFs, multi-squeeze) |
|
|
43
|
+
|
|
44
|
+
Pure TypeScript utilities (encoding helpers, random generation, format converters)
|
|
45
|
+
ship alongside the WASM-backed primitives with no `init()` dependency.
|
|
46
|
+
|
|
47
|
+
### Auxiliary tier (not part of `Module` union)
|
|
48
|
+
|
|
49
|
+
- **`Fortuna`:** CSPRNG requiring two core modules (`serpent` + `sha2`).
|
|
50
|
+
Initialized via the standard `init()` gate.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Repository Structure
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
leviathan-crypto/
|
|
59
|
+
├── src/
|
|
60
|
+
│ ├── asm/ ← AssemblyScript (compiles to .wasm)
|
|
61
|
+
│ │ ├── serpent/
|
|
62
|
+
│ │ │ ├── index.ts ← asc entry point → serpent.wasm
|
|
63
|
+
│ │ │ ├── serpent.ts ← block function + key schedule
|
|
64
|
+
│ │ │ ├── serpent_unrolled.ts ← unrolled S-boxes and round functions
|
|
65
|
+
│ │ │ ├── cbc.ts ← CBC mode
|
|
66
|
+
│ │ │ ├── ctr.ts ← CTR mode
|
|
67
|
+
│ │ │ └── buffers.ts ← static buffer layout + offset getters
|
|
68
|
+
│ │ ├── chacha/
|
|
69
|
+
│ │ │ ├── index.ts ← asc entry point → chacha.wasm
|
|
70
|
+
│ │ │ ├── chacha20.ts
|
|
71
|
+
│ │ │ ├── poly1305.ts
|
|
72
|
+
│ │ │ ├── wipe.ts
|
|
73
|
+
│ │ │ └── buffers.ts
|
|
74
|
+
│ │ ├── sha2/
|
|
75
|
+
│ │ │ ├── index.ts ← asc entry point → sha2.wasm
|
|
76
|
+
│ │ │ ├── sha256.ts
|
|
77
|
+
│ │ │ ├── sha512.ts
|
|
78
|
+
│ │ │ ├── hmac.ts
|
|
79
|
+
│ │ │ ├── hmac512.ts
|
|
80
|
+
│ │ │ └── buffers.ts
|
|
81
|
+
│ │ └── sha3/
|
|
82
|
+
│ │ ├── index.ts ← asc entry point → sha3.wasm
|
|
83
|
+
│ │ ├── keccak.ts
|
|
84
|
+
│ │ └── buffers.ts
|
|
85
|
+
│ └── ts/ ← TypeScript (public API)
|
|
86
|
+
│ ├── init.ts ← initModule() : WASM loading and module cache
|
|
87
|
+
│ ├── loader.ts ← embedded / streaming / manual loading logic
|
|
88
|
+
│ ├── types.ts ← Hash, KeyedHash, Blockcipher, Streamcipher, AEAD
|
|
89
|
+
│ ├── utils.ts ← encoding, wipe, xor, concat, randomBytes
|
|
90
|
+
│ ├── fortuna.ts ← Fortuna CSPRNG (requires serpent + sha2)
|
|
91
|
+
│ ├── embedded/ ← generated base64 files (gitignored, build artifact)
|
|
92
|
+
│ │ ├── serpent.ts
|
|
93
|
+
│ │ ├── chacha.ts
|
|
94
|
+
│ │ ├── sha2.ts
|
|
95
|
+
│ │ └── sha3.ts
|
|
96
|
+
│ ├── serpent/
|
|
97
|
+
│ │ ├── index.ts ← serpentInit() + Serpent, SerpentCtr, SerpentCbc
|
|
98
|
+
│ │ ├── seal.ts ← SerpentSeal (Serpent-CBC + HMAC-SHA256)
|
|
99
|
+
│ │ ├── stream.ts ← SerpentStream (chunked one-shot AEAD)
|
|
100
|
+
│ │ ├── stream-pool.ts ← SerpentStreamPool (Worker-based parallel AEAD)
|
|
101
|
+
│ │ ├── stream-sealer.ts ← SerpentStreamSealer / SerpentStreamOpener (incremental AEAD)
|
|
102
|
+
│ │ ├── stream.worker.ts ← Web Worker entry point for SerpentStreamPool
|
|
103
|
+
│ │ └── types.ts
|
|
104
|
+
│ ├── chacha20/
|
|
105
|
+
│ │ ├── index.ts ← chacha20Init() + ChaCha20, Poly1305, ChaCha20Poly1305, XChaCha20Poly1305
|
|
106
|
+
│ │ ├── pool.ts ← XChaCha20Poly1305Pool + PoolOpts
|
|
107
|
+
│ │ ├── pool.worker.ts ← Web Worker entry point (compiled to pool.worker.js; not a subpath export)
|
|
108
|
+
│ │ └── types.ts
|
|
109
|
+
│ ├── sha2/
|
|
110
|
+
│ │ ├── index.ts ← sha2Init() + SHA256, SHA512, SHA384, HMAC_SHA256, HMAC_SHA512, HMAC_SHA384, HKDF_SHA256, HKDF_SHA512
|
|
111
|
+
│ │ └── types.ts
|
|
112
|
+
│ ├── sha3/
|
|
113
|
+
│ │ ├── index.ts ← sha3Init() + SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE128, SHAKE256
|
|
114
|
+
│ │ └── types.ts
|
|
115
|
+
│ └── index.ts ← root barrel : dispatching init() + re-exports everything
|
|
116
|
+
├── test/
|
|
117
|
+
│ ├── unit/ ← Vitest (JS target, fast iteration)
|
|
118
|
+
│ │ ├── serpent/
|
|
119
|
+
│ │ ├── chacha20/
|
|
120
|
+
│ │ ├── sha2/
|
|
121
|
+
│ │ ├── sha3/
|
|
122
|
+
│ │ └── init.test.ts
|
|
123
|
+
│ ├── e2e/ ← Playwright (WASM target, cross-browser)
|
|
124
|
+
│ └── vectors/ ← test vector files (read-only reference data)
|
|
125
|
+
├── build/ ← WASM build output (gitignored)
|
|
126
|
+
├── dist/ ← npm publish output (gitignored)
|
|
127
|
+
├── docs/ ← project documentation
|
|
128
|
+
├── scripts/
|
|
129
|
+
│ ├── embed-wasm.ts ← reads build/*.wasm, generates src/ts/embedded/*.ts
|
|
130
|
+
│ ├── build-asm.js ← orchestrates AssemblyScript compilation
|
|
131
|
+
│ ├── gen-seal-vectors.ts ← generates KAT vectors for SerpentSeal / SerpentStream
|
|
132
|
+
│ └── gen-sealstream-vectors.ts ← generates KAT vectors for SerpentStreamSealer / Opener
|
|
133
|
+
├── package.json
|
|
134
|
+
├── asconfig.json ← four AssemblyScript entry points
|
|
135
|
+
├── tsconfig.json
|
|
136
|
+
├── vitest.config.ts
|
|
137
|
+
└── playwright.config.ts
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Architecture: TypeScript over WASM
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
> [!NOTE]
|
|
146
|
+
> The TypeScript layer never implements cryptographic algorithms. It handles the
|
|
147
|
+
> JS/WASM boundary: writing inputs into WASM linear memory, calling exported
|
|
148
|
+
> functions, reading outputs back. All algorithm logic lives in AssemblyScript.
|
|
149
|
+
>
|
|
150
|
+
> The exception is the Tier 2 composition layer: `SerpentSeal`, `SerpentStream`,
|
|
151
|
+
> `SerpentStreamPool`, `SerpentStreamSealer`, `SerpentStreamOpener`, and `HKDF`.
|
|
152
|
+
> These are pure TypeScript, they compose the WASM-backed Tier 1 primitives
|
|
153
|
+
> (Serpent-CBC, HMAC-SHA256, HKDF-SHA256) without adding new algorithm logic.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Four Independent WASM Modules
|
|
158
|
+
|
|
159
|
+
Each primitive family compiles to its own `.wasm` binary. Modules are fully
|
|
160
|
+
independent, separate linear memories, separate buffer layouts, no shared state.
|
|
161
|
+
|
|
162
|
+
| Module | Binary | Primitives |
|
|
163
|
+
|--------|--------|------------|
|
|
164
|
+
| `serpent` | `serpent.wasm` | Serpent-256 block cipher: ECB, CTR mode, CBC mode |
|
|
165
|
+
| `chacha20` | `chacha.wasm` | ChaCha20, Poly1305, ChaCha20-Poly1305 AEAD, XChaCha20-Poly1305 AEAD |
|
|
166
|
+
| `sha2` | `sha2.wasm` | SHA-256, SHA-384, SHA-512, HMAC-SHA256, HMAC-SHA384, HMAC-SHA512 |
|
|
167
|
+
| `sha3` | `sha3.wasm` | SHA3-224, SHA3-256, SHA3-384, SHA3-512, SHAKE128, SHAKE256 |
|
|
168
|
+
|
|
169
|
+
**Benefits:**
|
|
170
|
+
1. **Size:** consumers who only use Serpent don't load the SHA-3 binary
|
|
171
|
+
2. **Isolation:** key material in `serpent.wasm` memory cannot bleed into
|
|
172
|
+
`sha3.wasm` memory even in theory
|
|
173
|
+
|
|
174
|
+
Each module's buffer layout starts at offset 0 and is defined in its own
|
|
175
|
+
`buffers.ts`. Buffer layouts are fully independent across modules.
|
|
176
|
+
|
|
177
|
+
### Module contents
|
|
178
|
+
|
|
179
|
+
**`serpent.wasm`**
|
|
180
|
+
Serpent-256 block cipher. Key schedule, block encrypt, block decrypt. CTR mode
|
|
181
|
+
chunked streaming encrypt/decrypt. CBC mode chunked encrypt/decrypt.
|
|
182
|
+
Source: `src/asm/serpent/`
|
|
183
|
+
|
|
184
|
+
The serpent TypeScript module also includes a Tier 2 composition layer built on
|
|
185
|
+
top of these WASM primitives: `SerpentSeal` (Serpent-CBC + HMAC-SHA256 AEAD),
|
|
186
|
+
`SerpentStream` (chunked one-shot AEAD), `SerpentStreamPool` (Worker-based
|
|
187
|
+
parallel AEAD), and `SerpentStreamSealer` / `SerpentStreamOpener` (incremental
|
|
188
|
+
streaming AEAD). All Tier 2 classes use HKDF-SHA256 for per-chunk key derivation
|
|
189
|
+
and require both `serpent` and `sha2` to be initialized.
|
|
190
|
+
|
|
191
|
+
**`chacha.wasm`**
|
|
192
|
+
ChaCha20 stream cipher (RFC 8439). Poly1305 MAC (RFC 8439 §2.5). ChaCha20-Poly1305
|
|
193
|
+
AEAD (RFC 8439 §2.8). XChaCha20-Poly1305 AEAD (draft-irtf-cfrg-xchacha).
|
|
194
|
+
HChaCha20 subkey derivation.
|
|
195
|
+
Source: `src/asm/chacha/`
|
|
196
|
+
|
|
197
|
+
The chacha20 TypeScript module also includes `pool.ts` (`XChaCha20Poly1305Pool`)
|
|
198
|
+
and `pool.worker.ts`. The worker file compiles to `dist/chacha20/pool.worker.js`
|
|
199
|
+
and ships in the package, but it is **not** registered in the `exports` map, it
|
|
200
|
+
is an internal file loaded by the pool at runtime, not a public named subpath.
|
|
201
|
+
|
|
202
|
+
**`sha2.wasm`**
|
|
203
|
+
SHA-256 and SHA-512 (FIPS 180-4). SHA-384 (SHA-512 with different IVs, truncated
|
|
204
|
+
output, shares all SHA-512 buffers and compress function). HMAC-SHA256,
|
|
205
|
+
HMAC-SHA512, HMAC-SHA384 (RFC 2104). HKDF-SHA256 and HKDF-SHA512 (RFC 5869)
|
|
206
|
+
are pure TypeScript compositions over HMAC, with no new WASM logic.
|
|
207
|
+
Source: `src/asm/sha2/`
|
|
208
|
+
|
|
209
|
+
**`sha3.wasm`**
|
|
210
|
+
Keccak-f[1600] permutation (FIPS 202). SHA3-224, SHA3-256, SHA3-384, SHA3-512.
|
|
211
|
+
SHAKE128, SHAKE256 (XOFs, multi-squeeze capable, unbounded output length).
|
|
212
|
+
All six variants share one permutation, differing only in rate, domain
|
|
213
|
+
separation byte, and output length.
|
|
214
|
+
Source: `src/asm/sha3/`
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## `init()` API
|
|
219
|
+
|
|
220
|
+
WASM instantiation is async. `init()` is the explicit initialization gate,
|
|
221
|
+
it must be called once before any cryptographic class is used. This is honest
|
|
222
|
+
about the initialization cost and gives the developer control over when it is paid.
|
|
223
|
+
|
|
224
|
+
### Signature
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
type Module = 'serpent' | 'chacha20' | 'sha2' | 'sha3'
|
|
228
|
+
type Mode = 'embedded' | 'streaming' | 'manual'
|
|
229
|
+
|
|
230
|
+
interface InitOpts {
|
|
231
|
+
wasmUrl?: URL | string
|
|
232
|
+
wasmBinary?: Record<Module, Uint8Array | ArrayBuffer>
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async function init(
|
|
236
|
+
modules: Module | Module[],
|
|
237
|
+
mode?: Mode,
|
|
238
|
+
opts?: InitOpts
|
|
239
|
+
): Promise<void>
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Three loading modes
|
|
243
|
+
|
|
244
|
+
**`'embedded'` (default: zero-config)**
|
|
245
|
+
The `.wasm` binary is base64-encoded and inlined in the published package as a
|
|
246
|
+
generated TypeScript file (`src/ts/embedded/*.ts`). At runtime, the base64 string
|
|
247
|
+
is decoded and passed to `WebAssembly.instantiate()`. Works everywhere with no
|
|
248
|
+
bundler configuration. ~33% size overhead from base64 encoding. Cannot use
|
|
249
|
+
streaming compilation.
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
await init(['serpent', 'sha3'])
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**`'streaming'` (performance path)**
|
|
256
|
+
Uses `WebAssembly.instantiateStreaming()` for maximum load performance. The
|
|
257
|
+
browser compiles the WASM binary while still downloading it. `wasmUrl` is a
|
|
258
|
+
base URL, the loader appends the filename (`serpent.wasm`, `chacha.wasm`, etc.).
|
|
259
|
+
Requires the `.wasm` files to be served with `Content-Type: application/wasm`.
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
await init(['serpent', 'sha3'], 'streaming', { wasmUrl: '/assets/wasm/' })
|
|
263
|
+
// loads: /assets/wasm/serpent.wasm, /assets/wasm/sha3.wasm
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**`'manual'` (custom loading)**
|
|
267
|
+
Caller provides the compiled binary directly as a `Uint8Array` or `ArrayBuffer`.
|
|
268
|
+
For environments with custom loading requirements (CDN, service worker cache,
|
|
269
|
+
non-standard fetch).
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
await init(['chacha20'], 'manual', {
|
|
273
|
+
wasmBinary: { chacha20: myBuffer }
|
|
274
|
+
})
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Behavioral contracts
|
|
278
|
+
|
|
279
|
+
**Idempotent.** Calling `init()` for a module that is already initialized is a
|
|
280
|
+
no-op. Safe to call from multiple modules in a codebase.
|
|
281
|
+
|
|
282
|
+
**Module-scope cache.** The compiled `WebAssembly.Module` for each binary is
|
|
283
|
+
cached at module scope after first compilation. All subsequent class instantiations
|
|
284
|
+
use `WebAssembly.instantiate(cachedModule)`, fast, no recompilation.
|
|
285
|
+
|
|
286
|
+
**Error before init.** Calling any cryptographic class before `init()` throws:
|
|
287
|
+
```
|
|
288
|
+
leviathan-crypto: call init(['<module>']) before using <ClassName>
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
**No lazy auto-init.** Classes never silently call `init()` on first use.
|
|
292
|
+
Hidden initialization costs are worse than explicit ones.
|
|
293
|
+
|
|
294
|
+
**Thread safety.** v1.0 uses a single WASM module instance per binary, single
|
|
295
|
+
thread. WASM linear memory is not shared across Workers without `SharedArrayBuffer`
|
|
296
|
+
(which requires COOP/COEP headers). Two pool classes provide Worker-based
|
|
297
|
+
parallelism: `SerpentStreamPool` (chunked authenticated Serpent encryption) and
|
|
298
|
+
`XChaCha20Poly1305Pool` (AEAD). Each pool worker owns its own WASM instances with
|
|
299
|
+
isolated linear memory. For other primitives: create one instance per Worker if
|
|
300
|
+
Workers are used.
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Public API Classes
|
|
305
|
+
|
|
306
|
+
Names match conventional cryptographic notation.
|
|
307
|
+
|
|
308
|
+
| Module | Classes |
|
|
309
|
+
|--------|---------|
|
|
310
|
+
| `serpent` + `sha2` | `SerpentSeal`, `SerpentStream`, `SerpentStreamPool`, `SerpentStreamSealer`, `SerpentStreamOpener` |
|
|
311
|
+
| `serpent` | `Serpent`, `SerpentCtr`, `SerpentCbc` |
|
|
312
|
+
| `chacha20` | `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `XChaCha20Poly1305Pool` |
|
|
313
|
+
| `sha2` | `SHA256`, `SHA384`, `SHA512`, `HMAC_SHA256`, `HMAC_SHA384`, `HMAC_SHA512`, `HKDF_SHA256`, `HKDF_SHA512` |
|
|
314
|
+
| `sha3` | `SHA3_224`, `SHA3_256`, `SHA3_384`, `SHA3_512`, `SHAKE128`, `SHAKE256` |
|
|
315
|
+
| `serpent` + `sha2` | `Fortuna` |
|
|
316
|
+
|
|
317
|
+
HMAC names use underscore separator (`HMAC_SHA256`) matching RFC convention.
|
|
318
|
+
SHA-3 names use underscore separator (`SHA3_256`) for readability.
|
|
319
|
+
|
|
320
|
+
**`Fortuna`** requires `await Fortuna.create()` rather than `new Fortuna()` due
|
|
321
|
+
to the async `init()` dependency on two modules. It requires both `serpent` and
|
|
322
|
+
`sha2` to be initialized. In Node.js, Fortuna collects additional entropy from
|
|
323
|
+
`process.hrtime`, `process.cpuUsage`, `process.memoryUsage`, `os.loadavg`,
|
|
324
|
+
and `os.freemem` in addition to `crypto.randomBytes`.
|
|
325
|
+
|
|
326
|
+
### Usage pattern
|
|
327
|
+
|
|
328
|
+
All WASM-backed classes follow the same pattern:
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
import { init, SerpentSeal, SHA3_256 } from 'leviathan-crypto'
|
|
332
|
+
|
|
333
|
+
await init(['serpent', 'sha2', 'sha3'])
|
|
334
|
+
|
|
335
|
+
const seal = new SerpentSeal()
|
|
336
|
+
const ciphertext = seal.encrypt(key, plaintext) // throws on tamper at decrypt
|
|
337
|
+
|
|
338
|
+
const hasher = new SHA3_256()
|
|
339
|
+
const digest = hasher.hash(message)
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Utility exports (no `init()` required)
|
|
343
|
+
|
|
344
|
+
Pure TypeScript utilities ship alongside the WASM-backed primitives:
|
|
345
|
+
|
|
346
|
+
| Category | Exports |
|
|
347
|
+
|----------|---------|
|
|
348
|
+
| Encoding | `hexToBytes`, `bytesToHex`, `utf8ToBytes`, `bytesToUtf8`, `base64ToBytes`, `bytesToBase64` |
|
|
349
|
+
| Security | `constantTimeEqual`, `wipe`, `xor` |
|
|
350
|
+
| Helpers | `concat`, `randomBytes` |
|
|
351
|
+
| Types | `Hash`, `KeyedHash`, `Blockcipher`, `Streamcipher`, `AEAD` |
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## Build Pipeline
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
**Step by step:**
|
|
359
|
+
|
|
360
|
+
1. `npm run build:asm` — AssemblyScript compiler reads `src/asm/*/index.ts`, emits `build/*.wasm`
|
|
361
|
+
2. `npm run build:embed` — `scripts/embed-wasm.ts` reads each `.wasm`, writes base64 to `src/ts/embedded/*.ts`
|
|
362
|
+
3. `npm run build:ts` — TypeScript compiler emits `dist/`
|
|
363
|
+
4. `cp build/*.wasm dist/` — WASM binaries copied for streaming mode consumers
|
|
364
|
+
5. At runtime (subpath): `serpent/index.ts:serpentInit()` → `initModule()` → `loadEmbedded(thunk)` → `thunk()` → dynamic-import `embedded/serpent.ts` → decode base64 → `WebAssembly.instantiate` → cache in `init.ts`
|
|
365
|
+
6. At runtime (root): `index.ts:init(['serpent', 'sha3'])` → dispatches to each module's init function (`serpentInit`, `sha3Init`) via `Promise.all` → same path as step 5 per module
|
|
366
|
+
|
|
367
|
+
`src/ts/embedded/` is gitignored, these files are a build artifacts derived from the WASM
|
|
368
|
+
binaries. There is one source of truth for each binary: the AssemblyScript source.
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## Module Relationship Diagrams
|
|
373
|
+
|
|
374
|
+
### ASM layer — internal import graph
|
|
375
|
+
|
|
376
|
+
Each WASM module is fully independent. No cross-module imports exist.
|
|
377
|
+
|
|
378
|
+
**Serpent (`src/asm/serpent/`)**
|
|
379
|
+
|
|
380
|
+
```
|
|
381
|
+
buffers.ts
|
|
382
|
+
<- serpent.ts (offsets for key, block, subkey, work, CBC IV)
|
|
383
|
+
<- serpent_unrolled.ts (block offsets, subkey, work)
|
|
384
|
+
<- cbc.ts (IV, block, chunk offsets)
|
|
385
|
+
<- ctr.ts (nonce, counter, block, chunk offsets)
|
|
386
|
+
|
|
387
|
+
serpent.ts
|
|
388
|
+
<- serpent_unrolled.ts (S-boxes sb0-sb7, si0-si7, lk, kl, keyXor)
|
|
389
|
+
|
|
390
|
+
serpent_unrolled.ts
|
|
391
|
+
<- cbc.ts (encryptBlock_unrolled, decryptBlock_unrolled)
|
|
392
|
+
<- ctr.ts (encryptBlock_unrolled)
|
|
393
|
+
|
|
394
|
+
index.ts
|
|
395
|
+
re-exports: buffers + serpent + serpent_unrolled + cbc + ctr
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
**ChaCha (`src/asm/chacha/`)**
|
|
399
|
+
|
|
400
|
+
```
|
|
401
|
+
buffers.ts
|
|
402
|
+
<- chacha20.ts (key, nonce, counter, block, state, poly key, xchacha offsets)
|
|
403
|
+
<- poly1305.ts (poly key, msg, buf, tag, h, r, rs, s offsets)
|
|
404
|
+
<- wipe.ts (all buffer offsets, zeroes everything)
|
|
405
|
+
|
|
406
|
+
index.ts
|
|
407
|
+
re-exports: buffers + chacha20 + poly1305 + wipe
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
**SHA-2 (`src/asm/sha2/`)**
|
|
411
|
+
|
|
412
|
+
```
|
|
413
|
+
buffers.ts
|
|
414
|
+
<- sha256.ts (H, block, W, out, input, partial, total offsets)
|
|
415
|
+
<- sha512.ts (H, block, W, out, input, partial, total offsets)
|
|
416
|
+
<- hmac.ts (SHA-256 input, out, ipad, opad, inner offsets)
|
|
417
|
+
<- hmac512.ts (SHA-512 input, out, ipad, opad, inner offsets)
|
|
418
|
+
|
|
419
|
+
sha256.ts
|
|
420
|
+
<- hmac.ts (sha256Init, sha256Update, sha256Final)
|
|
421
|
+
|
|
422
|
+
sha512.ts
|
|
423
|
+
<- hmac512.ts (sha512Init, sha384Init, sha512Update, sha512Final, sha384Final)
|
|
424
|
+
|
|
425
|
+
index.ts
|
|
426
|
+
re-exports: buffers + sha256 + sha512 + hmac + hmac512
|
|
427
|
+
defines: wipeBuffers() inline
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
**SHA-3 (`src/asm/sha3/`)**
|
|
431
|
+
|
|
432
|
+
```
|
|
433
|
+
buffers.ts
|
|
434
|
+
<- keccak.ts (state, rate, absorbed, dsbyte, input, out offsets)
|
|
435
|
+
|
|
436
|
+
index.ts
|
|
437
|
+
re-exports: buffers + keccak
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### TS layer — internal import graph
|
|
441
|
+
|
|
442
|
+
```
|
|
443
|
+
+---------------------+
|
|
444
|
+
| index.ts | <- barrel: dispatching init()
|
|
445
|
+
| (public API root) | + re-exports everything
|
|
446
|
+
+--+--+--+--+--+--+--+
|
|
447
|
+
| | | | | |
|
|
448
|
+
+-------------------------+ | | | | +----------------------+
|
|
449
|
+
| +----------------+ | | +----------+ |
|
|
450
|
+
v v v v v v
|
|
451
|
+
serpent/ chacha20/ sha2/ sha3/ fortuna.ts types.ts
|
|
452
|
+
index.ts index.ts index.ts index.ts utils.ts
|
|
453
|
+
| | | | | | | | |
|
|
454
|
+
| | | | | | | | +-> init.ts (isInitialized)
|
|
455
|
+
| | | +-> utils.ts | | | | +-> serpent/index.ts (Serpent)
|
|
456
|
+
| | | | (constantTime| | | | +-> sha2/index.ts (SHA256)
|
|
457
|
+
| | | | Equal) | | | | +-> utils.ts (wipe, concat,
|
|
458
|
+
| | | | | | | | utf8ToBytes)
|
|
459
|
+
| | | +-> chacha20/ | | | |
|
|
460
|
+
| | | | types.ts | | | |
|
|
461
|
+
| | | | | | | |
|
|
462
|
+
| +----------+--+--+------------+--+----+--+--> init.ts <-- getInstance()
|
|
463
|
+
| | | | | initModule()
|
|
464
|
+
| | | | | isInitialized()
|
|
465
|
+
v v v v v
|
|
466
|
+
embedded/ embedded/ embedded/ embedded/
|
|
467
|
+
serpent.ts chacha.ts sha2.ts sha3.ts
|
|
468
|
+
(each module owns its own embedded thunk, no cross-module imports)
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
Each module's init function (`serpentInit`, `chacha20Init`, `sha2Init`,
|
|
472
|
+
`sha3Init`) calls `initModule()` from `init.ts`, passing its own embedded thunk. `initModule()` delegates to `loadEmbedded(thunk)` in `loader.ts`.
|
|
473
|
+
The loader calls the thunk, decodes base64, and instantiates the WASM binary.
|
|
474
|
+
`loader.ts` has no knowledge of module names or embedded file paths.
|
|
475
|
+
|
|
476
|
+
### TS layer — file-by-file imports
|
|
477
|
+
|
|
478
|
+
| File | Imports from | Symbols |
|
|
479
|
+
|------|-------------|---------|
|
|
480
|
+
| `init.ts` | *(none)* | — |
|
|
481
|
+
| `loader.ts` | `init.ts` | `Module` (type) |
|
|
482
|
+
| `types.ts` | *(none)* | — |
|
|
483
|
+
| `utils.ts` | *(none)* | — |
|
|
484
|
+
| `serpent/types.ts` | *(none)* | — |
|
|
485
|
+
| `chacha20/types.ts` | *(none)* | — |
|
|
486
|
+
| `sha2/types.ts` | *(none)* | — |
|
|
487
|
+
| `sha3/types.ts` | *(none)* | — |
|
|
488
|
+
| `serpent/index.ts` | `init.ts`, `embedded/serpent.ts` | `getInstance`, `initModule`, `Mode`, `InitOpts`, `WASM_BASE64` |
|
|
489
|
+
| `serpent/seal.ts` | `serpent/index.ts`, `sha2/index.ts`, `utils.ts` | `SerpentCbc`, `HMAC_SHA256`, `concat`, `constantTimeEqual`, `wipe` |
|
|
490
|
+
| `serpent/stream.ts` | `serpent/index.ts`, `sha2/index.ts`, `utils.ts` | `SerpentCtr`, `HMAC_SHA256`, `HKDF_SHA256`, `constantTimeEqual`, `concat` |
|
|
491
|
+
| `serpent/stream-pool.ts` | `serpent/stream.ts` | `sealChunk`, `openChunk`, `chunkInfo` |
|
|
492
|
+
| `serpent/stream-sealer.ts` | `serpent/index.ts`, `sha2/index.ts`, `utils.ts` | `SerpentCbc`, `HMAC_SHA256`, `HKDF_SHA256`, `concat`, `constantTimeEqual`, `wipe` |
|
|
493
|
+
| `chacha20/index.ts` | `init.ts`, `utils.ts`, `chacha20/types.ts`, `embedded/chacha.ts` | `getInstance`, `initModule`, `Mode`, `InitOpts`, `constantTimeEqual`, `ChaChaExports`, `WASM_BASE64` |
|
|
494
|
+
| `sha2/index.ts` | `init.ts`, `embedded/sha2.ts` | `getInstance`, `initModule`, `Mode`, `InitOpts`, `WASM_BASE64` |
|
|
495
|
+
| `sha3/index.ts` | `init.ts`, `embedded/sha3.ts` | `getInstance`, `initModule`, `Mode`, `InitOpts`, `WASM_BASE64` |
|
|
496
|
+
| `fortuna.ts` | `init.ts`, `serpent/index.ts`, `sha2/index.ts`, `utils.ts` | `isInitialized`, `Serpent`, `SHA256`, `wipe`/`concat`/`utf8ToBytes` |
|
|
497
|
+
| `index.ts` | `serpent/`, `chacha20/`, `sha2/`, `sha3/`, `init.ts`, `fortuna.ts`, `types.ts`, `utils.ts` | `serpentInit`, `chacha20Init`, `sha2Init`, `sha3Init` (from each module), *(all public exports)* |
|
|
498
|
+
|
|
499
|
+
### TS-to-WASM mapping
|
|
500
|
+
|
|
501
|
+
Each TS wrapper class maps to one WASM module and specific exported functions.
|
|
502
|
+
Tier 2 composition classes (`SerpentSeal`, `SerpentStream*`, `HKDF_*`) are pure
|
|
503
|
+
TypeScript, they call Tier 1 classes rather than WASM functions directly.
|
|
504
|
+
|
|
505
|
+
**serpent/index.ts → asm/serpent/ (Tier 1: direct WASM callers)**
|
|
506
|
+
|
|
507
|
+
| TS Class | WASM functions called |
|
|
508
|
+
|----------|---------------------|
|
|
509
|
+
| `Serpent` | `loadKey`, `encryptBlock`, `decryptBlock`, `wipeBuffers` + buffer getters |
|
|
510
|
+
| `SerpentCtr` | `loadKey`, `resetCounter`, `setCounter`, `encryptChunk`, `decryptChunk`, `wipeBuffers` + buffer getters |
|
|
511
|
+
| `SerpentCbc` | `loadKey`, `cbcEncryptChunk`, `cbcDecryptChunk`, `wipeBuffers` + buffer getters |
|
|
512
|
+
|
|
513
|
+
**serpent/seal.ts, stream.ts, stream-sealer.ts (Tier 2: pure TS composition)**
|
|
514
|
+
|
|
515
|
+
| TS Class | Composes |
|
|
516
|
+
|----------|---------|
|
|
517
|
+
| `SerpentSeal` | `SerpentCbc` + `HMAC_SHA256` |
|
|
518
|
+
| `SerpentStream` | `SerpentCtr` + `HMAC_SHA256` + `HKDF_SHA256` |
|
|
519
|
+
| `SerpentStreamPool` | `SerpentStream` (via worker) |
|
|
520
|
+
| `SerpentStreamSealer` | `SerpentCbc` + `HMAC_SHA256` + `HKDF_SHA256` |
|
|
521
|
+
| `SerpentStreamOpener` | `SerpentCbc` + `HMAC_SHA256` + `HKDF_SHA256` |
|
|
522
|
+
|
|
523
|
+
**chacha20/index.ts → asm/chacha/**
|
|
524
|
+
|
|
525
|
+
| TS Class | WASM functions called |
|
|
526
|
+
|----------|---------------------|
|
|
527
|
+
| `ChaCha20` | `chachaLoadKey`, `chachaSetCounter`, `chachaResetCounter`, `chachaEncryptChunk`, `chachaDecryptChunk`, `wipeBuffers` + buffer getters |
|
|
528
|
+
| `Poly1305` | `polyInit`, `polyUpdate`, `polyFinal`, `wipeBuffers` + buffer getters |
|
|
529
|
+
| `ChaCha20Poly1305` | `chachaLoadKey`, `chachaResetCounter`, `chachaGenPolyKey`, `chachaEncryptChunk`, `chachaDecryptChunk`, `polyInit`, `polyUpdate`, `polyFinal`, `wipeBuffers` + buffer getters |
|
|
530
|
+
| `XChaCha20Poly1305` | All of `ChaCha20Poly1305` + `hchacha20` + xchacha buffer getters |
|
|
531
|
+
|
|
532
|
+
**sha2/index.ts → asm/sha2/**
|
|
533
|
+
|
|
534
|
+
| TS Class | WASM functions called |
|
|
535
|
+
|----------|---------------------|
|
|
536
|
+
| `SHA256` | `sha256Init`, `sha256Update`, `sha256Final`, `wipeBuffers` + buffer getters |
|
|
537
|
+
| `SHA512` | `sha512Init`, `sha512Update`, `sha512Final`, `wipeBuffers` + buffer getters |
|
|
538
|
+
| `SHA384` | `sha384Init`, `sha512Update`, `sha384Final`, `wipeBuffers` + buffer getters |
|
|
539
|
+
| `HMAC_SHA256` | `hmac256Init`, `hmac256Update`, `hmac256Final`, `sha256Init`, `sha256Update`, `sha256Final`, `wipeBuffers` + buffer getters |
|
|
540
|
+
| `HMAC_SHA512` | `hmac512Init`, `hmac512Update`, `hmac512Final`, `sha512Init`, `sha512Update`, `sha512Final`, `wipeBuffers` + buffer getters |
|
|
541
|
+
| `HMAC_SHA384` | `hmac384Init`, `hmac384Update`, `hmac384Final`, `sha384Init`, `sha512Update`, `sha384Final`, `wipeBuffers` + buffer getters |
|
|
542
|
+
| `HKDF_SHA256` | Pure TS composition over `HMAC_SHA256` (extract + expand per RFC 5869) |
|
|
543
|
+
| `HKDF_SHA512` | Pure TS composition over `HMAC_SHA512` (extract + expand per RFC 5869) |
|
|
544
|
+
|
|
545
|
+
**sha3/index.ts → asm/sha3/**
|
|
546
|
+
|
|
547
|
+
| TS Class | WASM functions called |
|
|
548
|
+
|----------|---------------------|
|
|
549
|
+
| `SHA3_224` | `sha3_224Init`, `keccakAbsorb`, `sha3_224Final`, `wipeBuffers` + buffer getters |
|
|
550
|
+
| `SHA3_256` | `sha3_256Init`, `keccakAbsorb`, `sha3_256Final`, `wipeBuffers` + buffer getters |
|
|
551
|
+
| `SHA3_384` | `sha3_384Init`, `keccakAbsorb`, `sha3_384Final`, `wipeBuffers` + buffer getters |
|
|
552
|
+
| `SHA3_512` | `sha3_512Init`, `keccakAbsorb`, `sha3_512Final`, `wipeBuffers` + buffer getters |
|
|
553
|
+
| `SHAKE128` | `shake128Init`, `keccakAbsorb`, `shakePad`, `shakeSqueezeBlock`, `wipeBuffers` + buffer getters |
|
|
554
|
+
| `SHAKE256` | `shake256Init`, `keccakAbsorb`, `shakePad`, `shakeSqueezeBlock`, `wipeBuffers` + buffer getters |
|
|
555
|
+
|
|
556
|
+
### Cross-module dependencies
|
|
557
|
+
|
|
558
|
+
| Relationship | Notes |
|
|
559
|
+
|-------------|-------|
|
|
560
|
+
| `SerpentSeal`, `SerpentStream`, `SerpentStreamPool`, `SerpentStreamSealer`, `SerpentStreamOpener` → `serpent` + `sha2` | Tier 2 composition: Serpent-CBC/CTR + HMAC-SHA256 + HKDF-SHA256. Both modules must be initialized. |
|
|
561
|
+
| `Fortuna` → `Serpent` + `SHA256` | Only class requiring **two** WASM modules (`serpent` + `sha2`). Uses `Fortuna.create()` static factory instead of `new`. |
|
|
562
|
+
| `XChaCha20Poly1305` → `ChaCha20Poly1305` | Pure TS composition — calls `hchacha20()` for subkey derivation, then delegates to `ChaCha20Poly1305`. |
|
|
563
|
+
| `HKDF_SHA256`, `HKDF_SHA512` → `HMAC_SHA256`, `HMAC_SHA512` | Pure TS composition — extract and expand steps per RFC 5869. |
|
|
564
|
+
| All other classes | Each depends on exactly **one** WASM module. |
|
|
565
|
+
|
|
566
|
+
### Public API barrel (`src/ts/index.ts`)
|
|
567
|
+
|
|
568
|
+
The root barrel defines and exports the dispatching `init()` function.
|
|
569
|
+
It is the only file that imports all four module-scoped init functions.
|
|
570
|
+
|
|
571
|
+
| Source | Exports |
|
|
572
|
+
|--------|---------|
|
|
573
|
+
| *(barrel itself)* | `init` (dispatching function — calls per-module init functions via `Promise.all`) |
|
|
574
|
+
| `init.ts` | `Module`, `Mode`, `InitOpts`, `isInitialized`, `_resetForTesting` |
|
|
575
|
+
| `serpent/index.ts` | `Serpent`, `SerpentCtr`, `SerpentCbc`, `SerpentSeal`, `SerpentStream`, `SerpentStreamPool`, `SerpentStreamSealer`, `SerpentStreamOpener`, `StreamPoolOpts`, `_serpentReady` |
|
|
576
|
+
| `chacha20/index.ts` | `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `_chachaReady` |
|
|
577
|
+
| `chacha20/pool.ts` | `XChaCha20Poly1305Pool`, `PoolOpts` |
|
|
578
|
+
| `sha2/index.ts` | `SHA256`, `SHA512`, `SHA384`, `HMAC_SHA256`, `HMAC_SHA512`, `HMAC_SHA384`, `HKDF_SHA256`, `HKDF_SHA512`, `_sha2Ready` |
|
|
579
|
+
| `sha3/index.ts` | `SHA3_224`, `SHA3_256`, `SHA3_384`, `SHA3_512`, `SHAKE128`, `SHAKE256`, `_sha3Ready` |
|
|
580
|
+
| `fortuna.ts` | `Fortuna` |
|
|
581
|
+
| `types.ts` | `Hash`, `KeyedHash`, `Blockcipher`, `Streamcipher`, `AEAD` |
|
|
582
|
+
| `utils.ts` | `hexToBytes`, `bytesToHex`, `utf8ToBytes`, `bytesToUtf8`, `base64ToBytes`, `bytesToBase64`, `constantTimeEqual`, `wipe`, `xor`, `concat`, `randomBytes` |
|
|
583
|
+
|
|
584
|
+
Each subpath export also exports its own module-specific init function for
|
|
585
|
+
tree-shakeable loading: `serpentInit(mode?, opts?)`, `chacha20Init(mode?, opts?)`,
|
|
586
|
+
`sha2Init(mode?, opts?)`, `sha3Init(mode?, opts?)`.
|
|
587
|
+
|
|
588
|
+
---
|
|
589
|
+
|
|
590
|
+
## npm Package
|
|
591
|
+
|
|
592
|
+
**Subpath exports:**
|
|
593
|
+
|
|
594
|
+
```json
|
|
595
|
+
{
|
|
596
|
+
"exports": {
|
|
597
|
+
".": "./dist/index.js",
|
|
598
|
+
"./serpent": "./dist/serpent/index.js",
|
|
599
|
+
"./chacha20": "./dist/chacha20/index.js",
|
|
600
|
+
"./chacha20/pool": "./dist/chacha20/pool.js",
|
|
601
|
+
"./sha2": "./dist/sha2/index.js",
|
|
602
|
+
"./sha3": "./dist/sha3/index.js"
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
> [!NOTE]
|
|
608
|
+
> `dist/chacha20/pool.worker.js` ships in the package but is not in the
|
|
609
|
+
> `exports` map. It is an internal Web Worker entry point loaded by
|
|
610
|
+
> `XChaCha20Poly1305Pool` at runtime. Do not import it as a named subpath.
|
|
611
|
+
|
|
612
|
+
The root `.` export re-exports everything. Subpath exports allow bundlers to
|
|
613
|
+
tree-shake at the module level, a consumer importing only `./sha3` does not
|
|
614
|
+
include the Serpent wrapper classes or their embedded WASM binaries in their
|
|
615
|
+
bundle.
|
|
616
|
+
|
|
617
|
+
**Tree-shaking:** `"sideEffects": false` is set in `package.json`. Each
|
|
618
|
+
module's `index.ts` owns its own embedded import thunk. Bundlers that support
|
|
619
|
+
tree-shaking (webpack, Rollup, esbuild) can eliminate unused modules and
|
|
620
|
+
their embedded WASM binaries from the final bundle.
|
|
621
|
+
|
|
622
|
+
**Published:** `dist/` only. Contains compiled JS, TypeScript declarations,
|
|
623
|
+
and WASM binaries as assets for streaming mode. The embedded base64 is compiled
|
|
624
|
+
into the JS, not a separate file.
|
|
625
|
+
|
|
626
|
+
**Not published:** `src/`, `test/`, `build/`, `scripts/`, `docs/`
|
|
627
|
+
|
|
628
|
+
---
|
|
629
|
+
|
|
630
|
+
## Buffer Layouts
|
|
631
|
+
|
|
632
|
+
All offsets start at 0 per module. Independent linear memory. No offsets are
|
|
633
|
+
shared or coordinated across modules.
|
|
634
|
+
|
|
635
|
+
### Serpent module — 3 pages (192 KB)
|
|
636
|
+
|
|
637
|
+
Source: `src/asm/serpent/buffers.ts`
|
|
638
|
+
|
|
639
|
+
| Offset | Size | Name |
|
|
640
|
+
|--------|------|------|
|
|
641
|
+
| 0 | 32 | `KEY_BUFFER` — key input (padded to 32 bytes for all key sizes) |
|
|
642
|
+
| 32 | 16 | `BLOCK_PT_BUFFER` — single block plaintext |
|
|
643
|
+
| 48 | 16 | `BLOCK_CT_BUFFER` — single block ciphertext |
|
|
644
|
+
| 64 | 16 | `NONCE_BUFFER` — CTR mode nonce |
|
|
645
|
+
| 80 | 16 | `COUNTER_BUFFER` — 128-bit little-endian counter |
|
|
646
|
+
| 96 | 528 | `SUBKEY_BUFFER` — key schedule output (33 rounds × 4 × 4 bytes) |
|
|
647
|
+
| 624 | 65536 | `CHUNK_PT_BUFFER` — streaming plaintext (CTR/CBC) |
|
|
648
|
+
| 66160 | 65536 | `CHUNK_CT_BUFFER` — streaming ciphertext (CTR/CBC) |
|
|
649
|
+
| 131696 | 20 | `WORK_BUFFER` — 5 × i32 scratch registers (key schedule + S-box/LT rounds) |
|
|
650
|
+
| 131716 | 16 | `CBC_IV_BUFFER` — CBC initialization vector / chaining value |
|
|
651
|
+
| 131732 | — | END |
|
|
652
|
+
|
|
653
|
+
`wipeBuffers()` zeroes all 10 buffers (key, block pt/ct, nonce, counter, subkeys, work, chunk pt/ct, CBC IV).
|
|
654
|
+
|
|
655
|
+
### ChaCha20 module — 3 pages (192 KB)
|
|
656
|
+
|
|
657
|
+
Source: `src/asm/chacha/buffers.ts`
|
|
658
|
+
|
|
659
|
+
| Offset | Size | Name |
|
|
660
|
+
|--------|------|------|
|
|
661
|
+
| 0 | 32 | `KEY_BUFFER` — ChaCha20 256-bit key |
|
|
662
|
+
| 32 | 12 | `CHACHA_NONCE_BUFFER` — 96-bit nonce (3 × u32, LE) |
|
|
663
|
+
| 44 | 4 | `CHACHA_CTR_BUFFER` — u32 block counter |
|
|
664
|
+
| 48 | 64 | `CHACHA_BLOCK_BUFFER` — 64-byte keystream block output |
|
|
665
|
+
| 112 | 64 | `CHACHA_STATE_BUFFER` — 16 × u32 initial state |
|
|
666
|
+
| 176 | 65536 | `CHUNK_PT_BUFFER` — streaming plaintext |
|
|
667
|
+
| 65712 | 65536 | `CHUNK_CT_BUFFER` — streaming ciphertext |
|
|
668
|
+
| 131248 | 32 | `POLY_KEY_BUFFER` — one-time key r‖s |
|
|
669
|
+
| 131280 | 64 | `POLY_MSG_BUFFER` — message staging (≤ 64 bytes per polyUpdate) |
|
|
670
|
+
| 131344 | 16 | `POLY_BUF_BUFFER` — partial block accumulator |
|
|
671
|
+
| 131360 | 4 | `POLY_BUF_LEN_BUFFER` — u32 bytes in partial block |
|
|
672
|
+
| 131364 | 16 | `POLY_TAG_BUFFER` — 16-byte output MAC tag |
|
|
673
|
+
| 131380 | 40 | `POLY_H_BUFFER` — accumulator h: 5 × u64 |
|
|
674
|
+
| 131420 | 40 | `POLY_R_BUFFER` — clamped r: 5 × u64 |
|
|
675
|
+
| 131460 | 32 | `POLY_RS_BUFFER` — precomputed 5×r[1..4]: 4 × u64 |
|
|
676
|
+
| 131492 | 16 | `POLY_S_BUFFER` — s pad: 4 × u32 |
|
|
677
|
+
| 131508 | 24 | `XCHACHA_NONCE_BUFFER` — full 24-byte XChaCha20 nonce |
|
|
678
|
+
| 131532 | 32 | `XCHACHA_SUBKEY_BUFFER` — HChaCha20 output (key material) |
|
|
679
|
+
| 131564 | — | END |
|
|
680
|
+
|
|
681
|
+
`wipeBuffers()` zeroes all 14 buffer regions (key, chacha nonce/ctr/block/state, chunk pt/ct, poly key/msg/buf/tag/h/r/rs/s, xchacha nonce/subkey).
|
|
682
|
+
|
|
683
|
+
### SHA-2 module — 3 pages (192 KB)
|
|
684
|
+
|
|
685
|
+
Source: `src/asm/sha2/buffers.ts`
|
|
686
|
+
|
|
687
|
+
| Offset | Size | Name |
|
|
688
|
+
|--------|------|------|
|
|
689
|
+
| 0 | 32 | `SHA256_H` — SHA-256 hash state H0..H7 (8 × u32) |
|
|
690
|
+
| 32 | 64 | `SHA256_BLOCK` — SHA-256 block accumulator |
|
|
691
|
+
| 96 | 256 | `SHA256_W` — SHA-256 message schedule W[0..63] (64 × u32) |
|
|
692
|
+
| 352 | 32 | `SHA256_OUT` — SHA-256 digest output |
|
|
693
|
+
| 384 | 64 | `SHA256_INPUT` — SHA-256 user input staging (one block) |
|
|
694
|
+
| 448 | 4 | `SHA256_PARTIAL` — u32 partial block length |
|
|
695
|
+
| 452 | 8 | `SHA256_TOTAL` — u64 total bytes hashed |
|
|
696
|
+
| 460 | 64 | `HMAC256_IPAD` — HMAC-SHA256 K' XOR ipad |
|
|
697
|
+
| 524 | 64 | `HMAC256_OPAD` — HMAC-SHA256 K' XOR opad |
|
|
698
|
+
| 588 | 32 | `HMAC256_INNER` — HMAC-SHA256 inner hash |
|
|
699
|
+
| 620 | 64 | `SHA512_H` — SHA-512 hash state H0..H7 (8 × u64) |
|
|
700
|
+
| 684 | 128 | `SHA512_BLOCK` — SHA-512 block accumulator |
|
|
701
|
+
| 812 | 640 | `SHA512_W` — SHA-512 message schedule W[0..79] (80 × u64) |
|
|
702
|
+
| 1452 | 64 | `SHA512_OUT` — SHA-512 digest output (SHA-384 uses first 48 bytes) |
|
|
703
|
+
| 1516 | 128 | `SHA512_INPUT` — SHA-512 user input staging (one block) |
|
|
704
|
+
| 1644 | 4 | `SHA512_PARTIAL` — u32 partial block length |
|
|
705
|
+
| 1648 | 8 | `SHA512_TOTAL` — u64 total bytes hashed |
|
|
706
|
+
| 1656 | 128 | `HMAC512_IPAD` — HMAC-SHA512 K' XOR ipad (128-byte block size) |
|
|
707
|
+
| 1784 | 128 | `HMAC512_OPAD` — HMAC-SHA512 K' XOR opad |
|
|
708
|
+
| 1912 | 64 | `HMAC512_INNER` — HMAC-SHA512 inner hash |
|
|
709
|
+
| 1976 | — | END |
|
|
710
|
+
|
|
711
|
+
`wipeBuffers()` zeroes all 20 buffer regions (SHA-256 state/block/W/out/input/partial/total, HMAC-256 ipad/opad/inner, SHA-512 state/block/W/out/input/partial/total, HMAC-512 ipad/opad/inner).
|
|
712
|
+
|
|
713
|
+
### SHA-3 module — 3 pages (192 KB)
|
|
714
|
+
|
|
715
|
+
Source: `src/asm/sha3/buffers.ts`
|
|
716
|
+
|
|
717
|
+
| Offset | Size | Name |
|
|
718
|
+
|--------|------|------|
|
|
719
|
+
| 0 | 200 | `KECCAK_STATE` — 25 × u64 Keccak-f[1600] lane matrix (5×5, row-major x+5y) |
|
|
720
|
+
| 200 | 4 | `KECCAK_RATE` — u32 rate in bytes (variant-specific: 72–168) |
|
|
721
|
+
| 204 | 4 | `KECCAK_ABSORBED` — u32 bytes absorbed into current block |
|
|
722
|
+
| 208 | 1 | `KECCAK_DSBYTE` — u8 domain separation byte (0x06 for SHA-3, 0x1f for SHAKE) |
|
|
723
|
+
| 209 | 168 | `KECCAK_INPUT` — input staging buffer (max rate = SHAKE128 at 168 bytes) |
|
|
724
|
+
| 377 | 168 | `KECCAK_OUT` — output buffer (one SHAKE128 squeeze block) |
|
|
725
|
+
| 545 | — | END |
|
|
726
|
+
|
|
727
|
+
`wipeBuffers()` zeroes all 6 buffer regions (state, rate, absorbed, dsbyte, input, output).
|
|
728
|
+
|
|
729
|
+
---
|
|
730
|
+
|
|
731
|
+
## Test Suite
|
|
732
|
+
|
|
733
|
+
### Structure
|
|
734
|
+
|
|
735
|
+
|
|
736
|
+
For the full testing methodology and vector corpus, see: [test-suite.md](./test-suite.md)
|
|
737
|
+
|
|
738
|
+
### Gate discipline
|
|
739
|
+
|
|
740
|
+
Each primitive family has a gate test, the simplest authoritative vector for
|
|
741
|
+
that primitive. The gate must pass before any other tests in that family are
|
|
742
|
+
written or run. Gate tests are annotated with a `// GATE` comment.
|
|
743
|
+
|
|
744
|
+
### `init.test.ts` contracts
|
|
745
|
+
|
|
746
|
+
- `init()` with each of the three modes loads and caches the module correctly
|
|
747
|
+
- Idempotency: second `init()` call for same module is a no-op
|
|
748
|
+
- Error before init: clear error thrown for each class before its module is loaded
|
|
749
|
+
- Partial init: loading `['serpent']` does not make `sha3` classes available
|
|
750
|
+
|
|
751
|
+
---
|
|
752
|
+
|
|
753
|
+
## Correctness Contract
|
|
754
|
+
|
|
755
|
+
leviathan-crypto must produce byte-identical output to the authoritative
|
|
756
|
+
specification for every known test vector. Cross-checks against the leviathan
|
|
757
|
+
TypeScript reference and external tools (OpenSSL, Python hashlib, Node.js crypto)
|
|
758
|
+
provide additional verification layers.
|
|
759
|
+
|
|
760
|
+
The test vector corpus in `test/vectors/` is read-only. Integrity is verified via
|
|
761
|
+
`SHA256SUMS`, expected values are sourced directly from authoritative references.
|
|
762
|
+
They are the immutable truth, and must never be modified to make tests pass.
|
|
763
|
+
|
|
764
|
+
---
|
|
765
|
+
|
|
766
|
+
## Known Limitations (v1.0)
|
|
767
|
+
|
|
768
|
+
- **`SerpentCbc` is unauthenticated**: use `SerpentSeal` for authenticated
|
|
769
|
+
Serpent encryption, or pair `SerpentCbc` with `HMAC_SHA256` (Encrypt-then-MAC)
|
|
770
|
+
if direct CBC access is required.
|
|
771
|
+
- **Single-threaded WASM per instance**: one WASM instance per binary per thread.
|
|
772
|
+
`SerpentStreamPool` and `XChaCha20Poly1305Pool` provide Worker-based parallelism
|
|
773
|
+
for their respective AEAD paths; other primitive families remain single-threaded.
|
|
774
|
+
- **Max input per WASM call**: chunk-based APIs (CTR, CBC) accept at most 64KB
|
|
775
|
+
per call. Wrappers handle splitting automatically for larger inputs.
|
|
776
|
+
- **Browser WASM loading**: streaming mode requires files served with
|
|
777
|
+
`Content-Type: application/wasm`. Embedded mode works everywhere.
|
|
778
|
+
|
|
779
|
+
---
|
|
780
|
+
|
|
781
|
+
## Cross-References
|
|
782
|
+
|
|
783
|
+
- [README.md](./README.md) — library documentation index, exports table, and quick-start examples
|
|
784
|
+
- [test-suite.md](./test-suite.md) — testing methodology, vector corpus, and gate discipline
|
|
785
|
+
- [init.md](./init.md) — `init()` API, three loading modes, and idempotent behavior
|
|
786
|
+
- [loader.md](./loader.md) — internal WASM binary loading strategies (embedded, streaming, manual)
|
|
787
|
+
- [wasm.md](./wasm.md) — WebAssembly primer: modules, instances, memory, and the init gate
|
|
788
|
+
- [types.md](./types.md) — public TypeScript interfaces (`Hash`, `KeyedHash`, `Blockcipher`, `Streamcipher`, `AEAD`)
|
|
789
|
+
- [utils.md](./utils.md) — encoding helpers, `constantTimeEqual`, `wipe`, `randomBytes`
|
|
790
|
+
- [serpent.md](./serpent.md) — Serpent-256 TypeScript API (SerpentSeal, SerpentStream, raw modes)
|
|
791
|
+
- [chacha20.md](./chacha20.md) — ChaCha20/Poly1305 TypeScript API and XChaCha20-Poly1305 AEAD
|
|
792
|
+
- [sha2.md](./sha2.md) — SHA-2 hashes, HMAC, and HKDF TypeScript API
|
|
793
|
+
- [sha3.md](./sha3.md) — SHA-3 hashes and SHAKE XOFs TypeScript API
|
|
794
|
+
- [fortuna.md](./fortuna.md) — Fortuna CSPRNG with forward secrecy and entropy pooling
|
|
795
|
+
- [argon2id.md](./argon2id.md) — Argon2id password hashing and key derivation
|