cybersecurity · intermediate · ~15 min · safe pentest lab
Understand why secret-comparison must be constant-time and be able to implement a branchless, all-bytes byte comparison that eliminates a timing side channel, while still bounds-checking length and rejecting NULL by default.
A CSRF defense stores an expected anti-forgery token server-side and compares it against the token the browser submits with each state-changing request. If that comparison uses a normal strcmp/memcmp or bails out on the first mismatching byte, an attacker who can measure response time can recover the secret token one byte at a time: guesses that match a longer prefix take measurably longer to reject. Recovering the token lets the attacker forge requests and defeat CSRF protection entirely.
Asset: the secret expected CSRF token (a fixed n-byte buffer baked into the harness). Threat: a timing side channel that leaks how many leading bytes of a guess were correct. Insecure assumption: "it's fine to return early once the bytes differ."
Your task: implement ct_token_eq(a, b, n) that compares all n bytes of the two tokens in constant time — it must inspect every byte regardless of where (or whether) they differ, so the running time reveals nothing about the contents. Return 1 if the two n-byte tokens are equal, else 0.
Deny by default: if either pointer is NULL, reject (return 0). Never short-circuit on a mismatch, and never stop early at an embedded NUL — these are raw n-byte tokens, not C strings. An n of 0 means "nothing to compare", which is trivially equal (1).
Given expected and a copy match of the same n bytes, and forged differing in one byte:
ct_token_eq(expected, match, n) -> 1
ct_token_eq(expected, forged, n) -> 0
The function receives two token buffers a and b (each at least n bytes, or NULL) and a byte count n (size_t). Buffers are raw bytes and may contain any value including 0x00.
Return int 1 if the first n bytes of a and b are byte-for-byte equal, otherwise 0. Return 0 if either pointer is NULL.
a and b are token buffers of length n bytes.
int ct_token_eq(const char *a, const char *b, size_t n){
/* TODO: compare ALL n bytes of a and b in constant time.
Reject NULL by default; never return early on a mismatch.
Return 1 if equal, else 0. This insecure stub does neither. */
(void)a; (void)b; (void)n;
return -1;
}Returning early on mismatch, introducing timing side-channels.
a == NULL, b == NULL, tokens identical, tokens differing in first byte.
Solve this exercise in the browser editor — compile and run against the test harness, no setup required.