leviathan-crypto 1.0.0 → 1.2.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/README.md CHANGED
@@ -1,256 +1,144 @@
1
- [![MIT Licensed](https://img.shields.io/github/license/xero/text0wnz?logo=wikiversity&logoColor=979da4&labelColor=262a2e&color=b1a268)](https://github.com/xero/text0wnz/blob/main/LICENSE)
2
- [![Version](https://img.shields.io/github/package-json/version/xero/leviathan-crypto?labelColor=33383e&logo=npm&&logoColor=979da4&color=6e2aa5)](https://github.com/xero/leviathan-crypto/releases/latest)
3
- [![GitHub repo size](https://img.shields.io/github/repo-size/xero/leviathan-crypto?labelColor=262a2e&logo=googlecontaineroptimizedos&logoColor=979da4&color=6e2aa5)](https://github.com/xero/leviathan-crypto/)
4
- [![test suite](https://github.com/xero/leviathan-crypto/actions/workflows/test-suite.yml/badge.svg)](https://github.com/xero/leviathan-crypto/actions/workflows/test-suite.yml)
5
- [![wiki](https://github.com/xero/leviathan-crypto/actions/workflows/wiki.yml/badge.svg)](https://github.com/xero/leviathan-crypto/wiki)
6
-
7
- <img src="https://github.com/xero/leviathan-crypto/raw/main/docs/logo.svg" alt="Leviathan logo" width="400">
8
-
9
- # Leviathan-Crypto: Serpent-256 & XChaCha20-Poly1305 Cryptography for the Web
10
-
11
- Serpent-256, the most conservative AES finalist, employs 32 rounds and a
12
- maximum security margin, built to withstand future cryptanalytic advancements.
13
- Paired with the streamlined brilliance of ChaCha20-Poly1305, and complemented
14
- by SHA-2 and SHA-3. Two design philosophies, four cryptographic primitives,
15
- integrated into one coherent API.
16
-
17
- **WebAssembly (WASM) serves as the correctness layer.** It features spec-driven and
18
- vector-verified AssemblyScript implementations of Serpent-256, ChaCha20/Poly1305,
19
- SHA-2, and SHA-3. Each cryptographic primitive is compiled into its own isolated
20
- binary, executing outside the JavaScript JIT. This ensures no speculative
21
- optimization affects key material and eliminates data-dependent timing
22
- vulnerabilities from table lookups.
23
-
24
- **TypeScript acts as the ergonomics layer.** Fully typed classes, explicit
25
- `init()` gates, input validation, and authenticated compositions
26
- ([`SerpentSeal`](https://github.com/xero/leviathan-crypto/wiki/serpent#serpentseal),
27
- [`SerpentStream`](https://github.com/xero/leviathan-crypto/wiki/serpent#serpentstream),
28
- [`SerpentStreamSealer`](https://github.com/xero/leviathan-crypto/wiki/serpent#serpentstreamsealer--serpentstreamopener))
29
- ensure primitives are connected correctly, simplifying development and ensuring
30
- correctness. Advanced users retain the ability to directly access the raw block
31
- cipher classes.
32
-
33
- ## Why Serpent-256
34
-
35
- Serpent-256, an AES finalist, received more first-place security votes than
36
- Rijndael from the NIST evaluation committee. It was designed with a larger
37
- security margin: 32 rounds compared to AES's 10, 12, or 14.
38
-
39
- While Serpent won on security margin, AES (Rijndael) ultimately won the
40
- competition due to its performance. Rijndael was selected because speed
41
- was paramount for the hardware and embedded targets NIST was optimizing for
42
- in 2001. However, for software running on modern hardware where milliseconds
43
- of encryption latency are acceptable, this tradeoff is no longer as relevant.
44
-
45
- **Security Margin.** Serpent has been a target of cryptanalytic research
46
- since the AES competition. The current state-of-the-art is as follows:
47
-
48
- - **Best known reduced-round attack:**
49
- - multidimensional linear cryptanalysis reaching 12 of 32 rounds (Nguyen,
50
- Wu & Wang, ACISP 2011), less than half the full cipher, requiring 2¹¹⁸
51
- known plaintexts and 2²²⁸·⁸ time.
52
- - Multidimensional linear cryptanalysis reaches 12 of 32 rounds (Nguyen,
53
- Wu & Wang, ACISP 2011), less than half the full cipher. This requires
54
- 2¹¹⁸ known plaintexts and 2²²⁸·⁸ time. [source](https://personal.ntu.edu.sg/wuhj/research/publications/2011_ACISP_MLC.pdf) & [mirror](https://archive.is/6pwMM)
55
- - **Best known full-round attack:**
56
- - biclique cryptanalysis of full 32-round Serpent-256 (de Carvalho & Kowada,
57
- SBSeg 2020), time complexity 2²⁵⁵·²¹, only 0.79 bits below the 256-bit
58
- brute-force ceiling of 2²⁵⁶, and requires 2⁸⁸ chosen ciphertexts, making
59
- it strictly less practical than brute force. For comparison, the analogous
60
- biclique attack on full-round AES-256 (Bogdanov et al., 2011) reaches
61
- 2²⁵⁴·⁴. Serpent-256 is marginally harder to attack by this method than AES-256.
62
- - Biclique cryptanalysis of full 32-round Serpent-256 (de Carvalho & Kowada,
63
- SBSeg 2020) has a time complexity of 2²⁵⁵·²¹, only 0.79 bits below the 256-bit
64
- brute-force ceiling of 2²⁵⁶. It requires 2⁸⁸ chosen ciphertexts, making it
65
- strictly less practical than brute force. For comparison, the analogous biclique
66
- attack on full-round AES-256 (Bogdanov et al., 2011) reaches 2²⁵⁴·⁴.
67
- Serpent-256 is marginally harder to attack by this method than AES-256. [source](https://sol.sbc.org.br/index.php/sbseg/article/view/19225/19054) & [mirror](https://archive.is/ZZjrT)
68
- - Our independent research improved the published result by
69
- −0.20 bits through systematic search over v position, biclique nibble
70
- selection, and nabla pair. the best configuration (K31/K17, delta nibble 0,
71
- nabla nibble 10, v = state 66 nibbles 8+9) achieves 2²⁵⁵·¹⁹ with only 2⁴
72
- chosen ciphertexts. The K17 nabla result is a new finding not present in
73
- the published papers.
74
- - Our independent research improved the published result by −0.20 bits through
75
- systematic search over v position, biclique nibble selection, and nabla pair.
76
- The best configuration (K31/K17, delta nibble 0, nabla nibble 10, v = state
77
- 66 nibbles 8+9) achieves 2²⁵⁵·¹⁹ with only 2⁴ chosen ciphertexts. The K17 nabla
78
- result is a new finding not present in the published papers. [`biclique_research`](https://github.com/xero/BicliqueFinder/blob/main/biclique-research.md)
79
-
80
- See: [`serpent_audit.md`](https://github.com/xero/leviathan-crypto/wiki/serpent_audit)
81
- for the full analysis.
82
-
83
- **Implementation.** Implementation: Serpent's S-boxes are implemented as Boolean gate
84
- circuits, meaning there are no table lookups, data-dependent memory access, or
85
- data-dependent branches. Every bit is processed unconditionally on every block.
86
- This approach provides the most timing-safe cipher implementation available in a
87
- JavaScript runtime, where JIT optimization can otherwise introduce observable
88
- timing variations.
89
-
90
- **Key Size:** The default API only supports 256-bit keys. The absence of 128 or
91
- 192-bit variants mitigates the risk of key-size downgrade attacks.
1
+ [![Version](https://img.shields.io/github/package-json/version/xero/leviathan-crypto?labelColor=33383e&logo=npm&&logoColor=979da4&color=6e2aa5)](https://github.com/xero/leviathan-crypto/releases/latest) [![GitHub repo size](https://img.shields.io/github/repo-size/xero/leviathan-crypto?labelColor=262a2e&logo=googlecontaineroptimizedos&logoColor=979da4&color=6e2aa5)](https://github.com/xero/leviathan-crypto/) [![test suite](https://github.com/xero/leviathan-crypto/actions/workflows/test-suite.yml/badge.svg)](https://github.com/xero/leviathan-crypto/actions/workflows/test-suite.yml) [![wiki](https://github.com/xero/leviathan-crypto/actions/workflows/wiki.yml/badge.svg)](https://github.com/xero/leviathan-crypto/wiki)
92
2
 
93
- ## Primitives
3
+ ![side-effect free](https://github.com/xero/leviathan-crypto/raw/main/docs/badge-side-effect-free.svg) ![tree-shakeable](https://github.com/xero/leviathan-crypto/raw/main/docs/badge-tree-shakable.svg) ![zero dependencies](https://github.com/xero/leviathan-crypto/raw/main/docs/badge-zero-dependancies.svg) [![MIT Licensed](https://github.com/xero/leviathan-crypto/raw/main/docs/badge-mit-license.svg)](https://github.com/xero/text0wnz/blob/main/LICENSE)
94
4
 
95
- | Classes | Module | Auth | Notes |
96
- |---------|--------|------|-------|
97
- | `SerpentSeal` | `serpent`, `sha2` | **Yes** | Authenticated encryption: Serpent-CBC + HMAC-SHA256. Recommended for most use cases. |
98
- | `SerpentStream`, `SerpentStreamPool` | `serpent`, `sha2` | **Yes** | Chunked one-shot AEAD for large payloads. Pool variant parallelises across workers. |
99
- | `SerpentStreamSealer`, `SerpentStreamOpener` | `serpent`, `sha2` | **Yes** | Incremental streaming AEAD: seal and open one chunk at a time without buffering the full message. |
100
- | `SerpentStreamEncoder`, `SerpentStreamDecoder` | `serpent`, `sha2` | **Yes** | Length-prefixed framing over SerpentStreamSealer/Opener for flat byte streams (files, buffered TCP). |
101
- | `Serpent`, `SerpentCtr`, `SerpentCbc` | `serpent` | **No** | Raw ECB, CTR, CBC modes. Pair with HMAC-SHA256 for authentication. |
102
- | `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305` | `chacha20` | Yes (AEAD) | RFC 8439 |
103
- | `SHA256`, `SHA384`, `SHA512`, `HMAC_SHA256`, `HMAC_SHA384`, `HMAC_SHA512` | `sha2` | -- | FIPS 180-4, RFC 2104 |
104
- | `HKDF_SHA256`, `HKDF_SHA512` | `sha2` | -- | Key derivation: RFC 5869. Extract-and-expand over HMAC. |
105
- | `SHA3_224`, `SHA3_256`, `SHA3_384`, `SHA3_512`, `SHAKE128`, `SHAKE256` | `sha3` | -- | FIPS 202 |
106
- | `Fortuna` | `fortuna` | -- | Fortuna CSPRNG (Ferguson & Schneier). Requires `Fortuna.create()`. |
107
-
108
- >[!IMPORTANT]
109
- > All cryptographic computation runs in WASM (AssemblyScript), isolated outside the JavaScript JIT.
110
- > The TypeScript layer provides the public API with input validation, type safety, and developer ergonomics.
5
+ <img src="https://github.com/xero/leviathan-crypto/raw/main/docs/logo.svg" alt="Leviathan logo" width="400" >
111
6
 
112
- ## Quick Start
7
+ # Leviathan-Crypto
113
8
 
114
- ### Authenticated encryption with Serpent (recommended)
9
+ > Web cryptography built on Serpent-256 paranoia and XChaCha20-Poly1305 elegance.
115
10
 
116
- ```typescript
117
- import { init, SerpentSeal, randomBytes } from 'leviathan-crypto'
11
+ **Serpent-256 is the cipher for those who distrust consensus.** In 2001, when NIST selected AES, Serpent actually received more first-place security votes from the evaluation committee. However, it lost because the competition also considered performance on hardware embedded systems, which are no longer representative of the environments for which we develop software. Serpent's designers made no compromises: thirty-two rounds, S-boxes implemented using pure Boolean logic gates without table lookups, and every bit processed for each block. You use Serpent not because a committee recommended it, but because you trust the cryptanalysis. The current best attack on the full thirty-two-round Serpent-256 achieves 2²⁵⁵·¹⁹ — less than one bit below the brute-force ceiling, and strictly impractical. This includes our own independent research, which improved upon the published result. See [`serpent_audit.md`](https://github.com/xero/leviathan-crypto/wiki/serpent_audit).
118
12
 
119
- await init(['serpent', 'sha2'])
13
+ **XChaCha20-Poly1305 is the cipher for those who appreciate design that has nothing to hide.** Daniel Bernstein built ChaCha20 as a twenty-round ARX construction: add, rotate, and XOR, in a precise choreography that simply doesn't have the attack surface that table-based ciphers do. It has no S-boxes, no cache-timing leakage, and requires no hardware acceleration to be fast. Poly1305 adds a final layer of security: a one-time authenticator with an unconditional forgery bound, mathematically guaranteed regardless of attacker compute power. XChaCha20-Poly1305 is the construction you reach for when you want an AEAD whose security proof you can actually read without a PhD. See [`chacha_audit.md`](https://github.com/xero/leviathan-crypto/wiki/chacha_audit).
120
14
 
121
- const key = randomBytes(64) // 64-byte key (encKey + macKey)
122
-
123
- const seal = new SerpentSeal()
15
+ The tension between these two approaches constitutes the library's core identity. Serpent embodies defiance, ChaCha embodies elegance, yet both arrive at the same place: constant-time, side-channel resistant implementations, independently audited against their specifications. They represent two design philosophies that do not agree on anything, except the answer.
124
16
 
125
- // Encrypt and authenticate
126
- const ciphertext = seal.encrypt(key, plaintext)
17
+ **WebAssembly provides a correctness layer.** Each primitive compiles into its own isolated binary, executing outside the JavaScript JIT. This prevents speculative optimization from affecting key material and ensures that data-dependent timing vulnerabilities do not cross the boundary.
127
18
 
128
- // Decrypt and verify (throws on tamper)
129
- const decrypted = seal.decrypt(key, ciphertext)
19
+ **TypeScript acts as the ergonomics layer.** Fully typed classes, explicit `init()` gates, input validation, and authenticated compositions ensure primitives are connected correctly.
130
20
 
131
- seal.dispose()
132
- ```
21
+ ---
133
22
 
134
- ### Incremental streaming AEAD
23
+ #### **Zero Dependencies.**
24
+ With no npm dependency graph to audit, the supply chain attack surface is eliminated.
135
25
 
136
- Use `SerpentStreamSealer` when data arrives chunk by chunk and you cannot
137
- buffer the full message before encrypting.
26
+ #### **Tree-shakeable.**
27
+ Import only the cipher(s) you intend to use. Subpath exports allow bundlers to exclude everything else.
138
28
 
139
- ```typescript
140
- import { init, SerpentStreamSealer, SerpentStreamOpener, randomBytes } from 'leviathan-crypto'
29
+ #### **Side-effect Free.**
30
+ Nothing runs upon import. Initialization via `init()` is explicit and asynchronous.
141
31
 
142
- await init(['serpent', 'sha2'])
143
32
 
144
- const key = randomBytes(64)
33
+ ## Installation
145
34
 
146
- // Seal side
147
- const sealer = new SerpentStreamSealer(key, 65536)
148
- const header = sealer.header() // transmit to opener before any chunks
35
+ ```bash
36
+ # use bun
37
+ bun i leviathan-crypto
38
+ # or npm
39
+ npm install leviathan-crypto
40
+ ```
149
41
 
150
- const chunk0 = sealer.seal(data0) // exactly chunkSize bytes
151
- const chunk1 = sealer.seal(data1)
152
- const last = sealer.final(tail) // any size up to chunkSize; wipes key on return
42
+ > [!NOTE]
43
+ > The Serpent and ChaCha20 modules require a runtime with WebAssembly SIMD support. This has been a feature of all major browsers and runtimes since 2021. All other primitives (SHA-2, SHA-3, Poly1305) run on any WASM-capable runtime.
153
44
 
154
- // Open side
155
- const opener = new SerpentStreamOpener(key, header)
45
+ ---
156
46
 
157
- const pt0 = opener.open(chunk0)
158
- const pt1 = opener.open(chunk1)
159
- const ptN = opener.open(last) // detects final chunk; wipes key on return
47
+ ## Demos
160
48
 
161
- // Reordering, truncation, and cross-stream splicing all throw on open()
162
- ```
49
+ **`lvthn-web`** [ [demo](https://leviathan.3xi.club/web) · [source](https://github.com/xero/leviathan-demos/tree/main/lvthn-web) · [readme](https://github.com/xero/leviathan-demos/blob/main/lvthn-web/README.md) ]
163
50
 
164
- ### Large payload chunking
51
+ A browser encryption tool in a single, self-contained HTML file. Encrypt text or files using Serpent-256-CBC and Argon2id key derivation, then share the armored output. No server, installation, or network connection required after initial load. The code in is written to be read. The Encrypt-then-MAC construction, HMAC input (header with HMAC field zeroed + ciphertext), and Argon2id parameters are all intentional examples worth reading.
165
52
 
166
- ```typescript
167
- import { init, SerpentStream, randomBytes } from 'leviathan-crypto'
53
+ **`lvthn-chat`** [ [demo](https://leviathan.3xi.club/chat) · [source](https://github.com/xero/leviathan-demos/tree/main/lvthn-chat) · [readme](https://github.com/xero/leviathan-demos/blob/main/lvthn-chat/README.md) ]
168
54
 
169
- await init(['serpent', 'sha2'])
55
+ End-to-end encrypted chat featuring two-party messaging over X25519 key exchange and XChaCha20-Poly1305 message encryption. The relay server functions as a dumb WebSocket pipe that never sees plaintext. Each message incorporates sequence numbers, which allows the system to detect and reject replayed messages from an attacker. The demo deconstructs the protocol step by step, with visual feedback for both injection and replays.
170
56
 
171
- const key = randomBytes(32) // 32-byte key (HKDF handles expansion internally)
57
+ **`lvthn-cli`** [ [npm](https://www.npmjs.com/package/lvthn) · [source](https://github.com/xero/leviathan-demos/tree/main/lvthn-cli) · [readme](https://github.com/xero/leviathan-demos/blob/main/lvthn-cli/README.md) ]
172
58
 
173
- const stream = new SerpentStream()
174
- const ciphertext = stream.seal(key, largePlaintext) // default 64KB chunks
175
- const decrypted = stream.open(key, ciphertext)
59
+ File encryption CLI. Supports both Serpent-256 and XChaCha20-Poly1305, selectable via the `--cipher` flag. A single keyfile is compatible with both ciphers; the header byte determines decryption automatically. Encryption and decryption distribute 64KB chunks across a worker pool sized to hardwareConcurrency. Each worker owns an isolated WASM instance with no shared memory between workers.
176
60
 
177
- stream.dispose()
61
+ ```sh
62
+ bun i -g lvthn # or npm slow mode
63
+ lvthn keygen --armor -o my.key
64
+ cat secret.txt | lvthn encrypt -k my.key --armor > secret.enc
178
65
  ```
66
+ *[`lvthncli-serpent`](https://github.com/xero/leviathan-demos/tree/main/lvthncli-serpent) and [`lvthncli-chacha`](https://github.com/xero/leviathan-demos/tree/main/lvthncli-chacha) are additional educational tools: structurally identical to the main CLI tool, each implementing only a single cipher. By comparing the two, you can pinpoint the exact changes that occur when primitives are swapped; these are limited to `src/pool.ts` and `src/worker.ts`.*
179
67
 
180
- ### Fortuna CSPRNG
68
+ ---
181
69
 
182
- ```typescript
183
- import { init, Fortuna } from 'leviathan-crypto'
184
-
185
- await init(['serpent', 'sha2'])
70
+ ## Primitives
186
71
 
187
- const fortuna = await Fortuna.create()
188
- const random = fortuna.get(32) // 32 random bytes
72
+ | Classes | Module | Auth | Notes |
73
+ | ------------------------------------------------------------------------- | ----------------- | ------- | ---------------------------------------------------------------------------------------------------- |
74
+ | `SerpentSeal` | `serpent`, `sha2` | **Yes** | Authenticated encryption: Serpent-CBC + HMAC-SHA256. Recommended for most use cases. |
75
+ | `SerpentStream`, `SerpentStreamPool` | `serpent`, `sha2` | **Yes** | Chunked one-shot AEAD for large payloads. Pool variant parallelises across workers. |
76
+ | `SerpentStreamSealer`, `SerpentStreamOpener` | `serpent`, `sha2` | **Yes** | Incremental streaming AEAD: seal and open one chunk at a time without buffering the full message. |
77
+ | `SerpentStreamEncoder`, `SerpentStreamDecoder` | `serpent`, `sha2` | **Yes** | Length-prefixed framing over SerpentStreamSealer/Opener for flat byte streams (files, buffered TCP). |
78
+ | `Serpent`, `SerpentCtr`, `SerpentCbc` | `serpent` | **No** | Raw ECB, CTR, CBC modes. Unauthenticated — pair with HMAC-SHA256 for authentication. |
79
+ | `XChaCha20Poly1305`, `ChaCha20Poly1305` | `chacha20` | **Yes** | AEAD — RFC 8439. XChaCha20 recommended (192-bit nonce). |
80
+ | `ChaCha20` | `chacha20` | **No** | Raw stream cipher. Unauthenticated — use with `Poly1305` for authentication. |
81
+ | `Poly1305` | `chacha20` | **No** | One-time MAC — RFC 8439. Use via the AEAD classes unless you have a specific reason not to. |
82
+ | `SHA256`, `SHA384`, `SHA512`, `HMAC_SHA256`, `HMAC_SHA384`, `HMAC_SHA512` | `sha2` | - | FIPS 180-4, RFC 2104 |
83
+ | `HKDF_SHA256`, `HKDF_SHA512` | `sha2` | - | Key derivation — RFC 5869. Extract-and-expand over HMAC. |
84
+ | `SHA3_224`, `SHA3_256`, `SHA3_384`, `SHA3_512`, `SHAKE128`, `SHAKE256` | `sha3` | - | FIPS 202 |
85
+ | `Fortuna` | `fortuna` | - | Fortuna CSPRNG (Ferguson & Schneier). Requires `Fortuna.create()`. |
86
+
87
+ > [!IMPORTANT]
88
+ > All cryptographic computation runs in WASM (AssemblyScript), isolated outside the JavaScript JIT. The TypeScript layer provides the public API with input validation, type safety, and developer ergonomics.
89
+
90
+ > [!WARNING]
91
+ > `SerpentCtr` and `SerpentCbc` are **unauthenticated** cipher modes. They provide confidentiality but not integrity or authenticity. An attacker can modify ciphertext without detection. For authenticated Serpent encryption use `SerpentSeal` or `SerpentStreamSealer`. When using CBC/CTR directly, pair with `HMAC_SHA256` using the Encrypt-then-MAC pattern.
92
+
93
+ ---
189
94
 
190
- fortuna.stop()
191
- ```
95
+ ## Quick Start
192
96
 
193
- ### Hashing with SHA-3
97
+ ### Authenticated encryption with Serpent
194
98
 
195
99
  ```typescript
196
- import { init, SHA3_256 } from 'leviathan-crypto'
100
+ import { init, SerpentSeal, randomBytes } from 'leviathan-crypto'
197
101
 
198
- await init(['sha3'])
102
+ await init(['serpent', 'sha2'])
199
103
 
200
- const hasher = new SHA3_256()
201
- const digest = hasher.hash(new TextEncoder().encode('hello'))
202
- // digest is a 32-byte Uint8Array
104
+ const key = randomBytes(64) // 64-byte key (encKey + macKey)
203
105
 
204
- hasher.dispose()
205
- ```
106
+ const seal = new SerpentSeal()
206
107
 
207
- ## Utilities
108
+ // Encrypt and authenticate
109
+ const ciphertext = seal.encrypt(key, plaintext)
208
110
 
209
- These helpers are available immediately on import: no `init()` required.
111
+ // Decrypt and verify (throws on tamper)
112
+ const decrypted = seal.decrypt(key, ciphertext)
210
113
 
211
- | Function | Description |
212
- |----------|-------------|
213
- | `hexToBytes(hex)` | Hex string to `Uint8Array` (accepts uppercase, `0x` prefix) |
214
- | `bytesToHex(bytes)` | `Uint8Array` to lowercase hex string |
215
- | `utf8ToBytes(str)` | UTF-8 string to `Uint8Array` |
216
- | `bytesToUtf8(bytes)` | `Uint8Array` to UTF-8 string |
217
- | `base64ToBytes(b64)` | Base64/base64url string to `Uint8Array` (undefined on invalid) |
218
- | `bytesToBase64(bytes, url?)` | `Uint8Array` to base64 string (url=true for base64url) |
219
- | `constantTimeEqual(a, b)` | Constant-time byte comparison (XOR-accumulate) |
220
- | `wipe(data)` | Zero a typed array in place |
221
- | `xor(a, b)` | XOR two equal-length `Uint8Array`s |
222
- | `concat(a, b)` | Concatenate two `Uint8Array`s |
223
- | `randomBytes(n)` | Cryptographically secure random bytes via Web Crypto |
114
+ seal.dispose()
115
+ ```
224
116
 
225
- ## Authentication Warning
117
+ ### Authenticated encryption with XChaCha20-Poly1305
226
118
 
227
- `SerpentCtr` and `SerpentCbc` are **unauthenticated** cipher modes. They provide
228
- confidentiality but not integrity or authenticity. An attacker can modify
229
- ciphertext without detection.
119
+ ```typescript
120
+ import { init, XChaCha20Poly1305, randomBytes } from 'leviathan-crypto'
230
121
 
231
- >[!TIP]
232
- > **For authenticated Serpent encryption:** use [`SerpentSeal`](https://github.com/xero/leviathan-crypto/wiki/serpent#serpentseal) or [`SerpentStreamSealer`](https://github.com/xero/leviathan-crypto/wiki/serpent#serpentstreamsealer--serpentstreamopener)
233
- >
234
- > **Using Serpent CBC/CTR directly:** pair with `HMAC_SHA256` using the Encrypt-then-MAC pattern
122
+ await init(['chacha20'])
235
123
 
236
- >[!NOTE]
237
- > **[`SerpentStream`](https://github.com/xero/leviathan-crypto/wiki/serpent#serpentstream) and [`SerpentStreamSealer`](https://github.com/xero/leviathan-crypto/wiki/serpent#serpentstreamsealer--serpentstreamopener)
238
- > inherently satisfy the Cryptographic Doom Principle.** Message Authentication Code (MAC)
239
- > verification is the mandatory check on every `open()` call; decryption is impossible until
240
- > this verification succeeds. Per-chunk HKDF key derivation, using position-bound info,
241
- > extends this protection to stream integrity. Reordering, truncation, and cross-stream
242
- > substitution are all detected at the MAC layer, preventing any plaintext from being
243
- > produced in such cases. [Full analysis.](https://github.com/xero/leviathan-crypto/wiki/serpent_audit#24-serpentstream-encrypt-then-mac-and-the-cryptographic-doom-principle)
124
+ const key = randomBytes(32) // 32-byte key
125
+ const nonce = randomBytes(24) // 24-byte nonce (XChaCha20 extended nonce)
244
126
 
245
- ## Installation
127
+ const chacha = new XChaCha20Poly1305()
246
128
 
247
- ```bash
248
- # use bun
249
- bun i leviathan-crypto
250
- # or npm
251
- npm install leviathan-crypto
129
+ // Encrypt and authenticate
130
+ const ciphertext = chacha.encrypt(key, nonce, plaintext)
131
+
132
+ // Decrypt and verify (throws on tamper)
133
+ const decrypted = chacha.decrypt(key, nonce, ciphertext)
134
+
135
+ chacha.dispose()
252
136
  ```
253
137
 
138
+ For more examples, including streaming, chunking, hashing, and key derivation, see the [examples page](https://github.com/xero/leviathan-crypto/wiki/examples).
139
+
140
+ ---
141
+
254
142
  ## Loading Modes
255
143
 
256
144
  ```typescript
@@ -264,59 +152,111 @@ await init(['serpent'], 'streaming', { wasmUrl: '/assets/wasm/' })
264
152
  await init(['serpent'], 'manual', { wasmBinary: { serpent: myBuffer } })
265
153
  ```
266
154
 
155
+ ### Tree-shaking with subpath imports
156
+
157
+ Each cipher ships as its own subpath export. A bundler with tree-shaking support and `"sideEffects": false` will exclude every module you don't import:
158
+
159
+ ```typescript
160
+ // Only serpent.wasm ends up in your bundle
161
+ import { serpentInit, SerpentSeal } from 'leviathan-crypto/serpent'
162
+ await serpentInit()
163
+
164
+ // Only chacha20.wasm ends up in your bundle
165
+ import { chacha20Init, XChaCha20Poly1305 } from 'leviathan-crypto/chacha20'
166
+ await chacha20Init()
167
+ ```
168
+
169
+ | Subpath | Entry point |
170
+ | --------------------------- | -------------------------- |
171
+ | `leviathan-crypto` | `./dist/index.js` |
172
+ | `leviathan-crypto/serpent` | `./dist/serpent/index.js` |
173
+ | `leviathan-crypto/chacha20` | `./dist/chacha20/index.js` |
174
+ | `leviathan-crypto/sha2` | `./dist/sha2/index.js` |
175
+ | `leviathan-crypto/sha3` | `./dist/sha3/index.js` |
176
+
177
+ ---
178
+
267
179
  ## Documentation
268
180
 
269
- **Full API documentation:** [./docs](./docs/README.md)
270
-
271
- | Module | Description |
272
- |--------|-------------|
273
- | [serpent.md](./docs/serpent.md) | Serpent-256 TypeScript API (`SerpentSeal`, `SerpentStream`, `SerpentStreamPool`, `SerpentStreamSealer`, `SerpentStreamOpener`, `Serpent`, `SerpentCtr`, `SerpentCbc`) |
274
- | [asm_serpent.md](./docs/asm_serpent.md) | Serpent-256 WASM implementation (bitslice S-boxes, key schedule, CTR/CBC) |
275
- | [chacha20.md](./docs/chacha20.md) | ChaCha20/Poly1305 TypeScript API (`ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`) |
276
- | [asm_chacha.md](./docs/asm_chacha.md) | ChaCha20/Poly1305 WASM implementation (quarter-round, HChaCha20) |
277
- | [sha2.md](./docs/sha2.md) | SHA-2 TypeScript API (`SHA256`, `SHA512`, `SHA384`, `HMAC_SHA256`, `HMAC_SHA512`, `HMAC_SHA384`) |
278
- | [asm_sha2.md](./docs/asm_sha2.md) | SHA-2 WASM implementation (compression functions, HMAC) |
279
- | [sha3.md](./docs/sha3.md) | SHA-3 TypeScript API (`SHA3_224`, `SHA3_256`, `SHA3_384`, `SHA3_512`, `SHAKE128`, `SHAKE256`) |
280
- | [asm_sha3.md](./docs/asm_sha3.md) | SHA-3 WASM implementation (Keccak-f[1600], sponge construction) |
281
- | [fortuna.md](./docs/fortuna.md) | Fortuna CSPRNG (forward secrecy, 32 entropy pools) |
282
- | [init.md](./docs/init.md) | `init()` API and WASM loading modes |
283
- | [utils.md](./docs/utils.md) | Encoding helpers, `constantTimeEqual`, `wipe`, `randomBytes` |
284
- | [types.md](./docs/types.md) | TypeScript interfaces (`Hash`, `KeyedHash`, `Blockcipher`, `Streamcipher`, `AEAD`) |
285
- | [architecture.md](./docs/architecture.md.md) | Architecture overview, build pipeline, module relationships |
286
- | [test-suite.md](./test-suite.md) | Test suite structure, vector corpus, gate discipline |
181
+ | Document | MD/Wiki | Description |
182
+ | ------------ | --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
183
+ | architecture | [▼](./docs/architecture.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/architecture) | Architecture overview, build pipeline, module relationships |
184
+ | test-suite | [▼](./docs/test-suite.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/test-suite) | Test suite structure, vector corpus, gate discipline |
185
+ | security | [](./SECURITY.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/security_policy) | Project security policy covering posture, disclosure, and scopes |
186
+
187
+ ### API Surface
188
+
189
+ | Module | MD/Wiki | Description |
190
+ | ------------ | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
191
+ | serpent | [](./docs/serpent.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/serpent) | Serpent-256 TypeScript API (`SerpentSeal`, `SerpentStream`, `SerpentStreamPool`, `SerpentStreamSealer`, `SerpentStreamOpener`, `Serpent`, `SerpentCtr`, `SerpentCbc`) |
192
+ | asm_serpent | [](./docs/asm_serpent.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/asm_serpent) | Serpent-256 WASM implementation (bitslice S-boxes, key schedule, CTR/CBC) |
193
+ | chacha20 | [](./docs/chacha20.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/chacha20) | ChaCha20/Poly1305 TypeScript API (`ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`) |
194
+ | asm_chacha | [](./docs/asm_chacha.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/asm_chacha) | ChaCha20/Poly1305 WASM implementation (quarter-round, HChaCha20) |
195
+ | sha2 | [](./docs/sha2.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/sha2) | SHA-2 TypeScript API (`SHA256`, `SHA512`, `SHA384`, `HMAC_SHA256`, `HMAC_SHA512`, `HMAC_SHA384`) |
196
+ | asm_sha2 | [](./docs/asm_sha2.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/asm_sha2) | SHA-2 WASM implementation (compression functions, HMAC) |
197
+ | sha3 | [](./docs/sha3.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/sha3) | SHA-3 TypeScript API (`SHA3_224`, `SHA3_256`, `SHA3_384`, `SHA3_512`, `SHAKE128`, `SHAKE256`) |
198
+ | asm_sha3 | [](./docs/asm_sha3.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/asm_sha3) | SHA-3 WASM implementation (Keccak-f[1600], sponge construction) |
199
+ | fortuna | [▼](./docs/fortuna.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/fortuna) | Fortuna CSPRNG (forward secrecy, 32 entropy pools) |
200
+ | init | [▼](./docs/init.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/init) | `init()` API and WASM loading modes |
201
+ | utils | [▼](./docs/utils.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/utils) | Encoding helpers, `constantTimeEqual`, `wipe`, `randomBytes` |
202
+ | types | [▼](./docs/types.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/types) | TypeScript interfaces (`Hash`, `KeyedHash`, `Blockcipher`, `Streamcipher`, `AEAD`) |
203
+
204
+ ### Utilities
205
+
206
+ These helpers are available immediately on import with no `init()` required.
207
+
208
+ | Function | MD/Wiki | Description |
209
+ | ---------------------------- | ------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
210
+ | `hexToBytes(hex)` | [▼](./docs/utils.md#hextobytes) · [¶](https://github.com/xero/leviathan-crypto/wiki/utils#hextobytes) | Hex string to `Uint8Array` (accepts uppercase, `0x` prefix) |
211
+ | `bytesToHex(bytes)` | [▼](./docs/utils.md#bytestohex) · [¶](https://github.com/xero/leviathan-crypto/wiki/utils#bytestohex) | `Uint8Array` to lowercase hex string |
212
+ | `utf8ToBytes(str)` | [▼](./docs/utils.md#utf8tobytes) · [¶](https://github.com/xero/leviathan-crypto/wiki/utils#utf8tobytes) | UTF-8 string to `Uint8Array` |
213
+ | `bytesToUtf8(bytes)` | [▼](./docs/utils.md#bytestoutf8) · [¶](https://github.com/xero/leviathan-crypto/wiki/utils#bytestoutf8) | `Uint8Array` to UTF-8 string |
214
+ | `base64ToBytes(b64)` | [▼](./docs/utils.md#base64tobytes) · [¶](https://github.com/xero/leviathan-crypto/wiki/utils#base64tobytes) | Base64/base64url string to `Uint8Array` (undefined on invalid) |
215
+ | `bytesToBase64(bytes, url?)` | [▼](./docs/utils.md#bytestobase64) · [¶](https://github.com/xero/leviathan-crypto/wiki/utils#bytestobase64) | `Uint8Array` to base64 string (url=true for base64url) |
216
+ | `constantTimeEqual(a, b)` | [▼](./docs/utils.md#constanttimeequal) · [¶](https://github.com/xero/leviathan-crypto/wiki/utils#constanttimeequal) | Constant-time byte comparison (XOR-accumulate) |
217
+ | `wipe(data)` | [▼](./docs/utils.md#wipe) · [¶](https://github.com/xero/leviathan-crypto/wiki/utils#wipe) | Zero a typed array in place |
218
+ | `xor(a, b)` | [▼](./docs/utils.md#xor) · [¶](https://github.com/xero/leviathan-crypto/wiki/utils#xor) | XOR two equal-length `Uint8Array`s |
219
+ | `concat(a, b)` | [▼](./docs/utils.md#concat) · [¶](https://github.com/xero/leviathan-crypto/wiki/utils#concat) | Concatenate two `Uint8Array`s |
220
+
221
+ ### Algorithm correctness and verifications
222
+
223
+ | Primitive | MD/Wiki | Description |
224
+ | ------------- | ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
225
+ | serpent_audit | [▼](./docs/serpent_audit.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/serpent_audit) | Correctness verification, side-channel analysis, cryptanalytic paper review |
226
+ | chacha_audit | [▼](./docs/chacha_audit.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/chacha_audit) | XChaCha20-Poly1305 correctness, Poly1305 field arithmetic, HChaCha20 nonce extension |
227
+ | sha2_audit | [▼](./docs/sha2_audit.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/sha2_audit) | SHA-256/512/384 correctness, HMAC and HKDF composition, constant verification |
228
+ | sha3_audit | [▼](./docs/sha3_audit.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/sha3_audit) | Keccak permutation correctness, θ/ρ/π/χ/ι step verification, round constant derivation |
229
+ | hmac_audit | [▼](./docs/hmac_audit.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/hmac_audit) | HMAC-SHA256/512/384 construction, key processing, RFC 4231 vector coverage |
230
+ | hkdf_audit | [▼](./docs/hkdf_audit.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/hkdf_audit) | HKDF extract-then-expand, info field domain separation, SerpentStream key derivation |
231
+
232
+ >[!NOTE]
233
+ > Additional documentation available in [./docs](./docs/README.md) and on the [project wiki](https://github.com/xero/leviathan-crypto/wiki/).
234
+
235
+ ---
287
236
 
288
237
  ## License
238
+
289
239
  leviathan is written under the [MIT license](http://www.opensource.org/licenses/MIT).
290
240
 
291
241
  ```
292
- ██ ▐█████ ██ ▐█▌ ▄█▌ ███▌ ▀███████▀▄██▌ ▐█▌ ███▌ ██▌ ▓▓
293
- ▐█▌ ▐█▌ ▓█ ▐█▌ ▓██ ▐█▌██ ▐█▌ ███ ██▌ ▐█▌██ ▓██ ██
294
- ██▌ ░███ ▐█▌ ██ ▀▀ ██ ▐█▌ ██ ▐██▌ █▓ ▓█ ▐█▌ ▐███▌ █▓
295
- ██ ██ ▐█▌ █▓ ▐██ ▐█▌ █▓ ██ ▐██▄▄ ▐█▌ ▐█▌ ██ ▐█▌██ ▐█▌
296
- ▐█▌ ▐█▌ ██ ▐█▌ ██ ██ ██ ▐█▌ ██▀▀████▌ ██ ██ ██ ▐█▌▐█▌
297
- ▐▒▌ ▐▒▌ ▐▒▌ ██ ▒█ ██▀▀▀██▌ ▐▒▌ ▒█ █▓░ ▒█▀▀▀██▌ ▒█ ██▐█
298
- █▓ ▄▄▓█ █▓ ▄▄▓█ ▓▓ ▐▓▌ ▐▓▌ ▐█▌ ▐▒▌ █▓ ▐▓▌ ▐▓█ ▐▓▌ ▐▒▌▐▓▌ ▐███
299
- ▓██▀▀ ▓██▀▀ ▓█▓█ ▐█▌ ▐█▌ ▐▓▌ ▓█ ▐█▌ ▐█▓ ▐█▌ ▐▓▌▐█▌ ██▓
300
- ▓█ ▄▄▄▄▄▄▄▄▄▄ ▀▀ ▐█▌▌▌
301
- ▄████████████████████▄▄
302
- ▄██████████████████████ ▀████▄
303
- ▄█████████▀▀▀ ▀███████▄▄███████▌
304
- ▐████████▀ ▄▄▄▄ ▀████████▀██▀█▌
305
- ████████ ███▀▀ ████▀ █▀ █▀
306
- ███████▌ ▀██▀ ██
307
- ███████ ▀███ ▀██ ▀█▄
308
- ▀██████ ▄▄██ ▀▀ ██▄
309
- ▀█████▄ ▄██▄ ▄▀▄▀
310
- ▀████▄ ▄██▄
311
- ▐████ ▐███
312
- ▄▄██████████ ▐███ ▄▄
313
- ▄██▀▀▀▀▀▀▀▀▀▀ ▄████ ▄██▀
314
- ▄▀ ▄▄█████████▄▄ ▀▀▀▀▀ ▄███
315
- ▄██████▀▀▀▀▀▀██████▄ ▀▄▄▄▄████▀
316
- ████▀ ▄▄▄▄▄▄▄ ▀████▄ ▀█████▀ ▄▄▄▄
317
- █████▄▄█████▀▀▀▀▀▀▄ ▀███▄ ▄███▀
318
- ▀██████▀ ▀████▄▄▄████▀
319
- ▀█████▀
320
-
321
- Serpent256 & Xchacha20-Poly1305 Cryptography for the Web
242
+ ▄▄▄▄▄▄▄▄▄▄
243
+ ▄████████████████████▄▄
244
+ ▄██████████████████████ ▀████▄
245
+ ▄█████████▀▀▀ ▀███████▄▄███████▌
246
+ ▐████████▀ ▄▄▄▄ ▀████████▀██▀█▌
247
+ ████████ ███▀▀ ████▀ █▀ █▀
248
+ ███████▌ ▀██▀ ██
249
+ ███████ ▀███ ▀██ ▀█▄
250
+ ▀██████ ▄▄██ ▀▀ ██▄
251
+ ▀█████▄ ▄██▄ ▄▀▄▀
252
+ ▀████▄ ▄██▄
253
+ ▐████ ▐███
254
+ ▄▄██████████ ▐███ ▄▄
255
+ ▄██▀▀▀▀▀▀▀▀▀▀ ▄████ ▄██▀
256
+ ▄▀ ▄▄█████████▄▄ ▀▀▀▀▀ ▄███
257
+ ▄██████▀▀▀▀▀▀██████▄ ▀▄▄▄▄████▀
258
+ ████▀ ▄▄▄▄▄▄▄ ▀████▄ ▀█████▀ ▄▄▄▄
259
+ █████▄▄█████▀▀▀▀▀▀▄ ▀███▄ ▄███▀
260
+ ▀██████▀ ▀████▄▄▄████▀
261
+ ▀█████▀
322
262
  ```