HMAC MAC

Hash-based Message Authentication Code

Security Level Hash-dependent
Standardization RFC 2104, FIPS 198-1
Performance ⭐⭐⭐⭐
Quantum Safe ⚠️ Partial

🎯 Overview

HMAC (Hash-based Message Authentication Code) is the industry-standard method for providing data integrity and authenticity verification using a cryptographic hash function combined with a secret key. It ensures that messages have not been tampered with and can only be verified by parties possessing the secret key.

✨ Key Features

🔐

Cryptographic Security

Security based on underlying hash function strength

🔑

Key-Dependent

Requires secret key for generation and verification

📊

Industry Standard

RFC 2104 and FIPS 198-1 approved

High Performance

Fast computation and verification

🔧

Versatile

Works with any cryptographic hash function

🌐

Widely Adopted

Used in TLS, IPsec, JWT, and many protocols

🎯 Common Use Cases

🔌 API Authentication

Secure API request signing with HMAC-based authentication tokens

🛡️ Message Integrity

Verify messages haven't been tampered with during transmission

🔒 TLS/SSL Protocols

Handshake authentication and record protection in secure connections

🎫 JWT Tokens

JSON Web Token signing for secure authentication systems

🛡️ IPsec VPNs

Packet authentication in VPN protocols for secure networking

🗃️ Database Integrity

Verify database record authenticity and detect tampering

🔧 Algorithm Parameters

Hash Function

Options: SHA-256, SHA-512, BLAKE2b, BLAKE3

Recommendation: SHA-256 for general use, BLAKE3 for performance

Key Size

Minimum: Hash output size

Recommended: 32-64 bytes

Maximum: Unlimited (hashed if > block size)

Tag Size

Full: Hash output size

Minimum Secure: 128 bits (16 bytes)

Common: 256 bits (32 bytes)

Block Size

SHA-256: 64 bytes

SHA-512: 128 bytes

BLAKE2b: 128 bytes

🔒 Security Properties

🛡️

Unforgeability

Cannot create valid HMAC without secret key

🔍

Collision Resistance

Inherits security from underlying hash function

🔐

Key Recovery Resistance

Computationally infeasible to recover secret key

🎲

Pseudorandomness

Output appears random to attackers without key

🛡️

Length Extension Immunity

Immune to length extension attacks

⏱️

Timing Attack Resistance

Constant-time verification recommended

💻 Implementation

HMAC can be implemented with any cryptographic hash function. Choose the appropriate hash based on your security requirements and performance needs.

Basic HMAC Usage

HMAC with Different Hash Functions Python
```python from metamui_crypto import HMAC, SHA256, SHA512, BLAKE2b import os # Basic HMAC with SHA-256 key = os.urandom(32) # 256-bit key hmac_sha256 = HMAC(key, SHA256()) message = b"Hello, HMAC!" tag = hmac_sha256.compute(message) print(f"HMAC-SHA256: {tag.hex()}") # Verify HMAC is_valid = hmac_sha256.verify(message, tag) print(f"Verification: {is_valid}") # HMAC with different hash functions hmac_sha512 = HMAC(key, SHA512()) hmac_blake2b = HMAC(key, BLAKE2b()) # Incremental HMAC computation hmac = HMAC(key, SHA256()) hmac.update(b"First part ") hmac.update(b"Second part") final_tag = hmac.finalize() # Truncated HMAC (bandwidth-limited applications) full_tag = hmac_sha256.compute(message) truncated_tag = full_tag[:16] # 128-bit tag print(f"Full tag size: {len(full_tag)} bytes") print(f"Truncated tag size: {len(truncated_tag)} bytes") ```

API Authentication

HMAC-based API Request Signing Python
```python # API request signing class HMACAPIAuth: def __init__(self, api_key: str, api_secret: bytes): self.api_key = api_key self.hmac = HMAC(api_secret, SHA256()) def sign_request(self, method: str, path: str, body: bytes, timestamp: int) -> str: """Sign API request with HMAC""" # Create canonical request string canonical_request = f"{method}\n{path}\n{timestamp}\n" message = canonical_request.encode() + body # Compute HMAC signature = self.hmac.compute(message) return signature.hex() def verify_request(self, method: str, path: str, body: bytes, timestamp: int, signature: str) -> bool: """Verify API request signature""" expected_signature = self.sign_request(method, path, body, timestamp) # Constant-time comparison return self.constant_time_compare( bytes.fromhex(signature), bytes.fromhex(expected_signature) ) def constant_time_compare(self, a: bytes, b: bytes) -> bool: """Constant-time comparison to prevent timing attacks""" if len(a) != len(b): return False result = 0 for x, y in zip(a, b): result |= x ^ y return result == 0 # Usage example api_auth = HMACAPIAuth("my_api_key", b"secret_key_bytes") signature = api_auth.sign_request("POST", "/api/data", b'{"data": "value"}', 1234567890) print(f"API Signature: {signature}") ```

