Web cryptography built on Serpent-256 paranoia and XChaCha20-Poly1305 elegance.
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.
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.
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.
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.
TypeScript acts as the ergonomics layer. Fully typed classes, explicit
init() gates, input validation, and authenticated compositions ensure
primitives are connected correctly.
With no npm dependency graph to audit, the supply chain attack surface is eliminated.
Import only the cipher(s) you intend to use. Subpath exports allow bundlers to exclude everything else.
Nothing runs upon import. Initialization via init() is explicit and
asynchronous.
# use bun
bun i leviathan-crypto
# or npm
npm install leviathan-cryptoNote
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.
lvthn-web [ demo · source · readme ]
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 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.
lvthn-chat [ demo · source · readme ]
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.
lvthn-cli [ npm · source · readme ]
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.
bun i -g lvthn # or npm slow mode
lvthn keygen --armor -o my.key
cat secret.txt | lvthn encrypt -k my.key --armor > secret.enclvthncli-serpent and 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.
| Class | Module | Auth | Notes |
|---|---|---|---|
| Authenticated encryption | |||
SerpentSeal |
serpent, sha2 |
Yes | Serpent-CBC + HMAC-SHA256. Recommended Serpent default. 64-byte key. |
XChaCha20Seal |
chacha20 |
Yes | XChaCha20-Poly1305 with internal nonce management. Recommended ChaCha20 default. 32-byte key. |
SerpentStream, SerpentStreamPool |
serpent, sha2 |
Yes | Chunked one-shot AEAD for large payloads. Pool variant parallelises across workers. 32-byte key. |
SerpentStreamSealer, SerpentStreamOpener |
serpent, sha2 |
Yes | Incremental streaming AEAD: seal/open one chunk at a time. Pass { framed: true } for self-delimiting u32be length-prefix framing. 64-byte key. |
XChaCha20StreamSealer, XChaCha20StreamOpener |
chacha20 |
Yes | Incremental streaming AEAD using XChaCha20-Poly1305. Per-chunk random nonces, position-bound AAD. { framed: true } for length-prefixed framing. 32-byte key. |
XChaCha20StreamPool |
chacha20 |
Yes | Parallel chunked streaming AEAD. Same chunk crypto as XChaCha20StreamSealer; dispatches across workers. 32-byte key. |
XChaCha20Poly1305Pool |
chacha20 |
Yes | Worker-pool wrapper for XChaCha20Poly1305. Parallelises encryption across isolated WASM instances. |
| Stateless primitives caller manages nonces | |||
XChaCha20Poly1305 |
chacha20 |
Yes | RFC-faithful stateless AEAD. 24-byte nonce, caller-managed. Use XChaCha20Seal unless you need explicit nonce control. |
ChaCha20Poly1305 |
chacha20 |
Yes | RFC 8439 stateless AEAD. 12-byte nonce, caller-managed. Prefer XChaCha20Seal unless you need RFC 8439 exact compliance. |
| Unauthenticated primitives pair with HMAC or use AEAD | |||
Serpent |
serpent |
No | Serpent-256 ECB block cipher. Single-block encrypt/decrypt. |
SerpentCtr |
serpent |
No | Serpent-256 CTR mode stream cipher. Requires { dangerUnauthenticated: true }. |
SerpentCbc |
serpent |
No | Serpent-256 CBC mode with PKCS7 padding. Requires { dangerUnauthenticated: true }. |
ChaCha20 |
chacha20 |
No | ChaCha20 stream cipher — RFC 8439. Unauthenticated; use XChaCha20Seal unless you need raw keystream. |
Poly1305 |
chacha20 |
No | Poly1305 one-time MAC — RFC 8439. Use via the AEAD classes unless you have a specific reason not to. |
| Hashing and key derivation | |||
SHA256, SHA384, SHA512 |
sha2 |
— | SHA-2 family — FIPS 180-4. |
HMAC_SHA256, HMAC_SHA384, HMAC_SHA512 |
sha2 |
— | HMAC construction over SHA-2 — RFC 2104. |
HKDF_SHA256, HKDF_SHA512 |
sha2 |
— | Extract-and-expand key derivation over HMAC — RFC 5869. |
SHA3_224, SHA3_256, SHA3_384, SHA3_512 |
sha3 |
— | SHA-3 family — FIPS 202. Keccak-based, structurally independent of SHA-2. |
SHAKE128, SHAKE256 |
sha3 |
— | Extendable output functions (XOF) — FIPS 202. Variable-length output; useful for key derivation and stream generation. |
| CSPRNG | |||
Fortuna |
serpent, sha2 |
— | Fortuna CSPRNG (Ferguson & Schneier). 32 entropy pools, forward secrecy. Use Fortuna.create(). |
Important
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.
import { init, SerpentSeal, randomBytes } from 'leviathan-crypto'
await init(['serpent', 'sha2'])
const key = randomBytes(64) // 64-byte key (encKey + macKey)
const seal = new SerpentSeal()
// Encrypt and authenticate
const ciphertext = seal.encrypt(key, plaintext)
// Decrypt and verify (throws on tamper)
const decrypted = seal.decrypt(key, ciphertext)
seal.dispose()import { init, XChaCha20Seal, randomBytes } from 'leviathan-crypto'
await init(['chacha20'])
const key = randomBytes(32) // 32-byte key
const seal = new XChaCha20Seal(key)
// Encrypt and authenticate (nonce generated internally)
const ciphertext = seal.encrypt(plaintext)
// Decrypt and verify (throws on tamper)
const decrypted = seal.decrypt(ciphertext)
seal.dispose()For more examples, including streaming, chunking, hashing, and key derivation, see the examples page.
// Embedded (default): zero-config, base64-encoded WASM inline
await init(['serpent', 'sha3'])
// Streaming: uses instantiateStreaming for performance
await init(['serpent'], 'streaming', { wasmUrl: '/assets/wasm/' })
// Manual: provide your own binary
await init(['serpent'], 'manual', { wasmBinary: { serpent: myBuffer } })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:
// Only serpent.wasm ends up in your bundle
import { serpentInit, SerpentSeal } from 'leviathan-crypto/serpent'
await serpentInit()
// Only chacha20.wasm ends up in your bundle
import { chacha20Init, XChaCha20Seal } from 'leviathan-crypto/chacha20'
await chacha20Init()| Subpath | Entry point |
|---|---|
leviathan-crypto |
./dist/index.js |
leviathan-crypto/serpent |
./dist/serpent/index.js |
leviathan-crypto/chacha20 |
./dist/chacha20/index.js |
leviathan-crypto/sha2 |
./dist/sha2/index.js |
leviathan-crypto/sha3 |
./dist/sha3/index.js |
| Document | MD/Wiki | Description |
|---|---|---|
| architecture | ▼ · ¶ | Architecture overview, build pipeline, module relationships |
| test-suite | ▼ · ¶ | Test suite structure, vector corpus, gate discipline |
| security | ▼ · ¶ | Project security policy covering posture, disclosure, and scopes |
| Module | MD/Wiki | Description |
|---|---|---|
| serpent | ▼ · ¶ | Serpent-256 TypeScript API (SerpentSeal, SerpentStream, SerpentStreamPool, SerpentStreamSealer, SerpentStreamOpener, Serpent, SerpentCtr, SerpentCbc) |
| asm_serpent | ▼ · ¶ | Serpent-256 WASM implementation (bitslice S-boxes, key schedule, CTR/CBC) |
| chacha20 | ▼ · ¶ | ChaCha20/Poly1305 TypeScript API (XChaCha20Seal, XChaCha20StreamSealer, XChaCha20StreamOpener, ChaCha20, Poly1305, ChaCha20Poly1305, XChaCha20Poly1305, XChaCha20Poly1305Pool) |
| asm_chacha | ▼ · ¶ | ChaCha20/Poly1305 WASM implementation (quarter-round, HChaCha20) |
| sha2 | ▼ · ¶ | SHA-2 TypeScript API (SHA256, SHA512, SHA384, HMAC_SHA256, HMAC_SHA512, HMAC_SHA384) |
| asm_sha2 | ▼ · ¶ | SHA-2 WASM implementation (compression functions, HMAC) |
| sha3 | ▼ · ¶ | SHA-3 TypeScript API (SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE128, SHAKE256) |
| asm_sha3 | ▼ · ¶ | SHA-3 WASM implementation (Keccak-f[1600], sponge construction) |
| fortuna | ▼ · ¶ | Fortuna CSPRNG (forward secrecy, 32 entropy pools) |
| init | ▼ · ¶ | init() API and WASM loading modes |
| utils | ▼ · ¶ | Encoding helpers, constantTimeEqual, wipe, randomBytes |
| types | ▼ · ¶ | TypeScript interfaces (Hash, KeyedHash, Blockcipher, Streamcipher, AEAD) |
These helpers are available immediately on import with no init() required.
| Function | MD/Wiki | Description |
|---|---|---|
hexToBytes(hex) |
▼ · ¶ | Hex string to Uint8Array (accepts uppercase, 0x prefix) |
bytesToHex(bytes) |
▼ · ¶ | Uint8Array to lowercase hex string |
utf8ToBytes(str) |
▼ · ¶ | UTF-8 string to Uint8Array |
bytesToUtf8(bytes) |
▼ · ¶ | Uint8Array to UTF-8 string |
base64ToBytes(b64) |
▼ · ¶ | Base64/base64url string to Uint8Array (undefined on invalid) |
bytesToBase64(bytes, url?) |
▼ · ¶ | Uint8Array to base64 string (url=true for base64url) |
constantTimeEqual(a, b) |
▼ · ¶ | Constant-time byte comparison (XOR-accumulate) |
wipe(data) |
▼ · ¶ | Zero a typed array in place |
xor(a, b) |
▼ · ¶ | XOR two equal-length Uint8Arrays |
concat(...arrays) |
▼ · ¶ | Concatenate Uint8Arrays (variadic) |
hasSIMD() |
▼ · ¶ | Detects WebAssembly SIMD support. Cached after first call. Used internally for CTR/CBC/ChaCha20 dispatch. |
| Primitive | MD/Wiki | Description |
|---|---|---|
| serpent_audit | ▼ · ¶ | Correctness verification, side-channel analysis, cryptanalytic paper review |
| chacha_audit | ▼ · ¶ | XChaCha20-Poly1305 correctness, Poly1305 field arithmetic, HChaCha20 nonce extension |
| sha2_audit | ▼ · ¶ | SHA-256/512/384 correctness, HMAC and HKDF composition, constant verification |
| sha3_audit | ▼ · ¶ | Keccak permutation correctness, θ/ρ/π/χ/ι step verification, round constant derivation |
| hmac_audit | ▼ · ¶ | HMAC-SHA256/512/384 construction, key processing, RFC 4231 vector coverage |
| hkdf_audit | ▼ · ¶ | HKDF extract-then-expand, info field domain separation, SerpentStream key derivation |
Note
Additional documentation available in ./docs and on the project wiki.
leviathan-crypto is released under the MIT license.
▄▄▄▄▄▄▄▄▄▄
▄████████████████████▄▄
▄██████████████████████ ▀████▄
▄█████████▀▀▀ ▀███████▄▄███████▌
▐████████▀ ▄▄▄▄ ▀████████▀██▀█▌
████████ ███▀▀ ████▀ █▀ █▀
███████▌ ▀██▀ ██
███████ ▀███ ▀██ ▀█▄
▀██████ ▄▄██ ▀▀ ██▄
▀█████▄ ▄██▄ ▄▀▄▀
▀████▄ ▄██▄
▐████ ▐███
▄▄██████████ ▐███ ▄▄
▄██▀▀▀▀▀▀▀▀▀▀ ▄████ ▄██▀
▄▀ ▄▄█████████▄▄ ▀▀▀▀▀ ▄███
▄██████▀▀▀▀▀▀██████▄ ▀▄▄▄▄████▀
████▀ ▄▄▄▄▄▄▄ ▀████▄ ▀█████▀ ▄▄▄▄
█████▄▄█████▀▀▀▀▀▀▄ ▀███▄ ▄███▀
▀██████▀ ▀████▄▄▄████▀
▀█████▀