Key Takeaways
- →HMAC (Hash-based Message Authentication Code) is a specific construction for computing a keyed hash of a message, defined in FIPS PUB 198 (NIST, 2002) and RFC 2104 (Krawczyk, Bellare, Canetti, 1997). It uses a cryptographic hash function (SHA-256, SHA-384, SHA-512) combined with a secret key to produce a fixed-size authentication tag that verifies both the integrity and authenticity of a message.
- →The HMAC construction uses two key-derived values xor'ed with distinct constants ipad (0x36) and opad (0x5C). The inner hash is H((K ⊕ ipad) || message), and the outer hash is H((K ⊕ opad) || inner_hash). This two-layer construction prevents length-extension attacks that would break naive constructions like H(K || message) or H(message || K).
- →HMAC security depends on the strength of the underlying hash function and the secrecy of the key. With a 256-bit key and SHA-256, HMAC provides 256-bit security against key-recovery attacks. The best known generic attack on HMAC is a birthday-bound forgery attack requiring approximately 2^n/2 queries, where n is the output length of the hash function.
- →HMAC is widely used in web API authentication (AWS Signature V4, HMAC-based API keys), JWT signing (HS256, HS384, HS512 algorithms), TLS key derivation, IPsec, SSH, and financial transaction verification (ISO 8583, EMV payment cards). It is the recommended MAC construction in all NIST and IETF standards.
- →The HMAC specification requires keys to be at least as long as the hash function's output (256 bits for SHA-256) and recommends keys no longer than the block size of the hash function (512 bits for SHA-256). Keys longer than the block size are first hashed, then used as the HMAC key.
HMAC: The Keyed Hash Construction That Authenticates APIs, JWTs, and Financial Transactions
In 1996, cryptographers Mihir Bellare, Ran Canetti, and Hugo Krawczyk published "Keying Hash Functions for Message Authentication" at CRYPTO 96, revealing that naive keyed hash constructions — H(K || message), H(message || K), and H(message) xor H(K) — all had exploitable weaknesses. The simplest, H(K || message), was vulnerable to the length-extension attack: an attacker who knows H(K || M) can compute H(K || M || padding || extra) without knowing K, forging valid tags for appended messages. The three researchers proposed a two-layer construction using inner and outer padding (ipad and opad) that eliminated these attacks while maintaining the performance of the underlying hash function. Their construction was standardized in RFC 2104 (1997) and adopted as FIPS PUB 198 (2002, updated 2008 as FIPS PUB 198-1). The HMAC (Hash-based Message Authentication Code) is now the most widely deployed MAC in the world, securing billions of API calls, JWT tokens, and TLS connections daily.
Key takeaway: HMAC is a keyed hash construction that uses H((K ⊕ ipad) || message) then H((K ⊕ opad) || result) to prevent length-extension attacks. Standardized as FIPS 198 and RFC 2104, it provides data integrity and origin authentication with any cryptographic hash function.
- What HMAC Actually Is and Why the Two-Layer Construction Matters
- The HMAC Algorithm Step by Step
- HMAC vs Digital Signatures: When to Use Each
- HMAC in Web API Authentication and JWT
- HMAC Security Properties and Key Management
- Frequently Asked Questions
What HMAC Actually Is and Why the Two-Layer Construction Matters
HMAC is a keyed-hash message authentication code — a cryptographic checksum computed using a hash function and a secret key that simultaneously verifies that a message has not been tampered with (integrity) and that it originates from a party who knows the secret key (authenticity). This combination of services — integrity + origin authentication — distinguishes HMAC from plain hash functions (which provide only integrity).
The distinguishing feature of HMAC is its two-layer construction. Naive constructions like H(K || M) fail because of length-extension attacks. If an attacker sees H(K || M) and the length of K, they can compute H(K || M || P || M') for any M' without knowing K — because iterative hash functions like SHA-256 process the input in blocks and preserve internal state. The length extension allows forgery of valid tags for messages that contain M as a prefix.
HMAC solves this by hashing in two directions. The inner hash incorporates the message and the key xor'ed with ipad (0x3636...). The outer hash wraps the inner hash with the key xor'ed with opad (0x5C5C...). An attacker who extracts the inner hash output can attempt to extend it — but the inner hash is itself hashed by the outer layer, which uses a different key-derived value. The inner hash output is not directly exposed, and even if it were, extending it would produce a tag under a different key (K ⊕ opad instead of K ⊕ ipad).
The RFC 2104 authors proved that if the underlying hash function is collision-resistant, HMAC is secure against adaptive chosen-message attacks — the standard security definition for MACs. The proof, formalized by Bellare in 2006 (New Proofs for NMAC and HMAC, Journal of Cryptology), shows that HMAC's security reduces to the pseudorandom function property of the hash function, a weaker assumption than collision resistance.
HMAC variants and their parameters
| Algorithm | Hash function | Output length | Key length (recommended) | Block size |
|---|---|---|---|---|
| HMAC-MD5 | MD5 | 128 bits / 16 bytes | 128 bits | 512 bits |
| HMAC-SHA1 | SHA-1 | 160 bits / 20 bytes | 160 bits | 512 bits |
| HMAC-SHA256 | SHA-256 | 256 bits / 32 bytes | 256 bits | 512 bits |
| HMAC-SHA384 | SHA-384 | 384 bits / 48 bytes | 384 bits | 1024 bits |
| HMAC-SHA512 | SHA-512 | 512 bits / 64 bytes | 512 bits | 1024 bits |
| HMAC-SHA3-256 | SHA3-256 | 256 bits / 32 bytes | 256 bits | 1088 bits |
The HMAC Algorithm Step by Step
HMAC uses the following construction, applied to any iterated cryptographic hash function H that processes blocks of size B bytes and produces an output of L bytes:
HMAC(K, message) = H((K' ⊕ opad) || H((K' ⊕ ipad) || message))
Where:
- K' is the key, processed: if K is longer than B bytes, K' = H(K); otherwise K' = K padded with zeros to B bytes
- ipad = 0x36 repeated B times (the byte 0x36 = 00110110 binary)
- opad = 0x5C repeated B times (the byte 0x5C = 01011100 binary)
- ⊕ is bitwise XOR
- || is concatenation
Worked example: HMAC-SHA256 for message "hello" with key "key"
Step 1: Process the key. Key "key" = 0x6B6579 (3 bytes). Block size B = 64 bytes (512 bits) for SHA-256. Pad with zeros: K' = 0x6B6579 followed by 61 zero bytes.
Step 2: Compute K' ⊕ ipad. ipad = 0x363636...36 (64 bytes of 0x36). K' ⊕ ipad = 0x5D534F followed by 61 bytes of 0x36 (since 0x00 ⊕ 0x36 = 0x36).
Step 3: Compute inner hash. H((K' ⊕ ipad) || message) = SHA-256(0x5D534F363636...36 || "hello") = 0x8B5D0C1C0B1B9F0E... (32 bytes).
Step 4: Compute K' ⊕ opad. opad = 0x5C5C5C...5C (64 bytes of 0x5C). K' ⊕ opad = 0x373925 followed by 61 bytes of 0x5C (since 0x00 ⊕ 0x5C = 0x5C).
Step 5: Compute outer hash. H((K' ⊕ opad) || inner_hash) = SHA-256(0x3739255C5C5C...5C || 0x8B5D...) = 0xF5700C7A... (32 bytes).
Final HMAC-SHA256("key", "hello") = f5700c7a... (32 bytes, 64 hex chars).
Verification. The receiver computes HMAC-SHA256 with the same key and message. If the computed tag matches the received tag, the message is authentic and unmodified. The comparison must use a constant-time equality check (not memcmp or == that short-circuits on first mismatch) to prevent timing side-channel attacks. Many libraries provide hmac.compare_digest() or crypto.timingSafeEqual() for this purpose.
Python implementation:
import hmac
import hashlib
tag = hmac.new(b"key", b"hello", hashlib.sha256).hexdigest()
# tag = "f5700c7a..."
HMAC vs Digital Signatures: When to Use Each
HMAC and digital signatures both provide integrity and authentication, but they differ in key management and non-repudiation.
Comparison of HMAC and Digital Signatures
| Property | HMAC | Digital Signature (RSA, ECDSA, EdDSA) |
|---|---|---|
| Key type | Symmetric (single shared secret) | Asymmetric (private + public key pair) |
| Verification | Requires shared secret | Anyone with public key can verify |
| Non-repudiation | No — either party could have created the tag | Yes — only the private key holder could create the signature |
| Performance | Very fast (hash function only) | Slower (exponentiation or scalar multiplication) |
| Key distribution | Out-of-band secure channel required | Public key can be distributed insecurely (certificates) |
| Standard | FIPS 198, RFC 2104 | FIPS 186-5 (RSA, ECDSA, EdDSA) |
| Typical use | API authentication, JWT, TLS | Code signing, certificates, blockchain |
When to use HMAC: Use HMAC when both parties already share a secret key (API client and server, same-system components, mutually authenticated peers). HMAC is faster, simpler, and produces smaller tags (32 bytes for HMAC-SHA256 vs 64-256 bytes for RSA signatures). The primary requirement is a secure key distribution channel — if you can safely share the key, HMAC is the correct choice.
When to use digital signatures: Use digital signatures when the verifier should not have the ability to create valid tags (non-repudiation), when the key needs to be distributed publicly (software distribution, TLS certificates), or when the number of signers is small and the number of verifiers is large (a certificate authority signs once, millions of browsers verify). Digital signatures also enable delegation: a server can delegate signing authority to a separate hardware security module (HSM) while keeping the verification key public.
HMAC in Web API Authentication and JWT
HMAC is the foundation of several widely deployed web authentication protocols.
AWS Signature Version 4 (AWS SigV4). Every request to AWS services (S3, EC2, Lambda, DynamoDB) is authenticated using HMAC-SHA256. The client constructs a canonical request (HTTP method, path, query string, headers, payload hash), computes HMAC-SHA256 of that request using a derived signing key, and includes the resulting signature in the Authorization header. The derived signing key is itself computed through a chain of HMAC calls: HMAC-SHA256(("AWS4" + secret_key), date) → HMAC-SHA256(date_key, region) → HMAC-SHA256(region_key, service) → HMAC-SHA256(service_key, "aws4_request") = signing_key. This key-derivation chain ensures that a compromised date-specific signature does not leak the master secret key. AWS SigV4 was introduced in 2012 and is required for all AWS API operations.
JWT with HMAC (HS256, HS384, HS512). JSON Web Tokens (RFC 7519, Jones et al., 2015) can be secured with HMAC using the HS256 (HMAC-SHA256), HS384 (HMAC-SHA384), or HS512 (HMAC-SHA512) algorithms. The JWT structure is base64url(header) || "." || base64url(payload) || "." || base64url(signature). The signature is HMAC of the first two segments using the shared secret key. JWT libraries handle the entire process — the developer only provides the key. A critical security note: the HS256 key is a shared secret — it must be distributed to every service that needs to verify the token. If the key is compromised, anyone can forge tokens. For distributed systems with many verifiers, RS256 (RSA signature) or ES256 (ECDSA signature) is preferred.
Example JWT with HS256:
Header: {"alg": "HS256", "typ": "JWT"} Payload: {"sub": "1234567890", "name": "John Doe", "iat": 1516239022}
HMAC-SHA256 of base64url(header) || "." || base64url(payload) with key "secret" produces a 256-bit (32-byte) signature, base64url-encoded to 43 characters. The complete JWT is: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
HMAC-based One-Time Passwords (HOTP/TOTP). RFC 4226 (HOTP, M'Raihi et al., 2005) and RFC 6238 (TOTP, M'Raihi et al., 2011) use HMAC-SHA1 to generate one-time passwords. HOTP computes HMAC-SHA1(counter) and truncates the result to a 6-8 digit decimal code. TOTP uses time-based counter: counter = floor(unix_time / time_step), with the default time_step being 30 seconds. These algorithms are the basis for Google Authenticator, Authy, Duo Security, and all TOTP-based two-factor authentication. The shared secret between server and authenticator app is the HMAC key.
HMAC Security Properties and Key Management
HMAC's security depends on three factors: the strength of the underlying hash function, the secrecy and entropy of the key, and the implementation's resistance to side-channel attacks.
Hash function strength. HMAC inherits the collision resistance and preimage resistance of the underlying hash. HMAC-MD5 (using the broken MD5 hash) is not recommended because practical collision attacks against MD5 (Xie and Feng, 2008; Stevens et al., 2012) could theoretically be extended to HMAC-MD5 in certain scenarios. HMAC-SHA1 remains secure for now (no practical HMAC-SHA1 forgery attack exists as of 2026), but the SHA-1 collision attack (SHAttered, Google/CWI, 2017) has prompted deprecation. NIST SP 800-107 Rev. 1 (2012) recommends HMAC-SHA256 or higher for new deployments.
Key entropy and length. The key must be generated from a cryptographically secure random number generator (CSPRNG) with sufficient entropy. For HMAC-SHA256, a 256-bit (32-byte) random key provides 256 bits of security against brute-force key search. Keys shorter than the hash output length reduce security: a 128-bit HMAC-SHA256 key provides only 128 bits of security. Keys longer than the block size (64 bytes for SHA-256) are first hashed, which reduces their effective entropy to the hash output length (256 bits). Use exactly 32 random bytes for HMAC-SHA256, 64 random bytes for HMAC-SHA512.
Side-channel resistance. HMAC implementations must be constant-time to prevent timing side-channel attacks. The verification function must compare the computed and received tags using a constant-time comparison that does not short-circuit on the first differing byte. In Python, use hmac.compare_digest(). In Node.js, use crypto.timingSafeEqual(). In Go, use hmac.Equal(). In Java, use MessageDigest.isEqual(). The JWT standard (RFC 7519) explicitly recommends constant-time comparison in its security considerations.