Chapter 03 // Primitives

Message
Authentication Codes

Hash functions verify that data has not been accidentally corrupted. But what if an attacker deliberately modifies it? MACs add a secret key to the equation, providing both integrity and authenticity.

Scroll to explore
01 // The Problem

What hashes can't protect you from

Hash functions are public algorithms. Anyone can compute SHA-256 of anything. That is exactly the problem.

The missing piece

In the previous chapter, you learned that hash functions create unique fingerprints of data. If the data changes, the fingerprint changes. This sounds like it solves integrity, but there is a catch.

If Alice sends Bob a message with its hash appended, and Eve sits in the middle, Eve can replace the message, compute a fresh hash of her modified version, and forward both to Bob. Bob checks the hash, sees it matches, and trusts the message. The hash gave integrity against accidents (bit flips, network errors) but zero protection against a deliberate attacker. What is missing is a secret.

With hash only
Alicemsg + hash(msg)
Eve interceptsmodifies msg, recomputes hash
Bob fooledhash matches ✗
With MAC (keyed hash)
Alicemsg + MAC(key, msg)
Eve interceptsno key, cannot forge tag
Bob detectsMAC fails ✓
02 // The Solution

Keyed hashing: the MAC concept

A Message Authentication Code takes two inputs, a secret key and a message, and produces a fixed-size tag. Only someone who knows the key can produce or verify a valid tag.

🎯

Deterministic

The same key and message always produce the same tag. Compute HMAC(key, message) a thousand times and you will get the exact same result every time.

🛡️

Unforgeable

Without the key, producing a valid tag for any message is computationally infeasible. Even if you have seen tags for millions of other messages, you cannot forge a new one.

Verifiable

Bob computes MAC(key, received_message) and compares it with the received tag. If they match, the message is authentic and unmodified.

A MAC does not provide confidentiality. The message itself travels in plaintext. Eve can still read it. She just cannot modify it without detection. Combining confidentiality and integrity in a single operation is the topic of Chapter 6: Authenticated Encryption.

03 // Try It Yourself

Live MAC playground

Enter a message and a secret key to compute an HMAC tag in real time. Change a single character in either field and watch the entire tag change.

MAC Explorer

Enter a message and a secret key to compute an HMAC tag in real time.

HMAC-SHA-256 TagEnter a key to compute the tag

Change a single character in the key or the message. The entire tag changes unpredictably.

04 // The Naive Approach

Why not just hash the key with the message?

Your first instinct might be: take a hash function, prepend the secret key, and hash the whole thing. It sounds reasonable. It is dangerously broken.

How Merkle-Damgard works

Hash functions like SHA-256 do not process the entire input at once. They break the data into fixed-size blocks (64 bytes each) and feed them through a compression function one at a time. Each block takes the previous block's output as its starting state. The final block's output becomes the hash.

This is called the Merkle-Damgard construction, and it has a critical consequence: the hash output IS the internal state after the last block. If you know the hash, you know the state. And if you know the state, you can keep feeding in more blocks from that point forward, extending the message without ever knowing what came before.

The length extension attack

If Alice computes tag = hash(key || message) and sends the message with the tag, Eve can do the following without knowing the key: take the tag (which is the internal hash state), resume the hash computation from that state, feed in additional data, and obtain hash(key || message || padding || extra_data). She now has a valid tag for a message Alice never sent.

This is not a theoretical weakness. Length extension attacks have been used against real APIs that used hash(key || message) for authentication. The Flickr API signing vulnerability in 2009 is one well-known example.

Length Extension Attack

See why hash(key || message) is broken. Step through the attack.

How SHA-256 processes data (Merkle-Damgard)
IVfixed
keyblock 1
messageblock 2
state= tag output
Step 1: Alice creates a tag

Alice computes hash(key || message) and sends the message with its tag.

Original: hash(key || message)
...
Extended: hash(key || message || pad || extra)
...
05 // Under the Hood

HMAC: the right way to build a MAC

HMAC is the industry-standard MAC construction. It uses the same hash function you already know, but wraps it in a two-pass structure that eliminates length extension entirely.

Two hashes, two derived keys

HMAC starts by creating two derived keys from your original key. It XORs the key with a constant called ipad (0x36 repeated to fill a block) and another constant called opad (0x5c repeated). These two values never change. They are defined in the HMAC specification (RFC 2104).

The inner pass hashes (key XOR ipad) concatenated with the message. This produces a 32-byte intermediate result. The outer pass then hashes (key XOR opad) concatenated with that intermediate result. The output of the outer hash is the final HMAC tag.

Why does this stop length extension? Because the attacker only sees the outer hash output. To extend it, they would need the outer hash's internal state, but that state depends on the key XOR opad, which they do not know. The inner hash result is buried inside the outer computation, completely out of reach.

HMAC Construction
Inner pass
Key
XOR
ipad0x36...36
concat with message
SHA-256
inner hash32 bytes
feeds into
Outer pass
Key
XOR
opad0x5c...5c
concat with inner hash
SHA-256
HMAC tag32 bytes

In a single formula:

HMAC(key,msg)=Hash((keyopad)Hash((keyipad)msg))\text{HMAC}(key, msg) = \text{Hash}\bigl((key \oplus opad) \,\|\, \text{Hash}((key \oplus ipad) \,\|\, msg)\bigr)

The inner hash produces an intermediate digest. The outer hash seals it. An attacker who sees only the final output cannot extend either pass.

HMAC Step by Step

Watch HMAC-SHA-256 compute a tag one step at a time, with real intermediate values.

06 // See the Difference

Hash vs MAC: playing the attacker

In this interactive scenario, you play Eve. See why a hash alone fails to protect a message, and why a MAC stops you cold.

Play the Attacker

You are Eve, sitting between Alice and Bob. Try to tamper with a message in both scenarios.

Alice sends a message with its SHA-256 hash. You are Eve. Modify the message and see what happens.

Alice sends
Transfer $100 to Alice
SHA-256 Hash
Eve intercepts and modifies
Recomputed Hash (anyone can do this)
07 // The Challenge

Can you forge a MAC?

You have a message and its valid HMAC tag, but the key is secret. Can you produce a valid tag for a different message? Try as many times as you want.

Forgery Challenge

Alice sent a message with a valid HMAC-SHA-256 tag. The key is secret. Your mission: produce a valid tag for a different message without knowing the key.

Original Message
Pay Eve $50
Valid HMAC Tag
Target Message (forge a tag for this)
Pay Eve $50000
Secret Key
??? (hidden)
Attempts: 0
08 // In Practice

Where MACs protect you every day

MACs are one of the most deployed cryptographic primitives, working silently in APIs, browsers, and protocols across the internet.

🔐

API Authentication

Services like AWS sign every API request with HMAC. The server recomputes the MAC to verify the request came from someone with the secret key and was not modified in transit.

🍪

Cookie and Session Signing

Web frameworks sign session cookies with HMAC. If a user tampers with their session data, the server detects the invalid tag and rejects the request.

🔗

TLS Record Protection

Every TLS record includes a MAC or AEAD tag. This prevents an attacker from silently modifying encrypted traffic between your browser and a server.

📋

JWT Token Verification

JSON Web Tokens use HMAC-SHA256 to sign claims. The server verifies the signature before trusting the token contents, ensuring nobody tampered with the payload.

Up Next

Chapter 04 // Randomness and Secrets

Continue Learning