CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-cryptography

Cryptographic recipes and primitives for Python developers

Pending
Overview
Eval results
Files

aead-ciphers.mddocs/

AEAD Ciphers

Authenticated Encryption with Associated Data (AEAD) providing encryption and authentication in a single operation. AEAD ciphers ensure both confidentiality and authenticity of data while allowing additional authenticated data that remains unencrypted.

Core Imports

from cryptography.hazmat.primitives.ciphers.aead import AESGCM, ChaCha20Poly1305, AESCCM, AESSIV, AESOCB3, AESGCMSIV

Capabilities

AES-GCM (Galois/Counter Mode)

AES in Galois/Counter Mode providing authenticated encryption with excellent performance.

class AESGCM:
    def __init__(self, key: bytes):
        """
        Initialize AES-GCM with key.
        
        Args:
            key (bytes): 128, 192, or 256-bit key (16, 24, or 32 bytes)
        """
    
    @classmethod
    def generate_key(cls, bit_length: int) -> bytes:
        """
        Generate random key for AES-GCM.
        
        Args:
            bit_length (int): Key length in bits (128, 192, or 256)
            
        Returns:
            bytes: Random key of specified length
        """
    
    def encrypt(self, nonce: bytes, data: bytes, associated_data: bytes = None) -> bytes:
        """
        Encrypt data with authentication.
        
        Args:
            nonce (bytes): 96-bit (12 bytes) nonce. Must be unique per key.
            data (bytes): Data to encrypt
            associated_data (bytes, optional): Additional data to authenticate but not encrypt
            
        Returns:
            bytes: Encrypted data with authentication tag appended
            
        Note:
            Never reuse nonce with same key - this breaks security completely
        """
    
    def decrypt(self, nonce: bytes, data: bytes, associated_data: bytes = None) -> bytes:
        """
        Decrypt and verify authenticated data.
        
        Args:
            nonce (bytes): Same nonce used for encryption
            data (bytes): Encrypted data with authentication tag
            associated_data (bytes, optional): Same associated data from encryption
            
        Returns:
            bytes: Decrypted plaintext data
            
        Raises:
            InvalidTag: If authentication verification fails
        """

ChaCha20-Poly1305

Modern stream cipher with Poly1305 MAC for authenticated encryption.

class ChaCha20Poly1305:
    def __init__(self, key: bytes):
        """
        Initialize ChaCha20-Poly1305 with key.
        
        Args:
            key (bytes): 256-bit key (32 bytes)
        """
    
    @classmethod  
    def generate_key(cls) -> bytes:
        """
        Generate random 256-bit key.
        
        Returns:
            bytes: 32-byte random key
        """
    
    def encrypt(self, nonce: bytes, data: bytes, associated_data: bytes = None) -> bytes:
        """
        Encrypt data with ChaCha20-Poly1305.
        
        Args:
            nonce (bytes): 96-bit (12 bytes) nonce. Must be unique per key.
            data (bytes): Data to encrypt
            associated_data (bytes, optional): Additional authenticated data
            
        Returns:
            bytes: Encrypted data with Poly1305 tag appended
        """
    
    def decrypt(self, nonce: bytes, data: bytes, associated_data: bytes = None) -> bytes:
        """
        Decrypt and verify ChaCha20-Poly1305 data.
        
        Args:
            nonce (bytes): Same nonce used for encryption
            data (bytes): Encrypted data with authentication tag
            associated_data (bytes, optional): Same associated data from encryption
            
        Returns:
            bytes: Decrypted plaintext
            
        Raises:
            InvalidTag: If Poly1305 authentication fails
        """

AES-CCM (Counter with CBC-MAC)

AES Counter mode with CBC-MAC for authenticated encryption.

