CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-cryptography

Cryptographic recipes and primitives for Python developers

Pending
Overview
Eval results
Files

message-authentication.mddocs/

Message Authentication

Message authentication codes (MACs) and digital signatures for ensuring data integrity and authenticity. Protects against tampering and verifies message origin.

Core Imports

from cryptography.hazmat.primitives import hmac, cmac, poly1305
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.ciphers import algorithms

Capabilities

HMAC (Hash-based Message Authentication Code)

class HMAC:
    def __init__(self, key: bytes, algorithm, backend=None):
        """
        Initialize HMAC with key and hash algorithm.
        
        Args:
            key (bytes): Secret key for authentication
            algorithm: Hash algorithm (hashes.SHA256(), etc.)
            backend: Cryptographic backend
        """
    
    def update(self, data: bytes) -> None:
        """
        Add data to MAC computation.
        
        Args:
            data (bytes): Data to authenticate
        """
    
    def finalize(self) -> bytes:
        """
        Finalize MAC and return authentication tag.
        
        Returns:
            bytes: HMAC authentication tag
        """
    
    def verify(self, signature: bytes) -> None:
        """
        Verify HMAC signature.
        
        Args:
            signature (bytes): Expected HMAC tag
            
        Raises:
            InvalidSignature: If verification fails
        """
    
    def copy(self) -> 'HMAC':
        """Create copy of current HMAC state"""

CMAC (Cipher-based Message Authentication Code)

class CMAC:
    def __init__(self, key: bytes, algorithm, backend=None):
        """
        Initialize CMAC with key and cipher algorithm.
        
        Args:
            key (bytes): Secret key for authentication
            algorithm: Block cipher algorithm (algorithms.AES(), etc.)
            backend: Cryptographic backend
        """
    
    def update(self, data: bytes) -> None:
        """Add data to CMAC computation"""
    
    def finalize(self) -> bytes:
        """Finalize and return CMAC tag"""
    
    def verify(self, signature: bytes) -> None:
        """
        Verify CMAC signature.
        
        Raises:
            InvalidSignature: If verification fails
        """
    
    def copy(self) -> 'CMAC':
        """Create copy of CMAC state"""

Poly1305

class Poly1305:
    def __init__(self, key: bytes):
        """
        Initialize Poly1305 MAC.
        
        Args:
            key (bytes): 32-byte secret key
        """
    
    def update(self, data: bytes) -> None:
        """Add data to Poly1305 computation"""
    
    def finalize(self) -> bytes:
        """
        Finalize Poly1305 MAC.
        
        Returns:
            bytes: 16-byte authentication tag
        """
    
    def verify(self, tag: bytes) -> None:
        """
        Verify Poly1305 tag.
        
        Raises:
            InvalidSignature: If verification fails
        """
    
    @classmethod
    def generate_key(cls) -> bytes:
        """Generate random 32-byte key for Poly1305"""

Usage Examples

HMAC with SHA-256

from cryptography.hazmat.primitives import hmac, hashes
from cryptography.exceptions import InvalidSignature
import os

# Generate HMAC key
key = os.urandom(32)  # 256-bit key

# Create message to authenticate
message = b"Important message that needs authentication"

# Compute HMAC
h = hmac.HMAC(key, hashes.SHA256())
h.update(message)
tag = h.finalize()

print(f"HMAC-SHA256: {tag.hex()}")

# Verify HMAC
h_verify = hmac.HMAC(key, hashes.SHA256())
h_verify.update(message)

try:
    h_verify.verify(tag)
    print("HMAC verification: SUCCESS")
except InvalidSignature:
    print("HMAC verification: FAILED")

CMAC with AES

from cryptography.hazmat.primitives import cmac
from cryptography.hazmat.primitives.ciphers import algorithms
from cryptography.exceptions import InvalidSignature
import os

# Generate AES key for CMAC
key = os.urandom(32)  # 256-bit AES key

message = b"Data to authenticate with CMAC-AES"

# Compute CMAC
c = cmac.CMAC(key, algorithms.AES)
c.update(message)
tag = c.finalize()

print(f"CMAC-AES: {tag.hex()}")

# Verify CMAC
c_verify = cmac.CMAC(key, algorithms.AES)
c_verify.update(message)

try:
    c_verify.verify(tag)
    print("CMAC verification: SUCCESS")
except InvalidSignature:
    print("CMAC verification: FAILED")

Poly1305 Authentication

from cryptography.hazmat.primitives import poly1305
from cryptography.exceptions import InvalidSignature

# Generate Poly1305 key
key = poly1305.Poly1305.generate_key()

message = b"Message authenticated with Poly1305"

# Compute Poly1305 tag
p = poly1305.Poly1305(key)
p.update(message)
tag = p.finalize()

print(f"Poly1305 tag: {tag.hex()}")

# Verify tag
p_verify = poly1305.Poly1305(key)
p_verify.update(message)

try:
    p_verify.verify(tag)
    print("Poly1305 verification: SUCCESS")
except InvalidSignature:
    print("Poly1305 verification: FAILED")

Message Authentication for API Requests

from cryptography.hazmat.primitives import hmac, hashes
import time
import json
import hashlib

