Modern Cryptography · 6.2

🔖 HMAC & Message Authentication Codes

Proving data came from someone with the right key — and wasn't changed⏱ ~2 min

A hash function verifies integrity (data wasn't changed) but not authenticity (who sent it). Anyone can compute SHA-256. A Message Authentication Code (MAC) adds a secret key — only someone with the key can produce or verify the MAC.

HMAC — Hash-based Message Authentication Code

math
HMAC(K, M) = H((K ⊕ opad) || H((K ⊕ ipad) || M))
Where:
K = secret key
M = message
H = hash function (e.g., SHA-256)
opad = outer padding constant (0x5c repeated)
ipad = inner padding constant (0x36 repeated)
|| = concatenation
Result: HMAC-SHA256 produces a 256-bit authentication tag
Anyone with key K can verify; without K, the tag cannot be forged

HMAC vs Digital Signatures — When to Use Each

HMAC (Symmetric MAC)
  • Both sender and receiver share the same secret key
  • Very fast — just two hash computations
  • Provides authentication and integrity
  • Does NOT provide non-repudiation — either party could have created it
  • Use for: API authentication, JWT signing (HS256), internal microservice auth
Digital Signature (Asymmetric)
  • Sender signs with private key; anyone can verify with public key
  • Slower — requires asymmetric math
  • Provides authentication, integrity, AND non-repudiation
  • Third parties can verify without a shared secret
  • Use for: public documents, software signing, TLS certificates, email signing

HMAC in Practice

python
import hmac
import hashlib
key = b'super-secret-key-at-least-32-bytes-long'
message = b'Transfer $100 to account 12345'
# Compute HMAC-SHA256
mac = hmac.new(key, message, hashlib.sha256).hexdigest()
print(mac) # 64-character hex string
# Verify (use hmac.compare_digest — NEVER use ==)
def verify_mac(key, message, received_mac):
expected = hmac.new(key, message, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, received_mac)
# hmac.compare_digest prevents timing attacks:
# a == b short-circuits on first mismatch, leaking mac length info
# compare_digest always takes the same time regardless of where mismatch occurs
🔒 SecurityAlways use a constant-time comparison function (hmac.compare_digest in Python, crypto.timingSafeEqual in Node.js) when comparing MACs or tokens. String equality operators short-circuit at the first mismatched byte, leaking timing information that lets attackers forge MACs one byte at a time (timing oracle attack).
🧠Quick Checkfirst try = +5 XP

What does HMAC add that a plain hash doesn't?

0 XP🔥 0 days