class AESCCM:
    def __init__(self, key: bytes, tag_length: int = 16):
        """
        Initialize AES-CCM with key and tag length.
        
        Args:
            key (bytes): 128, 192, or 256-bit AES key
            tag_length (int): Authentication tag length (4, 6, 8, 10, 12, 14, 16 bytes)
        """
    
    @classmethod
    def generate_key(cls, bit_length: int) -> bytes:
        """Generate random AES key"""
    
    def encrypt(self, nonce: bytes, data: bytes, associated_data: bytes = None) -> bytes:
        """
        Encrypt with AES-CCM.
        
        Args:
            nonce (bytes): 7-15 byte nonce (longer nonce = shorter counter)
            data (bytes): Data to encrypt  
            associated_data (bytes, optional): Additional authenticated data
            
        Returns:
            bytes: Encrypted data with CBC-MAC tag
        """
    
    def decrypt(self, nonce: bytes, data: bytes, associated_data: bytes = None) -> bytes:
        """
        Decrypt and verify AES-CCM data.
        
        Raises:
            InvalidTag: If CBC-MAC verification fails
        """

AES-SIV (Synthetic Initialization Vector)

Misuse-resistant authenticated encryption that's secure even with nonce reuse.

class AESSIV:
    def __init__(self, key: bytes):
        """
        Initialize AES-SIV.
        
        Args:
            key (bytes): 256, 384, or 512-bit key (32, 48, or 64 bytes)
                        Key length determines AES variant (AES-128, AES-192, AES-256)
        """
    
    @classmethod
    def generate_key(cls, bit_length: int) -> bytes:
        """Generate random key for AES-SIV"""
    
    def encrypt(self, data: bytes, associated_data: List[bytes] = None) -> bytes:
        """
        Encrypt with AES-SIV (no nonce required).
        
        Args:
            data (bytes): Data to encrypt
            associated_data (List[bytes], optional): List of associated data items
            
        Returns:
            bytes: SIV (16 bytes) + encrypted data
            
        Note:
            AES-SIV is nonce-misuse resistant - safe even with repeated inputs
        """
    
    def decrypt(self, data: bytes, associated_data: List[bytes] = None) -> bytes:
        """
        Decrypt AES-SIV data.
        
        Args:
            data (bytes): SIV + encrypted data from encrypt()
            associated_data (List[bytes], optional): Same associated data from encryption
            
        Returns:
            bytes: Decrypted plaintext
            
        Raises:
            InvalidTag: If SIV verification fails
        """

AES-OCB3 (Offset Codebook Mode)

High-performance authenticated encryption mode.

class AESOCB3:
    def __init__(self, key: bytes):
        """
        Initialize AES-OCB3.
        
        Args:
            key (bytes): 128, 192, or 256-bit AES key
        """
    
    @classmethod
    def generate_key(cls, bit_length: int) -> bytes:
        """Generate random AES key"""
    
    def encrypt(self, nonce: bytes, data: bytes, associated_data: bytes = None) -> bytes:
        """
        Encrypt with AES-OCB3.
        
        Args:
            nonce (bytes): 1-15 byte nonce, must be unique per key
            data (bytes): Data to encrypt
            associated_data (bytes, optional): Additional authenticated data
            
        Returns:
            bytes: Encrypted data with authentication tag
        """
    
    def decrypt(self, nonce: bytes, data: bytes, associated_data: bytes = None) -> bytes:
        """
        Decrypt AES-OCB3 data.
        
        Raises:
            InvalidTag: If authentication verification fails
        """

AES-GCM-SIV (Galois/Counter Mode with SIV)

Misuse-resistant variant of AES-GCM that's secure with nonce reuse.

class AESGCMSIV:
    def __init__(self, key: bytes):
        """
        Initialize AES-GCM-SIV.
        
        Args:
            key (bytes): 128 or 256-bit key (16 or 32 bytes)
        """
    
    @classmethod
    def generate_key(cls, bit_length: int) -> bytes:
        """Generate random key for AES-GCM-SIV"""
    
    def encrypt(self, nonce: bytes, data: bytes, associated_data: bytes = None) -> bytes:
        """
        Encrypt with AES-GCM-SIV.
        
        Args:
            nonce (bytes): 96-bit (12 bytes) nonce
            data (bytes): Data to encrypt
            associated_data (bytes, optional): Additional authenticated data
            
        Returns:
            bytes: Encrypted data with authentication tag
            
        Note:
            Secure even with nonce reuse, but unique nonces still recommended
        """
    
    def decrypt(self, nonce: bytes, data: bytes, associated_data: bytes = None) -> bytes:
        """
        Decrypt AES-GCM-SIV data.
        
        Raises:
            InvalidTag: If authentication verification fails
        """

