# HMAC: How Keyed-Hash Authentication Works

**Source**: https://quantumsequrity.com/blog/hmac-family-explained
**Category**: Cryptography Foundations

---

[← Back to Blog](../../blog.html) Cryptography Foundations

# HMAC: How Keyed-Hash Authentication Works

11 min read

When two computers exchange a message, the receiver needs two things: confidence that the message has not been tampered with, and confidence that it actually came from someone who knows the shared secret. Encryption alone does not give either of these. You can encrypt a message and an attacker can still flip bits in the ciphertext, sometimes producing a meaningfully different plaintext after decryption. To detect tampering and authenticate the sender, you need a Message Authentication Code (MAC).

HMAC, short for Hash-based Message Authentication Code, is the dominant MAC construction on the internet. It was designed in 1996 by Mihir Bellare, Ran Canetti, and Hugo Krawczyk and standardized in IETF RFC 2104. It is now also FIPS 198-1, ISO/IEC 9797-2, and a building block inside almost every modern protocol: TLS, IPsec, SSH, JSON Web Tokens, OAuth signed tokens, and HKDF (which we covered in [HKDF (RFC 5869) Line by Line](hkdf-rfc-5869-deep-dive)).

This article walks through how HMAC works in plain English, why its design is robust, where you see it deployed today, and how it fits into the larger cryptographic toolkit including post-quantum derivations and signatures.

## The Problem MACs Solve

Imagine you and a friend share a secret key K. You want to send a message M and have your friend verify both that M came from you and that no one altered it.

A first attempt is to compute Hash(K || M), where || is concatenation. Append the hash to the message. The friend recomputes Hash(K || M) and checks. This works for a moment, but it has a deep flaw with the most common hash functions.

The Merkle-Damgard hashes (MD5, SHA-1, SHA-2) have a property called length-extension. If you know Hash(K || M) and the length of K || M, you can compute Hash(K || M || padding || extra_bytes) without knowing K. So an attacker can take your authenticated message, append "and also send $1000 to attacker", and produce a valid-looking authentication tag. This is the length-extension attack.

A second attempt is to compute Hash(M || K). This avoids length extension but introduces a different problem: collisions on M now become collisions on the MAC. If the hash function has a collision (two messages M1 and M2 with Hash(M1) = Hash(M2)), then Hash(M1 || K) = Hash(M2 || K) for any K, so an attacker who has a valid tag for M1 has a valid tag for M2.

HMAC fixes both problems at once.

## How HMAC Works

HMAC's construction looks slightly mysterious at first but it is carefully chosen. It is defined as:

HMAC(K, M) = Hash((K' xor opad) || Hash((K' xor ipad) || M))

K' is the key K, possibly hashed first if it is too long for the hash function's block, then padded with zeros to the block size. opad is the byte 0x5c repeated to fill the block. ipad is the byte 0x36 repeated to fill the block. xor is bitwise exclusive-or.

In English: HMAC takes the message, prepends an inner-padded key, hashes it once to get an inner hash, then takes that inner hash, prepends an outer-padded key, and hashes again to get the final tag.

Why two hash calls? The inner hash binds the message to the key in a way that resists length extension (because the outer hash truncates the inner state). The outer hash provides a final layer that makes the whole construction provably secure under standard hash assumptions.

The opad and ipad bytes are chosen specifically. They differ in many bit positions, which gives strong domain separation between the inner and outer hash calls. The original 1996 paper proves that HMAC is a secure pseudorandom function (PRF) as long as the underlying compression function is a PRF, regardless of whether the full hash has collision resistance.

## The PRF Property

A pseudorandom function is one whose output looks indistinguishable from random to anyone who does not know the key. This is exactly what you want from a MAC: an attacker who can see many (M, HMAC(K, M)) pairs cannot predict HMAC(K, M*) for a fresh M*.

HMAC's PRF property is what makes it useful beyond just authentication. It is also a key derivation primitive. Both HKDF (RFC 5869) and the SP 800-108 counter mode KDF use HMAC as their PRF. Inside ML-KEM and Falcon, intermediate derivations rely on HMAC-like or KMAC-like PRFs.

The PRF property is stronger than what you need for authentication alone, but it comes for free from HMAC's design.

## Hash Choice: HMAC-SHA-256 vs HMAC-SHA-512 vs HMAC-SHA-3

HMAC is a generic construction. It works with any iterated hash function. The choice of hash determines the output size and the performance.

HMAC-SHA-256 produces a 32-byte tag. It is the default in TLS 1.3, IPsec, and most modern protocols. SHA-256 has hardware acceleration on every modern CPU (Intel SHA Extensions, ARM Cryptography Extensions), so HMAC-SHA-256 runs at gigabytes per second.