class APIAuthenticator:
    def __init__(self, secret_key: bytes):
        self.secret_key = secret_key
    
    def sign_request(self, method: str, url: str, body: bytes = None, timestamp: int = None) -> str:
        """Sign API request with HMAC"""
        if timestamp is None:
            timestamp = int(time.time())
        
        # Create string to sign
        body_hash = hashlib.sha256(body or b"").hexdigest()
        string_to_sign = f"{method}\n{url}\n{body_hash}\n{timestamp}"
        
        # Compute HMAC
        h = hmac.HMAC(self.secret_key, hashes.SHA256())
        h.update(string_to_sign.encode())
        signature = h.finalize()
        
        return {
            'signature': signature.hex(),
            'timestamp': timestamp
        }
    
    def verify_request(self, method: str, url: str, body: bytes, signature: str, timestamp: int) -> bool:
        """Verify API request signature"""
        # Check timestamp (prevent replay attacks)
        current_time = int(time.time())
        if abs(current_time - timestamp) > 300:  # 5 minute window
            return False
        
        # Verify signature
        expected_sig = self.sign_request(method, url, body, timestamp)
        return expected_sig['signature'] == signature

# Usage
api_key = os.urandom(32)
authenticator = APIAuthenticator(api_key)

# Sign request
request_body = json.dumps({"user": "alice", "action": "transfer"}).encode()
auth_data = authenticator.sign_request("POST", "/api/transfer", request_body)

print(f"Request signature: {auth_data['signature']}")
print(f"Timestamp: {auth_data['timestamp']}")

# Verify request
is_valid = authenticator.verify_request(
    "POST", "/api/transfer", request_body, 
    auth_data['signature'], auth_data['timestamp']
)
print(f"Request valid: {is_valid}")

File Integrity with HMAC

from cryptography.hazmat.primitives import hmac, hashes
import os

class FileIntegrityChecker:
    def __init__(self, key: bytes):
        self.key = key
    
    def compute_file_hmac(self, filepath: str) -> bytes:
        """Compute HMAC for file contents"""
        h = hmac.HMAC(self.key, hashes.SHA256())
        
        with open(filepath, 'rb') as f:
            while chunk := f.read(8192):
                h.update(chunk)
        
        return h.finalize()
    
    def create_integrity_file(self, filepath: str):
        """Create .hmac file with integrity hash"""
        file_hmac = self.compute_file_hmac(filepath)
        
        with open(f"{filepath}.hmac", 'wb') as f:
            f.write(file_hmac)
        
        print(f"Integrity file created: {filepath}.hmac")
    
    def verify_file_integrity(self, filepath: str) -> bool:
        """Verify file against .hmac file"""
        try:
            with open(f"{filepath}.hmac", 'rb') as f:
                stored_hmac = f.read()
            
            current_hmac = self.compute_file_hmac(filepath)
            return stored_hmac == current_hmac
            
        except FileNotFoundError:
            print(f"Integrity file not found: {filepath}.hmac")
            return False

# Usage
integrity_key = os.urandom(32)
checker = FileIntegrityChecker(integrity_key)

# Create test file
with open('important_document.txt', 'w') as f:
    f.write("This is an important document that must not be tampered with.\n")

# Create integrity hash
checker.create_integrity_file('important_document.txt')

# Verify integrity
is_intact = checker.verify_file_integrity('important_document.txt')
print(f"File integrity: {'OK' if is_intact else 'COMPROMISED'}")

# Simulate tampering
with open('important_document.txt', 'a') as f:
    f.write("Unauthorized modification\n")

# Check integrity again
is_intact = checker.verify_file_integrity('important_document.txt')
print(f"File integrity after modification: {'OK' if is_intact else 'COMPROMISED'}")

Streaming Authentication

from cryptography.hazmat.primitives import hmac, hashes
from cryptography.exceptions import InvalidSignature

class StreamingMAC:
    def __init__(self, key: bytes):
        self.key = key
        self.hmac = hmac.HMAC(key, hashes.SHA256())
    
    def update(self, data: bytes):
        """Add data to streaming MAC"""
        self.hmac.update(data)
    
    def finalize(self) -> bytes:
        """Get final MAC tag"""
        return self.hmac.finalize()
    
    def verify_stream(self, expected_tag: bytes) -> bool:
        """Verify streaming MAC"""
        # Create new HMAC for verification (original is finalized)
        verify_hmac = hmac.HMAC(self.key, hashes.SHA256())
        
        # Re-process all data would require storing it
        # In practice, you'd compute MAC during initial processing
        try:
            verify_hmac.verify(expected_tag)
            return True
        except InvalidSignature:
            return False

# Usage for streaming data
key = os.urandom(32)
stream_mac = StreamingMAC(key)

# Process data in chunks
data_chunks = [b"chunk1", b"chunk2", b"chunk3", b"final_chunk"]

for chunk in data_chunks:
    stream_mac.update(chunk)
    print(f"Processed chunk: {chunk}")

# Get final authentication tag
final_tag = stream_mac.finalize()
print(f"Stream MAC: {final_tag.hex()}")

Security Considerations

  • Key Management: Use different keys for different purposes
  • Key Length: Use appropriately sized keys (32+ bytes for HMAC)
  • Timing Attacks: Use constant-time comparison for MAC verification
  • Replay Protection: Include timestamps or nonces in authenticated data
  • Algorithm Selection: HMAC-SHA256 for most applications, Poly1305 for performance
  • MAC-then-Encrypt: Apply MAC to plaintext, then encrypt both message and MAC
  • Verification: Always verify MAC before processing authenticated data

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