Usage Examples

Basic AES-GCM Encryption

from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.exceptions import InvalidTag
import os

# Generate key and nonce
key = AESGCM.generate_key(256)  # 256-bit key
aesgcm = AESGCM(key)

# Encrypt data
nonce = os.urandom(12)  # 96-bit nonce
plaintext = b"Secret message for authenticated encryption"
associated_data = b"public metadata"

ciphertext = aesgcm.encrypt(nonce, plaintext, associated_data)
print(f"Encrypted: {ciphertext.hex()}")

# Decrypt data
try:
    decrypted = aesgcm.decrypt(nonce, ciphertext, associated_data)
    print(f"Decrypted: {decrypted}")
except InvalidTag:
    print("Authentication failed - data may be tampered")

ChaCha20-Poly1305 for High-Performance Applications

from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
import os

# Initialize ChaCha20-Poly1305
key = ChaCha20Poly1305.generate_key()
chacha = ChaCha20Poly1305(key)

# Encrypt with associated data
nonce = os.urandom(12)
message = b"High-speed encrypted message"
metadata = b"timestamp:2024-01-01,user:alice"

encrypted = chacha.encrypt(nonce, message, metadata)

# Decrypt and verify
try:
    decrypted = chacha.decrypt(nonce, encrypted, metadata)
    print(f"Message: {decrypted}")
    print("Authentication successful")
except InvalidTag:
    print("Message authentication failed")

Secure File Encryption with AES-GCM

from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
import json

class SecureFileManager:
    def __init__(self, key=None):
        self.key = key or AESGCM.generate_key(256)
        self.aesgcm = AESGCM(self.key)
    
    def encrypt_file(self, input_path, output_path, metadata=None):
        """Encrypt file with optional metadata"""
        with open(input_path, 'rb') as f:
            plaintext = f.read()
        
        nonce = os.urandom(12)
        associated_data = json.dumps(metadata or {}).encode() if metadata else None
        
        ciphertext = self.aesgcm.encrypt(nonce, plaintext, associated_data)
        
        # Store nonce + ciphertext + metadata length + metadata
        with open(output_path, 'wb') as f:
            f.write(nonce)  # First 12 bytes
            f.write(len(associated_data or b"").to_bytes(4, 'big'))  # Metadata length
            if associated_data:
                f.write(associated_data)  # Metadata
            f.write(ciphertext)  # Encrypted content
    
    def decrypt_file(self, input_path, output_path):
        """Decrypt file and return metadata"""
        with open(input_path, 'rb') as f:
            nonce = f.read(12)
            metadata_len = int.from_bytes(f.read(4), 'big')
            associated_data = f.read(metadata_len) if metadata_len > 0 else None
            ciphertext = f.read()
        
        try:
            plaintext = self.aesgcm.decrypt(nonce, ciphertext, associated_data)
            
            with open(output_path, 'wb') as f:
                f.write(plaintext)
            
            metadata = json.loads(associated_data.decode()) if associated_data else {}
            return metadata
            
        except InvalidTag:
            raise ValueError("File decryption failed - file may be corrupted or tampered")

# Usage
file_manager = SecureFileManager()

# Encrypt file with metadata
file_manager.encrypt_file(
    'document.pdf', 
    'document.pdf.enc',
    metadata={'author': 'Alice', 'classification': 'confidential'}
)

# Decrypt file
metadata = file_manager.decrypt_file('document.pdf.enc', 'document_decrypted.pdf')
print(f"File metadata: {metadata}")

Misuse-Resistant Encryption with AES-SIV

from cryptography.hazmat.primitives.ciphers.aead import AESSIV

# AES-SIV is safe even with nonce reuse
key = AESSIV.generate_key(256)  # 256-bit key for AES-128-SIV
aessiv = AESSIV(key)

# Encrypt same data multiple times (normally dangerous)
data = b"Repeated message"
associated_data = [b"context1", b"context2"]

# These encryptions are safe even though input is identical
ciphertext1 = aessiv.encrypt(data, associated_data)
ciphertext2 = aessiv.encrypt(data, associated_data)

