ChaCha20-Poly1305 is an authenticated encryption (AEAD) algorithm that combines the ChaCha20 stream cipher with the Poly1305 authenticator, offering high security and performance.

What is ChaCha20-Poly1305?

ChaCha20-Poly1305 is an AEAD algorithm that provides both confidentiality and authentication in a single operation, being faster than AES-GCM on many platforms.

Components

ChaCha20

  • Stream Cipher: Stream cipher
  • ChaCha: Salsa20 variant
  • 256-bit key: 256-bit key
  • 96-bit nonce: 96-bit nonce
  • 512-bit state: 512-bit state

Poly1305

  • MAC: Message authentication code
  • One-time key: One-time key
  • 130-bit security: 130-bit security
  • Fast: Very fast
  • Secure: Cryptographically secure

Main Characteristics

Security

  • AEAD: Authenticated encryption
  • Nonce reuse: Resistant to nonce reuse
  • Fast: High performance
  • Modern: Modern algorithm

Performance

  • Software optimized: Software optimized
  • No hardware acceleration: No hardware acceleration required
  • Parallelizable: Parallelizable
  • Efficient: Very efficient

Compatibility

  • TLS 1.3: Native support
  • OpenSSL: Complete implementation
  • libsodium: Modern library
  • Wide support: Wide support

ChaCha20 Algorithm

Internal State

state = [
    constants[0], constants[1], constants[2], constants[3],
    key[0],       key[1],       key[2],       key[3],
    key[4],       key[5],       key[6],       key[7],
    counter,      nonce[0],     nonce[1],     nonce[2]
]

Quarter Round Function

1
2
3
4
5
6
def quarter_round(a, b, c, d):
    a += b; d ^= a; d = ((d << 16) | (d >> 16)) & 0xffffffff
    c += d; b ^= c; b = ((b << 12) | (b >> 20)) & 0xffffffff
    a += b; d ^= a; d = ((d << 8)  | (d >> 24)) & 0xffffffff
    c += d; b ^= c; b = ((b << 7)  | (b >> 25)) & 0xffffffff
    return a, b, c, d

ChaCha20 Function

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def chacha20_block(key, nonce, counter):
    # Initialize state
    state = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]  # constants
    state.extend(key)  # key (8 words)
    state.extend([counter, nonce[0], nonce[1], nonce[2]])  # counter + nonce
    
    # 20 rounds (10 double rounds)
    working_state = state[:]
    for i in range(10):
        # Column rounds
        working_state[0], working_state[4], working_state[8],  working_state[12] = quarter_round(working_state[0], working_state[4], working_state[8],  working_state[12])
        working_state[1], working_state[5], working_state[9],  working_state[13] = quarter_round(working_state[1], working_state[5], working_state[9],  working_state[13])
        working_state[2], working_state[6], working_state[10], working_state[14] = quarter_round(working_state[2], working_state[6], working_state[10], working_state[14])
        working_state[3], working_state[7], working_state[11], working_state[15] = quarter_round(working_state[3], working_state[7], working_state[11], working_state[15])
        
        # Diagonal rounds
        working_state[0], working_state[5], working_state[10], working_state[15] = quarter_round(working_state[0], working_state[5], working_state[10], working_state[15])
        working_state[1], working_state[6], working_state[11], working_state[12] = quarter_round(working_state[1], working_state[6], working_state[11], working_state[12])
        working_state[2], working_state[7], working_state[8],  working_state[13] = quarter_round(working_state[2], working_state[7], working_state[8],  working_state[13])
        working_state[3], working_state[4], working_state[9],  working_state[14] = quarter_round(working_state[3], working_state[4], working_state[9],  working_state[14])
    
    # Add original state
    for i in range(16):
        working_state[i] = (working_state[i] + state[i]) & 0xffffffff
    
    return working_state

Poly1305 Algorithm

Poly1305 Function

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def poly1305(key, message):
    # 256-bit key
    r = key[:16]  # 128 bits
    s = key[16:]  # 128 bits
    
    # Mask r
    r[3] &= 0x0f
    r[7] &= 0x0f
    r[11] &= 0x0f
    r[15] &= 0x0f
    r[4] &= 0xfc
    r[8] &= 0xfc
    r[12] &= 0xfc
    
    # Convert to integers
    r_int = int.from_bytes(r, 'little')
    s_int = int.from_bytes(s, 'little')
    
    # Process message in 16-byte blocks
    accumulator = 0
    p = (1 << 130) - 5  # 2^130 - 5
    
    for i in range(0, len(message), 16):
        block = message[i:i+16]
        if len(block) < 16:
            block += b'\x01'  # Padding
            block += b'\x00' * (16 - len(block))
        
        n = int.from_bytes(block, 'little')
        accumulator = (accumulator + n) * r_int % p
    
    accumulator = (accumulator + s_int) % (1 << 128)
    
    return accumulator.to_bytes(16, 'little')

ChaCha20-Poly1305 Implementation

Python

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import os
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305

def encrypt_chacha20_poly1305(key, nonce, plaintext, associated_data=b""):
    """Encrypt with ChaCha20-Poly1305"""
    cipher = ChaCha20Poly1305(key)
    ciphertext = cipher.encrypt(nonce, plaintext, associated_data)
    return ciphertext

def decrypt_chacha20_poly1305(key, nonce, ciphertext, associated_data=b""):
    """Decrypt with ChaCha20-Poly1305"""
    cipher = ChaCha20Poly1305(key)
    plaintext = cipher.decrypt(nonce, ciphertext, associated_data)
    return plaintext

