Cryptography
HMAC and MAC
JWT tokens signed with HS256 use HMAC-SHA256. AWS API requests are authenticated with HMAC-SHA256 signatures. TLS 1.3 session keys are derived via HKDF built on HMAC. If HMAC were replaced with raw SHA256(key || message), every one of these systems could be forged. A single correct choice - HMAC over naive hashing - protects trillions of API calls daily.
- **JWT HS256**: HMAC-SHA256 over header.payload. A length extension attack on H(secret || payload) would allow arbitrary claim injection without the secret key.
- **AWS Signature Version 4**: HMAC-SHA256 signing chain. Each signing step creates a derived HMAC from the previous step - a HKDF-like construction protecting against request replay.
- **TLS 1.3 key schedule**: HKDF (built on HMAC-SHA256) derives all session keys. Without HMAC correctness, TLS forward secrecy collapses.
- **WireGuard**: ChaCha20-Poly1305 for all tunnel encryption. Poly1305 one-time key derived per packet from ChaCha20 keystream.
MAC vs Hash: What is the Difference?
A hash function H(m) is a public function: anyone can verify or compute it. A MAC (Message Authentication Code) takes a key: MAC(k, m). Without the key, the MAC cannot be computed or verified. A hash provides integrity only; a MAC provides both integrity and authentication.
H(key || message) is broken for Merkle-Damgard hashes (MD5, SHA-1, SHA-256) due to length extension. H(message || key) is broken via birthday collision. Only HMAC or a dedicated MAC (CMAC, Poly1305) is safe.
Why is MAC(k, m) = SHA256(k || m) insecure?
HMAC: The Correct MAC Construction
HMAC (RFC 2104) wraps any Merkle-Damgard hash to produce a secure MAC. The two-pass construction with distinct inner/outer keys prevents length extension and birthday attacks simultaneously.
HMAC-SHA256 is the PRF in TLS 1.3 key schedule (via HKDF), JWT HS256 signatures, AWS request signing (Signature Version 4), and SSH session integrity. RFC 2202 provides test vectors confirming correct implementation.
Why must MAC verification use constant-time comparison (hmac.compare_digest) instead of ==?
CMAC: Block-Cipher-Based MAC
CMAC (NIST SP 800-38B) is a MAC built from a block cipher rather than a hash function. Based on CBC-MAC with subkey derivation, it avoids the length extension vulnerability inherent in naive CBC-MAC. Common in hardware security modules and payment systems where AES hardware is available.
CMAC-AES provides 64-bit security (half the block size) against forgery - sufficient for most use cases. For higher security margins, use HMAC-SHA256 (128-bit forgery resistance). The tradeoff: CMAC needs only AES hardware, HMAC needs a hash implementation.
What vulnerability does naive CBC-MAC have that CMAC fixes?
Poly1305: MAC for High-Throughput Systems
Poly1305 (Bernstein, 2004) is a one-time MAC based on polynomial evaluation over GF(2^130-5). Paired with ChaCha20, it forms the ChaCha20-Poly1305 AEAD used in TLS 1.3 and WireGuard. One key insight: Poly1305 uses a one-time key (derived per message from the cipher), so reuse of the MAC key is catastrophic.
Cloudflare serves ~40% of global HTTPS traffic using ChaCha20-Poly1305. Each TLS record generates a fresh Poly1305 key via ChaCha20 keystream. WireGuard uses ChaCha20-Poly1305 as its only authenticated encryption scheme - simplicity as a security property.
Poly1305 is a general-purpose MAC that can be used with a long-term key like HMAC
Poly1305 is a one-time MAC: the r component must be fresh and unpredictable for every message; in practice it is always derived from a cipher (ChaCha20) per message
Polynomial MACs are one-time constructions by design. Safe use always pairs Poly1305 with a cipher that generates a fresh key per message.
Why must the Poly1305 r key never be reused across messages?
Key Ideas
- **Hash != MAC**: a hash is public; a MAC requires a key. H(key || message) is broken due to length extension. Only HMAC or dedicated MACs are safe.
- **HMAC**: two-pass construction H(k XOR opad || H(k XOR ipad || m)). Standard for JWT, AWS signing, HKDF. Always use constant-time comparison for verification.
- **CMAC**: AES-based MAC with subkey derivation. FIPS-approved, used in EMV and IPsec. 64-bit forgery security (half block size).
- **Poly1305**: one-time polynomial MAC. Paired with ChaCha20 as AEAD. Reusing the r key enables forgery via polynomial algebra.
Related Topics
MACs appear in every authenticated cryptographic protocol:
- Hash Functions — HMAC wraps any Merkle-Damgard hash; the underlying hash's security properties bound HMAC's security.
- Key Management (HKDF) — HKDF uses HMAC as its PRF for both Extract and Expand phases.
- TLS 1.3 — Both HMAC-SHA256 (key schedule) and ChaCha20-Poly1305 (record encryption) appear in TLS 1.3.
Вопросы для размышления
- A developer stores JWT secrets in a database and uses H(secret || payload) for signing. List the specific attacks this enables.
- Why does TLS 1.3 use HMAC inside HKDF rather than a raw hash, even though the key is already uniformly random?
- Poly1305 reuse is catastrophic, yet WireGuard uses it safely for millions of packets. How?