⚠️ Security Guidelines

🚨 Critical Security Requirements

Key Length: Use keys at least as long as hash output size
Verification: Always use constant-time comparison
Tag Length: Never truncate below 128 bits for security
Key Generation: Use cryptographically secure random sources
Key Rotation: Rotate keys periodically in production
Memory Safety: Clear sensitive data after use

⚡ Performance Analysis

Hash Function Performance Comparison

HMAC-SHA256

Throughput 245 MB/s
Security 128-bit
Output 32 bytes
Use Case General purpose

HMAC-SHA512

Throughput 198 MB/s
Security 256-bit
Output 64 bytes
Use Case High security

HMAC-BLAKE2b

Throughput 892 MB/s
Security 128-256 bit
Output Variable
Use Case Performance critical

HMAC-BLAKE3

Throughput 1.2 GB/s
Security 128+ bit
Output Variable
Use Case High-speed applications

🚀 Real-World Applications

🔌

REST API Security

HMAC-SHA256 for request signing in OAuth, AWS Signature v4, and custom API authentication

🎫

JWT Token Signing

HS256/HS512 algorithms for JSON Web Token integrity in authentication systems

🔒

TLS Record Protection

HMAC in TLS 1.2 and earlier for record authentication and integrity verification

🛡️

IPsec Authentication

ESP and AH protocols use HMAC for packet authentication in VPN connections

📁

File Integrity Monitoring

Database record protection and backup verification systems

📨

Message Queue Security

Apache Kafka, RabbitMQ message authentication and producer verification

⚖️ HMAC vs Alternative MACs

HMAC

Security High
Speed Fast
Key Reuse Yes
Use Case General purpose

Poly1305

Security High
Speed Very Fast
Key Reuse No (one-time)
Use Case AEAD schemes

SipHash

Security PRF level
Speed Very Fast
Key Reuse Yes
Use Case Hash tables

Digital Signatures

Security Very High
Speed Slow
Key Management Asymmetric
Use Case Non-repudiation

📋 Standards & References

🏛️ RFC 2104

HMAC: Keyed-Hashing for Message Authentication - Original HMAC specification

🏛️ FIPS 198-1

The Keyed-Hash Message Authentication Code - NIST federal standard

🌐 RFC 7518

JSON Web Algorithms (JWA) - HMAC in JWT specifications

📊 RFC 4231

HMAC test vectors for SHA-224, SHA-256, SHA-384, and SHA-512

def __init__(self, secret: bytes):
    self.hmac = HMAC(secret, SHA256())

def create_token(self, payload: dict, expires_in: int = 3600) -> str:
    """Create JWT token with HMAC signature"""
    import time
    
    # Header
    header = {
        "alg": "HS256",
        "typ": "JWT"
    }
    
    # Add expiration to payload
    payload["exp"] = int(time.time()) + expires_in
    payload["iat"] = int(time.time())
    
    # Encode header and payload
    header_b64 = self.base64url_encode(json.dumps(header).encode())
    payload_b64 = self.base64url_encode(json.dumps(payload).encode())
    
    # Create signature
    message = header_b64 + b'.' + payload_b64
    signature = self.hmac.compute(message)
    signature_b64 = self.base64url_encode(signature)
    
    # Combine token
    return (header_b64 + b'.' + payload_b64 + b'.' + signature_b64).decode()

def verify_token(self, token: str) -> dict:
    """Verify and decode JWT token"""
    import time
    
    parts = token.encode().split(b'.')
    if len(parts) != 3:
        raise ValueError("Invalid JWT format")
    
    header_b64, payload_b64, signature_b64 = parts
    
    # Verify signature
    message = header_b64 + b'.' + payload_b64
    expected_signature = self.hmac.compute(message)
    provided_signature = self.base64url_decode(signature_b64)
    
    if not self.constant_time_compare(expected_signature, provided_signature):
        raise ValueError("Invalid JWT signature")
    
    # Decode payload
    payload = json.loads(self.base64url_decode(payload_b64))
    
    # Check expiration
    if payload.get("exp", 0) < time.time():
        raise ValueError("JWT token expired")
    
    return payload

