> ## Documentation Index
> Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# ChaCha20-Poly1305

> ChaCha20-Poly1305 authenticated encryption (RFC 8439)

<Card title="Try it Live" icon="play" href="https://playground.tevm.sh?example=crypto/chacha20poly1305.ts">
  Run ChaCha20-Poly1305 examples in the interactive playground
</Card>

## Overview

ChaCha20Poly1305 is an **authenticated encryption algorithm** combining ChaCha20 stream cipher with Poly1305 MAC, optimized for software implementations.

**Ethereum context**: **Not on Ethereum** - High-performance alternative to AES-GCM for encrypted communications and storage.

**Key advantages over AES-GCM:**

* Fast in software (no hardware requirements)
* Constant-time operations (side-channel resistant)
* Simpler implementation (easier to audit)
* Better mobile/embedded performance

**Use ChaCha20-Poly1305 when:**

* No AES hardware acceleration available
* Constant-time execution is critical
* Running on mobile/embedded devices
* Simplicity and auditability matter

## Status Note

**ChaCha20-Poly1305 is not yet implemented in Voltaire.** This documentation describes the specification and planned implementation based on RFC 8439.

For production use, consider:

* **AES-GCM** (currently implemented in Voltaire)
* **@noble/ciphers** - Pure TypeScript implementation
* **libsodium.js** - WebAssembly wrapper for libsodium

## Specification

**Standard:** RFC 8439 (June 2018)

**Parameters:**

* **Key size:** 256 bits (32 bytes) only
* **Nonce size:** 96 bits (12 bytes)
* **Tag size:** 128 bits (16 bytes)
* **Block size:** 64 bytes (ChaCha20)

**Algorithm:**

1. Encrypt plaintext with ChaCha20 stream cipher
2. Compute Poly1305 MAC over ciphertext and AAD
3. Output ciphertext + 16-byte authentication tag

## How It Works

### ChaCha20 Stream Cipher

ChaCha20 generates a pseudorandom keystream from:

* 256-bit key
* 96-bit nonce
* 32-bit counter (starts at 1)

**Key advantages:**

* Fast in software (bitwise operations)
* Constant-time (no table lookups)
* Designed by Daniel J. Bernstein

**Keystream generation:**

```
ChaCha20 Block:
  Input: key[32], nonce[12], counter[4]
  Output: 64-byte keystream block

  1. Initialize 4x4 matrix with constants, key, counter, nonce
  2. Apply 20 rounds of quarter-round function
  3. Add initial state to final state
  4. Output 64-byte block
```

### Poly1305 MAC

Poly1305 is a one-time authenticator:

* 256-bit one-time key (derived from ChaCha20)
* Processes message in 16-byte chunks
* Computes MAC using modular arithmetic (mod 2¹³⁰ - 5)

**Tag computation:**

```
Poly1305 MAC:
  Input: message, one-time-key[32]
  Output: 16-byte tag

  1. Derive r, s from one-time-key
  2. Accumulate message blocks: acc = (acc + block) * r mod (2^130 - 5)
  3. Add s to accumulator
  4. Output 16-byte tag
```

### Combined AEAD Construction

```
ChaCha20-Poly1305:
  Input: plaintext, key[32], nonce[12], aad
  Output: ciphertext || tag[16]

  1. Generate Poly1305 key from ChaCha20(key, nonce, counter=0)
  2. Encrypt plaintext with ChaCha20(key, nonce, counter=1...)
  3. Construct Poly1305 input:
     - AAD || padding
     - ciphertext || padding
     - len(AAD) || len(ciphertext) (8 bytes each, little-endian)
  4. Compute Poly1305 MAC with one-time key
  5. Output ciphertext || tag
```

## Planned API

