leviathan-crypto@0.0.0
WebAssembly cryptography library built on the paranoia of Serpent-256 and the elegance of XChaCha20-Poly1305
WebAssembly cryptography library built on the paranoia of Serpent-256 and the elegance of XChaCha20-Poly1305
Seal and SealStream family are cipher-agnostic. Drop in SerpentCipher or XChaCha20Cipher and they handle nonces, key derivation, and authentication for you. Explicit init() gates give you full control over how and when WASM loads. Strict typing catches misuse before it reaches production.
init() is explicit and asynchronous.
# use bun
bun i leviathan-crypto
# or npm
npm install leviathan-crypto
Serpent, ChaCha20, ML-KEM, and constantTimeEqual require WebAssembly SIMD support. This has been a baseline feature of all major browsers and runtimes since 2021. SHA-2 and SHA-3 run on any WASM-capable runtime.
Three loading strategies are available. Choose based on your runtime and bundler setup.
import { init } from 'leviathan-crypto'
import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
// Embedded: gzip+base64 blobs bundled in the package
await init({ serpent: serpentWasm, sha2: sha2Wasm })
// URL: streaming compilation from a served .wasm file
await init({ serpent: new URL('/assets/wasm/serpent.wasm', import.meta.url) })
// Pre-compiled: pass a WebAssembly.Module directly (edge runtimes, KV cache)
await init({ serpent: compiledModule })
See loader for more details.
Each module ships as its own subpath export. Bundlers with tree-shaking support and "sideEffects": false drop every module you don't import.
// Only serpent.wasm + sha2.wasm end up in your bundle
import { serpentInit } from 'leviathan-crypto/serpent'
import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
import { sha2Init } from 'leviathan-crypto/sha2'
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
await serpentInit(serpentWasm)
await sha2Init(sha2Wasm)
// ML-KEM requires kyber + sha3
import { kyberInit } from 'leviathan-crypto/kyber'
import { kyberWasm } from 'leviathan-crypto/kyber/embedded'
import { sha3Init } from 'leviathan-crypto/sha3'
import { sha3Wasm } from 'leviathan-crypto/sha3/embedded'
await kyberInit(kyberWasm)
await sha3Init(sha3Wasm)
| Subpath | Entry point |
|---|---|
leviathan-crypto | ./dist/index.js |
leviathan-crypto/stream | ./dist/stream/index.js |
leviathan-crypto/serpent | ./dist/serpent/index.js |
leviathan-crypto/serpent/embedded | ./dist/serpent/embedded.js |
leviathan-crypto/chacha20 | ./dist/chacha20/index.js |
leviathan-crypto/chacha20/embedded | ./dist/chacha20/embedded.js |
leviathan-crypto/sha2 | ./dist/sha2/index.js |
leviathan-crypto/sha2/embedded | ./dist/sha2/embedded.js |
leviathan-crypto/sha3 | ./dist/sha3/index.js |
leviathan-crypto/sha3/embedded | ./dist/sha3/embedded.js |
leviathan-crypto/kyber | ./dist/kyber/index.js |
leviathan-crypto/kyber/embedded | ./dist/kyber/embedded.js |
See init.md for the full WASM loading reference.
One-shot authenticated encryption. Seal handles nonces, key derivation, and authentication. Zero config beyond init().
import { init, Seal, XChaCha20Cipher } from 'leviathan-crypto'
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
await init({ chacha20: chacha20Wasm, sha2: sha2Wasm })
const key = XChaCha20Cipher.keygen()
const blob = Seal.encrypt(XChaCha20Cipher, key, plaintext)
const pt = Seal.decrypt(XChaCha20Cipher, key, blob) // throws AuthenticationError on tamper
Prefer Serpent-256? Swap the cipher object and everything else stays the same.
import { SerpentCipher } from 'leviathan-crypto'
import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
await init({ serpent: serpentWasm, sha2: sha2Wasm })
const key = SerpentCipher.keygen()
const blob = Seal.encrypt(SerpentCipher, key, plaintext)
Data too large to buffer in memory? SealStream and OpenStream encrypt and decrypt in chunks without loading the full message.
import { SealStream, OpenStream } from 'leviathan-crypto/stream'
const sealer = new SealStream(XChaCha20Cipher, key, { chunkSize: 65536 })
const preamble = sealer.preamble // send first
const ct0 = sealer.push(chunk0)
const ct1 = sealer.push(chunk1)
const ctLast = sealer.finalize(lastChunk)
const opener = new OpenStream(XChaCha20Cipher, key, preamble)
const pt0 = opener.pull(ct0)
const pt1 = opener.pull(ct1)
const ptLast = opener.finalize(ctLast)
Need parallel throughput? SealStreamPool distributes chunks across Web Workers with the same wire format.
import { SealStreamPool } from 'leviathan-crypto/stream'
const pool = await SealStreamPool.create(XChaCha20Cipher, key, { wasm: chacha20Wasm })
const encrypted = await pool.seal(plaintext)
const decrypted = await pool.open(encrypted)
pool.destroy()
Want post-quantum security? KyberSuite wraps ML-KEM and a cipher suite into a hybrid construction. It plugs directly into SealStream. The sender encrypts with the public encapsulation key and only the recipient's private decapsulation key can open it.
import { KyberSuite, MlKem768 } from 'leviathan-crypto/kyber'
import { kyberWasm } from 'leviathan-crypto/kyber/embedded'
import { sha3Wasm } from 'leviathan-crypto/sha3/embedded'
await init({ kyber: kyberWasm, sha3: sha3Wasm, chacha20: chacha20Wasm, sha2: sha2Wasm })
const suite = KyberSuite(new MlKem768(), XChaCha20Cipher)
const { encapsulationKey: ek, decapsulationKey: dk } = suite.keygen()
// sender — encrypts with the public key
const sealer = new SealStream(suite, ek)
const preamble = sealer.preamble // 1108 bytes: 20B header + 1088B KEM ciphertext
const ct0 = sealer.push(chunk0)
const ctLast = sealer.finalize(lastChunk)
// recipient — decrypts with the private key
const opener = new OpenStream(suite, dk, preamble)
const pt0 = opener.pull(ct0)
const ptLast = opener.finalize(ctLast)
More examples including hashing, key derivation, Fortuna, and raw primitives? See the examples page.
lvthn-web [ demo · source · readme ]
A self-contained browser encryption tool in a single HTML file. Encrypt text or files with Serpent-256-CBC and Argon2id key derivation, then share the armored output. No server, no install, no network connection after initial load. The code is written to be read. The Encrypt-then-MAC construction, HMAC input, and Argon2id parameters are all intentional examples worth studying.
lvthn-chat [ demo · source · readme ]
End-to-end encrypted chat over X25519 key exchange and XChaCha20-Poly1305 message encryption. The relay server is a dumb WebSocket pipe that never sees plaintext. Messages carry sequence numbers so the protocol detects and rejects replayed messages. The demo deconstructs the protocol step by step with visual feedback for injection and replay attacks.
lvthn-cli [ npm · source · readme ]
File encryption CLI supporting both Serpent-256 and XChaCha20-Poly1305 via --cipher. A single keyfile works with both ciphers. The header byte determines decryption automatically. Chunks distribute across a worker pool sized to hardwareConcurrency. Each worker owns an isolated WASM instance with no shared memory.
bun i -g lvthn # or npm slow mode
lvthn keygen --armor -o my.key
cat secret.txt | lvthn encrypt -k my.key --armor > secret.enc
kyber [ demo · source · readme ]
Post-quantum cryptography demo simulating a complete ML-KEM key encapsulation ceremony between two browser-side clients. A live wire at the top of the page logs every value that crosses the channel; importantly, the shared secret never appears in the wire. After the ceremony completes, both sides independently derive a symmetric key using HKDF-SHA256 and exchange messages encrypted with XChaCha20-Poly1305. Each wire frame is expandable, revealing the raw nonce, ciphertext, Poly1305 tag, and AAD.
| I want to... | |
|---|---|
| Encrypt data | Seal with SerpentCipher or XChaCha20Cipher |
| Encrypt a stream or large file | SealStream to encrypt, OpenStream to decrypt |
| Encrypt in parallel | SealStreamPool distributes chunks across Web Workers |
| Add post-quantum security | KyberSuite wraps MlKem512, MlKem768, or MlKem1024 with any cipher suite |
| Hash data | SHA256, SHA384, SHA512, SHA3_256, SHA3_512, SHAKE256 ... |
| Authenticate a message | HMAC_SHA256, HMAC_SHA384, or HMAC_SHA512 |
| Derive keys | HKDF_SHA256 or HKDF_SHA512 |
| Generate random bytes | Fortuna for forward-secret generation, randomBytes for one-off use |
| Compare secrets safely | constantTimeEqual uses a WASM SIMD path to prevent timing attacks |
| Work with bytes | hexToBytes, bytesToHex, wipe, xor, concat ... |
For raw primitives, low-level cipher access, and ASM internals see the full API reference.
| architecture | Repository structure, module relationships, build pipeline, and buffer layouts |
| test-suite | How the test suite works, vector corpus, and gate discipline |
| lexicon | Glossary of cryptographic terms |
| wasm | WebAssembly primer in the context of this library |
| cdn | Use leviathan-crypto directly from a CDN with no bundler |
| argon2id | Passphrase-based encryption using Argon2id alongside leviathan primitives |
Full documentation including API reference, ASM internals, audits, & benchmarks see the project wiki.
██ ▐█████ ██ ▐█▌ ▄█▌ ███▌ ▀███████▀▄██▌ ▐█▌ ███▌ ██▌ ▓▓
▐█▌ ▐█▌ ▓█ ▐█▌ ▓██ ▐█▌██ ▐█▌ ███ ██▌ ▐█▌██ ▓██ ██
██▌ ░███ ▐█▌ ██ ▀▀ ██ ▐█▌ ██ ▐██▌ █▓ ▓█ ▐█▌ ▐███▌ █▓
██ ██ ▐█▌ █▓ ▐██ ▐█▌ █▓ ██ ▐██▄▄ ▐█▌ ▐█▌ ██ ▐█▌██ ▐█▌
▐█▌ ▐█▌ ██ ▐█▌ ██ ██ ██ ▐█▌ ██▀▀████▌ ██ ██ ██ ▐█▌▐█▌
▐▒▌ ▐▒▌ ▐▒▌ ██ ▒█ ██▀▀▀██▌ ▐▒▌ ▒█ █▓░ ▒█▀▀▀██▌ ▒█ ██▐█
█▓ ▄▄▓█ █▓ ▄▄▓█ ▓▓ ▐▓▌ ▐▓▌ ▐█▌ ▐▒▌ █▓ ▐▓▌ ▐▓█ ▐▓▌ ▐▒▌▐▓▌ ▐███
▓██▀▀ ▓██▀▀ ▓█▓█ ▐█▌ ▐█▌ ▐▓▌ ▓█ ▐█▌ ▐█▓ ▐█▌ ▐▓▌▐█▌ ██▓
▓█ ▄▄▄▄▄▄▄▄▄▄ ▀▀ ▐█▌▌▌
▄████████████████████▄▄
▄██████████████████████ ▀████▄
▄█████████▀▀▀ ▀███████▄▄███████▌
▐████████▀ ▄▄▄▄ ▀████████▀██▀█▌
████████ ███▀▀ ████▀ █▀ █▀
███████▌ ▀██▀ ██
███████ ▀███ ▀██ ▀█▄
▀██████ ▄▄██ ▀▀ ██▄
▀█████▄ ▄██▄ ▄▀▄▀
▀████▄ ▄██▄
▐████ ▐███
▄▄██████████ ▐███ ▄▄
▄██▀▀▀▀▀▀▀▀▀▀ ▄████ ▄██▀
▄▀ ▄▄█████████▄▄ ▀▀▀▀▀ ▄███
▄██████▀▀▀▀▀▀██████▄ ▀▄▄▄▄████▀
████▀ ▄▄▄▄▄▄▄ ▀████▄ ▀█████▀ ▄▄▄▄
█████▄▄█████▀▀▀▀▀▀▄ ▀███▄ ▄███▀
▀██████▀ ▀████▄▄▄████▀
▀█████▀