def base64url_encode(self, data: bytes) -> bytes:
    """Base64URL encoding without padding"""
    return base64.urlsafe_b64encode(data).rstrip(b'=')

def base64url_decode(self, data: bytes) -> bytes:
    """Base64URL decoding with padding"""
    padding = 4 - (len(data) % 4)
    if padding != 4:
        data += b'=' * padding
    return base64.urlsafe_b64decode(data)

File integrity verification

class HMACFileIntegrity: def init(self, master_key: bytes): self.master_key = master_key self.hmac = HMAC(master_key, SHA256())

def compute_file_hmac(self, filepath: str, chunk_size: int = 8192) -> bytes:
    """Compute HMAC for large file"""
    hmac = HMAC(self.master_key, SHA256())
    
    with open(filepath, 'rb') as f:
        while chunk := f.read(chunk_size):
            hmac.update(chunk)
    
    return hmac.finalize()

def create_manifest(self, file_paths: list) -> dict:
    """Create integrity manifest for multiple files"""
    manifest = {
        'version': '1.0',
        'algorithm': 'HMAC-SHA256',
        'files': {}
    }
    
    for filepath in file_paths:
        file_hmac = self.compute_file_hmac(filepath)
        file_size = os.path.getsize(filepath)
        
        manifest['files'][filepath] = {
            'hmac': file_hmac.hex(),
            'size': file_size
        }
    
    return manifest

def verify_manifest(self, manifest: dict) -> dict:
    """Verify files against manifest"""
    results = {
        'valid': [],
        'invalid': [],
        'missing': []
    }
    
    for filepath, file_info in manifest['files'].items():
        if not os.path.exists(filepath):
            results['missing'].append(filepath)
            continue
        
        # Verify size
        if os.path.getsize(filepath) != file_info['size']:
            results['invalid'].append(filepath)
            continue
        
        # Verify HMAC
        computed_hmac = self.compute_file_hmac(filepath)
        expected_hmac = bytes.fromhex(file_info['hmac'])
        
        if computed_hmac == expected_hmac:
            results['valid'].append(filepath)
        else:
            results['invalid'].append(filepath)
    
    return results

Database record authentication

class HMACDatabaseAuth: def init(self, table_key: bytes): self.table_key = table_key self.hmac = HMAC(table_key, SHA256())

def authenticate_record(self, record_data: dict) -> str:
    """Create HMAC for database record"""
    # Create canonical representation
    canonical = json.dumps(record_data, sort_keys=True, separators=(',', ':'))
    
    # Compute HMAC
    tag = self.hmac.compute(canonical.encode())
    return tag.hex()

def verify_record(self, record_data: dict, stored_hmac: str) -> bool:
    """Verify database record authenticity"""
    computed_hmac = self.authenticate_record(record_data)
    return self.constant_time_compare(
        bytes.fromhex(computed_hmac),
        bytes.fromhex(stored_hmac)
    )

def batch_authenticate(self, records: list) -> list:
    """Authenticate multiple records efficiently"""
    results = []
    
    for record in records:
        hmac_tag = self.authenticate_record(record)
        results.append({
            'record': record,
            'hmac': hmac_tag
        })
    
    return results

Network protocol authentication

class HMACProtocol: def init(self, shared_secret: bytes): self.shared_secret = shared_secret self.sequence_number = 0

def create_authenticated_message(self, message: bytes) -> bytes:
    """Create authenticated protocol message"""
    # Message header
    header = struct.pack('>HI', 
                       0x0100,  # Protocol version
                       self.sequence_number)
    
    # Compute HMAC over header + message
    hmac = HMAC(self.shared_secret, SHA256())
    hmac.update(header)
    hmac.update(message)
    tag = hmac.finalize()
    
    self.sequence_number += 1
    
    # Return: header + message + tag
    return header + message + tag

