🔏 Poly1305 One-time Authenticator
📋 Quick Navigation
📖 Overview
Poly1305 is a cryptographic message authentication code (MAC) designed by Daniel J. Bernstein. It's a one-time authenticator that provides extremely fast authentication while maintaining strong security guarantees. Poly1305 is designed to be used with a unique key for each message, making it ideal for high-performance applications like network protocols.
✨ Key Features
High Performance
Extremely fast computation on modern processors
One-time Security
Designed for use with unique keys per message
Provable Security
Based on well-understood mathematical foundations
Constant Time
Resistant to timing attacks when properly implemented
Standardized
RFC 8439 (ChaCha20-Poly1305) and RFC 7539
Widely Adopted
Used in TLS 1.3, WireGuard, and modern protocols
🎯 Common Use Cases
🔐 Encryption & Authentication
- AEAD Encryption: Combined with ChaCha20 for authenticated encryption
- Network Protocols: High-speed packet authentication
- Real-time Communications: Low-latency authentication
🌐 Protocol Integration
- TLS 1.3: Cipher suite ChaCha20-Poly1305-SHA256
- VPN Protocols: WireGuard uses ChaCha20-Poly1305
- IoT Devices: Efficient authentication for resource-constrained devices
🔧 Algorithm Details
📊 Technical Parameters
🛡️ Security Properties
Unforgeability
2^-128 forgery probability per attempt
Key Uniqueness
Each key must be used only once
Collision Resistance
Based on polynomial evaluation security
Side-channel Resistance
Constant-time implementation possible
Quantum Resistance
Classical security assumptions
🧮 Mathematical Foundation
Poly1305 is based on polynomial evaluation over a finite field:
tag = ((m₁ × r^n + m₂ × r^(n-1) + ... + mₙ × r + 1) mod (2^130 - 5)) + s
💻 Implementation
Poly1305 provides a simple yet powerful API for high-speed message authentication. The key requirement is that each key must be used only once to maintain security guarantees.
🚀 Basic Usage
Advanced Usage with Key Derivation
from metamui_crypto import Poly1305, ChaCha20
import os
def secure_authenticate_with_nonce(master_key: bytes, nonce: bytes, message: bytes) -> bytes:
"""
Authenticate message using Poly1305 with nonce-derived key.
Args:
master_key: 32-byte master key
nonce: 12-byte nonce (must be unique)
message: Message to authenticate
Returns:
16-byte authentication tag
"""
# Derive one-time key using ChaCha20
chacha = ChaCha20(master_key, nonce)
poly_key = chacha.encrypt(b'\x00' * 32) # Generate 32 zero bytes as key
# Authenticate with derived key
poly = Poly1305(poly_key)
tag = poly.authenticate(message)
# Clear sensitive key material
poly_key = b'\x00' * len(poly_key)
return tag
# Example usage
master_key = os.urandom(32)
nonce = os.urandom(12)
message = b"Confidential data requiring authentication"
tag = secure_authenticate_with_nonce(master_key, nonce, message)
print(f"Derived key authentication tag: {tag.hex()}")
Batch Authentication
from metamui_crypto import Poly1305
import os
def authenticate_multiple_messages(messages: list[bytes]) -> list[tuple[bytes, bytes]]:
"""
Authenticate multiple messages with unique keys.
Args:
messages: List of messages to authenticate
Returns:
List of (key, tag) tuples for each message
"""
results = []
for message in messages:
# Generate unique key for each message
key = Poly1305.generate_key()
poly = Poly1305(key)
tag = poly.authenticate(message)
results.append((key, tag))
print(f"Message: {message[:20]}... -> Tag: {tag.hex()[:16]}...")
return results
# Example with multiple messages
messages = [
b"First message to authenticate",
b"Second message with different content",
b"Third message for batch processing"
]
auth_data = authenticate_multiple_messages(messages)
print(f"Authenticated {len(auth_data)} messages")
Integration with ChaCha20-Poly1305 AEAD
from metamui_crypto import ChaCha20Poly1305
import os
def aead_encrypt_decrypt_example():
"""
Demonstrate ChaCha20-Poly1305 AEAD usage.
"""
# Generate key and nonce
key = os.urandom(32)
nonce = os.urandom(12)
# Additional authenticated data (not encrypted)
aad = b"protocol_version=1.0,timestamp=1234567890"
# Message to encrypt and authenticate
plaintext = b"Secret message requiring both confidentiality and authenticity"
# Create AEAD instance
aead = ChaCha20Poly1305(key)
# Encrypt and authenticate
ciphertext, tag = aead.encrypt(nonce, plaintext, aad)
print(f"Plaintext: {plaintext}")
print(f"AAD: {aad}")
print(f"Ciphertext: {ciphertext.hex()}")
print(f"Auth tag: {tag.hex()}")
# Decrypt and verify
try:
decrypted = aead.decrypt(nonce, ciphertext, tag, aad)
print(f"Decrypted: {decrypted}")
print("Authentication and decryption successful!")
except ValueError as e:
print(f"Authentication failed: {e}")
aead_encrypt_decrypt_example()
Security Considerations
Critical Requirements
- Key Uniqueness: Each Poly1305 key MUST be used only once
- Key Generation: Use cryptographically secure random number generator
- Key Clamping: Implementation must properly clamp the key
- Constant Time: Avoid timing side-channels in verification
- Key Derivation: When reusing master keys, derive unique sub-keys
Implementation Guidelines
from metamui_crypto import Poly1305
import secrets
import hmac
class SecurePoly1305:
"""
Secure wrapper for Poly1305 with proper key management.
"""
def __init__(self, master_key: bytes):
if len(master_key) != 32:
raise ValueError("Master key must be 32 bytes")
self._master_key = master_key
self._used_nonces = set()
def authenticate_with_nonce(self, nonce: bytes, message: bytes) -> bytes:
"""
Authenticate message with nonce-derived key.
Args:
nonce: Unique nonce (recommended 16+ bytes)
message: Message to authenticate
Returns:
Authentication tag
"""
if nonce in self._used_nonces:
raise ValueError("Nonce reuse detected - security violation!")
# Derive unique key using HKDF-like construction
derived_key = hmac.new(
self._master_key,
b"poly1305-key" + nonce,
digestmod='sha256'
).digest()
# Authenticate with derived key
poly = Poly1305(derived_key)
tag = poly.authenticate(message)
# Track nonce usage
self._used_nonces.add(nonce)
# Clear derived key
derived_key = b'\x00' * len(derived_key)
return tag
def verify_with_nonce(self, nonce: bytes, message: bytes, tag: bytes) -> bool:
"""
Verify authentication tag with nonce-derived key.
"""
# Re-derive the same key
derived_key = hmac.new(
self._master_key,
b"poly1305-key" + nonce,
digestmod='sha256'
).digest()
# Verify with derived key
poly = Poly1305(derived_key)
is_valid = poly.verify(message, tag)
# Clear derived key
derived_key = b'\x00' * len(derived_key)
return is_valid
# Example secure usage
master_key = secrets.token_bytes(32)
secure_poly = SecurePoly1305(master_key)
# Authenticate with unique nonces
nonce1 = secrets.token_bytes(16)
nonce2 = secrets.token_bytes(16)
message1 = b"First message"
message2 = b"Second message"
tag1 = secure_poly.authenticate_with_nonce(nonce1, message1)
tag2 = secure_poly.authenticate_with_nonce(nonce2, message2)
# Verify
valid1 = secure_poly.verify_with_nonce(nonce1, message1, tag1)
valid2 = secure_poly.verify_with_nonce(nonce2, message2, tag2)
print(f"Message 1 valid: {valid1}")
print(f"Message 2 valid: {valid2}")
Common Vulnerabilities
- Key Reuse: Using same key for multiple messages
- Weak Key Generation: Using predictable or low-entropy keys
- Timing Attacks: Non-constant-time verification
- Key Exposure: Storing keys in memory too long
- Nonce Collision: Reusing nonces in key derivation
Performance Analysis
Benchmarks
Performance measurements on modern hardware:
| Platform | Speed (cycles/byte) | Throughput (GB/s) |
|---|---|---|
| Intel x64 | 0.63 | 4.8 |
| ARM64 | 0.85 | 3.5 |
| ARM32 | 2.1 | 1.4 |
| RISC-V | 1.8 | 1.7 |
Optimization Techniques
from metamui_crypto import Poly1305
import time
def benchmark_poly1305():
"""
Benchmark Poly1305 performance with different message sizes.
"""
key = Poly1305.generate_key()
poly = Poly1305(key)
sizes = [64, 256, 1024, 4096, 16384, 65536]
for size in sizes:
message = b'A' * size
# Warm up
for _ in range(100):
poly.authenticate(message)
# Benchmark
start_time = time.perf_counter()
iterations = 1000
for _ in range(iterations):
tag = poly.authenticate(message)
end_time = time.perf_counter()
total_time = end_time - start_time
throughput = (size * iterations) / total_time / (1024 * 1024) # MB/s
print(f"Size: {size:6d} bytes, Throughput: {throughput:8.2f} MB/s")
benchmark_poly1305()
Use Cases and Examples
Network Protocol Authentication
from metamui_crypto import Poly1305
import struct
import socket
class AuthenticatedUDPSocket:
"""
UDP socket with Poly1305 authentication.
"""
def __init__(self, shared_key: bytes):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.shared_key = shared_key
self.sequence_number = 0
def send_authenticated(self, data: bytes, address: tuple):
"""
Send data with authentication tag.
"""
# Create packet header
seq_num = self.sequence_number
self.sequence_number += 1
# Derive unique key from sequence number
key_material = struct.pack('<Q', seq_num) + self.shared_key
derived_key = hmac.new(
self.shared_key,
key_material,
digestmod='sha256'
).digest()
# Authenticate packet
poly = Poly1305(derived_key)
packet = struct.pack('<Q', seq_num) + data
tag = poly.authenticate(packet)
# Send packet with tag
authenticated_packet = packet + tag
self.socket.sendto(authenticated_packet, address)
print(f"Sent packet {seq_num} with authentication")
def receive_authenticated(self) -> tuple[bytes, tuple]:
"""
Receive and verify authenticated data.
"""
authenticated_packet, address = self.socket.recvfrom(4096)
if len(authenticated_packet) < 24: # 8 bytes seq + 16 bytes tag minimum
raise ValueError("Packet too short")
# Split packet and tag
packet = authenticated_packet[:-16]
tag = authenticated_packet[-16:]
# Extract sequence number
seq_num = struct.unpack('<Q', packet[:8])[0]
# Derive same key
key_material = struct.pack('<Q', seq_num) + self.shared_key
derived_key = hmac.new(
self.shared_key,
key_material,
digestmod='sha256'
).digest()
# Verify authentication
poly = Poly1305(derived_key)
if not poly.verify(packet, tag):
raise ValueError("Authentication failed")
# Return data without sequence number
data = packet[8:]
print(f"Received authenticated packet {seq_num}")
return data, address
# Example usage
import secrets
shared_key = secrets.token_bytes(32)
auth_socket = AuthenticatedUDPSocket(shared_key)
File Integrity Protection
from metamui_crypto import Poly1305
import os
import json
class FileIntegrityManager:
"""
Manage file integrity using Poly1305 authentication.
"""
def __init__(self, master_key: bytes):
self.master_key = master_key
self.integrity_db = {}
def protect_file(self, filepath: str) -> str:
"""
Generate integrity tag for file.
Returns:
Hex-encoded authentication tag
"""
# Read file content
with open(filepath, 'rb') as f:
content = f.read()
# Get file metadata
stat = os.stat(filepath)
metadata = {
'size': stat.st_size,
'mtime': stat.st_mtime,
'mode': stat.st_mode
}
# Create authentication input
auth_input = json.dumps(metadata, sort_keys=True).encode() + content
# Derive file-specific key
file_key = hmac.new(
self.master_key,
filepath.encode() + b"file-integrity",
digestmod='sha256'
).digest()
# Generate authentication tag
poly = Poly1305(file_key)
tag = poly.authenticate(auth_input)
# Store in database
tag_hex = tag.hex()
self.integrity_db[filepath] = {
'tag': tag_hex,
'metadata': metadata
}
return tag_hex
def verify_file(self, filepath: str) -> bool:
"""
Verify file integrity.
"""
if filepath not in self.integrity_db:
return False
try:
# Read current file
with open(filepath, 'rb') as f:
content = f.read()
# Get current metadata
stat = os.stat(filepath)
current_metadata = {
'size': stat.st_size,
'mtime': stat.st_mtime,
'mode': stat.st_mode
}
# Check if metadata changed
stored_metadata = self.integrity_db[filepath]['metadata']
if current_metadata != stored_metadata:
return False
# Recreate authentication input
auth_input = json.dumps(current_metadata, sort_keys=True).encode() + content
# Derive same key
file_key = hmac.new(
self.master_key,
filepath.encode() + b"file-integrity",
digestmod='sha256'
).digest()
# Verify tag
poly = Poly1305(file_key)
stored_tag = bytes.fromhex(self.integrity_db[filepath]['tag'])
return poly.verify(auth_input, stored_tag)
except (OSError, ValueError):
return False
def save_database(self, db_path: str):
"""Save integrity database to file."""
with open(db_path, 'w') as f:
json.dump(self.integrity_db, f, indent=2)
def load_database(self, db_path: str):
"""Load integrity database from file."""
with open(db_path, 'r') as f:
self.integrity_db = json.load(f)
# Example usage
master_key = secrets.token_bytes(32)
integrity_mgr = FileIntegrityManager(master_key)
# Protect important files
important_files = [
"config.json",
"database.db",
"certificates/server.crt"
]
for filepath in important_files:
if os.path.exists(filepath):
tag = integrity_mgr.protect_file(filepath)
print(f"Protected {filepath}: {tag[:16]}...")
# Later, verify integrity
for filepath in important_files:
if os.path.exists(filepath):
is_valid = integrity_mgr.verify_file(filepath)
print(f"File {filepath}: {'VALID' if is_valid else 'CORRUPTED'}")
Comparison with Other MACs
Poly1305 vs HMAC
| Aspect | Poly1305 | HMAC-SHA256 |
|---|---|---|
| Speed | ~4.8 GB/s | ~1.2 GB/s |
| Key Usage | One-time only | Reusable |
| Tag Size | 16 bytes | 32 bytes (truncatable) |
| Security Basis | Polynomial evaluation | Hash function |
| Standardization | RFC 8439 | RFC 2104, FIPS 198-1 |
Poly1305 vs GCM
| Aspect | Poly1305 | AES-GCM |
|---|---|---|
| Algorithm Type | Universal hash | Galois field multiplication |
| Performance | Faster on most platforms | Hardware accelerated on some |
| Key Schedule | None required | AES key schedule |
| Parallelization | Limited | Highly parallelizable |
| Side-channel Resistance | Good with proper implementation | Requires careful implementation |
Migration Guide
From HMAC to Poly1305
# Before: HMAC-based authentication
import hmac
import hashlib
def hmac_authenticate(key: bytes, message: bytes) -> bytes:
return hmac.new(key, message, hashlib.sha256).digest()
def hmac_verify(key: bytes, message: bytes, tag: bytes) -> bool:
expected = hmac_authenticate(key, message)
return hmac.compare_digest(expected, tag)
# After: Poly1305 with proper key derivation
from metamui_crypto import Poly1305
import secrets
def poly1305_authenticate(master_key: bytes, nonce: bytes, message: bytes) -> bytes:
# Derive one-time key
derived_key = hmac.new(
master_key,
b"poly1305-v1" + nonce,
hashlib.sha256
).digest()
poly = Poly1305(derived_key)
return poly.authenticate(message)
def poly1305_verify(master_key: bytes, nonce: bytes, message: bytes, tag: bytes) -> bool:
# Derive same key
derived_key = hmac.new(
master_key,
b"poly1305-v1" + nonce,
hashlib.sha256
).digest()
poly = Poly1305(derived_key)
return poly.verify(message, tag)
# Migration example
master_key = secrets.token_bytes(32)
nonce = secrets.token_bytes(16)
message = b"Message to authenticate"
# Old way
hmac_tag = hmac_authenticate(master_key, message)
# New way
poly_tag = poly1305_authenticate(master_key, nonce, message)
print(f"HMAC tag: {hmac_tag.hex()}")
print(f"Poly1305 tag: {poly_tag.hex()}")
Test Vectors
RFC 8439 Test Vectors
def test_rfc8439_vectors():
"""
Test vectors from RFC 8439 Section 2.5.2
"""
# Test vector 1
key = bytes.fromhex(
"85d6be7857556d337f4452fe42d506a8"
"0103808afb0db2fd4abff6af4149f51b"
)
message = b"Cryptographic Forum Research Group"
expected_tag = bytes.fromhex("a8061dc1305136c6c22b8baf0c0127a9")
poly = Poly1305(key)
computed_tag = poly.authenticate(message)
assert computed_tag == expected_tag, f"Expected {expected_tag.hex()}, got {computed_tag.hex()}"
# Test verification
assert poly.verify(message, expected_tag)
print("RFC 8439 test vector passed!")
def test_additional_vectors():
"""
Additional test vectors for edge cases.
"""
# Empty message
key = b'\x00' * 32
poly = Poly1305(key)
empty_tag = poly.authenticate(b"")
assert len(empty_tag) == 16
assert poly.verify(b"", empty_tag)
# Single byte message
single_byte_tag = poly.authenticate(b"\x01")
assert poly.verify(b"\x01", single_byte_tag)
# Maximum length message (simulated)
large_message = b"A" * 1000000
large_tag = poly.authenticate(large_message)
assert poly.verify(large_message, large_tag)
print("Additional test vectors passed!")
# Run tests
test_rfc8439_vectors()
test_additional_vectors()
References
Standards and Specifications
- RFC 8439: ChaCha20 and Poly1305 for IETF Protocols
- RFC 7539: ChaCha20 and Poly1305 for IETF Protocols (obsoleted by RFC 8439)
- IRTF CFRG: Crypto Forum Research Group specifications
Academic Papers
- “The Poly1305-AES message-authentication code” by Daniel J. Bernstein
- “Cryptographic competitions” - CAESAR competition documentation
- “High-speed cryptography in characteristic 2” - Polynomial evaluation security
Implementation References
- libsodium: Reference implementation
- BoringSSL: Google’s implementation
- OpenSSL: Standard library implementation
- Rust crypto: RustCrypto implementation
Security Analysis
- “Security of the Poly1305 MAC” - Formal security proofs
- “Timing attacks on implementations” - Side-channel analysis
- “Post-quantum security” - Quantum resistance analysis
This documentation is part of the MetaMUI Crypto Primitives library. For more information, see the main documentation or visit our GitHub repository.