Basic

Comparing without leaking

I compared a secret with == and it returned early on the first wrong byte. The time it takes tells an attacker how much they guessed right.

What you will see

False

About 1 min of reading

Try It

Replace a leaky compare with a constant-time one.

1

Compare a token with the constant-time function. It returns True for a match.

import hmac print(hmac.compare_digest(b'abc', b'abc'))

Before you run it, say out loud what it will print. Then open the answer.

What it prints
True
2

A mismatch returns False, and the time is the same as any other mismatch.

import hmac print(hmac.compare_digest(b'abc', b'abd'))

Before you run it, say out loud what it will print. Then open the answer.

What it prints
False
Never use == to check a secret or a signature. The early exit on the first differing byte is a timing channel an attacker can measure. Use compare_digest.

Data Flow

A byte compare that exits early leaks the matched prefix through timing; a constant-time compare over fixed-length digests removes that channel.
1 Guess compared to secret
↓
2 Early exit leaks prefix
↓
3 Constant-time compare all bytes
↓
4 Hash to fixed length
Line by line

Read the first example one line at a time. A line that prints shows what appears on screen; the rest set things up quietly.

import hmac sets nothing on screen
print(hmac.compare_digest(b'abc', b'abc')) True
Why this works
Early exit is a leak

A byte-by-byte compare that stops at the first mismatch tells an attacker, by timing, how many leading bytes they got right. For a secret, that turns a guess into a search. Use a constant-time compare.

Length still leaks

Constant-time compare hides the position of the first difference, not the length. For a secret, hash to a fixed length first so even the length is not a signal. compare_digest on digests is the safe form.

Examples

The leaky compare

if guess == secret: ...   # stops at first mismatch

== returns as soon as a byte differs, so a correct prefix takes longer. Timing is a channel to the secret.

Constant time

hmac.compare_digest(guess, secret)

compare_digest checks every byte in the same time regardless of where it differs. No timing signal leaks.

Hash first, then compare

compare_digest(hash(guess), stored)

For long secrets, hash both sides then compare the digests. Same length, same time, no early exit.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← A form from another site
Previous Lesson
A form from another site
Next Lesson
The standing checklist
The standing checklist →