leviathan-crypto 2.0.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +88 -281
- package/LICENSE +4 -0
- package/README.md +275 -87
- package/dist/aes/aes-cbc.d.ts +40 -0
- package/dist/aes/aes-cbc.js +158 -0
- package/dist/aes/aes-ctr.d.ts +50 -0
- package/dist/aes/aes-ctr.js +141 -0
- package/dist/aes/aes-gcm-siv.d.ts +67 -0
- package/dist/aes/aes-gcm-siv.js +217 -0
- package/dist/aes/aes-gcm.d.ts +61 -0
- package/dist/aes/aes-gcm.js +226 -0
- package/dist/aes/cipher-suite.d.ts +21 -0
- package/dist/aes/cipher-suite.js +179 -0
- package/dist/aes/embedded.d.ts +1 -0
- package/dist/aes/embedded.js +26 -0
- package/dist/aes/generator.d.ts +14 -0
- package/dist/aes/generator.js +103 -0
- package/dist/aes/index.d.ts +58 -0
- package/dist/aes/index.js +125 -0
- package/dist/aes/ops.d.ts +60 -0
- package/dist/aes/ops.js +164 -0
- package/dist/aes/pool-worker.d.ts +1 -0
- package/dist/aes/pool-worker.js +92 -0
- package/dist/aes/types.d.ts +1 -0
- package/dist/aes/types.js +23 -0
- package/dist/aes.wasm +0 -0
- package/dist/blake3/embedded.d.ts +1 -0
- package/dist/blake3/embedded.js +26 -0
- package/dist/blake3/index.d.ts +143 -0
- package/dist/blake3/index.js +620 -0
- package/dist/blake3/types.d.ts +102 -0
- package/dist/blake3/types.js +31 -0
- package/dist/blake3/validate.d.ts +29 -0
- package/dist/blake3/validate.js +80 -0
- package/dist/blake3.wasm +0 -0
- package/dist/chacha20/cipher-suite.d.ts +10 -0
- package/dist/chacha20/cipher-suite.js +98 -13
- package/dist/chacha20/generator.d.ts +12 -0
- package/dist/chacha20/generator.js +91 -0
- package/dist/chacha20/index.d.ts +100 -3
- package/dist/chacha20/index.js +169 -35
- package/dist/chacha20/ops.d.ts +57 -6
- package/dist/chacha20/ops.js +107 -27
- package/dist/chacha20/pool-worker.js +14 -0
- package/dist/chacha20/types.d.ts +1 -32
- package/dist/cte-wasm.d.ts +1 -0
- package/dist/cte-wasm.js +3 -0
- package/dist/cte.wasm +0 -0
- package/dist/curve25519.wasm +0 -0
- package/dist/ecdsa/der.d.ts +23 -0
- package/dist/ecdsa/der.js +192 -0
- package/dist/ecdsa/ecprivatekey-der.d.ts +32 -0
- package/dist/ecdsa/ecprivatekey-der.js +230 -0
- package/dist/ecdsa/embedded.d.ts +1 -0
- package/dist/ecdsa/embedded.js +25 -0
- package/dist/ecdsa/index.d.ts +124 -0
- package/dist/ecdsa/index.js +366 -0
- package/dist/ecdsa/types.d.ts +31 -0
- package/dist/ecdsa/types.js +28 -0
- package/dist/ecdsa/validate.d.ts +18 -0
- package/dist/ecdsa/validate.js +92 -0
- package/dist/ed25519/embedded.d.ts +1 -0
- package/dist/ed25519/embedded.js +31 -0
- package/dist/ed25519/index.d.ts +70 -0
- package/dist/ed25519/index.js +308 -0
- package/dist/ed25519/types.d.ts +27 -0
- package/dist/ed25519/types.js +27 -0
- package/dist/ed25519/validate.d.ts +7 -0
- package/dist/ed25519/validate.js +77 -0
- package/dist/embedded/aes-pool-worker.d.ts +1 -0
- package/dist/embedded/aes-pool-worker.js +5 -0
- package/dist/embedded/aes.d.ts +1 -0
- package/dist/embedded/aes.js +3 -0
- package/dist/embedded/blake3.d.ts +1 -0
- package/dist/embedded/blake3.js +3 -0
- package/dist/embedded/chacha20-pool-worker.d.ts +1 -0
- package/dist/embedded/chacha20-pool-worker.js +5 -0
- package/dist/embedded/chacha20.d.ts +1 -1
- package/dist/embedded/chacha20.js +2 -2
- package/dist/embedded/curve25519.d.ts +1 -0
- package/dist/embedded/curve25519.js +3 -0
- package/dist/embedded/mldsa.d.ts +1 -0
- package/dist/embedded/mldsa.js +3 -0
- package/dist/embedded/mlkem.d.ts +1 -0
- package/dist/embedded/mlkem.js +3 -0
- package/dist/embedded/p256.d.ts +1 -0
- package/dist/embedded/p256.js +3 -0
- package/dist/embedded/serpent-pool-worker.d.ts +1 -0
- package/dist/embedded/serpent-pool-worker.js +5 -0
- package/dist/embedded/serpent.d.ts +1 -1
- package/dist/embedded/serpent.js +2 -2
- package/dist/embedded/sha2.d.ts +1 -1
- package/dist/embedded/sha2.js +2 -2
- package/dist/embedded/sha3.d.ts +1 -1
- package/dist/embedded/sha3.js +2 -2
- package/dist/embedded/slhdsa.d.ts +1 -0
- package/dist/embedded/slhdsa.js +3 -0
- package/dist/errors.d.ts +92 -1
- package/dist/errors.js +111 -1
- package/dist/fortuna.d.ts +18 -12
- package/dist/fortuna.js +166 -99
- package/dist/index.d.ts +42 -11
- package/dist/index.js +65 -20
- package/dist/init.d.ts +1 -3
- package/dist/init.js +73 -7
- package/dist/keccak/embedded.js +1 -1
- package/dist/keccak/index.d.ts +2 -0
- package/dist/keccak/index.js +4 -2
- package/dist/loader.d.ts +1 -19
- package/dist/loader.js +26 -32
- package/dist/merkle/blake3-tree.d.ts +35 -0
- package/dist/merkle/blake3-tree.js +187 -0
- package/dist/merkle/checkpoint.d.ts +58 -0
- package/dist/merkle/checkpoint.js +217 -0
- package/dist/merkle/index.d.ts +19 -0
- package/dist/merkle/index.js +37 -0
- package/dist/merkle/merkle-log.d.ts +130 -0
- package/dist/merkle/merkle-log.js +207 -0
- package/dist/merkle/merkle-verifier.d.ts +126 -0
- package/dist/merkle/merkle-verifier.js +296 -0
- package/dist/merkle/proof.d.ts +70 -0
- package/dist/merkle/proof.js +300 -0
- package/dist/merkle/sha256-tree.d.ts +33 -0
- package/dist/merkle/sha256-tree.js +145 -0
- package/dist/merkle/signed-log.d.ts +156 -0
- package/dist/merkle/signed-log.js +356 -0
- package/dist/merkle/signed-note.d.ts +309 -0
- package/dist/merkle/signed-note.js +648 -0
- package/dist/merkle/sth.d.ts +31 -0
- package/dist/merkle/sth.js +31 -0
- package/dist/merkle/storage.d.ts +40 -0
- package/dist/merkle/storage.js +71 -0
- package/dist/merkle/tree.d.ts +68 -0
- package/dist/merkle/tree.js +94 -0
- package/dist/mldsa/embedded.d.ts +1 -0
- package/dist/{kyber → mldsa}/embedded.js +5 -5
- package/dist/mldsa/expand.d.ts +53 -0
- package/dist/mldsa/expand.js +188 -0
- package/dist/mldsa/format.d.ts +16 -0
- package/dist/mldsa/format.js +68 -0
- package/dist/mldsa/hashvariant.d.ts +32 -0
- package/dist/mldsa/hashvariant.js +248 -0
- package/dist/mldsa/index.d.ts +142 -0
- package/dist/mldsa/index.js +463 -0
- package/dist/mldsa/keygen.d.ts +16 -0
- package/dist/mldsa/keygen.js +232 -0
- package/dist/mldsa/params.d.ts +21 -0
- package/dist/mldsa/params.js +55 -0
- package/dist/mldsa/sha3-helpers.d.ts +30 -0
- package/dist/mldsa/sha3-helpers.js +124 -0
- package/dist/mldsa/sign.d.ts +36 -0
- package/dist/mldsa/sign.js +380 -0
- package/dist/mldsa/types.d.ts +91 -0
- package/dist/mldsa/types.js +25 -0
- package/dist/mldsa/validate.d.ts +55 -0
- package/dist/mldsa/validate.js +125 -0
- package/dist/mldsa/verify.d.ts +29 -0
- package/dist/mldsa/verify.js +269 -0
- package/dist/mldsa.wasm +0 -0
- package/dist/mlkem/embedded.d.ts +1 -0
- package/dist/mlkem/embedded.js +27 -0
- package/dist/mlkem/indcpa.d.ts +49 -0
- package/dist/{kyber → mlkem}/indcpa.js +48 -48
- package/dist/mlkem/index.d.ts +37 -0
- package/dist/{kyber → mlkem}/index.js +41 -31
- package/dist/mlkem/kem.d.ts +21 -0
- package/dist/{kyber → mlkem}/kem.js +48 -13
- package/dist/{kyber → mlkem}/params.d.ts +4 -4
- package/dist/{kyber → mlkem}/params.js +2 -2
- package/dist/mlkem/suite.d.ts +12 -0
- package/dist/{kyber → mlkem}/suite.js +17 -12
- package/dist/{kyber → mlkem}/types.d.ts +4 -3
- package/dist/{kyber → mlkem}/types.js +1 -1
- package/dist/mlkem/validate.d.ts +23 -0
- package/dist/{kyber → mlkem}/validate.js +24 -20
- package/dist/{kyber.wasm → mlkem.wasm} +0 -0
- package/dist/p256.wasm +0 -0
- package/dist/ratchet/index.d.ts +8 -0
- package/dist/ratchet/index.js +38 -0
- package/dist/ratchet/kdf-chain.d.ts +13 -0
- package/dist/ratchet/kdf-chain.js +85 -0
- package/dist/ratchet/ratchet-keypair.d.ts +9 -0
- package/dist/ratchet/ratchet-keypair.js +61 -0
- package/dist/ratchet/root-kdf.d.ts +4 -0
- package/dist/ratchet/root-kdf.js +124 -0
- package/dist/ratchet/skipped-key-store.d.ts +14 -0
- package/dist/ratchet/skipped-key-store.js +154 -0
- package/dist/ratchet/types.d.ts +36 -0
- package/dist/ratchet/types.js +26 -0
- package/dist/serpent/cipher-suite.d.ts +10 -0
- package/dist/serpent/cipher-suite.js +144 -56
- package/dist/serpent/generator.d.ts +12 -0
- package/dist/serpent/generator.js +97 -0
- package/dist/serpent/index.d.ts +62 -1
- package/dist/serpent/index.js +97 -21
- package/dist/serpent/pool-worker.js +28 -102
- package/dist/serpent/serpent-cbc.d.ts +16 -6
- package/dist/serpent/serpent-cbc.js +58 -37
- package/dist/serpent/shared-ops.d.ts +63 -0
- package/dist/serpent/shared-ops.js +178 -0
- package/dist/serpent/types.d.ts +1 -5
- package/dist/serpent.wasm +0 -0
- package/dist/sha2/hash.d.ts +2 -0
- package/dist/sha2/hash.js +53 -0
- package/dist/sha2/hkdf.js +5 -5
- package/dist/sha2/index.d.ts +22 -1
- package/dist/sha2/index.js +80 -11
- package/dist/sha2/types.d.ts +41 -2
- package/dist/sha2.wasm +0 -0
- package/dist/sha3/hash.d.ts +2 -0
- package/dist/sha3/hash.js +53 -0
- package/dist/sha3/index.d.ts +87 -3
- package/dist/sha3/index.js +317 -19
- package/dist/sha3/kmac.d.ts +121 -0
- package/dist/sha3/kmac.js +800 -0
- package/dist/sha3.wasm +0 -0
- package/dist/shared/pkcs7.d.ts +22 -0
- package/dist/shared/pkcs7.js +84 -0
- package/dist/sign/ctx.d.ts +41 -0
- package/dist/sign/ctx.js +102 -0
- package/dist/sign/envelope.d.ts +45 -0
- package/dist/sign/envelope.js +152 -0
- package/dist/sign/hasher.d.ts +9 -0
- package/dist/sign/hasher.js +132 -0
- package/dist/sign/index.d.ts +11 -0
- package/dist/sign/index.js +34 -0
- package/dist/sign/sign-stream.d.ts +25 -0
- package/dist/sign/sign-stream.js +112 -0
- package/dist/sign/suites/ecdsa-p256.d.ts +2 -0
- package/dist/sign/suites/ecdsa-p256.js +120 -0
- package/dist/sign/suites/ed25519.d.ts +3 -0
- package/dist/sign/suites/ed25519.js +165 -0
- package/dist/sign/suites/hybrid-classical.d.ts +23 -0
- package/dist/sign/suites/hybrid-classical.js +526 -0
- package/dist/sign/suites/hybrid-pq.d.ts +4 -0
- package/dist/sign/suites/hybrid-pq.js +234 -0
- package/dist/sign/suites/mldsa.d.ts +7 -0
- package/dist/sign/suites/mldsa.js +161 -0
- package/dist/sign/suites/slhdsa.d.ts +7 -0
- package/dist/sign/suites/slhdsa.js +176 -0
- package/dist/sign/types.d.ts +106 -0
- package/dist/sign/types.js +28 -0
- package/dist/sign/verify-stream.d.ts +30 -0
- package/dist/sign/verify-stream.js +227 -0
- package/dist/slhdsa/embedded.d.ts +1 -0
- package/dist/slhdsa/embedded.js +26 -0
- package/dist/slhdsa/index.d.ts +149 -0
- package/dist/slhdsa/index.js +493 -0
- package/dist/slhdsa/params.d.ts +26 -0
- package/dist/slhdsa/params.js +70 -0
- package/dist/slhdsa/prehash.d.ts +68 -0
- package/dist/slhdsa/prehash.js +307 -0
- package/dist/slhdsa/sign.d.ts +39 -0
- package/dist/slhdsa/sign.js +116 -0
- package/dist/slhdsa/types.d.ts +129 -0
- package/dist/slhdsa/types.js +27 -0
- package/dist/slhdsa/validate.d.ts +60 -0
- package/dist/slhdsa/validate.js +127 -0
- package/dist/slhdsa/verify.d.ts +32 -0
- package/dist/slhdsa/verify.js +107 -0
- package/dist/slhdsa.wasm +0 -0
- package/dist/stream/header.js +8 -8
- package/dist/stream/index.d.ts +1 -0
- package/dist/stream/index.js +1 -0
- package/dist/stream/open-stream.js +65 -22
- package/dist/stream/seal-stream-pool.d.ts +2 -0
- package/dist/stream/seal-stream-pool.js +100 -33
- package/dist/stream/seal-stream.d.ts +1 -1
- package/dist/stream/seal-stream.js +48 -19
- package/dist/stream/seal.js +6 -6
- package/dist/stream/types.d.ts +3 -1
- package/dist/stream/types.js +1 -1
- package/dist/types.d.ts +22 -1
- package/dist/types.js +1 -1
- package/dist/utils.d.ts +9 -10
- package/dist/utils.js +84 -59
- package/dist/wasm-source.d.ts +9 -8
- package/dist/wasm-source.js +1 -1
- package/dist/x25519/embedded.d.ts +1 -0
- package/dist/x25519/embedded.js +31 -0
- package/dist/x25519/index.d.ts +43 -0
- package/dist/x25519/index.js +159 -0
- package/dist/x25519/types.d.ts +25 -0
- package/dist/x25519/types.js +27 -0
- package/dist/x25519/validate.d.ts +2 -0
- package/dist/x25519/validate.js +39 -0
- package/package.json +123 -64
- package/SECURITY.md +0 -276
- package/dist/ct-wasm.d.ts +0 -1
- package/dist/ct-wasm.js +0 -3
- package/dist/ct.wasm +0 -0
- package/dist/docs/aead.md +0 -323
- package/dist/docs/architecture.md +0 -932
- package/dist/docs/argon2id.md +0 -302
- package/dist/docs/chacha20.md +0 -674
- package/dist/docs/exports.md +0 -241
- package/dist/docs/fortuna.md +0 -313
- package/dist/docs/init.md +0 -302
- package/dist/docs/loader.md +0 -161
- package/dist/docs/serpent.md +0 -519
- package/dist/docs/sha2.md +0 -613
- package/dist/docs/sha3.md +0 -546
- package/dist/docs/types.md +0 -276
- package/dist/docs/utils.md +0 -367
- package/dist/embedded/kyber.d.ts +0 -1
- package/dist/embedded/kyber.js +0 -3
- package/dist/kyber/embedded.d.ts +0 -1
- package/dist/kyber/indcpa.d.ts +0 -49
- package/dist/kyber/index.d.ts +0 -38
- package/dist/kyber/kem.d.ts +0 -21
- package/dist/kyber/suite.d.ts +0 -13
- package/dist/kyber/validate.d.ts +0 -19
package/README.md
CHANGED
|
@@ -1,32 +1,66 @@
|
|
|
1
|
-
[](https://github.com/xero/leviathan-crypto/releases/latest) [](https://github.com/xero/leviathan-crypto/releases/latest) [](https://www.npmjs.com/package/leviathan-crypto) [](https://github.com/xero/leviathan-crypto/actions/workflows/test-suite.yml) [](https://github.com/xero/leviathan-crypto/wiki)
|
|
2
2
|
|
|
3
|
-
    [](https://github.com/xero/leviathan-crypto/blob/main/LICENSE)
|
|
4
4
|
|
|
5
5
|
<img src="https://github.com/xero/leviathan-crypto/raw/main/docs/logo.svg" alt="Leviathan logo" width="400" >
|
|
6
6
|
|
|
7
|
-
# Leviathan-
|
|
7
|
+
# Leviathan Crypto: post-quantum WASM cryptography
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
**Zero runtime dependencies.** No NPM graph to audit. No supply chain attack surface.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
**Tree-shakeable.** Import only what you use. Subpath exports let bundlers exclude everything else.
|
|
12
12
|
|
|
13
|
-
**
|
|
13
|
+
**Side-effect free.** Nothing runs on import. [`init()`](https://github.com/xero/leviathan-crypto/wiki/init) is explicit and asynchronous.
|
|
14
14
|
|
|
15
|
-
**
|
|
15
|
+
**Cipher Triptych.** Leviathan provides three ciphers. The implementations all use a round structure that operates as a bitsliced Boolean circuit, implemented with register-only logic and no S-box lookup tables. Each compiles to an independent, v128 SIMD-optimized WebAssembly module with isolated linear memory, which prevents cross-module memory access by design. Every operation zeroes key material on exit, including on failure.
|
|
16
16
|
|
|
17
|
-
**
|
|
17
|
+
**[Serpent-256](https://github.com/xero/leviathan-crypto/wiki/serpent_reference): maximum paranoia.** 32 rounds of eight different 4-bit S-boxes, each bitsliced as a Boolean circuit with no table lookups. An ouroboros devouring every bit, in every block, through every round.
|
|
18
18
|
|
|
19
|
-
**
|
|
19
|
+
**[XChaCha20-Poly1305](https://github.com/xero/leviathan-crypto/wiki/chacha_reference): precise elegance.** 20 rounds of add-rotate-XOR alternating column and diagonal quarter-rounds, choreography without S-boxes or cache-timing leakage. A dance closing with Poly1305's unconditional forgery bound.
|
|
20
20
|
|
|
21
|
-
**
|
|
21
|
+
**[AES-256-GCM-SIV](https://github.com/xero/leviathan-crypto/wiki/aes_reference): industry standard, sharpened.** 14 rounds bitsliced into Boolean gates with tower-field S-box with no table lookups. A fresh POLYVAL key per nonce leaves GHASH-key recovery with no target.
|
|
22
22
|
|
|
23
|
-
**
|
|
23
|
+
**A uniform API.** The same call shape drives [authenticated encryption](https://github.com/xero/leviathan-crypto/wiki/aead), [signatures and key agreement](https://github.com/xero/leviathan-crypto/wiki/signing), [post-quantum KEM](https://github.com/xero/leviathan-crypto/wiki/mlkem), [hashing](https://github.com/xero/leviathan-crypto/wiki/hashing), [transparency logs](https://github.com/xero/leviathan-crypto/wiki/merkle), [post-quantum ratchet](https://github.com/xero/leviathan-crypto/wiki/ratchet), the [Fortuna CSPRNG](https://github.com/xero/leviathan-crypto/wiki/fortuna), [utilities](https://github.com/xero/leviathan-crypto/wiki/utils), and [types](https://github.com/xero/leviathan-crypto/wiki/types).
|
|
24
24
|
|
|
25
|
-
**
|
|
25
|
+
**The correctness contract.** Every cipher, hash, KEM, and signature scheme derives independently from its authoritative spec, never ported from another implementation. Known-answer test vectors come from spec authors, and cross-checks run against multiple independent reference implementations. The test suite covers unit tests at the primitive level plus end-to-end tests across three browser engines (Chromium, Firefox, WebKit) and Node.js. Detailed reference documentation ships at the [project wiki](https://github.com/xero/leviathan-crypto/wiki).
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Highlights
|
|
30
|
+
|
|
31
|
+
| **_I want to..._** | |
|
|
32
|
+
|---|---|
|
|
33
|
+
| Encrypt data | [`Seal`](https://github.com/xero/leviathan-crypto/wiki/aead#seal) with [`SerpentCipher`](https://github.com/xero/leviathan-crypto/wiki/serpent#serpentcipher), [`XChaCha20Cipher`](https://github.com/xero/leviathan-crypto/wiki/chacha20#xchacha20cipher), or [`AESGCMSIVCipher`](https://github.com/xero/leviathan-crypto/wiki/aes#aesgcmsivcipher) |
|
|
34
|
+
| Encrypt a stream or large file | [`SealStream`](https://github.com/xero/leviathan-crypto/wiki/aead#sealstream) to encrypt, [`OpenStream`](https://github.com/xero/leviathan-crypto/wiki/aead#openstream) to decrypt |
|
|
35
|
+
| Encrypt in parallel | [`SealStreamPool`](https://github.com/xero/leviathan-crypto/wiki/aead#sealstreampool) distributes chunks across Web Workers |
|
|
36
|
+
| Add post-quantum security | [`MlKemSuite`](https://github.com/xero/leviathan-crypto/wiki/mlkem#mlkemsuite) wraps [`MlKem512`](https://github.com/xero/leviathan-crypto/wiki/mlkem#parameter-sets), [`MlKem768`](https://github.com/xero/leviathan-crypto/wiki/mlkem#parameter-sets), or [`MlKem1024`](https://github.com/xero/leviathan-crypto/wiki/mlkem#parameter-sets) with any cipher suite |
|
|
37
|
+
| Build a forward-secret session | [`ratchetInit`](https://github.com/xero/leviathan-crypto/wiki/ratchet#ratchetinit), [`KDFChain`](https://github.com/xero/leviathan-crypto/wiki/ratchet#kdfchain), [`kemRatchetEncap`](https://github.com/xero/leviathan-crypto/wiki/ratchet#kemratchetencap) / [`kemRatchetDecap`](https://github.com/xero/leviathan-crypto/wiki/ratchet#kemratchetdecap), [`SkippedKeyStore`](https://github.com/xero/leviathan-crypto/wiki/ratchet#skippedkeystore) |
|
|
38
|
+
| Sign data with a classical signature | [`Ed25519Suite`](https://github.com/xero/leviathan-crypto/wiki/signaturesuite#ed25519-suites) / [`Ed25519PreHashSuite`](https://github.com/xero/leviathan-crypto/wiki/signaturesuite#ed25519-suites) ([ed25519](https://github.com/xero/leviathan-crypto/wiki/ed25519)) or [`EcdsaP256Suite`](https://github.com/xero/leviathan-crypto/wiki/signaturesuite#ecdsa-p256-suite) ([ecdsa-p256](https://github.com/xero/leviathan-crypto/wiki/ecdsa-p256)) via [`Sign`](https://github.com/xero/leviathan-crypto/wiki/signing#sign) / [`SignStream`](https://github.com/xero/leviathan-crypto/wiki/signing#signstream) / [`VerifyStream`](https://github.com/xero/leviathan-crypto/wiki/signing#verifystream) |
|
|
39
|
+
| Sign data with a post-quantum signature | `MlDsa44/65/87Suite` (+ `*PreHashSuite`) for lattice ML-DSA ([mldsa](https://github.com/xero/leviathan-crypto/wiki/mldsa)) or `SlhDsa128f/192f/256fSuite` (+ `*PreHashSuite`) for hash-based SLH-DSA ([slhdsa](https://github.com/xero/leviathan-crypto/wiki/slhdsa)). Full catalog in [signaturesuite](https://github.com/xero/leviathan-crypto/wiki/signaturesuite) |
|
|
40
|
+
| Sign data with a classical+PQ hybrid | [`MlDsa44Ed25519Suite`](https://github.com/xero/leviathan-crypto/wiki/signaturesuite#classicalpq-hybrid-composite-encoding), [`MlDsa65Ed25519Suite`](https://github.com/xero/leviathan-crypto/wiki/signaturesuite#classicalpq-hybrid-composite-encoding), [`MlDsa44EcdsaP256Suite`](https://github.com/xero/leviathan-crypto/wiki/signaturesuite#classicalpq-hybrid-composite-encoding), [`MlDsa65EcdsaP256Suite`](https://github.com/xero/leviathan-crypto/wiki/signaturesuite#classicalpq-hybrid-composite-encoding) for `draft-ietf-lamps-pq-composite-sigs` |
|
|
41
|
+
| Sign data with a PQ-only hybrid | [`MlDsa44SlhDsa128fSuite`](https://github.com/xero/leviathan-crypto/wiki/signaturesuite#pq-only-hybrid-suites), [`MlDsa65SlhDsa192fSuite`](https://github.com/xero/leviathan-crypto/wiki/signaturesuite#pq-only-hybrid-suites), [`MlDsa87SlhDsa256fSuite`](https://github.com/xero/leviathan-crypto/wiki/signaturesuite#pq-only-hybrid-suites) for ML-DSA + SLH-DSA composites at matching NIST categories |
|
|
42
|
+
| Build a transparency log | [`MerkleLog`](https://github.com/xero/leviathan-crypto/wiki/merkle#merklelog) for append plus inclusion / consistency proofs, [`MerkleVerifier`](https://github.com/xero/leviathan-crypto/wiki/merkle#merkleverifier) for clients, [`SignedLog`](https://github.com/xero/leviathan-crypto/wiki/merkle#signedlog) for custom storage backends |
|
|
43
|
+
| Exchange a key with a peer | [`X25519`](https://github.com/xero/leviathan-crypto/wiki/x25519) for Curve25519 Diffie-Hellman |
|
|
44
|
+
| Hash data | [`SHA256`](https://github.com/xero/leviathan-crypto/wiki/sha2#sha256), [`SHA384`](https://github.com/xero/leviathan-crypto/wiki/sha2#sha384), [`SHA512`](https://github.com/xero/leviathan-crypto/wiki/sha2#sha512), [`SHA3_256`](https://github.com/xero/leviathan-crypto/wiki/sha3#sha3_256), [`SHA3_512`](https://github.com/xero/leviathan-crypto/wiki/sha3#sha3_512), [`SHAKE256`](https://github.com/xero/leviathan-crypto/wiki/sha3#shake256) ... |
|
|
45
|
+
| Authenticate a message | [`HMAC_SHA256`](https://github.com/xero/leviathan-crypto/wiki/sha2#hmac_sha256), [`HMAC_SHA384`](https://github.com/xero/leviathan-crypto/wiki/sha2#hmac_sha384), [`HMAC_SHA512`](https://github.com/xero/leviathan-crypto/wiki/sha2#hmac_sha512), or [`KMAC256`](https://github.com/xero/leviathan-crypto/wiki/kmac#kmac256) |
|
|
46
|
+
| Derive keys | [`HKDF_SHA256`](https://github.com/xero/leviathan-crypto/wiki/sha2#hkdf_sha256) or [`HKDF_SHA512`](https://github.com/xero/leviathan-crypto/wiki/sha2#hkdf_sha512) |
|
|
47
|
+
| Generate random bytes | [`Fortuna`](https://github.com/xero/leviathan-crypto/wiki/fortuna#api-reference) for forward-secret generation, [`randomBytes`](https://github.com/xero/leviathan-crypto/wiki/utils#randombytes) for one-off use |
|
|
48
|
+
| Compare secrets safely | [`constantTimeEqual`](https://github.com/xero/leviathan-crypto/wiki/utils#constanttimeequal) uses a WASM SIMD path to prevent timing attacks |
|
|
49
|
+
| Work with bytes | [`hexToBytes`](https://github.com/xero/leviathan-crypto/wiki/utils#hextobytes), [`bytesToHex`](https://github.com/xero/leviathan-crypto/wiki/utils#bytestohex), [`wipe`](https://github.com/xero/leviathan-crypto/wiki/utils#wipe), [`xor`](https://github.com/xero/leviathan-crypto/wiki/utils#xor), [`concat`](https://github.com/xero/leviathan-crypto/wiki/utils#concat) ... |
|
|
50
|
+
|
|
51
|
+
*For raw primitives, low-level cipher access, and ASM internals see the [full API reference](https://github.com/xero/leviathan-crypto/wiki/index).*
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Architecture: TypeScript over WASM
|
|
56
|
+
|
|
57
|
+
<img src="https://github.com/xero/leviathan-crypto/raw/main/docs/layers.svg" alt="Typescript Over Wasm Layered Diagram" width="700">
|
|
58
|
+
|
|
59
|
+
The TypeScript layer never implements cryptographic algorithms. It manages the boundary between JavaScript and WebAssembly by writing inputs into WASM linear memory, calling exported functions, and reading back outputs. All algorithm logic resides within AssemblyScript.
|
|
28
60
|
|
|
29
|
-
|
|
61
|
+
Higher-level classes like `Seal`, `SealStream`, and `SealStreamPool` are pure TypeScript, but they compose WASM-backed primitives (Serpent-CBC, HMAC-SHA256, ChaCha20-Poly1305, and HKDF-SHA256) rather than implementing new cryptographic logic. TypeScript orchestrates, while WASM computes. Pool workers instantiate their own WASM modules and directly call primitives, bypassing the main-thread module cache.
|
|
62
|
+
|
|
63
|
+
See [wasm.md](https://github.com/xero/leviathan-crypto/wiki/wasm) for a fuller primer on WebAssembly in the context of this library.
|
|
30
64
|
|
|
31
65
|
---
|
|
32
66
|
|
|
@@ -39,8 +73,15 @@ bun i leviathan-crypto
|
|
|
39
73
|
npm install leviathan-crypto
|
|
40
74
|
```
|
|
41
75
|
|
|
76
|
+
v3 is the current stable line; semver applies. Runs in modern browsers, Node.js 22+, Bun, Deno, and Cloudflare Workers.
|
|
77
|
+
|
|
78
|
+
> [!IMPORTANT]
|
|
79
|
+
> [Serpent](https://github.com/xero/leviathan-crypto/wiki/serpent), [ChaCha20](https://github.com/xero/leviathan-crypto/wiki/chacha20), [ML-KEM](https://github.com/xero/leviathan-crypto/wiki/mlkem), [AES](https://github.com/xero/leviathan-crypto/wiki/aes), [ML-DSA](https://github.com/xero/leviathan-crypto/wiki/mldsa), [BLAKE3](https://github.com/xero/leviathan-crypto/wiki/blake3), and [constantTimeEqual](https://github.com/xero/leviathan-crypto/wiki/utils#constanttimeequal) require WebAssembly SIMD support. This has been a baseline feature of all major browsers and runtimes [since 2021](https://caniuse.com/wasm-simd).
|
|
80
|
+
|
|
81
|
+
SIMD throughput on Apple Silicon peaks at ~1.3 GB/s for ChaCha20 and ~40 MB/s for Serpent, single-threaded; 1.2-3.2× over scalar. Full matrix across V8, SpiderMonkey, and JSC in [benchmarks](https://github.com/xero/leviathan-crypto/wiki/benchmarks).
|
|
82
|
+
|
|
42
83
|
> [!NOTE]
|
|
43
|
-
>
|
|
84
|
+
> Found a security issue? Don't open a public issue. See [SECURITY.md](./SECURITY.md#reporting-a-vulnerability) for the disclosure policy.
|
|
44
85
|
|
|
45
86
|
### Loading
|
|
46
87
|
|
|
@@ -58,9 +99,25 @@ await init({ serpent: serpentWasm, sha2: sha2Wasm })
|
|
|
58
99
|
await init({ serpent: new URL('/assets/wasm/serpent.wasm', import.meta.url) })
|
|
59
100
|
|
|
60
101
|
// Pre-compiled: pass a WebAssembly.Module directly (edge runtimes, KV cache)
|
|
102
|
+
const compiledModule = await WebAssembly.compileStreaming(fetch('/assets/wasm/serpent.wasm'))
|
|
61
103
|
await init({ serpent: compiledModule })
|
|
62
104
|
```
|
|
63
105
|
|
|
106
|
+
All three patterns also work straight from a CDN with no install or bundler:
|
|
107
|
+
|
|
108
|
+
```html
|
|
109
|
+
<script type="module">
|
|
110
|
+
import { init, Seal, SerpentCipher } from 'https://unpkg.com/leviathan-crypto/dist/index.js'
|
|
111
|
+
import { serpentWasm } from 'https://unpkg.com/leviathan-crypto/dist/serpent/embedded.js'
|
|
112
|
+
import { sha2Wasm } from 'https://unpkg.com/leviathan-crypto/dist/sha2/embedded.js'
|
|
113
|
+
|
|
114
|
+
await init({ serpent: serpentWasm, sha2: sha2Wasm })
|
|
115
|
+
// ... use as normal
|
|
116
|
+
</script>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
See the [CDN reference](https://github.com/xero/leviathan-crypto/wiki/cdn) for unpkg/esm.sh, version pinning, SRI, and import maps.
|
|
120
|
+
|
|
64
121
|
### Tree-shaking with subpath imports
|
|
65
122
|
|
|
66
123
|
Each module ships as its own subpath export. Bundlers with tree-shaking support and `"sideEffects": false` drop every module you don't import.
|
|
@@ -75,41 +132,79 @@ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
|
75
132
|
await serpentInit(serpentWasm)
|
|
76
133
|
await sha2Init(sha2Wasm)
|
|
77
134
|
|
|
78
|
-
// ML-KEM requires
|
|
79
|
-
import {
|
|
80
|
-
import {
|
|
135
|
+
// ML-KEM requires mlkem + sha3
|
|
136
|
+
import { mlkemInit } from 'leviathan-crypto/mlkem'
|
|
137
|
+
import { mlkemWasm } from 'leviathan-crypto/mlkem/embedded'
|
|
81
138
|
import { sha3Init } from 'leviathan-crypto/sha3'
|
|
82
139
|
import { sha3Wasm } from 'leviathan-crypto/sha3/embedded'
|
|
83
140
|
|
|
84
|
-
await
|
|
141
|
+
await mlkemInit(mlkemWasm)
|
|
85
142
|
await sha3Init(sha3Wasm)
|
|
86
143
|
```
|
|
87
144
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
|
91
|
-
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
94
|
-
|
|
|
95
|
-
|
|
|
96
|
-
|
|
97
|
-
|
|
|
98
|
-
|
|
|
99
|
-
| `leviathan-crypto
|
|
100
|
-
| `leviathan-crypto/
|
|
101
|
-
| `leviathan-crypto/
|
|
102
|
-
|
|
103
|
-
|
|
145
|
+
Real bundle sizes (esbuild minified + gzip):
|
|
146
|
+
|
|
147
|
+
| Use case | gzip bundle |
|
|
148
|
+
|---|---:|
|
|
149
|
+
| `Seal` + `XChaCha20Cipher` | ~17 KB |
|
|
150
|
+
| `Seal` + `SerpentCipher` | ~29 KB |
|
|
151
|
+
| Merkle log + ML-DSA-44 cosig | ~29 KB |
|
|
152
|
+
| Full root barrel (every export) | ~53 KB |
|
|
153
|
+
|
|
154
|
+
| Subpath | Module |
|
|
155
|
+
| ------------------------------------ | ------------------------------------------------------- |
|
|
156
|
+
| `leviathan-crypto` | root barrel (all exports) |
|
|
157
|
+
| `leviathan-crypto/stream` | cipher-agnostic seal layer |
|
|
158
|
+
| `leviathan-crypto/serpent` | Serpent-256 |
|
|
159
|
+
| `leviathan-crypto/serpent/embedded` | Serpent-256 WASM blob |
|
|
160
|
+
| `leviathan-crypto/chacha20` | XChaCha20-Poly1305 |
|
|
161
|
+
| `leviathan-crypto/chacha20/embedded` | XChaCha20-Poly1305 WASM blob |
|
|
162
|
+
| `leviathan-crypto/sha2` | SHA-2 family (224 / 256 / 384 / 512, HMAC, HKDF) |
|
|
163
|
+
| `leviathan-crypto/sha2/embedded` | SHA-2 WASM blob |
|
|
164
|
+
| `leviathan-crypto/sha3` | SHA-3 / SHAKE family |
|
|
165
|
+
| `leviathan-crypto/sha3/embedded` | SHA-3 WASM blob |
|
|
166
|
+
| `leviathan-crypto/keccak` | Keccak alias for SHA-3 |
|
|
167
|
+
| `leviathan-crypto/keccak/embedded` | Keccak WASM blob (same bytes as `sha3/embedded`) |
|
|
168
|
+
| `leviathan-crypto/mlkem` | ML-KEM |
|
|
169
|
+
| `leviathan-crypto/mlkem/embedded` | ML-KEM WASM blob |
|
|
170
|
+
| `leviathan-crypto/aes` | AES-256-GCM-SIV |
|
|
171
|
+
| `leviathan-crypto/aes/embedded` | AES WASM blob |
|
|
172
|
+
| `leviathan-crypto/blake3` | BLAKE3 |
|
|
173
|
+
| `leviathan-crypto/blake3/embedded` | BLAKE3 WASM blob |
|
|
174
|
+
| `leviathan-crypto/ecdsa` | ECDSA-P256 |
|
|
175
|
+
| `leviathan-crypto/ecdsa/embedded` | NIST P-256 WASM blob |
|
|
176
|
+
| `leviathan-crypto/ed25519` | Ed25519 (pure and Ed25519ph) |
|
|
177
|
+
| `leviathan-crypto/ed25519/embedded` | Curve25519 WASM blob |
|
|
178
|
+
| `leviathan-crypto/mldsa` | ML-DSA |
|
|
179
|
+
| `leviathan-crypto/mldsa/embedded` | ML-DSA WASM blob |
|
|
180
|
+
| `leviathan-crypto/slhdsa` | SLH-DSA |
|
|
181
|
+
| `leviathan-crypto/slhdsa/embedded` | SLH-DSA WASM blob |
|
|
182
|
+
| `leviathan-crypto/x25519` | X25519 (Curve25519 Diffie-Hellman) |
|
|
183
|
+
| `leviathan-crypto/x25519/embedded` | Curve25519 WASM blob (same bytes as `ed25519/embedded`) |
|
|
184
|
+
| `leviathan-crypto/ratchet` | forward-secret ratchet (SPQR) |
|
|
185
|
+
| `leviathan-crypto/sign` | scheme-agnostic signature layer |
|
|
186
|
+
| `leviathan-crypto/merkle` | Merkle log substrate |
|
|
187
|
+
|
|
188
|
+
Subpaths resolve to `./dist/<mod>/index.js` and `./dist/<mod>/embedded.js`.
|
|
189
|
+
|
|
190
|
+
> [!NOTE]
|
|
191
|
+
> See also:
|
|
192
|
+
> - [`package.json`](https://github.com/xero/leviathan-crypto/blob/main/package.json) for the exact export map
|
|
193
|
+
> - [exports reference](https://github.com/xero/leviathan-crypto/wiki/exports) for what each subpath exports
|
|
194
|
+
> - [WASM loading reference](https://github.com/xero/leviathan-crypto/wiki/loader) for the three loading strategies
|
|
104
195
|
|
|
105
196
|
---
|
|
106
197
|
|
|
107
198
|
## Quick Start
|
|
108
199
|
|
|
109
|
-
|
|
200
|
+
**_One-shot authenticated encryption._** [`Seal`](https://github.com/xero/leviathan-crypto/wiki/aead#seal) handles nonces, key derivation, and authentication. Zero config beyond [`init()`](https://github.com/xero/leviathan-crypto/wiki/init#init).
|
|
110
201
|
|
|
111
202
|
```typescript
|
|
112
|
-
import {
|
|
203
|
+
import {
|
|
204
|
+
init,
|
|
205
|
+
Seal,
|
|
206
|
+
XChaCha20Cipher,
|
|
207
|
+
} from 'leviathan-crypto'
|
|
113
208
|
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
|
|
114
209
|
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
115
210
|
|
|
@@ -117,22 +212,36 @@ await init({ chacha20: chacha20Wasm, sha2: sha2Wasm })
|
|
|
117
212
|
|
|
118
213
|
const key = XChaCha20Cipher.keygen()
|
|
119
214
|
const blob = Seal.encrypt(XChaCha20Cipher, key, plaintext)
|
|
120
|
-
|
|
215
|
+
// throws AuthenticationError on tamper
|
|
216
|
+
const pt = Seal.decrypt(XChaCha20Cipher, key, blob)
|
|
121
217
|
```
|
|
122
218
|
|
|
123
|
-
_Prefer Serpent-256?_ Swap the cipher
|
|
124
|
-
|
|
125
|
-
```
|
|
126
|
-
import {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
219
|
+
**_Prefer Serpent-256?_** Swap the cipher and its module.
|
|
220
|
+
|
|
221
|
+
```diff
|
|
222
|
+
import {
|
|
223
|
+
init,
|
|
224
|
+
Seal,
|
|
225
|
+
- XChaCha20Cipher,
|
|
226
|
+
+ SerpentCipher,
|
|
227
|
+
} from 'leviathan-crypto'
|
|
228
|
+
-import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
|
|
229
|
+
+import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
|
|
230
|
+
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
231
|
+
|
|
232
|
+
-await init({ chacha20: chacha20Wasm, sha2: sha2Wasm })
|
|
233
|
+
+await init({ serpent: serpentWasm, sha2: sha2Wasm })
|
|
234
|
+
|
|
235
|
+
-const key = XChaCha20Cipher.keygen()
|
|
236
|
+
+const key = SerpentCipher.keygen()
|
|
237
|
+
-const blob = Seal.encrypt(XChaCha20Cipher, key, plaintext)
|
|
238
|
+
+const blob = Seal.encrypt(SerpentCipher, key, plaintext)
|
|
239
|
+
// throws AuthenticationError on tamper
|
|
240
|
+
-const pt = Seal.decrypt(XChaCha20Cipher, key, blob)
|
|
241
|
+
+const pt = Seal.decrypt(SerpentCipher, key, blob)
|
|
133
242
|
```
|
|
134
243
|
|
|
135
|
-
_Data too large to buffer in memory?_ [`SealStream`](https://github.com/xero/leviathan-crypto/wiki/aead
|
|
244
|
+
**_Data too large to buffer in memory?_** [`SealStream`](https://github.com/xero/leviathan-crypto/wiki/aead#sealstream) and [`OpenStream`](https://github.com/xero/leviathan-crypto/wiki/aead#openstream) encrypt and decrypt in chunks without loading the full message.
|
|
136
245
|
|
|
137
246
|
```typescript
|
|
138
247
|
import { SealStream, OpenStream } from 'leviathan-crypto/stream'
|
|
@@ -150,7 +259,7 @@ const pt1 = opener.pull(ct1)
|
|
|
150
259
|
const ptLast = opener.finalize(ctLast)
|
|
151
260
|
```
|
|
152
261
|
|
|
153
|
-
_Need parallel throughput?_ [`SealStreamPool`](https://github.com/xero/leviathan-crypto/wiki/aead
|
|
262
|
+
**_Need parallel throughput?_** [`SealStreamPool`](https://github.com/xero/leviathan-crypto/wiki/aead#sealstreampool) distributes chunks across Web Workers with the same wire format.
|
|
154
263
|
|
|
155
264
|
```typescript
|
|
156
265
|
import { SealStreamPool } from 'leviathan-crypto/stream'
|
|
@@ -161,86 +270,165 @@ const decrypted = await pool.open(encrypted)
|
|
|
161
270
|
pool.destroy()
|
|
162
271
|
```
|
|
163
272
|
|
|
164
|
-
_Want post-quantum security?_ [`
|
|
273
|
+
**_Want post-quantum security?_** [`MlKemSuite`](https://github.com/xero/leviathan-crypto/wiki/mlkem#mlkemsuite) wraps ML-KEM and a cipher suite into a hybrid construction. It plugs directly into [`SealStream`](https://github.com/xero/leviathan-crypto/wiki/aead#sealstream). The sender encrypts with the public encapsulation key and only the recipient's private decapsulation key can open it.
|
|
165
274
|
|
|
166
275
|
```typescript
|
|
167
|
-
import {
|
|
168
|
-
import {
|
|
276
|
+
import { MlKemSuite, MlKem768 } from 'leviathan-crypto/mlkem'
|
|
277
|
+
import { mlkemWasm } from 'leviathan-crypto/mlkem/embedded'
|
|
169
278
|
import { sha3Wasm } from 'leviathan-crypto/sha3/embedded'
|
|
170
279
|
|
|
171
|
-
await init({
|
|
280
|
+
await init({ mlkem: mlkemWasm, sha3: sha3Wasm, chacha20: chacha20Wasm, sha2: sha2Wasm })
|
|
172
281
|
|
|
173
|
-
const suite =
|
|
282
|
+
const suite = MlKemSuite(new MlKem768(), XChaCha20Cipher)
|
|
174
283
|
const { encapsulationKey: ek, decapsulationKey: dk } = suite.keygen()
|
|
175
284
|
|
|
176
|
-
// sender
|
|
285
|
+
// sender: encrypts with the public key
|
|
177
286
|
const sealer = new SealStream(suite, ek)
|
|
178
287
|
const preamble = sealer.preamble // 1108 bytes: 20B header + 1088B KEM ciphertext
|
|
179
288
|
const ct0 = sealer.push(chunk0)
|
|
180
289
|
const ctLast = sealer.finalize(lastChunk)
|
|
181
290
|
|
|
182
|
-
// recipient
|
|
291
|
+
// recipient: decrypts with the private key
|
|
183
292
|
const opener = new OpenStream(suite, dk, preamble)
|
|
184
293
|
const pt0 = opener.pull(ct0)
|
|
185
294
|
const ptLast = opener.finalize(ctLast)
|
|
186
295
|
```
|
|
187
296
|
|
|
188
|
-
|
|
297
|
+
**_Need post-quantum signatures?_** The [sign module](https://github.com/xero/leviathan-crypto/wiki/signing) wraps ML-DSA (FIPS 204) behind a `SignatureSuite` abstraction. `Sign` covers single-shot attached / detached signatures; `SignStream` and `VerifyStream` handle chunked input via HashML-DSA.
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
import { init, Sign, SignStream, MlDsa65Suite, MlDsa65PreHashSuite } from 'leviathan-crypto'
|
|
301
|
+
import { mldsaWasm } from 'leviathan-crypto/mldsa/embedded'
|
|
302
|
+
import { sha3Wasm } from 'leviathan-crypto/sha3/embedded'
|
|
303
|
+
|
|
304
|
+
await init({ mldsa: mldsaWasm, sha3: sha3Wasm })
|
|
305
|
+
|
|
306
|
+
const { pk, sk } = MlDsa65Suite.keygen()
|
|
307
|
+
const msg = new TextEncoder().encode('hello world')
|
|
308
|
+
const ctx = new TextEncoder().encode('myapp/v1')
|
|
309
|
+
|
|
310
|
+
// single-shot
|
|
311
|
+
const blob = Sign.sign(MlDsa65Suite, sk, msg, ctx)
|
|
312
|
+
const payload = Sign.verify(MlDsa65Suite, pk, blob, ctx)
|
|
313
|
+
|
|
314
|
+
// streamed (over chunked input)
|
|
315
|
+
const signer = new SignStream(MlDsa65PreHashSuite, sk, ctx)
|
|
316
|
+
signer.update(chunk1)
|
|
317
|
+
signer.update(chunk2)
|
|
318
|
+
const sig = signer.finalize()
|
|
319
|
+
// wire output is signer.preamble + chunk1 + chunk2 + sig
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Six ML-DSA suites ship: `MlDsa44Suite` / `MlDsa65Suite` / `MlDsa87Suite` for pure ML-DSA, and `MlDsa44PreHashSuite` / `MlDsa65PreHashSuite` / `MlDsa87PreHashSuite` for HashML-DSA. See the [signing reference](https://github.com/xero/leviathan-crypto/wiki/signing) for the wire format and error reference, and the [signaturesuite reference](https://github.com/xero/leviathan-crypto/wiki/signaturesuite) for the full 22-entry catalog.
|
|
323
|
+
|
|
324
|
+
**_Want belt-and-suspenders post-quantum signatures?_** Three PQ-only hybrid suites pair ML-DSA (lattice) with SLH-DSA (hash-based) at each NIST security category. The combined signature is secure as long as either family holds; a future break in one PQ assumption does not transfer to the other. The wire is one combined byte string the receiver verifies through the same `Sign` entry points.
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
import { init, Sign, MlDsa65SlhDsa192fSuite } from 'leviathan-crypto'
|
|
328
|
+
import { mldsaWasm } from 'leviathan-crypto/mldsa/embedded'
|
|
329
|
+
import { sha3Wasm } from 'leviathan-crypto/sha3/embedded'
|
|
330
|
+
import { slhdsaWasm } from 'leviathan-crypto/slhdsa/embedded'
|
|
331
|
+
|
|
332
|
+
await init({ mldsa: mldsaWasm, sha3: sha3Wasm, slhdsa: slhdsaWasm })
|
|
333
|
+
|
|
334
|
+
const { pk, sk } = MlDsa65SlhDsa192fSuite.keygen()
|
|
335
|
+
const msg = new TextEncoder().encode('release manifest v1.2.3')
|
|
336
|
+
const ctx = new TextEncoder().encode('release-signing/v1')
|
|
337
|
+
|
|
338
|
+
const blob = Sign.sign (MlDsa65SlhDsa192fSuite, sk, msg, ctx)
|
|
339
|
+
const payload = Sign.verify(MlDsa65SlhDsa192fSuite, pk, blob, ctx)
|
|
340
|
+
// throws SigningError if either half fails to verify
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Three hybrid suites ship at the matching NIST categories: `MlDsa44SlhDsa128fSuite` (category 1), `MlDsa65SlhDsa192fSuite` (category 3), `MlDsa87SlhDsa256fSuite` (category 5). The PQ-only hybrids complement the planned classical+PQ hybrids; the two families defend against different threat models and the [signaturesuite reference](https://github.com/xero/leviathan-crypto/wiki/signaturesuite) covers when to pick which.
|
|
344
|
+
|
|
345
|
+
**_Need classical ECDSA for X.509, JWS, or TLS interop?_** [`EcdsaP256Suite`](https://github.com/xero/leviathan-crypto/wiki/signaturesuite#ecdsa-p256-suite) wraps ECDSA over NIST P-256 (FIPS 186-5 §6) with SHA-256 prehash baked in. Hedged-by-default per `draft-irtf-cfrg-det-sigs-with-noise-05`, low-S enforced on signer and verifier per RFC 6979 §3.5. Wire bytes are 64-byte raw `r || s`; the [`ecdsaSignatureToDer`](https://github.com/xero/leviathan-crypto/wiki/ecdsa-p256#der-utility) / [`ecdsaSignatureFromDer`](https://github.com/xero/leviathan-crypto/wiki/ecdsa-p256#der-utility) helpers convert between raw and the RFC 3279 §2.2.3 DER form for ecosystem interop.
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
import { init, Sign, EcdsaP256Suite, ecdsaSignatureToDer } from 'leviathan-crypto'
|
|
349
|
+
import { p256Wasm } from 'leviathan-crypto/ecdsa/embedded'
|
|
350
|
+
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
351
|
+
|
|
352
|
+
await init({ p256: p256Wasm, sha2: sha2Wasm })
|
|
353
|
+
|
|
354
|
+
const { pk, sk } = EcdsaP256Suite.keygen()
|
|
355
|
+
const msg = new TextEncoder().encode('hello world')
|
|
356
|
+
const sig = Sign.signDetached(EcdsaP256Suite, sk, msg, new Uint8Array(0))
|
|
357
|
+
const ok = Sign.verifyDetached(EcdsaP256Suite, pk, msg, sig, new Uint8Array(0))
|
|
358
|
+
|
|
359
|
+
const der = ecdsaSignatureToDer(sig) // X.509 / JWS / TLS interop
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
ECDSA-P256 is classical (not post-quantum); pair it with an ML-DSA or SLH-DSA suite when the threat model assumes a future CRQC. ECDSA has no native context parameter, so `EcdsaP256Suite` rejects non-empty `user_ctx`; the reserved classical+PQ hybrid suites at `0x22` / `0x23` will provide context-bound classical+PQ signing.
|
|
363
|
+
|
|
364
|
+
**_Building a secure messenger?_** The [ratchet module](https://github.com/xero/leviathan-crypto/wiki/ratchet) provides Sparse Post-Quantum Ratchet primitives for consumers who need forward secrecy and post-compromise security at the session layer. [`ratchetInit`](https://github.com/xero/leviathan-crypto/wiki/ratchet#ratchetinit) bootstraps the symmetric chains, [`KDFChain`](https://github.com/xero/leviathan-crypto/wiki/ratchet#kdfchain) derives per-message keys, [`kemRatchetEncap`](https://github.com/xero/leviathan-crypto/wiki/ratchet#kemratchetencap) / [`kemRatchetDecap`](https://github.com/xero/leviathan-crypto/wiki/ratchet#kemratchetdecap) perform the ML-KEM ratchet step, and [`SkippedKeyStore`](https://github.com/xero/leviathan-crypto/wiki/ratchet#skippedkeystore) handles out-of-order delivery.
|
|
365
|
+
|
|
366
|
+
```typescript
|
|
367
|
+
import { ratchetInit, KDFChain } from 'leviathan-crypto/ratchet'
|
|
368
|
+
|
|
369
|
+
await init({ sha2: sha2Wasm }) // KDF layer only; add mlkem + sha3 for KEM steps
|
|
370
|
+
|
|
371
|
+
const { nextRootKey, sendChainKey, recvChainKey } = ratchetInit(sharedSecret)
|
|
372
|
+
const chain = new KDFChain(sendChainKey)
|
|
373
|
+
const { key: messageKey, counter } = chain.stepWithCounter()
|
|
374
|
+
// encrypt a message with messageKey; include counter in the wire header
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
These are the primitives, not a full session. You compose them into your
|
|
378
|
+
transport, header format, and epoch orchestration. See the
|
|
379
|
+
[ratchet guide](https://github.com/xero/leviathan-crypto/wiki/ratchet)
|
|
380
|
+
for full construction details.
|
|
381
|
+
|
|
382
|
+
**_Looking for examples of hashing, key derivation, Fortuna, and raw primitives?_** See [examples](https://github.com/xero/leviathan-crypto/wiki/examples).
|
|
189
383
|
|
|
190
384
|
---
|
|
191
385
|
|
|
192
386
|
## Demos
|
|
193
387
|
|
|
194
|
-
**`
|
|
388
|
+
**`web`** [ [demo](https://leviathan.3xi.club/web) · [source](https://github.com/xero/leviathan-demos/tree/main/web) · [readme](https://github.com/xero/leviathan-demos/blob/main/web/README.md) ]
|
|
195
389
|
|
|
196
|
-
A self-contained browser encryption tool in a single HTML file. Encrypt text or files with Serpent-256-CBC and
|
|
390
|
+
A self-contained browser encryption tool in a single HTML file. Encrypt text or files with Serpent-256-CBC and scrypt 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 scrypt parameters are all intentional examples worth studying.
|
|
197
391
|
|
|
198
|
-
**`
|
|
392
|
+
**`tamper`** [ [demo](https://leviathan.3xi.club/tamper) · [source](https://github.com/xero/leviathan-demos/tree/main/tamper) · [readme](https://github.com/xero/leviathan-demos/blob/main/tamper/README.md) ]
|
|
199
393
|
|
|
200
|
-
|
|
394
|
+
A crypto attack-resilience demo. It runs a real two-party encrypted channel, then lets you attack it: forge a replay and the sequence check rejects it, tamper with a frame and the Poly1305 tag fails. Key exchange uses X25519 with HKDF-SHA256, message encryption uses XChaCha20-Poly1305, and the relay server is a dumb WebSocket pipe that never sees plaintext. The demo deconstructs the protocol step by step with visual feedback for injection and replay attacks. For a real, production-ready secure messenger built on the same library, see [COVCOM](https://github.com/xero/covcom).
|
|
201
395
|
|
|
202
|
-
**`
|
|
396
|
+
**`cli`** [ [npm](https://www.npmjs.com/package/lvthn) · [source](https://github.com/xero/leviathan-demos/tree/main/cli) · [readme](https://github.com/xero/leviathan-demos/blob/main/cli/README.md) ]
|
|
203
397
|
|
|
204
|
-
|
|
398
|
+
Command-line file encryption tool supporting Serpent-256-CBC+HMAC-SHA256, XChaCha20-Poly1305, and AES-256-GCM-SIV via `--cipher`. A single keyfile works with all three 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. The tool can export its own interactive completions for a variety of shells.
|
|
205
399
|
|
|
206
400
|
```sh
|
|
207
|
-
bun
|
|
401
|
+
bun add -g lvthn
|
|
208
402
|
lvthn keygen --armor -o my.key
|
|
209
403
|
cat secret.txt | lvthn encrypt -k my.key --armor > secret.enc
|
|
210
404
|
```
|
|
211
405
|
|
|
212
|
-
|
|
406
|
+
**`kyber`** [ [demo](https://leviathan.3xi.club/kyber) · [source](https://github.com/xero/leviathan-demos/tree/main/kyber) · [readme](https://github.com/xero/leviathan-demos/blob/main/kyber/README.md) ]
|
|
213
407
|
|
|
214
|
-
|
|
408
|
+
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.
|
|
215
409
|
|
|
216
|
-
|
|
217
|
-
|---|---|
|
|
218
|
-
| Encrypt data | [`Seal`](https://github.com/xero/leviathan-crypto/wiki/aead.md#seal) with [`SerpentCipher`](https://github.com/xero/leviathan-crypto/wiki/serpent.md#serpentcipher) or [`XChaCha20Cipher`](https://github.com/xero/leviathan-crypto/wiki/chacha20.md#xchacha20cipher) |
|
|
219
|
-
| Encrypt a stream or large file | [`SealStream`](https://github.com/xero/leviathan-crypto/wiki/aead.md#sealstream) to encrypt, [`OpenStream`](https://github.com/xero/leviathan-crypto/wiki/aead.md#openstream) to decrypt |
|
|
220
|
-
| Encrypt in parallel | [`SealStreamPool`](https://github.com/xero/leviathan-crypto/wiki/aead.md#sealstreampool) distributes chunks across Web Workers |
|
|
221
|
-
| Add post-quantum security | [`KyberSuite`](https://github.com/xero/leviathan-crypto/wiki/kyber.md#kybersuite) wraps [`MlKem512`](https://github.com/xero/leviathan-crypto/wiki/kyber.md#parameter-sets), [`MlKem768`](https://github.com/xero/leviathan-crypto/wiki/kyber.md#parameter-sets), or [`MlKem1024`](https://github.com/xero/leviathan-crypto/wiki/kyber.md#parameter-sets) with any cipher suite |
|
|
222
|
-
| Hash data | [`SHA256`](https://github.com/xero/leviathan-crypto/wiki/sha2.md#sha256), [`SHA384`](https://github.com/xero/leviathan-crypto/wiki/sha2.md#sha384), [`SHA512`](https://github.com/xero/leviathan-crypto/wiki/sha2.md#sha512), [`SHA3_256`](https://github.com/xero/leviathan-crypto/wiki/sha3.md#sha3_256), [`SHA3_512`](https://github.com/xero/leviathan-crypto/wiki/sha3.md#sha3_512), [`SHAKE256`](https://github.com/xero/leviathan-crypto/wiki/sha3.md#shake256) ... |
|
|
223
|
-
| Authenticate a message | [`HMAC_SHA256`](https://github.com/xero/leviathan-crypto/wiki/sha2.md#hmac_sha256), [`HMAC_SHA384`](https://github.com/xero/leviathan-crypto/wiki/sha2.md#hmac_sha384), or [`HMAC_SHA512`](https://github.com/xero/leviathan-crypto/wiki/sha2.md#hmac_sha512) |
|
|
224
|
-
| Derive keys | [`HKDF_SHA256`](https://github.com/xero/leviathan-crypto/wiki/sha2.md#hkdf_sha256) or [`HKDF_SHA512`](https://github.com/xero/leviathan-crypto/wiki/sha2.md#hkdf_sha512) |
|
|
225
|
-
| Generate random bytes | [`Fortuna`](https://github.com/xero/leviathan-crypto/wiki/fortuna.md#api-reference) for forward-secret generation, [`randomBytes`](https://github.com/xero/leviathan-crypto/wiki/utils.md#randombytes) for one-off use |
|
|
226
|
-
| Compare secrets safely | [`constantTimeEqual`](https://github.com/xero/leviathan-crypto/wiki/utils.md#constanttimeequal) uses a WASM SIMD path to prevent timing attacks |
|
|
227
|
-
| Work with bytes | [`hexToBytes`](https://github.com/xero/leviathan-crypto/wiki/utils.md#hextobytes), [`bytesToHex`](https://github.com/xero/leviathan-crypto/wiki/utils.md#bytestohex), [`wipe`](https://github.com/xero/leviathan-crypto/wiki/utils.md#wipe), [`xor`](https://github.com/xero/leviathan-crypto/wiki/utils.md#xor), [`concat`](https://github.com/xero/leviathan-crypto/wiki/utils.md#concat) ... |
|
|
410
|
+
**`jwt`** [ [demo](https://leviathan.3xi.club/jwt) · [source](https://github.com/xero/leviathan-demos/tree/main/jwt) · [readme](https://github.com/xero/leviathan-demos/blob/main/jwt/README.md) ]
|
|
228
411
|
|
|
229
|
-
|
|
412
|
+
Classical and post-quantum JSON Web Token signing demo in a single self-contained HTML file. It signs the same claims across eleven algorithms: EdDSA and ES256, the post-quantum ML-DSA and SLH-DSA families, and the leviathan hybrid composites. Every algorithm runs through one uniform path on the `Sign` suite API, with no per-algorithm branching. The token renders with its three segments color-coded and a live byte readout, so the cost of quantum resistance is visible: the same token grows from about 220 bytes under Ed25519 to past 66 kilobytes under SLH-DSA-SHAKE-256f. Tamper with the payload and verification rejects it, because the signature covers the original bytes.
|
|
413
|
+
|
|
414
|
+
**`COVCOM`** [ [demo](https://leviathan.3xi.club/covcom) · [source](https://github.com/xero/covcom/) · [readme](https://github.com/xero/covcom/blob/master/README.md) ]
|
|
415
|
+
|
|
416
|
+
Covert communications app suite for private group conversations. Invite, talk, close the client, and the chat vanishes. Every message is encrypted with XChaCha20 and signed with Ed25519. A BLAKE3 fingerprint on each key allows peers to verify one another. SPQR's manual and epoch ratchets add forward secrecy, while post-quantum ML-KEM-768 encapsulation keeps recorded communications unreadable and secure against future cryptanalysis.
|
|
230
417
|
|
|
231
418
|
---
|
|
232
419
|
|
|
233
420
|
## Going deeper
|
|
234
421
|
|
|
235
|
-
|
|
|
422
|
+
| **Document** | |
|
|
236
423
|
|---|---|
|
|
237
|
-
| [Architecture](https://github.com/xero/leviathan-crypto/wiki/architecture
|
|
238
|
-
| [Test Suite](https://github.com/xero/leviathan-crypto/wiki/test-suite
|
|
424
|
+
| [Architecture](https://github.com/xero/leviathan-crypto/wiki/architecture) | Repository structure, module relationships, build pipeline, and buffer layouts |
|
|
425
|
+
| [Test Suite](https://github.com/xero/leviathan-crypto/wiki/test-suite) | How the test suite works, vector corpus, and gate discipline |
|
|
239
426
|
| [Security Policy](./SECURITY.md) | Security posture and vulnerability disclosure details |
|
|
240
|
-
| [
|
|
241
|
-
| [
|
|
242
|
-
| [
|
|
243
|
-
| [
|
|
427
|
+
| [Audits](https://github.com/xero/leviathan-crypto/wiki/audits) | Every primitive has a published audit covering spec conformance, known-answer tests, constant-time discipline, and ACVP validation where applicable. |
|
|
428
|
+
| [Lexicon](https://github.com/xero/leviathan-crypto/wiki/lexicon) | Glossary of cryptographic terms |
|
|
429
|
+
| [WASM Primer](https://github.com/xero/leviathan-crypto/wiki/wasm) | WebAssembly primer in the context of this library |
|
|
430
|
+
| [CDN](https://github.com/xero/leviathan-crypto/wiki/cdn) | Use leviathan-crypto directly from a CDN with no bundler |
|
|
431
|
+
| [argon2id](https://github.com/xero/leviathan-crypto/wiki/argon2id) | Passphrase-based encryption using Argon2id alongside leviathan primitives |
|
|
244
432
|
|
|
245
433
|
---
|
|
246
434
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AES-128/192/256 in CBC mode with PKCS7 padding.
|
|
3
|
+
*
|
|
4
|
+
* **WARNING: CBC mode is unauthenticated.** Always authenticate the output
|
|
5
|
+
* with HMAC-SHA256 (Encrypt-then-MAC) or use an authenticated cipher
|
|
6
|
+
* (`XChaCha20Poly1305`, `Seal` with `SerpentCipher`) instead.
|
|
7
|
+
*
|
|
8
|
+
* Holds exclusive access to the `aes` WASM module from construction until
|
|
9
|
+
* `dispose()`. Constructing a second AES-using class while this instance
|
|
10
|
+
* is live throws. Call `dispose()` when done.
|
|
11
|
+
*/
|
|
12
|
+
export declare class AESCbc {
|
|
13
|
+
private readonly x;
|
|
14
|
+
private _tok;
|
|
15
|
+
constructor(opts?: {
|
|
16
|
+
dangerUnauthenticated: true;
|
|
17
|
+
});
|
|
18
|
+
/**
|
|
19
|
+
* Encrypt plaintext with AES CBC + PKCS7 padding.
|
|
20
|
+
*
|
|
21
|
+
* @param key 16, 24, or 32 bytes (AES-128 / 192 / 256)
|
|
22
|
+
* @param iv 16 bytes, must be random and unique per (key, message)
|
|
23
|
+
* @param plaintext any length, PKCS7 padding applied automatically
|
|
24
|
+
* @returns ciphertext (length = ceil((plaintext.length + 1) / 16) * 16)
|
|
25
|
+
*/
|
|
26
|
+
encrypt(key: Uint8Array, iv: Uint8Array, plaintext: Uint8Array): Uint8Array;
|
|
27
|
+
/**
|
|
28
|
+
* Decrypt AES CBC + PKCS7.
|
|
29
|
+
*
|
|
30
|
+
* All failure modes, empty input, non-multiple-of-16 length, and any
|
|
31
|
+
* PKCS7 validation failure, throw the same generic `RangeError` with
|
|
32
|
+
* message `'invalid ciphertext'`. Padding validation runs branch-free
|
|
33
|
+
* over the last 16 bytes regardless of where the mismatch is, closing
|
|
34
|
+
* the Vaudenay 2002 padding-oracle surface for callers using
|
|
35
|
+
* `{ dangerUnauthenticated: true }` without an outer HMAC.
|
|
36
|
+
*/
|
|
37
|
+
decrypt(key: Uint8Array, iv: Uint8Array, ciphertext: Uint8Array): Uint8Array;
|
|
38
|
+
/** Wipe WASM state and release exclusive module access. Idempotent. */
|
|
39
|
+
dispose(): void;
|
|
40
|
+
}
|