ChaCha20Poly1305 Reusable for Asyncio
This library provides a Python implementation of the ChaCha20Poly1305 authenticated encryption algorithm, specifically designed for asynchronous I/O (asyncio) contexts. While it aimed to offer a reusable cipher object for efficiency, it is now considered obsolete for new projects due to the `cryptography` library's adoption of a high-performance, Rust-based ChaCha20Poly1305 implementation. The library is currently at version 0.13.2, with recent maintenance releases, but its utility for new development is superseded.
Common errors
-
ValueError: MAC did not verify
cause During decryption, the authentication tag (MAC) generated from the provided ciphertext, associated data, and key does not match the tag received. This indicates that either the ciphertext or the associated data has been tampered with, or an incorrect key/nonce/tag was used for decryption.fixEnsure that the `key`, `nonce`, `ciphertext`, `tag`, and `associated_data` used during decryption are precisely the same as those used during encryption and that none have been modified in transit. Verify the integrity of the message source. -
AttributeError: module 'chacha20poly1305_reuseable' has no attribute 'ChaCha20Poly1305Reusable'
cause The main class `ChaCha20Poly1305Reusable` was not found. This typically happens if the library is not installed, or the import path is incorrect (e.g., trying `import chacha20poly1305_reuseable` directly and accessing attributes from it).fixFirst, ensure the library is installed: `pip install chacha20poly1305-reuseable`. Then, use the correct import statement: `from chacha20poly1305_reuseable import ChaCha20Poly1305Reusable`. -
Security warning: Nonce reuse detected with ChaCha20Poly1305
cause Attempting to encrypt multiple distinct messages using the same cryptographic key and the same nonce. While the library itself handles nonce generation for its `encrypt` method, manual or incorrect nonce management can lead to this vulnerability.fixAlways use a unique, randomly generated nonce for every single encryption operation performed with a given key. The `encrypt` method in `chacha20poly1305-reuseable` automatically generates a new nonce for each call, so this issue primarily arises from manual misuse if a fixed nonce is forced.
Warnings
- breaking This library is considered obsolete for new projects. The underlying `cryptography` library now provides its own highly optimized, Rust-based implementation of ChaCha20Poly1305. It is recommended to use `cryptography.hazmat.primitives.ciphers.aead.ChaCha20Poly1305` directly instead of this wrapper library for modern applications.
- gotcha Despite the library's name including 'reuseable', this refers to the Python object instance, not the cryptographic nonce. Reusing the same nonce (IV) with the same key for two different plaintexts is a critical security vulnerability that completely compromises the confidentiality of ChaCha20Poly1305. A unique nonce *must* be used for every encryption with a given key.
- gotcha Older versions of `chacha20poly1305-reuseable` (prior to v0.12.1) had compatibility issues with `cryptography` library versions, specifically `cryptography 42`, due to internal cipher checks. This could lead to runtime errors or unexpected behavior if `cryptography` was updated independently.
Install
-
pip install chacha20poly1305-reuseable
Imports
- ChaCha20Poly1305Reusable
from chacha20poly1305_reuseable import ChaCha20Poly1305Reusable
Quickstart
import os
from chacha20poly1305_reuseable import ChaCha20Poly1305Reusable
# In a real application, securely load your key (32 bytes).
# For demonstration, a random key is generated.
key = os.urandom(32)
# Associated data (AAD) is optional, but recommended for integrity checks.
associated_data = b"Config data, not encrypted but authenticated."
plaintext = b"This is the secret message to be encrypted."
# Initialize the cipher object with the key
cipher = ChaCha20Poly1305Reusable(key)
# Encrypt the plaintext
# The nonce is generated internally and returned, must be unique per encryption.
nonce, ciphertext, tag = cipher.encrypt(plaintext, associated_data)
print(f"Encryption successful:")
print(f" Nonce: {nonce.hex()}")
print(f" Ciphertext: {ciphertext.hex()}")
print(f" Tag: {tag.hex()}")
# Decrypt the ciphertext
try:
decrypted_text = cipher.decrypt(nonce, ciphertext, tag, associated_data)
print(f"Decryption successful: {decrypted_text.decode()}")
assert decrypted_text == plaintext
print("Message integrity verified.")
except Exception as e:
print(f"Decryption failed: {e}")
# The 'cipher' object can be reused for subsequent encryptions/decryptions
# with a *new*, unique nonce for each operation.
new_plaintext = b"Another secret message, secured by the same key."
new_nonce, new_ciphertext, new_tag = cipher.encrypt(new_plaintext, associated_data)
print(f"\nReused cipher for new encryption (new nonce): {new_nonce.hex()}")