Cryptographic recipes and primitives for Python developers
—
Message authentication codes (MACs) and digital signatures for ensuring data integrity and authenticity. Protects against tampering and verifies message origin.
from cryptography.hazmat.primitives import hmac, cmac, poly1305
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.ciphers import algorithmsclass 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"""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"""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"""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")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")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")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}")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'}")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()}")Install with Tessl CLI
npx tessl i tessl/pypi-cryptography