def verify_authenticated_message(self, packet: bytes) -> tuple:
    """Verify and extract authenticated message"""
    if len(packet) < 38:  # 6 bytes header + 32 bytes tag minimum
        raise ValueError("Packet too short")
    
    # Parse packet
    header = packet[:6]
    message = packet[6:-32]
    tag = packet[-32:]
    
    # Extract sequence number
    version, seq_num = struct.unpack('>HI', header)
    
    if version != 0x0100:
        raise ValueError("Invalid protocol version")
    
    # Verify HMAC
    hmac = HMAC(self.shared_secret, SHA256())
    hmac.update(header)
    hmac.update(message)
    expected_tag = hmac.finalize()
    
    if not self.constant_time_compare(tag, expected_tag):
        raise ValueError("HMAC verification failed")
    
    return message, seq_num ```

Implementation Details

# HMAC algorithm implementation (simplified)
class HMACCore:
    def __init__(self, key: bytes, hash_function):
        self.hash_function = hash_function
        self.block_size = hash_function.block_size
        self.output_size = hash_function.digest_size
        
        # Process key
        if len(key) > self.block_size:
            # Hash long keys
            key = hash_function.hash(key)
        
        # Pad key to block size
        if len(key) < self.block_size:
            key = key + b'\x00' * (self.block_size - len(key))
        
        # Create inner and outer padding
        self.inner_key = bytes(k ^ 0x36 for k in key)
        self.outer_key = bytes(k ^ 0x5c for k in key)
    
    def compute(self, message: bytes) -> bytes:
        """Compute HMAC"""
        # Inner hash: H(K ⊕ ipad || message)
        inner_hasher = self.hash_function()
        inner_hasher.update(self.inner_key)
        inner_hasher.update(message)
        inner_hash = inner_hasher.finalize()
        
        # Outer hash: H(K ⊕ opad || inner_hash)
        outer_hasher = self.hash_function()
        outer_hasher.update(self.outer_key)
        outer_hasher.update(inner_hash)
        
        return outer_hasher.finalize()
    
    def verify(self, message: bytes, tag: bytes) -> bool:
        """Verify HMAC tag"""
        computed_tag = self.compute(message)
        return self.constant_time_compare(computed_tag, tag)

Security Considerations

Best Practices

  1. Key Management
    • Use keys at least as long as hash output
    • Generate keys using cryptographically secure random sources
    • Rotate keys regularly
    • Store keys securely
  2. Hash Function Selection
    • Use SHA-256 or stronger for new applications
    • Avoid SHA-1 for security-critical applications
    • Consider BLAKE2b or BLAKE3 for performance
    • Ensure hash function is collision-resistant
  3. Tag Length
    • Use full hash output for maximum security
    • Minimum 128 bits for truncated tags
    • Consider bandwidth vs security trade-offs
    • Document truncation decisions
  4. Implementation Security
    • Use constant-time comparison for verification
    • Clear sensitive data from memory
    • Protect against side-channel attacks
    • Validate all inputs

Common Pitfalls

# DON'T: Use weak keys
weak_key = b"password"  # Too short and predictable
# WRONG: Weak key compromises security
hmac = HMAC(weak_key, SHA256())

# DO: Use strong, random keys
strong_key = os.urandom(32)  # 256 bits of randomness
hmac = HMAC(strong_key, SHA256())

# DON'T: Use timing-vulnerable comparison
def bad_verify(tag1, tag2):
    # WRONG: Early return leaks timing information
    for i in range(len(tag1)):
        if tag1[i] != tag2[i]:
            return False
    return True

# DO: Use constant-time comparison
def secure_verify(tag1, tag2):
    if len(tag1) != len(tag2):
        return False
    result = 0
    for a, b in zip(tag1, tag2):
        result |= a ^ b
    return result == 0

# DON'T: Truncate tags too aggressively
tag = hmac.compute(message)
short_tag = tag[:4]  # WRONG: Only 32 bits, easily brute-forced

# DO: Use adequate tag length
tag = hmac.compute(message)
secure_tag = tag[:16]  # 128 bits minimum for security

# DON'T: Reuse keys across different contexts
api_hmac = HMAC(shared_key, SHA256())
file_hmac = HMAC(shared_key, SHA256())  # WRONG: Same key for different purposes

# DO: Derive separate keys for different purposes
from metamui_crypto import HKDF
hkdf = HKDF(SHA256())
api_key = hkdf.derive(shared_key, b"api_auth", 32)
file_key = hkdf.derive(shared_key, b"file_integrity", 32)

Performance Characteristics

Benchmarks

Hash Function Message Size Throughput Time Relative Speed
HMAC-SHA256 1 KB 245 MB/s 4.1 μs 1.00x
HMAC-SHA512 1 KB 198 MB/s 5.1 μs 0.81x
HMAC-BLAKE2b 1 KB 892 MB/s 1.1 μs 3.64x
HMAC-BLAKE3 1 KB 1.2 GB/s 0.83 μs 4.90x

Performance Optimization

# Batch HMAC computation
class BatchHMAC:
    def __init__(self, key: bytes, hash_function):
        self.hmac = HMAC(key, hash_function)
    
    def compute_batch(self, messages: list) -> list:
        """Compute HMAC for multiple messages efficiently"""
        results = []
        
        for message in messages:
            tag = self.hmac.compute(message)
            results.append(tag)
        
        return results

# Streaming HMAC for large data
def stream_hmac(key: bytes, data_stream, hash_function):
    """Compute HMAC for streaming data"""
    hmac = HMAC(key, hash_function)
    
    for chunk in data_stream:
        hmac.update(chunk)
    
    return hmac.finalize()

# Pre-computed HMAC for repeated operations
class PrecomputedHMAC:
    def __init__(self, key: bytes, hash_function):
        self.hash_function = hash_function
        
        # Pre-compute key processing
        block_size = hash_function.block_size
        if len(key) > block_size:
            key = hash_function.hash(key)
        if len(key) < block_size:
            key = key + b'\x00' * (block_size - len(key))
        
        self.inner_key = bytes(k ^ 0x36 for k in key)
        self.outer_key = bytes(k ^ 0x5c for k in key)
    
    def compute(self, message: bytes) -> bytes:
        """Fast HMAC computation with pre-computed keys"""
        # Inner hash
        inner = self.hash_function()
        inner.update(self.inner_key)
        inner.update(message)
        inner_result = inner.finalize()
        
        # Outer hash
        outer = self.hash_function()
        outer.update(self.outer_key)
        outer.update(inner_result)
        
        return outer.finalize()

Use Cases

1. Secure API Authentication

class SecureAPIClient:
    def __init__(self, api_key: str, api_secret: str):
        self.api_key = api_key
        self.api_secret = api_secret.encode()
    
    def make_request(self, method: str, endpoint: str, 
                    data: dict = None) -> dict:
        """Make authenticated API request"""
        import time
        import requests
        
        # Prepare request
        timestamp = str(int(time.time()))
        body = json.dumps(data) if data else ""
        
        # Create signature
        message = f"{method}\n{endpoint}\n{timestamp}\n{body}"
        hmac = HMAC(self.api_secret, SHA256())
        signature = hmac.compute(message.encode()).hex()
        
        # Headers
        headers = {
            'X-API-Key': self.api_key,
            'X-Timestamp': timestamp,
            'X-Signature': signature,
            'Content-Type': 'application/json'
        }
        
        # Make request
        response = requests.request(
            method=method,
            url=f"https://api.example.com{endpoint}",
            headers=headers,
            data=body if body else None
        )
        
        return response.json()

2. Message Queue Authentication

class HMACMessageQueue:
    def __init__(self, queue_key: bytes):
        self.queue_key = queue_key
        self.hmac = HMAC(queue_key, SHA256())
    
    def publish_message(self, topic: str, message: dict) -> dict:
        """Publish authenticated message to queue"""
        import time
        
        # Create message envelope
        envelope = {
            'topic': topic,
            'timestamp': time.time(),
            'message_id': os.urandom(16).hex(),
            'payload': message
        }
        
        # Serialize and sign
        serialized = json.dumps(envelope, sort_keys=True)
        signature = self.hmac.compute(serialized.encode())
        
        return {
            'envelope': envelope,
            'signature': signature.hex()
        }
    
    def verify_message(self, signed_message: dict) -> dict:
        """Verify and extract message"""
        envelope = signed_message['envelope']
        signature = bytes.fromhex(signed_message['signature'])
        
        # Verify signature
        serialized = json.dumps(envelope, sort_keys=True)
        expected_signature = self.hmac.compute(serialized.encode())
        
        if not self.constant_time_compare(signature, expected_signature):
            raise ValueError("Message authentication failed")
        
        return envelope['payload']

3. Configuration File Protection

class HMACConfigProtection:
    def __init__(self, config_key: bytes):
        self.config_key = config_key
        self.hmac = HMAC(config_key, SHA256())
    
    def save_config(self, config: dict, filepath: str):
        """Save configuration with HMAC protection"""
        # Serialize configuration
        config_json = json.dumps(config, sort_keys=True, indent=2)
        
        # Compute HMAC
        hmac_tag = self.hmac.compute(config_json.encode())
        
        # Create protected configuration
        protected_config = {
            'version': '1.0',
            'config': config,
            'hmac': hmac_tag.hex()
        }
        
        # Save to file
        with open(filepath, 'w') as f:
            json.dump(protected_config, f, indent=2)
    
    def load_config(self, filepath: str) -> dict:
        """Load and verify configuration"""
        # Load from file
        with open(filepath, 'r') as f:
            protected_config = json.load(f)
        
        # Extract components
        config = protected_config['config']
        stored_hmac = bytes.fromhex(protected_config['hmac'])
        
        # Verify HMAC
        config_json = json.dumps(config, sort_keys=True, indent=2)
        computed_hmac = self.hmac.compute(config_json.encode())
        
        if not self.constant_time_compare(stored_hmac, computed_hmac):
            raise ValueError("Configuration integrity check failed")
        
        return config

Comparison with Other MACs

HMAC vs Poly1305

Feature HMAC Poly1305
Key Reuse Yes No (one-time)
Speed Moderate Very Fast
Security Basis Hash function Information-theoretic
Tag Size Variable 16 bytes
Use Case General purpose Stream ciphers

HMAC vs CMAC

Feature HMAC CMAC
Primitive Hash function Block cipher
Key Size Flexible Block cipher key size
Performance Hash-dependent Cipher-dependent
Standardization RFC 2104 NIST SP 800-38B

When to Use HMAC

# Use HMAC for general authentication
if need_general_purpose_mac:
    mac = HMAC(key, SHA256())

# Use HMAC for API authentication
if building_api_authentication:
    mac = HMAC(api_secret, SHA256())

# Use HMAC for file integrity
if need_file_integrity_check:
    mac = HMAC(file_key, SHA256())

# Use HMAC for protocol authentication
if implementing_secure_protocol:
    mac = HMAC(protocol_key, SHA256())

Migration Guide

From Custom MAC to HMAC

# Before: Custom MAC (insecure)
def custom_mac(key, message):
    # WRONG: Simple concatenation is vulnerable
    return hashlib.sha256(key + message).digest()

# After: HMAC (secure)
from metamui_crypto import HMAC, SHA256
hmac = HMAC(key, SHA256())
tag = hmac.compute(message)

From MD5-based MAC to HMAC

# Before: MD5-based MAC (weak)
import hashlib
def md5_mac(key, message):
    return hashlib.md5(key + message).digest()

# After: HMAC with strong hash function
hmac = HMAC(key, SHA256())
tag = hmac.compute(message)

Test Vectors

RFC 2104 Test Vectors

# Test Case 1
key = b'\x0b' * 20
message = b"Hi There"
hmac = HMAC(key, SHA256())
tag = hmac.compute(message)
assert tag.hex() == "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"

# Test Case 2
key = b"Jefe"
message = b"what do ya want for nothing?"
hmac = HMAC(key, SHA256())
tag = hmac.compute(message)
assert tag.hex() == "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"

# Test Case 3
key = b'\xaa' * 20
message = b'\xdd' * 50
hmac = HMAC(key, SHA256())
tag = hmac.compute(message)
assert tag.hex() == "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"

Incremental Computation Test

# Test incremental vs one-shot computation
key = os.urandom(32)
message = b"The quick brown fox jumps over the lazy dog"

# One-shot
hmac1 = HMAC(key, SHA256())
tag1 = hmac1.compute(message)

# Incremental
hmac2 = HMAC(key, SHA256())
hmac2.update(b"The quick brown ")
hmac2.update(b"fox jumps over ")
hmac2.update(b"the lazy dog")
tag2 = hmac2.finalize()

assert tag1 == tag2
  • Poly1305 - High-speed one-time authenticator
  • SipHash - Fast short-input PRF
  • HMAC-DRBG - HMAC-based random number generator
  • SHA-256 - Common hash function for HMAC
  • Blake3 - Fast alternative hash function

References

  • RFC 2104 - HMAC: Keyed-Hashing for Message Authentication
  • FIPS 198-1 - The Keyed-Hash Message Authentication Code
  • RFC 4231 - Identifiers and Test Vectors for HMAC-SHA-224, HMAC-SHA-256, HMAC-SHA-384, and HMAC-SHA-512
  • NIST SP 800-107 - Recommendation for Applications Using Approved Hash Algorithms