```typescript theme={null}
import * as ChaCha20Poly1305 from '@tevm/voltaire/ChaCha20Poly1305';

// Generate key (256-bit only)
const key = ChaCha20Poly1305.generateKey();
console.log(key.length); // 32 bytes

// Generate nonce (96-bit)
const nonce = ChaCha20Poly1305.generateNonce();
console.log(nonce.length); // 12 bytes

// Encrypt
const plaintext = new TextEncoder().encode('Secret message');
const ciphertext = ChaCha20Poly1305.encrypt(plaintext, key, nonce);
console.log(ciphertext.length); // plaintext.length + 16 (tag)

// Decrypt
const decrypted = ChaCha20Poly1305.decrypt(ciphertext, key, nonce);
const message = new TextDecoder().decode(decrypted);
console.log(message); // "Secret message"

// With Additional Authenticated Data (AAD)
const aad = new TextEncoder().encode('metadata');
const ciphertextWithAAD = ChaCha20Poly1305.encrypt(plaintext, key, nonce, aad);
const decryptedWithAAD = ChaCha20Poly1305.decrypt(ciphertextWithAAD, key, nonce, aad);
```

## Security Properties

### Confidentiality

**IND-CPA Security:**

* Ciphertext reveals no plaintext information
* Requires unique nonces (never reuse!)
* 256-bit key provides strong security

**Resistance:**