print(f"Encryption 1: {ciphertext1.hex()}")
print(f"Encryption 2: {ciphertext2.hex()}")
print(f"Same result: {ciphertext1 == ciphertext2}")  # True - deterministic

# Decrypt both
decrypted1 = aessiv.decrypt(ciphertext1, associated_data)
decrypted2 = aessiv.decrypt(ciphertext2, associated_data)

print(f"Decrypted: {decrypted1}")

Multi-Algorithm AEAD Wrapper

from cryptography.hazmat.primitives.ciphers.aead import AESGCM, ChaCha20Poly1305, AESSIV
from cryptography.exceptions import InvalidTag
import os
import struct

class UniversalAEAD:
    ALGORITHMS = {
        'AES-GCM': AESGCM,
        'ChaCha20-Poly1305': ChaCha20Poly1305,
        'AES-SIV': AESSIV
    }
    
    def __init__(self, algorithm='AES-GCM', key_size=256):
        self.algorithm = algorithm
        self.key_size = key_size
        
        if algorithm == 'AES-GCM':
            self.key = AESGCM.generate_key(key_size)
            self.cipher = AESGCM(self.key)
        elif algorithm == 'ChaCha20-Poly1305':
            self.key = ChaCha20Poly1305.generate_key()
            self.cipher = ChaCha20Poly1305(self.key)
        elif algorithm == 'AES-SIV':
            self.key = AESSIV.generate_key(key_size)
            self.cipher = AESSIV(self.key)
    
    def encrypt(self, data, associated_data=None):
        """Encrypt with algorithm identification"""
        if self.algorithm == 'AES-SIV':
            # AES-SIV doesn't use nonce
            ciphertext = self.cipher.encrypt(data, [associated_data] if associated_data else None)
            nonce = b''
        else:
            # Nonce-based algorithms
            nonce = os.urandom(12)
            ciphertext = self.cipher.encrypt(nonce, data, associated_data)
        
        # Format: algorithm_id (1 byte) + nonce_len (1 byte) + nonce + ciphertext
        algorithm_id = list(self.ALGORITHMS.keys()).index(self.algorithm)
        header = struct.pack('BB', algorithm_id, len(nonce))
        
        return header + nonce + ciphertext
    
    def decrypt(self, encrypted_data, associated_data=None):
        """Decrypt with automatic algorithm detection"""
        algorithm_id, nonce_len = struct.unpack('BB', encrypted_data[:2])
        algorithm = list(self.ALGORITHMS.keys())[algorithm_id]
        
        nonce = encrypted_data[2:2+nonce_len]
        ciphertext = encrypted_data[2+nonce_len:]
        
        if algorithm == 'AES-SIV':
            return self.cipher.decrypt(ciphertext, [associated_data] if associated_data else None)
        else:
            return self.cipher.decrypt(nonce, ciphertext, associated_data)

# Usage
# Try different algorithms
for alg in ['AES-GCM', 'ChaCha20-Poly1305', 'AES-SIV']:
    aead = UniversalAEAD(alg)
    
    data = b"Test message for " + alg.encode()
    metadata = b"algorithm:" + alg.encode()
    
    encrypted = aead.encrypt(data, metadata)
    decrypted = aead.decrypt(encrypted, metadata)
    
    print(f"{alg}: {decrypted}")

Security Considerations

  • Nonce Management: Never reuse nonces with the same key (except AES-SIV/GCM-SIV)
  • Key Rotation: Regularly rotate encryption keys
  • Associated Data: Use associated data for context binding
  • Algorithm Selection:
    • AES-GCM: Fastest, requires careful nonce management
    • ChaCha20-Poly1305: Fast, good for software implementations
    • AES-SIV: Misuse-resistant but slower
  • Authentication: Always verify authentication tags before processing decrypted data
  • Side-Channel Protection: AEAD algorithms resist timing attacks
  • Nonce Sizes: Respect algorithm-specific nonce requirements

Install with Tessl CLI

npx tessl i tessl/pypi-cryptography

docs

aead-ciphers.md

asymmetric-cryptography.md

hash-functions.md

index.md

key-derivation.md

key-serialization.md

message-authentication.md

symmetric-ciphers.md

symmetric-encryption.md

two-factor-auth.md

utilities.md

x509-certificates.md

tile.json