# Usage example
key = os.urandom(32)  # 256 bits
nonce = os.urandom(12)  # 96 bits
plaintext = b"Hello, World!"
associated_data = b"metadata"

# Encrypt
ciphertext = encrypt_chacha20_poly1305(key, nonce, plaintext, associated_data)
print(f"Ciphertext: {ciphertext.hex()}")

# Decrypt
decrypted = decrypt_chacha20_poly1305(key, nonce, ciphertext, associated_data)
print(f"Decrypted: {decrypted}")

OpenSSL

1
2
3
4
5
# Encrypt file
openssl enc -chacha20-poly1305 -in plaintext.txt -out ciphertext.bin -K $(hexdump -v -e '/1 "%02x"' < key.bin) -iv $(hexdump -v -e '/1 "%02x"' < nonce.bin)

# Decrypt file
openssl enc -chacha20-poly1305 -d -in ciphertext.bin -out decrypted.txt -K $(hexdump -v -e '/1 "%02x"' < key.bin) -iv $(hexdump -v -e '/1 "%02x"' < nonce.bin)

JavaScript (Node.js)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const crypto = require('crypto');

function encryptChaCha20Poly1305(key, nonce, plaintext, associatedData = Buffer.alloc(0)) {
    const cipher = crypto.createCipher('chacha20-poly1305', key);
    cipher.setAAD(associatedData);
    cipher.setAutoPadding(false);
    
    let ciphertext = cipher.update(plaintext);
    ciphertext = Buffer.concat([ciphertext, cipher.final()]);
    
    const tag = cipher.getAuthTag();
    
    return Buffer.concat([ciphertext, tag]);
}

function decryptChaCha20Poly1305(key, nonce, ciphertext, associatedData = Buffer.alloc(0)) {
    const tag = ciphertext.slice(-16);
    const encrypted = ciphertext.slice(0, -16);
    
    const decipher = crypto.createDecipher('chacha20-poly1305', key);
    decipher.setAAD(associatedData);
    decipher.setAuthTag(tag);
    
    let plaintext = decipher.update(encrypted);
    plaintext = Buffer.concat([plaintext, decipher.final()]);
    
    return plaintext;
}

Applications

TLS/SSL

  • TLS 1.3: Native cipher suite
  • Chrome: Full support
  • Firefox: Full support
  • OpenSSL: Complete implementation

VPN

  • WireGuard: Modern VPN protocol
  • OpenVPN: Encryption option
  • IPsec: Encryption suite
  • Custom VPN: Custom implementations

Communications

  • Signal: Secure messaging
  • WhatsApp: Message encryption
  • Telegram: Message encryption
  • Matrix: Messaging protocol

Storage

  • Disk Encryption: Disk encryption
  • File Encryption: File encryption
  • Database Encryption: Database encryption
  • Backup Encryption: Backup encryption

Advantages over AES-GCM

Performance

  • Software: Faster in software
  • No hardware: No hardware acceleration required
  • Parallelizable: Better parallelization
  • Efficient: More efficient

Security

  • Nonce reuse: More resistant to reuse
  • Side-channel: Less vulnerable to side-channel attacks
  • Modern: More modern algorithm
  • Proven: Well proven

Implementation

  • Simple: Simpler implementation
  • Portable: More portable
  • Fast: Faster
  • Secure: Equally secure

Comparison with Other Algorithms

ChaCha20-Poly1305 vs AES-GCM

  • Performance: ChaCha20-Poly1305 faster in software
  • Hardware: AES-GCM faster with hardware acceleration
  • Security: Both equally secure
  • Adoption: AES-GCM more adopted

ChaCha20-Poly1305 vs ChaCha20 + HMAC

  • AEAD: ChaCha20-Poly1305 is native AEAD
  • Performance: ChaCha20-Poly1305 faster
  • Security: ChaCha20-Poly1305 more secure
  • Simplicity: ChaCha20-Poly1305 simpler

Best Practices

Key Generation

  • 256 bits: Use 256-bit keys
  • Random: Generate random keys
  • Secure: Use secure generators
  • Unique: Unique keys per session

Nonce Management

  • 96 bits: 96-bit nonce
  • Unique: Unique nonce per message
  • Random: Generate random nonce
  • Counter: Use counter if possible

Associated Data

  • Metadata: Include metadata
  • Headers: Include headers
  • Context: Include context
  • Consistent: Be consistent
  • AES - Alternative symmetric algorithm
  • TLS/SSL - Protocol that uses ChaCha20-Poly1305
  • Hash Functions - Algorithms that complement ChaCha20-Poly1305
  • Cryptanalysis - Analysis of ChaCha20-Poly1305
  • CISO - Role that oversees ChaCha20-Poly1305
  • General Cybersecurity - Discipline that includes ChaCha20-Poly1305
  • Security Breaches - Incidents that affect ChaCha20-Poly1305
  • Attack Vectors - Attacks against ChaCha20-Poly1305
  • Incident Response - Process that includes ChaCha20-Poly1305
  • SIEM - System that monitors ChaCha20-Poly1305
  • SOAR - Automation that manages ChaCha20-Poly1305
  • EDR - Tool that protects ChaCha20-Poly1305
  • Firewall - Device that can inspect ChaCha20-Poly1305
  • VPN - Connection that uses ChaCha20-Poly1305
  • Dashboards - Visualization of ChaCha20-Poly1305 metrics
  • Logs - ChaCha20-Poly1305 operation logs

References