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/dist/docs/chacha20.md
DELETED
|
@@ -1,674 +0,0 @@
|
|
|
1
|
-
# ChaCha20 TypeScript API
|
|
2
|
-
|
|
3
|
-
> [!NOTE]
|
|
4
|
-
> API reference for `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, and `XChaCha20Cipher`. Covers initialization, all class methods, usage examples, and error conditions.
|
|
5
|
-
|
|
6
|
-
> ### Table of Contents
|
|
7
|
-
> - [Overview](#overview)
|
|
8
|
-
> - [Security Notes](#security-notes)
|
|
9
|
-
> - [Module Init](#module-init)
|
|
10
|
-
> - [API Reference](#api-reference)
|
|
11
|
-
> - [`ChaCha20`](#chacha20)
|
|
12
|
-
> - [`Poly1305`](#poly1305)
|
|
13
|
-
> - [`ChaCha20Poly1305`](#chacha20poly1305)
|
|
14
|
-
> - [`XChaCha20Poly1305`](#xchacha20poly1305)
|
|
15
|
-
> - [XChaCha20Cipher](#xchacha20cipher)
|
|
16
|
-
> - [Usage Examples](#usage-examples)
|
|
17
|
-
> - [Error Conditions](#error-conditions)
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Overview
|
|
22
|
-
|
|
23
|
-
**ChaCha20** is a modern stream cipher designed by Daniel J. Bernstein. It is fast
|
|
24
|
-
on all platforms (including those without hardware AES), resistant to timing attacks
|
|
25
|
-
by design, and widely deployed in TLS, SSH, and WireGuard. ChaCha20 encrypts data
|
|
26
|
-
by generating a pseudorandom keystream from a 256-bit key and a nonce, then XORing
|
|
27
|
-
it with the plaintext. It does **not** provide authentication on its own. A modified message will decrypt to garbage with no warning.
|
|
28
|
-
|
|
29
|
-
**Poly1305** is a one-time message authentication code (MAC). Given a unique 256-bit
|
|
30
|
-
key and a message, it produces a 16-byte tag that proves the message has not been
|
|
31
|
-
tampered with. The critical requirement is that each Poly1305 key is used **exactly
|
|
32
|
-
once**. Reusing a key completely breaks its security. You almost never need to use
|
|
33
|
-
Poly1305 directly; the AEAD constructions below handle key derivation for you.
|
|
34
|
-
|
|
35
|
-
**ChaCha20-Poly1305** (RFC 8439) combines both primitives into an AEAD
|
|
36
|
-
(Authenticated Encryption with Associated Data). It encrypts your data and
|
|
37
|
-
produces an authentication tag in a single operation. On decryption, it verifies
|
|
38
|
-
the tag before returning any plaintext. If someone tampered with the ciphertext,
|
|
39
|
-
you get an error instead of corrupted data. The nonce is 96 bits (12 bytes).
|
|
40
|
-
|
|
41
|
-
**XChaCha20-Poly1305** extends the nonce to 192 bits (24 bytes) using the HChaCha20
|
|
42
|
-
subkey derivation step. This makes random nonce generation completely safe. With a
|
|
43
|
-
24-byte nonce, the probability of a collision is negligible even after billions of
|
|
44
|
-
messages. **For most users, `Seal` with `XChaCha20Cipher` is the recommended choice.** It
|
|
45
|
-
produces a self-contained authenticated blob with no nonce management, no
|
|
46
|
-
instantiation, and no `dispose()`. For protocol interop requiring explicit nonce
|
|
47
|
-
control, use `XChaCha20Poly1305` directly.
|
|
48
|
-
|
|
49
|
-
---
|
|
50
|
-
|
|
51
|
-
## Security Notes
|
|
52
|
-
|
|
53
|
-
> [!IMPORTANT]
|
|
54
|
-
> Read this section before writing any code. These are not theoretical concerns.
|
|
55
|
-
> They are the mistakes that cause real-world breaches.
|
|
56
|
-
|
|
57
|
-
- **Use `Seal` with `XChaCha20Cipher` unless you need explicit nonce control.**
|
|
58
|
-
It is the safest default: authenticated encryption in a single static call.
|
|
59
|
-
If you are unsure which class to pick, pick this one. Use `XChaCha20Poly1305`
|
|
60
|
-
when protocol interop requires you to manage nonces yourself.
|
|
61
|
-
|
|
62
|
-
- **Never reuse a nonce with the same key.** This is the single most important
|
|
63
|
-
rule. If you encrypt two different messages with the same key and the same nonce,
|
|
64
|
-
an attacker can XOR the two ciphertexts together and recover both plaintexts.
|
|
65
|
-
With `ChaCha20Poly1305` (12-byte nonce), random generation has a meaningful
|
|
66
|
-
collision risk after roughly 2^32 messages under one key. With
|
|
67
|
-
`XChaCha20Poly1305` (24-byte nonce), random generation is safe for any practical
|
|
68
|
-
message count. Just call `randomBytes(24)` for each message.
|
|
69
|
-
|
|
70
|
-
- **Poly1305 keys are single-use.** Each Poly1305 key must be used to authenticate
|
|
71
|
-
exactly one message. The AEAD classes (`ChaCha20Poly1305` and
|
|
72
|
-
`XChaCha20Poly1305`) handle this automatically by deriving a fresh Poly1305 key
|
|
73
|
-
from the ChaCha20 keystream for each encryption. If you use the standalone
|
|
74
|
-
`Poly1305` class directly, it is your responsibility to never reuse a key.
|
|
75
|
-
|
|
76
|
-
- **AEAD protects both confidentiality and authenticity.** If authentication fails
|
|
77
|
-
during decryption, the plaintext is never returned. You get an error. This is
|
|
78
|
-
intentional. Do not try to work around it. If decryption fails, the ciphertext was corrupted or tampered with.
|
|
79
|
-
|
|
80
|
-
- **Associated data (AAD) is authenticated but not encrypted.** Use AAD for data
|
|
81
|
-
that must travel in the clear (headers, routing metadata, user IDs) but must be
|
|
82
|
-
verified as unmodified. If someone changes the AAD, decryption will fail even
|
|
83
|
-
if the ciphertext itself is untouched.
|
|
84
|
-
|
|
85
|
-
- **Always call `dispose()` when you are done.** This wipes key material and
|
|
86
|
-
intermediate state from WASM memory. Failing to call `dispose()` leaves
|
|
87
|
-
sensitive data in memory longer than necessary.
|
|
88
|
-
|
|
89
|
-
- **`ChaCha20` alone has no authentication.** If you use the raw `ChaCha20` class
|
|
90
|
-
without pairing it with a MAC, an attacker can flip bits in the ciphertext and
|
|
91
|
-
the corresponding bits in the plaintext will flip silently. Unless you are
|
|
92
|
-
building your own authenticated construction (and you probably should not be),
|
|
93
|
-
use one of the AEAD classes instead.
|
|
94
|
-
|
|
95
|
-
---
|
|
96
|
-
|
|
97
|
-
## Module Init
|
|
98
|
-
|
|
99
|
-
Each module subpath exports its own init function for consumers who want
|
|
100
|
-
tree-shakeable imports.
|
|
101
|
-
|
|
102
|
-
### `chacha20Init(source)`
|
|
103
|
-
|
|
104
|
-
Initializes only the chacha20 WASM binary. Equivalent to calling the
|
|
105
|
-
root `init({ chacha20: chacha20Wasm })` but without pulling the other three
|
|
106
|
-
modules into the bundle.
|
|
107
|
-
|
|
108
|
-
**Signature:**
|
|
109
|
-
|
|
110
|
-
```typescript
|
|
111
|
-
async function chacha20Init(source: WasmSource): Promise<void>
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
**Usage:**
|
|
115
|
-
|
|
116
|
-
```typescript
|
|
117
|
-
import { chacha20Init, XChaCha20Poly1305 } from 'leviathan-crypto/chacha20'
|
|
118
|
-
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
|
|
119
|
-
|
|
120
|
-
await chacha20Init(chacha20Wasm)
|
|
121
|
-
const aead = new XChaCha20Poly1305()
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
---
|
|
125
|
-
|
|
126
|
-
## API Reference
|
|
127
|
-
|
|
128
|
-
All classes require calling `await init({ chacha20: chacha20Wasm })` or the subpath `chacha20Init()`
|
|
129
|
-
before construction. If you construct a class before initialization, it throws:
|
|
130
|
-
```
|
|
131
|
-
Error: leviathan-crypto: call init({ chacha20: ... }) before using this class
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
---
|
|
135
|
-
|
|
136
|
-
### `ChaCha20`
|
|
137
|
-
|
|
138
|
-
Raw ChaCha20 stream cipher. **No authentication.** Use `XChaCha20Poly1305` instead
|
|
139
|
-
unless you are building a custom protocol and understand the risks.
|
|
140
|
-
|
|
141
|
-
#### Constructor
|
|
142
|
-
|
|
143
|
-
```typescript
|
|
144
|
-
new ChaCha20()
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
Throws if `init({ chacha20: chacha20Wasm })` has not been called.
|
|
148
|
-
|
|
149
|
-
---
|
|
150
|
-
|
|
151
|
-
#### `beginEncrypt(key: Uint8Array, nonce: Uint8Array): void`
|
|
152
|
-
|
|
153
|
-
Prepares the cipher for encryption with the given key and nonce.
|
|
154
|
-
|
|
155
|
-
| Parameter | Type | Description |
|
|
156
|
-
|-----------|------|-------------|
|
|
157
|
-
| `key` | `Uint8Array` | 32 bytes (256 bits) |
|
|
158
|
-
| `nonce` | `Uint8Array` | 12 bytes (96 bits) |
|
|
159
|
-
|
|
160
|
-
**Throws** `RangeError` if `key` is not 32 bytes or `nonce` is not 12 bytes.
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
#### `encryptChunk(chunk: Uint8Array): Uint8Array`
|
|
165
|
-
|
|
166
|
-
Encrypts a chunk of plaintext. Call repeatedly for streaming encryption. Returns
|
|
167
|
-
a new `Uint8Array` containing the ciphertext (same length as input).
|
|
168
|
-
|
|
169
|
-
| Parameter | Type | Description |
|
|
170
|
-
|-----------|------|-------------|
|
|
171
|
-
| `chunk` | `Uint8Array` | Plaintext bytes (up to the module's chunk size limit) |
|
|
172
|
-
|
|
173
|
-
**Throws** `RangeError` if the chunk exceeds the maximum chunk size.
|
|
174
|
-
|
|
175
|
-
---
|
|
176
|
-
|
|
177
|
-
#### `beginDecrypt(key: Uint8Array, nonce: Uint8Array): void`
|
|
178
|
-
|
|
179
|
-
Prepares the cipher for decryption. Identical to `beginEncrypt`. ChaCha20 is symmetric; encryption and decryption are the same XOR operation.
|
|
180
|
-
|
|
181
|
-
| Parameter | Type | Description |
|
|
182
|
-
|-----------|------|-------------|
|
|
183
|
-
| `key` | `Uint8Array` | 32 bytes (256 bits) |
|
|
184
|
-
| `nonce` | `Uint8Array` | 12 bytes (96 bits) |
|
|
185
|
-
|
|
186
|
-
**Throws** `RangeError` if `key` is not 32 bytes or `nonce` is not 12 bytes.
|
|
187
|
-
|
|
188
|
-
---
|
|
189
|
-
|
|
190
|
-
#### `decryptChunk(chunk: Uint8Array): Uint8Array`
|
|
191
|
-
|
|
192
|
-
Decrypts a chunk of ciphertext. Returns a new `Uint8Array` containing the
|
|
193
|
-
plaintext (same length as input).
|
|
194
|
-
|
|
195
|
-
| Parameter | Type | Description |
|
|
196
|
-
|-----------|------|-------------|
|
|
197
|
-
| `chunk` | `Uint8Array` | Ciphertext bytes |
|
|
198
|
-
|
|
199
|
-
**Throws** `RangeError` if the chunk exceeds the maximum chunk size.
|
|
200
|
-
|
|
201
|
-
---
|
|
202
|
-
|
|
203
|
-
#### `dispose(): void`
|
|
204
|
-
|
|
205
|
-
Wipes all key material and intermediate state from WASM memory. Always call this
|
|
206
|
-
when you are done with the instance.
|
|
207
|
-
|
|
208
|
-
---
|
|
209
|
-
|
|
210
|
-
### `Poly1305`
|
|
211
|
-
|
|
212
|
-
Standalone Poly1305 one-time MAC. **Each key must be used exactly once.** You
|
|
213
|
-
almost certainly want `ChaCha20Poly1305` or `XChaCha20Poly1305` instead. They
|
|
214
|
-
handle Poly1305 key derivation automatically.
|
|
215
|
-
|
|
216
|
-
#### Constructor
|
|
217
|
-
|
|
218
|
-
```typescript
|
|
219
|
-
new Poly1305()
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
Throws if `init({ chacha20: chacha20Wasm })` has not been called.
|
|
223
|
-
|
|
224
|
-
---
|
|
225
|
-
|
|
226
|
-
#### `mac(key: Uint8Array, msg: Uint8Array): Uint8Array`
|
|
227
|
-
|
|
228
|
-
Computes a 16-byte Poly1305 authentication tag over the given message.
|
|
229
|
-
|
|
230
|
-
| Parameter | Type | Description |
|
|
231
|
-
|-----------|------|-------------|
|
|
232
|
-
| `key` | `Uint8Array` | 32 bytes. Must be unique per message. |
|
|
233
|
-
| `msg` | `Uint8Array` | The message to authenticate (any length) |
|
|
234
|
-
|
|
235
|
-
**Returns** `Uint8Array`: a 16-byte authentication tag.
|
|
236
|
-
|
|
237
|
-
**Throws** `RangeError` if `key` is not 32 bytes.
|
|
238
|
-
|
|
239
|
-
---
|
|
240
|
-
|
|
241
|
-
#### `dispose(): void`
|
|
242
|
-
|
|
243
|
-
Wipes all key material and intermediate state from WASM memory.
|
|
244
|
-
|
|
245
|
-
---
|
|
246
|
-
|
|
247
|
-
### `ChaCha20Poly1305`
|
|
248
|
-
|
|
249
|
-
ChaCha20-Poly1305 AEAD as specified in RFC 8439. Provides authenticated encryption
|
|
250
|
-
with a 12-byte (96-bit) nonce. The Poly1305 one-time key is derived automatically
|
|
251
|
-
from the ChaCha20 keystream (counter 0).
|
|
252
|
-
|
|
253
|
-
If you are generating nonces randomly, prefer `XChaCha20Poly1305` (24-byte nonce)
|
|
254
|
-
to avoid collision risk.
|
|
255
|
-
|
|
256
|
-
#### Constructor
|
|
257
|
-
|
|
258
|
-
```typescript
|
|
259
|
-
new ChaCha20Poly1305()
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
Throws if `init({ chacha20: chacha20Wasm })` has not been called.
|
|
263
|
-
|
|
264
|
-
---
|
|
265
|
-
|
|
266
|
-
#### `encrypt(key, nonce, plaintext, aad?): Uint8Array`
|
|
267
|
-
|
|
268
|
-
Encrypts plaintext and returns the ciphertext with the 16-byte Poly1305 tag
|
|
269
|
-
appended.
|
|
270
|
-
|
|
271
|
-
> [!WARNING]
|
|
272
|
-
> Each `ChaCha20Poly1305` instance allows only **one** `encrypt()` call. A second
|
|
273
|
-
> call throws to prevent accidental nonce reuse. Create a new instance for each
|
|
274
|
-
> encryption, or use `Seal` with `XChaCha20Cipher` for automatic nonce management.
|
|
275
|
-
|
|
276
|
-
| Parameter | Type | Default | Description |
|
|
277
|
-
|-----------|------|---------|-------------|
|
|
278
|
-
| `key` | `Uint8Array` | | 32 bytes (256-bit key) |
|
|
279
|
-
| `nonce` | `Uint8Array` | | 12 bytes (96-bit nonce) |
|
|
280
|
-
| `plaintext` | `Uint8Array` | | Data to encrypt (up to the module's chunk size limit) |
|
|
281
|
-
| `aad` | `Uint8Array` | `new Uint8Array(0)` | Associated data. Authenticated but not encrypted. |
|
|
282
|
-
|
|
283
|
-
**Returns** `Uint8Array`: ciphertext + 16-byte tag (length = plaintext.length + 16).
|
|
284
|
-
|
|
285
|
-
**Throws:**
|
|
286
|
-
- `RangeError` if `key` is not 32 bytes
|
|
287
|
-
- `RangeError` if `nonce` is not 12 bytes
|
|
288
|
-
- `RangeError` if `plaintext` exceeds the maximum chunk size
|
|
289
|
-
- `Error` if `encrypt()` has already been called on this instance
|
|
290
|
-
|
|
291
|
-
---
|
|
292
|
-
|
|
293
|
-
#### `decrypt(key, nonce, ciphertext, aad?): Uint8Array`
|
|
294
|
-
|
|
295
|
-
Verifies the authentication tag and decrypts the ciphertext. The `ciphertext`
|
|
296
|
-
parameter must include the appended 16-byte tag (i.e., the exact output of
|
|
297
|
-
`encrypt()`). If authentication fails, an error is thrown and no plaintext is
|
|
298
|
-
returned.
|
|
299
|
-
|
|
300
|
-
Tag comparison uses a constant-time XOR-accumulate pattern; no timing side channel leaks whether the tag was "close" to correct.
|
|
301
|
-
|
|
302
|
-
| Parameter | Type | Default | Description |
|
|
303
|
-
|-----------|------|---------|-------------|
|
|
304
|
-
| `key` | `Uint8Array` | | 32 bytes (same key used for encryption) |
|
|
305
|
-
| `nonce` | `Uint8Array` | | 12 bytes (same nonce used for encryption) |
|
|
306
|
-
| `ciphertext` | `Uint8Array` | | Encrypted data with appended tag (output of `encrypt()`) |
|
|
307
|
-
| `aad` | `Uint8Array` | `new Uint8Array(0)` | Associated data (must match what was passed to `encrypt()`) |
|
|
308
|
-
|
|
309
|
-
**Returns** `Uint8Array`: the decrypted plaintext.
|
|
310
|
-
|
|
311
|
-
**Throws:**
|
|
312
|
-
- `RangeError` if `key` is not 32 bytes
|
|
313
|
-
- `RangeError` if `nonce` is not 12 bytes
|
|
314
|
-
- `RangeError` if `ciphertext` is shorter than 16 bytes (no room for a tag)
|
|
315
|
-
- `Error('ChaCha20Poly1305: authentication failed')` if the tag does not match
|
|
316
|
-
|
|
317
|
-
---
|
|
318
|
-
|
|
319
|
-
#### `dispose(): void`
|
|
320
|
-
|
|
321
|
-
Wipes all key material and intermediate state from WASM memory.
|
|
322
|
-
|
|
323
|
-
---
|
|
324
|
-
|
|
325
|
-
### `XChaCha20Poly1305`
|
|
326
|
-
|
|
327
|
-
XChaCha20-Poly1305 AEAD (draft-irtf-cfrg-xchacha). RFC-faithful stateless
|
|
328
|
-
primitive. Key and nonce are passed per-call. Use when protocol interop
|
|
329
|
-
requires explicit nonce control. For most use cases, prefer `Seal` with
|
|
330
|
-
`XChaCha20Cipher` (automatic nonce management, no instantiation).
|
|
331
|
-
|
|
332
|
-
It uses a 24-byte (192-bit) nonce, which is large enough that randomly generated
|
|
333
|
-
nonces will never collide in practice. Internally, it derives a subkey via
|
|
334
|
-
HChaCha20 and delegates to `ChaCha20Poly1305`.
|
|
335
|
-
|
|
336
|
-
Like `ChaCha20Poly1305`, the `encrypt()` method returns a single `Uint8Array`
|
|
337
|
-
with the tag appended to the ciphertext. The `decrypt()` method expects this
|
|
338
|
-
combined format and splits it internally.
|
|
339
|
-
|
|
340
|
-
#### Constructor
|
|
341
|
-
|
|
342
|
-
```typescript
|
|
343
|
-
new XChaCha20Poly1305()
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
Throws if `init({ chacha20: chacha20Wasm })` has not been called.
|
|
347
|
-
|
|
348
|
-
---
|
|
349
|
-
|
|
350
|
-
#### `encrypt(key, nonce, plaintext, aad?): Uint8Array`
|
|
351
|
-
|
|
352
|
-
Encrypts plaintext and returns the ciphertext with the 16-byte tag appended.
|
|
353
|
-
|
|
354
|
-
| Parameter | Type | Default | Description |
|
|
355
|
-
|-----------|------|---------|-------------|
|
|
356
|
-
| `key` | `Uint8Array` | | 32 bytes (256-bit key) |
|
|
357
|
-
| `nonce` | `Uint8Array` | | 24 bytes (192-bit nonce) |
|
|
358
|
-
| `plaintext` | `Uint8Array` | | Data to encrypt |
|
|
359
|
-
| `aad` | `Uint8Array` | `new Uint8Array(0)` | Associated data. Authenticated but not encrypted. |
|
|
360
|
-
|
|
361
|
-
**Returns** `Uint8Array`: ciphertext + 16-byte tag (length = plaintext.length + 16).
|
|
362
|
-
|
|
363
|
-
**Throws:**
|
|
364
|
-
- `RangeError` if `key` is not 32 bytes
|
|
365
|
-
- `RangeError` if `nonce` is not 24 bytes
|
|
366
|
-
|
|
367
|
-
---
|
|
368
|
-
|
|
369
|
-
#### `decrypt(key, nonce, ciphertext, aad?): Uint8Array`
|
|
370
|
-
|
|
371
|
-
Verifies the authentication tag and decrypts the ciphertext. The `ciphertext`
|
|
372
|
-
parameter must include the appended 16-byte tag (i.e., the exact output of
|
|
373
|
-
`encrypt()`).
|
|
374
|
-
|
|
375
|
-
| Parameter | Type | Default | Description |
|
|
376
|
-
|-----------|------|---------|-------------|
|
|
377
|
-
| `key` | `Uint8Array` | | 32 bytes (same key used for encryption) |
|
|
378
|
-
| `nonce` | `Uint8Array` | | 24 bytes (same nonce used for encryption) |
|
|
379
|
-
| `ciphertext` | `Uint8Array` | | Encrypted data with appended tag (output of `encrypt()`) |
|
|
380
|
-
| `aad` | `Uint8Array` | `new Uint8Array(0)` | Associated data (must match what was passed to `encrypt()`) |
|
|
381
|
-
|
|
382
|
-
**Returns** `Uint8Array`: the decrypted plaintext.
|
|
383
|
-
|
|
384
|
-
**Throws:**
|
|
385
|
-
- `RangeError` if `key` is not 32 bytes
|
|
386
|
-
- `RangeError` if `nonce` is not 24 bytes
|
|
387
|
-
- `RangeError` if `ciphertext` is shorter than 16 bytes (no room for a tag)
|
|
388
|
-
- `Error('ChaCha20Poly1305: authentication failed')` if the tag does not match
|
|
389
|
-
|
|
390
|
-
---
|
|
391
|
-
|
|
392
|
-
#### `dispose(): void`
|
|
393
|
-
|
|
394
|
-
Wipes all key material and intermediate state from WASM memory.
|
|
395
|
-
|
|
396
|
-
---
|
|
397
|
-
|
|
398
|
-
## XChaCha20Cipher
|
|
399
|
-
|
|
400
|
-
`CipherSuite` implementation for XChaCha20-Poly1305. Pass to `Seal`,
|
|
401
|
-
`SealStream`, or `OpenStream`. Never instantiated directly.
|
|
402
|
-
|
|
403
|
-
Requires `init({ chacha20: chacha20Wasm, sha2: sha2Wasm })`.
|
|
404
|
-
|
|
405
|
-
> [!NOTE]
|
|
406
|
-
> `sha2` is required by the stream layer for HKDF key derivation, not by
|
|
407
|
-
> `XChaCha20Poly1305` itself.
|
|
408
|
-
|
|
409
|
-
| Property | Value |
|
|
410
|
-
|----------|-------|
|
|
411
|
-
| `formatEnum` | `0x01` |
|
|
412
|
-
| `keySize` | `32` |
|
|
413
|
-
| `tagSize` | `16` (Poly1305) |
|
|
414
|
-
| `padded` | `false` |
|
|
415
|
-
| `wasmModules` | `['chacha20', 'sha2']` |
|
|
416
|
-
|
|
417
|
-
#### `XChaCha20Cipher.keygen(): Uint8Array`
|
|
418
|
-
|
|
419
|
-
Returns `randomBytes(32)`. Convenience method. Not on the `CipherSuite` interface.
|
|
420
|
-
|
|
421
|
-
#### Usage with `Seal`
|
|
422
|
-
|
|
423
|
-
```typescript
|
|
424
|
-
import { init, Seal, XChaCha20Cipher } from 'leviathan-crypto'
|
|
425
|
-
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
|
|
426
|
-
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
427
|
-
|
|
428
|
-
await init({ chacha20: chacha20Wasm, sha2: sha2Wasm })
|
|
429
|
-
|
|
430
|
-
const key = XChaCha20Cipher.keygen()
|
|
431
|
-
const blob = Seal.encrypt(XChaCha20Cipher, key, plaintext)
|
|
432
|
-
const pt = Seal.decrypt(XChaCha20Cipher, key, blob) // throws on tamper
|
|
433
|
-
```
|
|
434
|
-
|
|
435
|
-
#### Usage with `SealStream` / `OpenStream`
|
|
436
|
-
|
|
437
|
-
```typescript
|
|
438
|
-
import { SealStream, OpenStream } from 'leviathan-crypto/stream'
|
|
439
|
-
import { XChaCha20Cipher } from 'leviathan-crypto/chacha20'
|
|
440
|
-
|
|
441
|
-
const sealer = new SealStream(XChaCha20Cipher, key)
|
|
442
|
-
const preamble = sealer.preamble // 20 bytes, send before first chunk
|
|
443
|
-
const ct0 = sealer.push(chunk0)
|
|
444
|
-
const ctLast = sealer.finalize(lastChunk)
|
|
445
|
-
|
|
446
|
-
const opener = new OpenStream(XChaCha20Cipher, key, preamble)
|
|
447
|
-
const pt0 = opener.pull(ct0)
|
|
448
|
-
const ptLast = opener.finalize(ctLast)
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
See [aead.md](./aead.md) for the full `Seal`, `SealStream`, and `OpenStream` API.
|
|
452
|
-
|
|
453
|
-
---
|
|
454
|
-
|
|
455
|
-
## Usage Examples
|
|
456
|
-
|
|
457
|
-
### Example 1: Seal with XChaCha20Cipher (recommended)
|
|
458
|
-
|
|
459
|
-
One-shot AEAD. No instantiation, no `dispose()`. The blob format is
|
|
460
|
-
`preamble(20) || ciphertext || tag(16)`.
|
|
461
|
-
|
|
462
|
-
```typescript
|
|
463
|
-
import { init, Seal, XChaCha20Cipher, utf8ToBytes, bytesToUtf8 } from 'leviathan-crypto'
|
|
464
|
-
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
|
|
465
|
-
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
466
|
-
|
|
467
|
-
await init({ chacha20: chacha20Wasm, sha2: sha2Wasm })
|
|
468
|
-
|
|
469
|
-
const key = XChaCha20Cipher.keygen()
|
|
470
|
-
const blob = Seal.encrypt(XChaCha20Cipher, key, utf8ToBytes('Hello, world!'))
|
|
471
|
-
const pt = Seal.decrypt(XChaCha20Cipher, key, blob) // throws on tamper
|
|
472
|
-
|
|
473
|
-
console.log(bytesToUtf8(pt)) // "Hello, world!"
|
|
474
|
-
|
|
475
|
-
// Optional: bind metadata without encrypting it (AAD)
|
|
476
|
-
const aad = utf8ToBytes('document-v2')
|
|
477
|
-
const blob2 = Seal.encrypt(XChaCha20Cipher, key, utf8ToBytes('Hello, world!'), { aad })
|
|
478
|
-
const pt2 = Seal.decrypt(XChaCha20Cipher, key, blob2, { aad })
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
> For explicit nonce control, use `XChaCha20Poly1305` directly. Same
|
|
482
|
-
> API shape, 24-byte nonce passed per call. See Example 2.
|
|
483
|
-
|
|
484
|
-
### Example 2: ChaCha20Poly1305
|
|
485
|
-
|
|
486
|
-
Same idea as above, but with a 12-byte nonce. Use this if you are implementing
|
|
487
|
-
a protocol that specifies RFC 8439 ChaCha20-Poly1305 explicitly.
|
|
488
|
-
|
|
489
|
-
Both `ChaCha20Poly1305` and `XChaCha20Poly1305` return a single `Uint8Array`
|
|
490
|
-
with the tag appended, and `decrypt()` expects the same combined format.
|
|
491
|
-
The only difference is the nonce size (12 vs 24 bytes).
|
|
492
|
-
|
|
493
|
-
> [!NOTE]
|
|
494
|
-
> Each `ChaCha20Poly1305` instance allows only one `encrypt()` call. A second
|
|
495
|
-
> call throws to prevent accidental nonce reuse. Create a new instance for each
|
|
496
|
-
> encryption.
|
|
497
|
-
|
|
498
|
-
```typescript
|
|
499
|
-
import { init, ChaCha20Poly1305, randomBytes, utf8ToBytes, bytesToUtf8 } from 'leviathan-crypto'
|
|
500
|
-
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
|
|
501
|
-
|
|
502
|
-
await init({ chacha20: chacha20Wasm })
|
|
503
|
-
|
|
504
|
-
const key = randomBytes(32)
|
|
505
|
-
const aead = new ChaCha20Poly1305()
|
|
506
|
-
|
|
507
|
-
// Encrypt
|
|
508
|
-
const nonce = randomBytes(12) // 12 bytes, use XChaCha20Poly1305 for high-volume random generation
|
|
509
|
-
const plaintext = utf8ToBytes('Sensitive data')
|
|
510
|
-
const sealed = aead.encrypt(key, nonce, plaintext)
|
|
511
|
-
// sealed = ciphertext || tag(16), store/transmit nonce and sealed together
|
|
512
|
-
|
|
513
|
-
// Decrypt (new instance, encrypt is single-use)
|
|
514
|
-
const aead2 = new ChaCha20Poly1305()
|
|
515
|
-
const decrypted = aead2.decrypt(key, nonce, sealed)
|
|
516
|
-
console.log(bytesToUtf8(decrypted)) // "Sensitive data"
|
|
517
|
-
|
|
518
|
-
aead.dispose()
|
|
519
|
-
aead2.dispose()
|
|
520
|
-
```
|
|
521
|
-
|
|
522
|
-
### Example 3: Detecting Tampered Ciphertext
|
|
523
|
-
|
|
524
|
-
AEAD decryption fails loudly if anyone has modified the ciphertext or
|
|
525
|
-
the associated data. This is a feature. It prevents you from processing
|
|
526
|
-
corrupted or maliciously altered data.
|
|
527
|
-
|
|
528
|
-
```typescript
|
|
529
|
-
import { init, Seal, XChaCha20Cipher, randomBytes, utf8ToBytes } from 'leviathan-crypto'
|
|
530
|
-
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
|
|
531
|
-
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
532
|
-
|
|
533
|
-
await init({ chacha20: chacha20Wasm, sha2: sha2Wasm })
|
|
534
|
-
|
|
535
|
-
const key = XChaCha20Cipher.keygen()
|
|
536
|
-
const sealed = Seal.encrypt(XChaCha20Cipher, key, utf8ToBytes('Original message'))
|
|
537
|
-
|
|
538
|
-
// Simulate tampering: flip one bit in the ciphertext
|
|
539
|
-
const tampered = new Uint8Array(sealed)
|
|
540
|
-
tampered[20] ^= 0x01 // byte 20 is the first ciphertext byte (after the 20-byte preamble)
|
|
541
|
-
|
|
542
|
-
try {
|
|
543
|
-
const plaintext = Seal.decrypt(XChaCha20Cipher, key, tampered)
|
|
544
|
-
// This line is never reached
|
|
545
|
-
console.log(plaintext)
|
|
546
|
-
} catch (err) {
|
|
547
|
-
console.error(err.message)
|
|
548
|
-
// "ChaCha20Poly1305: authentication failed"
|
|
549
|
-
// The plaintext is never returned. Decryption stops immediately on failure.
|
|
550
|
-
}
|
|
551
|
-
```
|
|
552
|
-
|
|
553
|
-
### Example 4: Using Associated Data (AAD)
|
|
554
|
-
|
|
555
|
-
Associated data is metadata that you want to authenticate (prove unmodified) but
|
|
556
|
-
not encrypt. Common uses: user IDs, message sequence numbers, protocol version
|
|
557
|
-
headers, routing information.
|
|
558
|
-
|
|
559
|
-
```typescript
|
|
560
|
-
import { init, Seal, XChaCha20Cipher, randomBytes, utf8ToBytes, bytesToUtf8 } from 'leviathan-crypto'
|
|
561
|
-
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
|
|
562
|
-
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
563
|
-
|
|
564
|
-
await init({ chacha20: chacha20Wasm, sha2: sha2Wasm })
|
|
565
|
-
|
|
566
|
-
const key = XChaCha20Cipher.keygen()
|
|
567
|
-
|
|
568
|
-
// The user ID travels in the clear, but decryption will fail if anyone changes it
|
|
569
|
-
const userId = utf8ToBytes('user-12345')
|
|
570
|
-
const message = utf8ToBytes('Your account balance is $1,000,000')
|
|
571
|
-
|
|
572
|
-
const sealed = Seal.encrypt(XChaCha20Cipher, key, message, { aad: userId })
|
|
573
|
-
|
|
574
|
-
// Decrypt, pass the same AAD
|
|
575
|
-
const decrypted = Seal.decrypt(XChaCha20Cipher, key, sealed, { aad: userId })
|
|
576
|
-
console.log(bytesToUtf8(decrypted))
|
|
577
|
-
// "Your account balance is $1,000,000"
|
|
578
|
-
|
|
579
|
-
// If someone changes the AAD, decryption fails
|
|
580
|
-
const wrongUserId = utf8ToBytes('user-99999')
|
|
581
|
-
try {
|
|
582
|
-
Seal.decrypt(XChaCha20Cipher, key, sealed, { aad: wrongUserId })
|
|
583
|
-
} catch (err) {
|
|
584
|
-
console.error(err.message)
|
|
585
|
-
// "ChaCha20Poly1305: authentication failed"
|
|
586
|
-
// Even though the ciphertext was not modified, the AAD mismatch is detected.
|
|
587
|
-
}
|
|
588
|
-
```
|
|
589
|
-
|
|
590
|
-
### Example 5: Encrypting and Decrypting Binary Data
|
|
591
|
-
|
|
592
|
-
The API works with raw bytes, not just text. Here is an example encrypting
|
|
593
|
-
arbitrary binary content.
|
|
594
|
-
|
|
595
|
-
```typescript
|
|
596
|
-
import { init, Seal, XChaCha20Cipher, randomBytes } from 'leviathan-crypto'
|
|
597
|
-
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
|
|
598
|
-
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
599
|
-
|
|
600
|
-
await init({ chacha20: chacha20Wasm, sha2: sha2Wasm })
|
|
601
|
-
|
|
602
|
-
const key = XChaCha20Cipher.keygen()
|
|
603
|
-
|
|
604
|
-
// Encrypt binary data (e.g., an image thumbnail, a protobuf, a file chunk)
|
|
605
|
-
const binaryData = new Uint8Array([0x89, 0x50, 0x4e, 0x47, /* ...more bytes... */])
|
|
606
|
-
const sealed = Seal.encrypt(XChaCha20Cipher, key, binaryData)
|
|
607
|
-
|
|
608
|
-
// Decrypt
|
|
609
|
-
const recovered = Seal.decrypt(XChaCha20Cipher, key, sealed)
|
|
610
|
-
// `recovered` is byte-identical to `binaryData`
|
|
611
|
-
```
|
|
612
|
-
|
|
613
|
-
### Example 6: Raw ChaCha20 Stream Cipher (Advanced)
|
|
614
|
-
|
|
615
|
-
Use this only if you are building a custom protocol and will add your own
|
|
616
|
-
authentication layer. For almost all use cases, use `Seal` with `XChaCha20Cipher` instead.
|
|
617
|
-
|
|
618
|
-
```typescript
|
|
619
|
-
import { init, ChaCha20, randomBytes } from 'leviathan-crypto'
|
|
620
|
-
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
|
|
621
|
-
|
|
622
|
-
await init({ chacha20: chacha20Wasm })
|
|
623
|
-
|
|
624
|
-
const key = randomBytes(32)
|
|
625
|
-
const nonce = randomBytes(12)
|
|
626
|
-
const cipher = new ChaCha20()
|
|
627
|
-
|
|
628
|
-
// Encrypt
|
|
629
|
-
cipher.beginEncrypt(key, nonce)
|
|
630
|
-
const ct1 = cipher.encryptChunk(new Uint8Array([1, 2, 3, 4]))
|
|
631
|
-
const ct2 = cipher.encryptChunk(new Uint8Array([5, 6, 7, 8]))
|
|
632
|
-
|
|
633
|
-
// Decrypt, uses the same key and nonce
|
|
634
|
-
cipher.beginDecrypt(key, nonce)
|
|
635
|
-
const pt1 = cipher.decryptChunk(ct1)
|
|
636
|
-
const pt2 = cipher.decryptChunk(ct2)
|
|
637
|
-
// pt1 = [1, 2, 3, 4], pt2 = [5, 6, 7, 8]
|
|
638
|
-
|
|
639
|
-
// WARNING: Without authentication, an attacker can flip bits in ciphertext
|
|
640
|
-
// and the corresponding plaintext bits will flip with no error.
|
|
641
|
-
// Pair with HMAC (Encrypt-then-MAC) or use XChaCha20Poly1305 instead.
|
|
642
|
-
|
|
643
|
-
cipher.dispose()
|
|
644
|
-
```
|
|
645
|
-
|
|
646
|
-
---
|
|
647
|
-
|
|
648
|
-
## Error Conditions
|
|
649
|
-
|
|
650
|
-
| Condition | Error Type | Message |
|
|
651
|
-
|-----------|-----------|---------|
|
|
652
|
-
| `init({ chacha20: ... })` not called before constructing a class | `Error` | `leviathan-crypto: call init({ chacha20: ... }) before using this class` |
|
|
653
|
-
| Key is not 32 bytes | `RangeError` | `ChaCha20 key must be 32 bytes (got N)` / `key must be 32 bytes (got N)` / `Poly1305 key must be 32 bytes (got N)` |
|
|
654
|
-
| `ChaCha20` nonce is not 12 bytes | `RangeError` | `ChaCha20 nonce must be 12 bytes (got N)` |
|
|
655
|
-
| `ChaCha20Poly1305` nonce is not 12 bytes | `RangeError` | `nonce must be 12 bytes (got N)` |
|
|
656
|
-
| `XChaCha20Poly1305` nonce is not 24 bytes | `RangeError` | `XChaCha20 nonce must be 24 bytes (got N)` |
|
|
657
|
-
| `ChaCha20Poly1305` ciphertext shorter than 16 bytes | `RangeError` | `ciphertext too short — must include 16-byte tag (got N)` |
|
|
658
|
-
| `XChaCha20Poly1305` ciphertext shorter than 16 bytes | `RangeError` | `ciphertext too short — must include 16-byte tag (got N)` |
|
|
659
|
-
| `ChaCha20Poly1305.encrypt()` called a second time | `Error` | Single-use encrypt guard. Create a new instance for each encryption. |
|
|
660
|
-
| Chunk or plaintext exceeds WASM buffer size | `RangeError` | `plaintext exceeds N bytes — split into smaller chunks` / `chunk exceeds maximum size of N bytes — split into smaller chunks` |
|
|
661
|
-
| Authentication tag does not match on decrypt | `Error` | `ChaCha20Poly1305: authentication failed` |
|
|
662
|
-
| Empty plaintext | | Allowed. Encrypting zero bytes produces just a 16-byte tag (AEAD) or zero bytes (raw ChaCha20). |
|
|
663
|
-
|
|
664
|
-
> ## Cross-References
|
|
665
|
-
>
|
|
666
|
-
> - [index](./README.md) — Project Documentation index
|
|
667
|
-
> - [lexicon](./lexicon.md) — Glossary of cryptographic terms
|
|
668
|
-
> - [asm_chacha](./asm_chacha.md) — WASM (AssemblyScript) implementation details for the chacha20 module
|
|
669
|
-
> - [authenticated encryption](./aead.md) — `Seal`, `SealStream`, `OpenStream`: use `XChaCha20Cipher` as the suite argument
|
|
670
|
-
> - [serpent](./serpent.md) — `SerpentCipher`: alternative `CipherSuite` for `Seal` and streaming
|
|
671
|
-
> - [sha2](./sha2.md) — SHA-2 hashes and HMAC. Needed for Encrypt-then-MAC if using raw ChaCha20
|
|
672
|
-
> - [types](./types.md) — `AEAD` and `Streamcipher` interfaces implemented by ChaCha20 classes
|
|
673
|
-
> - [architecture](./architecture.md) — architecture overview, module relationships, buffer layouts, and build pipeline
|
|
674
|
-
> - [chacha_audit](./chacha_audit.md) — XChaCha20-Poly1305 implementation audit
|