chacha20 - xero/leviathan-crypto GitHub Wiki
API reference for ChaCha20, Poly1305, ChaCha20Poly1305, XChaCha20Poly1305, and XChaCha20Cipher. Covers initialization, all class methods, usage examples, and error conditions.
ChaCha20 is a modern stream cipher designed by Daniel J. Bernstein. It is fast on all platforms (including those without hardware AES), resistant to timing attacks by design, and widely deployed in TLS, SSH, and WireGuard. ChaCha20 encrypts data by generating a pseudorandom keystream from a 256-bit key and a nonce, then XORing it with the plaintext. It does not provide authentication on its own. A modified message will decrypt to garbage with no warning.
Poly1305 is a one-time message authentication code (MAC). Given a unique 256-bit key and a message, it produces a 16-byte tag that proves the message has not been tampered with. The critical requirement is that each Poly1305 key is used exactly once. Reusing a key completely breaks its security. You almost never need to use Poly1305 directly; the AEAD constructions below handle key derivation for you.
ChaCha20-Poly1305 (RFC 8439) combines both primitives into an AEAD (Authenticated Encryption with Associated Data). It encrypts your data and produces an authentication tag in a single operation. On decryption, it verifies the tag before returning any plaintext. If someone tampered with the ciphertext, you get an error instead of corrupted data. The nonce is 96 bits (12 bytes).
XChaCha20-Poly1305 extends the nonce to 192 bits (24 bytes) using the HChaCha20
subkey derivation step. This makes random nonce generation completely safe. With a
24-byte nonce, the probability of a collision is negligible even after billions of
messages. For most users, Seal with XChaCha20Cipher
is the recommended choice. It produces a self-contained authenticated blob with
no nonce management, no instantiation, and no dispose(). For protocol interop
requiring explicit nonce control, use XChaCha20Poly1305 directly.
Important
Read this section before writing any code. These are not theoretical concerns. They are the mistakes that cause real-world breaches.
-
Use
SealwithXChaCha20Cipherunless you need explicit nonce control. It is the safest default: authenticated encryption in a single static call. If you are unsure which class to pick, pick this one. UseXChaCha20Poly1305when protocol interop requires you to manage nonces yourself. -
Never reuse a nonce with the same key. This is the single most important rule. If you encrypt two different messages with the same key and the same nonce, an attacker can XOR the two ciphertexts together and recover both plaintexts. With
ChaCha20Poly1305(12-byte nonce), random generation has a meaningful collision risk after roughly 2^32 messages under one key. WithXChaCha20Poly1305(24-byte nonce), random generation is safe for any practical message count. Just callrandomBytes(24)for each message. -
Poly1305 keys are single-use. Each Poly1305 key must be used to authenticate exactly one message. The AEAD classes (
ChaCha20Poly1305andXChaCha20Poly1305) handle this automatically by deriving a fresh Poly1305 key from the ChaCha20 keystream for each encryption. If you use the standalonePoly1305class directly, it is your responsibility to never reuse a key. -
AEAD protects both confidentiality and authenticity. If authentication fails during decryption, the plaintext is never returned. You get an error. This is intentional. Do not try to work around it. If decryption fails, the ciphertext was corrupted or tampered with.
-
Associated data (AAD) is authenticated but not encrypted. Use AAD for data that must travel in the clear (headers, routing metadata, user IDs) but must be verified as unmodified. If someone changes the AAD, decryption will fail even if the ciphertext itself is untouched.
-
Always call
dispose()when you are done. This wipes key material and intermediate state from WASM memory. Failing to calldispose()leaves sensitive data in memory longer than necessary. -
ChaCha20alone has no authentication. If you use the rawChaCha20class without pairing it with a MAC, an attacker can flip bits in the ciphertext and the corresponding bits in the plaintext will flip silently. Unless you are building your own authenticated construction (and you probably should not be), use one of the AEAD classes instead.
Each module subpath exports its own init function for consumers who want tree-shakeable imports.
Initializes only the chacha20 WASM binary. Equivalent to calling the
root init({ chacha20: chacha20Wasm }) but without pulling the other three
modules into the bundle.
Signature:
async function chacha20Init(source: WasmSource): Promise<void>Usage:
import { chacha20Init, XChaCha20Poly1305 } from 'leviathan-crypto/chacha20'
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
await chacha20Init(chacha20Wasm)
const aead = new XChaCha20Poly1305()All classes require calling await init({ chacha20: chacha20Wasm }) or the subpath chacha20Init()
before construction. If you construct a class before initialization, it throws:
Error: leviathan-crypto: call init({ chacha20: ... }) before using this class
Raw ChaCha20 stream cipher. No authentication. Use XChaCha20Poly1305 instead
unless you are building a custom protocol and understand the risks.
Caution
ChaCha20 is stateful and holds exclusive access to the chacha20 WASM
module for its entire lifetime. Constructing a second ChaCha20, or any
atomic chacha20 class (Poly1305, ChaCha20Poly1305, XChaCha20Poly1305,
or a XChaCha20Cipher-backed Seal/SealStream), while this instance is
live throws. Call dispose() when done. Pool workers are unaffected.
new ChaCha20()Throws if init({ chacha20: chacha20Wasm }) has not been called, or if
another stateful chacha20 instance is still live.
Prepares the cipher for encryption with the given key and nonce.
| Parameter | Type | Description |
|---|---|---|
key |
Uint8Array |
32 bytes (256 bits) |
nonce |
Uint8Array |
12 bytes (96 bits) |
Throws RangeError if key is not 32 bytes or nonce is not 12 bytes.
Note
Block counter convention. beginEncrypt() starts the ChaCha20 keystream
at block counter 1, not 0. Block 0 is reserved for Poly1305 one-time key
generation per RFC 8439 §2.6. The class is designed for use inside an AEAD
construction; if you need to match RFC 8439 §2.4.2 verbatim (keystream
starting at counter 0), use the lower-level primitives directly.
Encrypts a chunk of plaintext. Call repeatedly for streaming encryption. Returns
a new Uint8Array containing the ciphertext (same length as input).
| Parameter | Type | Description |
|---|---|---|
chunk |
Uint8Array |
Plaintext bytes (up to the module's chunk size limit) |
Throws RangeError if the chunk exceeds the maximum chunk size.
Prepares the cipher for decryption. Identical to beginEncrypt. ChaCha20 is
symmetric; encryption and decryption are the same XOR operation.
| Parameter | Type | Description |
|---|---|---|
key |
Uint8Array |
32 bytes (256 bits) |
nonce |
Uint8Array |
12 bytes (96 bits) |
Throws RangeError if key is not 32 bytes or nonce is not 12 bytes.
Decrypts a chunk of ciphertext. Returns a new Uint8Array containing the
plaintext (same length as input).
| Parameter | Type | Description |
|---|---|---|
chunk |
Uint8Array |
Ciphertext bytes |
Throws RangeError if the chunk exceeds the maximum chunk size.
Wipes all key material and intermediate state from WASM memory. Always call this when you are done with the instance.
After dispose(), all instance methods (beginEncrypt, encryptChunk,
beginDecrypt, decryptChunk) throw Error: ChaCha20: instance has been disposed. Disposal is permanent; construct a new instance if you need to
continue.
Standalone Poly1305 one-time MAC. Each key must be used exactly once. You
almost certainly want ChaCha20Poly1305 or XChaCha20Poly1305 instead. They
handle Poly1305 key derivation automatically.
new Poly1305()Throws if init({ chacha20: chacha20Wasm }) has not been called.
Computes a 16-byte Poly1305 authentication tag over the given message.
| Parameter | Type | Description |
|---|---|---|
key |
Uint8Array |
32 bytes. Must be unique per message. |
msg |
Uint8Array |
The message to authenticate (any length) |
Returns Uint8Array: a 16-byte authentication tag.
Throws RangeError if key is not 32 bytes.
Wipes all key material and intermediate state from WASM memory.
ChaCha20-Poly1305 AEAD as specified in RFC 8439. Provides authenticated encryption with a 12-byte (96-bit) nonce. The Poly1305 one-time key is derived automatically from the ChaCha20 keystream (counter 0).
If you are generating nonces randomly, prefer XChaCha20Poly1305 (24-byte nonce)
to avoid collision risk.
new ChaCha20Poly1305()Throws if init({ chacha20: chacha20Wasm }) has not been called.
Encrypts plaintext and returns the ciphertext with the 16-byte Poly1305 tag appended.
Warning
Each ChaCha20Poly1305 instance allows only one encrypt() call. A second
call throws to prevent accidental nonce reuse. Create a new instance for each
encryption, or use Seal with XChaCha20Cipher for automatic nonce management.
Strict single-use on any throw. Any throw from encrypt(), including
RangeError from wrong-length key or nonce, exclusivity errors, or any
failure inside the WASM call, is terminal. The guard is set before
argument validation, so a subsequent encrypt() on the same instance throws
the single-use error rather than retrying. Always allocate a new AEAD
instance per message.
| Parameter | Type | Default | Description |
|---|---|---|---|
key |
Uint8Array |
32 bytes (256-bit key) | |
nonce |
Uint8Array |
12 bytes (96-bit nonce) | |
plaintext |
Uint8Array |
Data to encrypt (up to the module's chunk size limit) | |
aad |
Uint8Array |
new Uint8Array(0) |
Associated data. Authenticated but not encrypted. |
Returns Uint8Array: ciphertext + 16-byte tag (length = plaintext.length + 16).
Throws:
-
RangeErrorifkeyis not 32 bytes (terminal: instance locked) -
RangeErrorifnonceis not 12 bytes (terminal: instance locked) -
RangeErrorifplaintextexceeds the maximum chunk size (terminal: instance locked) -
Errorifencrypt()has already been called on this instance
Verifies the authentication tag and decrypts the ciphertext. The ciphertext
parameter must include the appended 16-byte tag (i.e., the exact output of
encrypt()). If authentication fails, an error is thrown and no plaintext is
returned.
Tag comparison uses a constant-time XOR-accumulate pattern; no timing side channel leaks whether the tag was "close" to correct.
| Parameter | Type | Default | Description |
|---|---|---|---|
key |
Uint8Array |
32 bytes (same key used for encryption) | |
nonce |
Uint8Array |
12 bytes (same nonce used for encryption) | |
ciphertext |
Uint8Array |
Encrypted data with appended tag (output of encrypt()) |
|
aad |
Uint8Array |
new Uint8Array(0) |
Associated data (must match what was passed to encrypt()) |
Returns Uint8Array: the decrypted plaintext.
Throws:
-
RangeErrorifkeyis not 32 bytes -
RangeErrorifnonceis not 12 bytes -
RangeErrorifciphertextis shorter than 16 bytes (no room for a tag) -
Error('ChaCha20Poly1305: authentication failed')if the tag does not match
Wipes all key material and intermediate state from WASM memory.
XChaCha20-Poly1305 AEAD (draft-irtf-cfrg-xchacha). RFC-faithful stateless
primitive. Key and nonce are passed per-call. Use when protocol interop
requires explicit nonce control. For most use cases, prefer Seal
with XChaCha20Cipher(automatic nonce management, no instantiation).
It uses a 24-byte (192-bit) nonce, which is large enough that randomly generated
nonces will never collide in practice. Internally, it derives a subkey via
HChaCha20 and delegates to ChaCha20Poly1305.
Like ChaCha20Poly1305, the encrypt() method returns a single Uint8Array
with the tag appended to the ciphertext. The decrypt() method expects this
combined format and splits it internally.
new XChaCha20Poly1305()Throws if init({ chacha20: chacha20Wasm }) has not been called.
Encrypts plaintext and returns the ciphertext with the 16-byte tag appended.
Warning
Same single-use contract as ChaCha20Poly1305.encrypt(). Each instance
allows only one encrypt() call. A second call throws to prevent
accidental nonce reuse. Create a new instance for each encryption, or use
Seal with XChaCha20Cipher for automatic nonce management.
Strict single-use on any throw. Any throw from encrypt() is terminal,
whether the cause is a RangeError from wrong-length key or nonce, an
exclusivity error, or a failure inside the WASM call. The guard is set
before argument validation, so a subsequent encrypt() on the same instance
throws the single-use error rather than retrying with a reused nonce. Always
allocate a new AEAD instance per message.
| Parameter | Type | Default | Description |
|---|---|---|---|
key |
Uint8Array |
32 bytes (256-bit key) | |
nonce |
Uint8Array |
24 bytes (192-bit nonce) | |
plaintext |
Uint8Array |
Data to encrypt | |
aad |
Uint8Array |
new Uint8Array(0) |
Associated data. Authenticated but not encrypted. |
Returns Uint8Array: ciphertext + 16-byte tag (length = plaintext.length + 16).
Throws:
-
RangeErrorifkeyis not 32 bytes (terminal: instance locked) -
RangeErrorifnonceis not 24 bytes (terminal: instance locked) -
RangeErrorifplaintextexceeds the maximum chunk size (terminal: instance locked) -
Errorifencrypt()has already been called on this instance
Verifies the authentication tag and decrypts the ciphertext. The ciphertext
parameter must include the appended 16-byte tag (i.e., the exact output of
encrypt()).
| Parameter | Type | Default | Description |
|---|---|---|---|
key |
Uint8Array |
32 bytes (same key used for encryption) | |
nonce |
Uint8Array |
24 bytes (same nonce used for encryption) | |
ciphertext |
Uint8Array |
Encrypted data with appended tag (output of encrypt()) |
|
aad |
Uint8Array |
new Uint8Array(0) |
Associated data (must match what was passed to encrypt()) |
Returns Uint8Array: the decrypted plaintext.
Throws:
-
RangeErrorifkeyis not 32 bytes -
RangeErrorifnonceis not 24 bytes -
RangeErrorifciphertextis shorter than 16 bytes (no room for a tag) -
Error('ChaCha20Poly1305: authentication failed')if the tag does not match
Wipes all key material and intermediate state from WASM memory.
ChaCha20 block-function PRF for Fortuna's generator slot (RFC 8439 §2.3).
Uses a fixed zero nonce; Fortuna's counter is the only varying input.
Implements the Generator interface. Plain const object — no instantiation,
no dispose().
Requires init({ chacha20: chacha20Wasm }). See fortuna.md
for full usage with Fortuna.create().
| Property | Value |
|---|---|
keySize |
32 |
blockSize |
64 |
counterSize |
4 |
wasmModules |
['chacha20'] |
Produces n bytes from (key, counter). Neither input is mutated. Wipes WASM
key/state/keystream scratch before returning.
| Parameter | Type | Description |
|---|---|---|
key |
Uint8Array |
32 bytes |
counter |
Uint8Array |
4 bytes, read as a little-endian u32 |
n |
number |
Output byte count: 0 ≤ n ≤ 2³⁰ |
Returns a new Uint8Array of length n.
Throws:
-
RangeError('ChaCha20Generator: key must be 32 bytes (got N)')if key length ≠ 32 -
RangeError('ChaCha20Generator: counter must be 4 bytes (got N)')if counter length ≠ 4 -
RangeError('ChaCha20Generator: n must be a non-negative safe integer <= 2^30 (got N)')if n is out of range -
Errorif another stateful instance currently owns thechacha20WASM module
import { init, Fortuna } from 'leviathan-crypto'
import { ChaCha20Generator } from 'leviathan-crypto/chacha20'
import { SHA256Hash } from 'leviathan-crypto/sha2'
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
await init({ chacha20: chacha20Wasm, sha2: sha2Wasm })
const rng = await Fortuna.create({ generator: ChaCha20Generator, hash: SHA256Hash })
const bytes = rng.get(32)
rng.stop()CipherSuite implementation for XChaCha20-Poly1305. Pass to Seal,
SealStream, or OpenStream. Never instantiated directly.
Requires init({ chacha20: chacha20Wasm, sha2: sha2Wasm }).
Note
sha2 is required by the stream layer for HKDF key derivation, not by
XChaCha20Poly1305 itself.
| Property | Value |
|---|---|
formatEnum |
0x01 |
keySize |
32 |
tagSize |
16 (Poly1305) |
padded |
false |
wasmModules |
['chacha20'] |
Returns randomBytes(32). Convenience method. Not on the CipherSuite interface.
import { init, Seal, XChaCha20Cipher } from 'leviathan-crypto'
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
await init({ chacha20: chacha20Wasm, sha2: sha2Wasm })
const key = XChaCha20Cipher.keygen()
const blob = Seal.encrypt(XChaCha20Cipher, key, plaintext)
const pt = Seal.decrypt(XChaCha20Cipher, key, blob) // throws on tamperimport { SealStream, OpenStream } from 'leviathan-crypto/stream'
import { XChaCha20Cipher } from 'leviathan-crypto/chacha20'
const sealer = new SealStream(XChaCha20Cipher, key)
const preamble = sealer.preamble // 20 bytes, send before first chunk
const ct0 = sealer.push(chunk0)
const ctLast = sealer.finalize(lastChunk)
const opener = new OpenStream(XChaCha20Cipher, key, preamble)
const pt0 = opener.pull(ct0)
const ptLast = opener.finalize(ctLast)See aead.md for the full Seal, SealStream, and OpenStream API.
One-shot AEAD. No instantiation, no dispose(). The blob format is
preamble(20) || ciphertext || tag(16).
import { init, Seal, XChaCha20Cipher, utf8ToBytes, bytesToUtf8 } from 'leviathan-crypto'
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
await init({ chacha20: chacha20Wasm, sha2: sha2Wasm })
const key = XChaCha20Cipher.keygen()
const blob = Seal.encrypt(XChaCha20Cipher, key, utf8ToBytes('Hello, world!'))
const pt = Seal.decrypt(XChaCha20Cipher, key, blob) // throws on tamper
console.log(bytesToUtf8(pt)) // "Hello, world!"
// Optional: bind metadata without encrypting it (AAD)
const aad = utf8ToBytes('document-v2')
const blob2 = Seal.encrypt(XChaCha20Cipher, key, utf8ToBytes('Hello, world!'), { aad })
const pt2 = Seal.decrypt(XChaCha20Cipher, key, blob2, { aad })For explicit nonce control, use
XChaCha20Poly1305directly. Same API shape, 24-byte nonce passed per call. See Example 2.
Same idea as above, but with a 12-byte nonce. Use this if you are implementing a protocol that specifies RFC 8439 ChaCha20-Poly1305 explicitly.
Both ChaCha20Poly1305 and XChaCha20Poly1305 return a single Uint8Array
with the tag appended, and decrypt() expects the same combined format.
The only difference is the nonce size (12 vs 24 bytes).
Note
Each ChaCha20Poly1305 instance allows only one encrypt() call. A second
call throws to prevent accidental nonce reuse. Create a new instance for each
encryption.
import { init, ChaCha20Poly1305, randomBytes, utf8ToBytes, bytesToUtf8 } from 'leviathan-crypto'
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
await init({ chacha20: chacha20Wasm })
const key = randomBytes(32)
const aead = new ChaCha20Poly1305()
// Encrypt
const nonce = randomBytes(12) // 12 bytes, use XChaCha20Poly1305 for high-volume random generation
const plaintext = utf8ToBytes('Sensitive data')
const sealed = aead.encrypt(key, nonce, plaintext)
// sealed = ciphertext || tag(16), store/transmit nonce and sealed together
// Decrypt (new instance, encrypt is single-use)
const aead2 = new ChaCha20Poly1305()
const decrypted = aead2.decrypt(key, nonce, sealed)
console.log(bytesToUtf8(decrypted)) // "Sensitive data"
aead.dispose()
aead2.dispose()AEAD decryption fails loudly if anyone has modified the ciphertext or the associated data. This is a feature. It prevents you from processing corrupted or maliciously altered data.
import { init, Seal, XChaCha20Cipher, randomBytes, utf8ToBytes } from 'leviathan-crypto'
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
await init({ chacha20: chacha20Wasm, sha2: sha2Wasm })
const key = XChaCha20Cipher.keygen()
const sealed = Seal.encrypt(XChaCha20Cipher, key, utf8ToBytes('Original message'))
// Simulate tampering: flip one bit in the ciphertext
const tampered = new Uint8Array(sealed)
tampered[20] ^= 0x01 // byte 20 is the first ciphertext byte (after the 20-byte preamble)
try {
const plaintext = Seal.decrypt(XChaCha20Cipher, key, tampered)
// This line is never reached
console.log(plaintext)
} catch (err) {
console.error(err.message)
// "ChaCha20Poly1305: authentication failed"
// The plaintext is never returned. Decryption stops immediately on failure.
}Associated data is metadata that you want to authenticate (prove unmodified) but not encrypt. Common uses: user IDs, message sequence numbers, protocol version headers, routing information.
import { init, Seal, XChaCha20Cipher, randomBytes, utf8ToBytes, bytesToUtf8 } from 'leviathan-crypto'
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
await init({ chacha20: chacha20Wasm, sha2: sha2Wasm })
const key = XChaCha20Cipher.keygen()
// The user ID travels in the clear, but decryption will fail if anyone changes it
const userId = utf8ToBytes('user-12345')
const message = utf8ToBytes('Your account balance is $1,000,000')
const sealed = Seal.encrypt(XChaCha20Cipher, key, message, { aad: userId })
// Decrypt, pass the same AAD
const decrypted = Seal.decrypt(XChaCha20Cipher, key, sealed, { aad: userId })
console.log(bytesToUtf8(decrypted))
// "Your account balance is $1,000,000"
// If someone changes the AAD, decryption fails
const wrongUserId = utf8ToBytes('user-99999')
try {
Seal.decrypt(XChaCha20Cipher, key, sealed, { aad: wrongUserId })
} catch (err) {
console.error(err.message)
// "ChaCha20Poly1305: authentication failed"
// Even though the ciphertext was not modified, the AAD mismatch is detected.
}The API works with raw bytes, not just text. Here is an example encrypting arbitrary binary content.
import { init, Seal, XChaCha20Cipher, randomBytes } from 'leviathan-crypto'
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
await init({ chacha20: chacha20Wasm, sha2: sha2Wasm })
const key = XChaCha20Cipher.keygen()
// Encrypt binary data (e.g., an image thumbnail, a protobuf, a file chunk)
const binaryData = new Uint8Array([0x89, 0x50, 0x4e, 0x47, /* ...more bytes... */])
const sealed = Seal.encrypt(XChaCha20Cipher, key, binaryData)
// Decrypt
const recovered = Seal.decrypt(XChaCha20Cipher, key, sealed)
// `recovered` is byte-identical to `binaryData`Use this only if you are building a custom protocol and will add your own
authentication layer. For almost all use cases, use Seal with XChaCha20Cipher instead.
import { init, ChaCha20, randomBytes } from 'leviathan-crypto'
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
await init({ chacha20: chacha20Wasm })
const key = randomBytes(32)
const nonce = randomBytes(12)
const cipher = new ChaCha20()
// Encrypt
cipher.beginEncrypt(key, nonce)
const ct1 = cipher.encryptChunk(new Uint8Array([1, 2, 3, 4]))
const ct2 = cipher.encryptChunk(new Uint8Array([5, 6, 7, 8]))
// Decrypt, uses the same key and nonce
cipher.beginDecrypt(key, nonce)
const pt1 = cipher.decryptChunk(ct1)
const pt2 = cipher.decryptChunk(ct2)
// pt1 = [1, 2, 3, 4], pt2 = [5, 6, 7, 8]
// WARNING: Without authentication, an attacker can flip bits in ciphertext
// and the corresponding plaintext bits will flip with no error.
// Use the seal api or XChaCha20Poly1305 instead.
cipher.dispose()| Condition | Error Type | Message |
|---|---|---|
init({ chacha20: ... }) not called before constructing a class |
Error |
leviathan-crypto: call init({ chacha20: ... }) before using this class |
| 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)
|
ChaCha20 nonce is not 12 bytes |
RangeError |
ChaCha20 nonce must be 12 bytes (got N) |
ChaCha20Poly1305 nonce is not 12 bytes |
RangeError |
nonce must be 12 bytes (got N) |
XChaCha20Poly1305 nonce is not 24 bytes |
RangeError |
XChaCha20 nonce must be 24 bytes (got N) |
ChaCha20Poly1305 ciphertext shorter than 16 bytes |
RangeError |
ciphertext too short — must include 16-byte tag (got N) |
XChaCha20Poly1305 ciphertext shorter than 16 bytes |
RangeError |
ciphertext too short — must include 16-byte tag (got N) |
ChaCha20Poly1305.encrypt() called a second time |
Error |
Single-use encrypt guard. Create a new instance for each encryption. |
| 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
|
| Authentication tag does not match on decrypt | Error |
ChaCha20Poly1305: authentication failed |
| Empty plaintext | Allowed. Encrypting zero bytes produces just a 16-byte tag (AEAD) or zero bytes (raw ChaCha20). | |
ChaCha20Generator.generate() key ≠ 32 bytes |
RangeError |
ChaCha20Generator: key must be 32 bytes (got N) |
ChaCha20Generator.generate() counter ≠ 4 bytes |
RangeError |
ChaCha20Generator: counter must be 4 bytes (got N) |
ChaCha20Generator.generate() n out of range |
RangeError |
ChaCha20Generator: n must be a non-negative safe integer <= 2^30 (got N) |
| Document | Description |
|---|---|
| index | Project Documentation index |
| lexicon | Glossary of cryptographic terms |
| asm_chacha | WASM (AssemblyScript) implementation details for the chacha20 module |
| authenticated encryption |
Seal, SealStream, OpenStream: use XChaCha20Cipher as the suite argument |
| serpent |
SerpentCipher: alternative CipherSuite for Seal and streaming |
| sha2 | SHA-2 hashes and HMAC. Needed for Encrypt-then-MAC if using raw ChaCha20 |
| types |
AEAD and Streamcipher interfaces implemented by ChaCha20 classes |
| architecture | architecture overview, module relationships, buffer layouts, and build pipeline |
| chacha_audit | XChaCha20-Poly1305 implementation audit |