HMAC-SHA-384 produces a 48-byte tag. It is used in CNSA Suite (NSA's Commercial National Security Algorithm) for high-security applications. SHA-384 is the same as SHA-512 with a different IV and truncated output. We discuss CNSA in [2035 NSA CNSA Deadline Plan](2035-nsa-cnsa-deadline-plan).

HMAC-SHA-512 produces a 64-byte tag. It is faster than HMAC-SHA-256 on 64-bit CPUs without hardware SHA-256 (rare today) and gives more headroom against quantum brute force.

HMAC-SHA3-256, HMAC-SHA3-384, and HMAC-SHA3-512 are also defined. They are slower than HMAC-SHA-2 in software but provide a backup if SHA-2 ever falls.

The SHA-3 family also has KMAC, defined in NIST SP 800-185, which we cover in [KMAC and cSHAKE Explained](kmac-cshake-explained). KMAC is a domain-separated keyed PRF designed specifically as a MAC, with a cleaner construction than HMAC because it does not need the ipad/opad workaround.

For new designs in 2026, HMAC-SHA-256 is the safe default. KMAC256 is a strong alternative with cleaner semantics.

## Where HMAC Is Used

HMAC is everywhere. A non-exhaustive list:

In TLS 1.3 (RFC 8446), HMAC-SHA-256 (or SHA-384) is the integrity check on every record. The Finished messages in the handshake are HMAC-protected.

In IPsec (RFC 4868), HMAC-SHA-2 protects each packet's authentication.

In SSH (RFC 6668), HMAC-SHA-256 is one of the negotiated MAC algorithms.

In JSON Web Tokens (RFC 7519), the HS256 signature algorithm is HMAC-SHA-256.

In OAuth 2.0, HMAC-based token signatures are common in implementations like Amazon's signed URLs and AWS Signature Version 4.

In HKDF (RFC 5869), HMAC is the underlying PRF in both Extract and Expand.

In TOTP (RFC 6238) and HOTP (RFC 4226), HMAC-SHA-1 (legacy) or HMAC-SHA-256 generates one-time codes for two-factor authentication.

In NIST FIPS 203 (ML-KEM), the encapsulation procedure uses SHA-3 derivatives that play a similar role to HMAC.

In the IETF Messaging Layer Security protocol (RFC 9420), HMAC underlies the entire group key schedule.

## Constant-Time Comparison

A subtle but critical implementation detail is that the receiver must compare the received tag and the recomputed tag in constant time. A naive memcmp() that returns as soon as it finds a mismatching byte leaks information about how many leading bytes match. An attacker who can measure timing precisely can recover the tag byte by byte over many requests, even without knowing the key.

The fix is to compare every byte regardless of mismatches. In C, this looks like:

```
int constant_time_equal(const uint8_t *a, const uint8_t *b, size_t n) {
    uint8_t diff = 0;
    for (size_t i = 0; i < n; i++) {
        diff |= a[i] ^ b[i];
    }
    return diff == 0;
}
```

Most modern crypto libraries provide a constant-time compare function: OpenSSL's CRYPTO_memcmp, libsodium's sodium_memcmp, Rust's subtle::ConstantTimeEq, Go's crypto/subtle.ConstantTimeCompare. Use them.

## Common Mistakes

Truncating tags inappropriately. Truncating an HMAC tag is allowed but reduces security. The output is uniform, so any prefix is also uniform, but truncation lowers the work needed for forgery to 2^(t/2) where t is the tag length in bits. RFC 4868 allows HMAC-SHA-256 truncated to 128 bits. Going below 128 is rarely a good idea.

Using HMAC for password hashing. HMAC is fast, which is bad for passwords. Use Argon2id, scrypt, or PBKDF2 instead. See [Argon2id Explained](argon2id-explained).

Using a non-secret as the key. The HMAC key must be uniform and high-entropy. Do not use a password directly as the key; derive a key first.

Reusing the same key across protocols. If two protocols both use HMAC-SHA-256 with the same key, an attacker who can interleave them can sometimes confuse one with the other. Use distinct keys or clear domain separation.

Not authenticating the algorithm. JWT's "alg=none" attack is a famous example: an attacker changed the algorithm field from HS256 to "none" and submitted a token without a signature, and naive parsers accepted it. Always pin or check the algorithm field.

## HMAC vs Other MACs

HMAC is the most widely deployed MAC, but other constructions exist.

CMAC (NIST SP 800-38B) is a block-cipher-based MAC. It uses AES (or another block cipher) directly. CMAC tags are 128 bits (one AES block). CMAC is faster than HMAC on platforms with hardware AES but slower without it. It is approved for FIPS use.

GMAC (used in AES-GCM) is the authentication-only mode of AES-GCM. It uses Galois field multiplication. It is fast but more sensitive to nonce reuse.

KMAC (NIST SP 800-185) is the SHA-3 family's MAC. It is built directly on top of cSHAKE with a clean keyed prefix and explicit length encoding. We cover it in [KMAC and cSHAKE Explained](kmac-cshake-explained).

Poly1305 is a one-time MAC used in ChaCha20-Poly1305 (RFC 8439). It is extremely fast but requires fresh keys for every message.

For most general-purpose authentication, HMAC remains the best-understood, most widely deployed, and most interoperable choice.

## HMAC and Post-Quantum

The post-quantum question for HMAC is what Grover's algorithm does to it. Grover gives a square-root speedup against unstructured search. For HMAC with a uniform key K of n bits, brute-force key recovery costs 2^n classically and 2^(n/2) with Grover.

For HMAC-SHA-256 with a 256-bit key, the quantum effective security is 128 bits. That is fine for the foreseeable future.

For HMAC-SHA-512 with a 512-bit key, quantum effective security is 256 bits. Excellent margin.

For HMAC-SHA-256 with a 128-bit key (the minimum FIPS-compliant size for some uses), quantum effective security is 64 bits. Borderline; most modern guidance says to use 256-bit keys for post-quantum-safe symmetric strength.

NIST has specifically tracked HMAC's post-quantum security in its transition planning. The key sizes recommended in CNSA 2.0 and the NIST guidance for post-quantum-era systems align with these calculations.

## Frequently Asked Questions

### Is HMAC the same as a digital signature?

No. HMAC uses a shared secret key: the sender and receiver both have it. Digital signatures use public-key crypto: only the sender has the private key, and anyone can verify with the public key. HMAC gives authentication and integrity but not non-repudiation. See [ML-DSA vs SLH-DSA](mldsa-vs-slhdsa) for digital signatures.

### Can HMAC be parallelized?

Not within a single message; HMAC processes the message sequentially through the hash. Across messages, you can compute many HMACs in parallel. For very long messages, ParallelHash from SP 800-185 (covered in [ParallelHash: Multi-Core SHA-3 Hashing](parallel-hash-explained)) provides a parallelizable alternative.

### What if my hash function gets broken?

HMAC's security is based on the underlying compression function being a PRF, which is a weaker assumption than full collision resistance. Even SHA-1, which has known collision attacks, is still arguably safe as HMAC-SHA-1 because the attacks do not apply to keyed PRF use. That said, deprecate any HMAC-SHA-1 deployments. Move to HMAC-SHA-256 or stronger.

### Is HMAC FIPS-approved?

Yes. FIPS 198-1 defines HMAC, and HMAC with any approved hash (SHA-2 or SHA-3 family) is approved for FIPS 140-3 modules.

### Should I worry about timing attacks on HMAC verification?

Yes, always use constant-time comparison. This is one of the most common cryptographic implementation bugs.

## Sources

1. Bellare, M., Canetti, R., and Krawczyk, H. "Keying Hash Functions for Message Authentication." Crypto 1996. https://cseweb.ucsd.edu/~mihir/papers/kmd5.pdf
2. Krawczyk, H., Bellare, M., and Canetti, R. "HMAC: Keyed-Hashing for Message Authentication." IETF RFC 2104, February 1997. https://datatracker.ietf.org/doc/html/rfc2104
3. NIST FIPS 198-1. "The Keyed-Hash Message Authentication Code (HMAC)." July 2008. https://csrc.nist.gov/pubs/fips/198-1/final
4. NIST FIPS 180-4. "Secure Hash Standard (SHS)." August 2015. https://csrc.nist.gov/pubs/fips/180-4/upd1/final
5. Kelly, S. and Frankel, S. "Using HMAC-SHA-256, HMAC-SHA-384, and HMAC-SHA-512 with IPsec." IETF RFC 4868, May 2007. https://datatracker.ietf.org/doc/html/rfc4868
6. NIST Special Publication 800-185. "SHA-3 Derived Functions: cSHAKE, KMAC, TupleHash, and ParallelHash." December 2016. https://csrc.nist.gov/pubs/sp/800/185/final

## Related Articles

- [HKDF (RFC 5869) Line by Line](hkdf-rfc-5869-deep-dive)
- [KMAC and cSHAKE: Domain-Separated SHA-3 Functions](kmac-cshake-explained)
- [BLAKE3 Hashing: Fast, Modern, Parallel](blake3-hashing)
- [ML-DSA vs SLH-DSA: Two Post-Quantum Signature Approaches](mldsa-vs-slhdsa)
- [NIST FIPS Guide: Reading the Post-Quantum Standards](nist-fips-guide)

---

### Protect Your Data Before Q-Day Arrives

QNSQY's NIST-standardized post-quantum encryption protects files against both current and quantum-era threats.

[Try QNSQY](../../pricing.html)
