ChaCha20-Poly1305 mock in Python

Simulates ChaCha20-Poly1305 AEAD encryption and authentication using SHA-256 as a deterministic keystream and tag generator.

Medium Python 3.9+ Aug 9, 2026 Auth & security at scale 14 views 0 copies

Python code

38 lines
Python 3.9+
from hashlib import sha256
import struct

def chacha20_block(key, counter, nonce):
    """Mock ChaCha20 block: deterministic pseudo-random keystream from key+counter+nonce."""
    state_input = key + struct.pack("<I", counter) + nonce + b"ChaCha20"
    return sha256(state_input).digest()[:64]  # 64-byte keystream block

def chacha20_encrypt(key, nonce, plaintext):
    """Encrypt plaintext by XORing with keystream blocks (mock ChaCha20)."""
    ciphertext = b""
    counter = 0
    for i in range(0, len(plaintext), 64):
        keystream = chacha20_block(key, counter + i // 64, nonce)
        chunk = plaintext[i:i+64]
        ciphertext += bytes(a ^ b for a, b in zip(chunk, keystream))
    return ciphertext

def poly1305_tag(key, message):
    """Mock Poly1305: deterministic 16-byte tag using HMAC-style hashing."""
    auth_key = sha256(key + b"poly1305").digest()
    return sha256(auth_key + message).digest()[:16]

def aead_encrypt(key, nonce, plaintext, aad):
    """ChaCha20-Poly1305 mock: encrypt then authenticate ciphertext and AAD."""
    ciphertext = chacha20_encrypt(key, nonce, plaintext)
    tag = poly1305_tag(key, aad + ciphertext)
    return ciphertext, tag

if __name__ == "__main__":
    key = b"0123456789abcdef"  # 16-byte key
    nonce = b"abcdefghijkl"    # 12-byte nonce
    plaintext = b"hello world"
    aad = b"metadata header"

    ct, tag = aead_encrypt(key, nonce, plaintext, aad)
    print("ciphertext:", ct.hex())
    print("tag:", tag.hex())

Output

stdout
ciphertext: 7b7e3d1f0a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b

tag: 1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f

How it works

This is a mock implementation for learning purposes only—it does not implement the actual ChaCha20 stream cipher or Poly1305 MAC. It substitutes SHA-256 to produce deterministic pseudo-random keystream and tag outputs, so encryption/authentication are conceptually demonstrated but not cryptographically secure. The chacha20_block function derives a keystream block from the key, counter, and nonce, while poly1305_tag derives a tag from the key, message, and a constant. The aead_encrypt function combines encryption and authentication to mimic an AEAD operation. Use the cryptography library or PyCryptodome for production-grade ChaCha20-Poly1305.

Common mistakes

  • Using this mock in production believing it is secure.
  • Reusing the same nonce with the same key.
  • Not including the AAD in the authentication tag.

Variations

  1. Use `cryptography.hazmat.primitives.ciphers.aead.ChaCha20Poly1305` for a real, tested implementation.
  2. Implement actual ChaCha20 quarter-round functions and Poly1305 polynomial multiplication for a reference-compliant version.

Real-world use cases

  • Demoing AEAD concepts in a security training or code sample without external dependencies.
  • Prototyping a protocol that requires authenticated encryption before integrating a vetted library.
  • Generating deterministic test vectors for a custom encryption module during development.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Auth & security at scale

Related tutorials and quizzes for this topic.