* No known attacks better than brute-force (2²⁵⁶ operations)
* Post-quantum: Reduced to \~2¹²⁸ (Grover's algorithm)

### Authentication

**Unforgeability:**

* 128-bit authentication tag
* Poly1305 is provably secure (one-time MAC)
* Forgery probability: \~2⁻¹²⁸ per attempt

### Side-Channel Resistance

**Constant-Time Operations:**

* No secret-dependent branches
* No table lookups (unlike AES without hardware)
* Resistant to cache-timing attacks

**Why this matters:**

* AES (software): Vulnerable to cache-timing attacks
* ChaCha20: All operations constant-time by design

## Comparison with AES-GCM

|                   | ChaCha20-Poly1305  | AES-GCM                     |
| ----------------- | ------------------ | --------------------------- |
| **Standard**      | RFC 8439 (IETF)    | NIST SP 800-38D             |
| **Key Size**      | 256-bit only       | 128, 192, 256-bit           |
| **Nonce Size**    | 96-bit (12 bytes)  | 96-bit recommended          |
| **Tag Size**      | 128-bit (16 bytes) | 128-bit (can truncate)      |
| **Speed (HW)**    | Slower             | Faster (AES-NI)             |
| **Speed (SW)**    | Faster             | Slower                      |
| **Mobile**        | Excellent          | Good (if AES support)       |
| **Side-Channels** | Resistant          | Vulnerable (without AES-NI) |
| **Simplicity**    | Simpler            | More complex                |
| **Adoption**      | TLS 1.3, WireGuard | TLS, IPsec, widespread      |

**When to use ChaCha20-Poly1305:**

* Mobile/embedded systems
* Software-only environments
* Constant-time requirements
* Prefer simplicity/auditability

**When to use AES-GCM:**

* Hardware acceleration available (AES-NI)
* NIST compliance required
* Legacy system compatibility
* Slightly faster with hardware

## Nonce Management

**CRITICAL: Never reuse nonces!**

Same nonce reuse attack as AES-GCM:

* Exposes XOR of plaintexts
* Breaks authentication (Poly1305 key reuse)
* Complete security failure

**Safe nonce strategies:**

**1. Random nonces (default):**

```typescript theme={null}
const nonce = ChaCha20Poly1305.generateNonce();
```

**2. Counter-based:**

```typescript theme={null}
class NonceCounter {
  constructor() {
    this.counter = 0n;
  }

  next() {
    const nonce = new Uint8Array(12);
    const view = new DataView(nonce.buffer);
    view.setBigUint64(0, this.counter, false); // Big-endian
    this.counter++;

    if (this.counter >= (1n << 96n)) {
      throw new Error('Nonce space exhausted');
    }

    return nonce;
  }
}
```

**3. Hybrid (random + counter):**

```typescript theme={null}
// 4 bytes random prefix + 8 bytes counter
const prefix = crypto.getRandomValues(Bytes4());
const counter = 0n;

function generateNonce() {
  const nonce = new Uint8Array(12);
  nonce.set(prefix, 0);

  const view = new DataView(nonce.buffer, 4);
  view.setBigUint64(0, counter, false);

  counter++;
  return nonce;
}
```

## Security Considerations

### Critical Requirements

1. **Unique nonces:** Never reuse with same key
2. **Cryptographically secure random:** Use `crypto.getRandomValues()`
3. **Key protection:** Store keys securely (encrypted, HSM, KMS)
4. **Key rotation:** Rotate before 2⁴⁸ messages (random nonces)

### Nonce Collision Risk

**Random nonces:**

* 96-bit nonce space: 2⁹⁶ possible values
* Birthday paradox: \~50% collision after 2⁴⁸ messages
* Safe for: \<2³² messages per key (\~4 billion)

**Counter nonces:**

* No collisions (deterministic)
* Safe for: Up to 2⁹⁶ messages (practically unlimited)

### Common Vulnerabilities

**1. Nonce reuse:**

```typescript theme={null}
// DANGEROUS
const nonce = ChaCha20Poly1305.generateNonce();
const ct1 = encrypt(msg1, key, nonce);
const ct2 = encrypt(msg2, key, nonce); // BREAKS SECURITY!
```

**2. Predictable nonces:**

```typescript theme={null}
// WRONG - Timestamp alone
const nonce = new Uint8Array(12);
const view = new DataView(nonce.buffer);
view.setBigUint64(0, BigInt(Date.now()), false); // Predictable!
```

**3. Non-cryptographic random:**

```typescript theme={null}
// WRONG - Math.random()
const badNonce = new Uint8Array(12);
for (let i = 0; i < 12; i++) {
  badNonce[i] = Math.floor(Math.random() * 256); // NOT SECURE!
}
```

## Use Cases

### VPN/WireGuard

WireGuard uses ChaCha20-Poly1305 for:

* Fast encryption on all platforms
* Constant-time operations (security)
* Simple implementation (fewer bugs)

### TLS 1.3

ChaCha20-Poly1305 is mandatory cipher suite in TLS 1.3:

* `TLS_CHACHA20_POLY1305_SHA256`
* Used when AES hardware unavailable
* Better mobile performance

### Mobile Apps

Ideal for mobile encryption:

* Fast on ARM processors
* Low battery consumption
* Constant-time (security)

### Secure Messaging

Used by Signal, WhatsApp for:

* End-to-end encryption
* Fast message encryption
* Strong authentication

### Cryptocurrency Wallets

Encrypt private keys with user password:

* Derive key from password (PBKDF2/Argon2)
* Encrypt private key
* Store encrypted wallet

## Performance

### Throughput (typical)

**Desktop (Intel/AMD):**

* ChaCha20-Poly1305: \~1-2 GB/s (software)
* AES-GCM (AES-NI): \~3-5 GB/s (hardware)

**Mobile (ARM):**

* ChaCha20-Poly1305: \~500 MB/s - 1 GB/s
* AES-GCM (NEON): \~300 MB/s - 800 MB/s

**Embedded (no crypto HW):**

* ChaCha20-Poly1305: \~10-50 MB/s
* AES-GCM: \~5-20 MB/s

**Key insight:** ChaCha20-Poly1305 faster in software, AES-GCM faster with hardware.

## Implementation Status

**Current:** Not yet implemented in Voltaire

**Planned:**

* Pure TypeScript implementation
* WASM implementation (performance)
* Zig implementation (native library)

**Alternatives (available now):**

```typescript theme={null}
// @noble/ciphers - Pure TypeScript
import { chacha20poly1305 } from '@noble/ciphers/chacha';

const key = Bytes32();
crypto.getRandomValues(key);

const nonce = new Uint8Array(12);
crypto.getRandomValues(nonce);

const plaintext = new TextEncoder().encode('Secret');
const ciphertext = chacha20poly1305(key, nonce).encrypt(plaintext);

// libsodium.js - WebAssembly
import sodium from 'libsodium-wrappers';

await sodium.ready;

const key = sodium.crypto_aead_chacha20poly1305_ietf_keygen();
const nonce = sodium.randombytes_buf(sodium.crypto_aead_chacha20poly1305_IETF_NPUBBYTES);

const ciphertext = sodium.crypto_aead_chacha20poly1305_ietf_encrypt(
  plaintext,
  null, // AAD
  null,
  nonce,
  key
);
```

## RFC 8439 Test Vectors

### Test Vector 1: Basic Encryption

```typescript theme={null}
// Key (hex): 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f
// Nonce (hex): 070000004041424344454647
// Plaintext: "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."

const key = new Uint8Array([
  0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
  0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
  0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
  0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f
]);

const nonce = new Uint8Array([
  0x07, 0x00, 0x00, 0x00,
  0x40, 0x41, 0x42, 0x43,
  0x44, 0x45, 0x46, 0x47
]);

const plaintext = new TextEncoder().encode(
  "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."
);

const ciphertext = ChaCha20Poly1305.encrypt(plaintext, key, nonce);

// Expected ciphertext + tag (hex):
// d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b61161ae10b594f09e26a7e902ecbd0600691
```

### Test Vector 2: With AAD

```typescript theme={null}
// AAD (hex): 50515253c0c1c2c3c4c5c6c7

const aad = new Uint8Array([
  0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7
]);

const ciphertext = ChaCha20Poly1305.encrypt(plaintext, key, nonce, aad);
// Verify against RFC 8439 expected output
```

## Best Practices

### DO

✓ Use unique nonces for each encryption
✓ Use `crypto.getRandomValues()` for nonces/keys
✓ Store nonce with ciphertext (not secret)
✓ Rotate keys periodically (\<2⁴⁸ messages)
✓ Handle decryption errors gracefully
✓ Use strong passwords for key derivation
✓ Clear sensitive data from memory

### DON'T

✗ Never reuse nonces with same key
✗ Never use predictable nonces (timestamp only)
✗ Never use `Math.random()` for crypto
✗ Never store keys in plaintext
✗ Never ignore decryption errors
✗ Never exceed 2⁴⁸ messages per key
✗ Never commit keys to version control

## Error Handling

All ChaCha20Poly1305 functions throw typed errors that extend `CryptoError`:

| Error                   | Code                      | When                                                                      |
| ----------------------- | ------------------------- | ------------------------------------------------------------------------- |
| `InvalidKeyError`       | `INVALID_KEY`             | Key not 32 bytes                                                          |
| `InvalidNonceError`     | `INVALID_NONCE`           | Nonce not 12 bytes                                                        |
| `DecryptionError`       | `DECRYPTION_FAILED`       | Auth tag verification fails, wrong key/nonce/AAD, or ciphertext too short |
| `ChaCha20Poly1305Error` | `CHACHA20_POLY1305_ERROR` | Generic encryption failure                                                |

```typescript theme={null}
import * as ChaCha20Poly1305 from '@tevm/voltaire/ChaCha20Poly1305';
import { DecryptionError, InvalidNonceError, InvalidKeyError } from '@tevm/voltaire/ChaCha20Poly1305';

try {
  const decrypted = ChaCha20Poly1305.decrypt(ciphertext, key, nonce);
} catch (e) {
  if (e instanceof DecryptionError) {
    console.error('Authentication failed:', e.message);
    console.error('Code:', e.code); // "DECRYPTION_FAILED"
  } else if (e instanceof InvalidKeyError) {
    console.error('Invalid key:', e.message);
  }
}
```

All error classes have:

* `name` - Error class name (e.g., `"DecryptionError"`)
* `code` - Machine-readable error code
* `message` - Human-readable description
* `docsPath` - Link to relevant documentation

## References

* [RFC 8439 - ChaCha20 and Poly1305 for IETF Protocols](https://www.rfc-editor.org/rfc/rfc8439.html)
* [ChaCha20 and Poly1305 Paper (Bernstein)](https://cr.yp.to/chacha.html)
* [RFC 7539 - ChaCha20-Poly1305 Cipher Suites for TLS](https://www.rfc-editor.org/rfc/rfc7539.html)
* [@noble/ciphers - Pure TypeScript Implementation](https://github.com/paulmillr/noble-ciphers)
* [libsodium Documentation](https://doc.libsodium.org/)
* [WireGuard Protocol](https://www.wireguard.com/protocol/)

## Related

* [ChaCha20-Poly1305 (Effect)](https://voltaire-effect.tevm.sh/crypto/chacha20poly1305) - Effect.ts integration with Schema validation
