cybersecurity · intermediate · ~12 min · safe pentest lab

Constant-time byte equality

The OR-fold pattern that removes data-dependent branching.

Challenge

Your job

Implement:

#include <stddef.h>
int ct_memcmp(const void *a, const void *b, size_t n);

Return 0 if the first n bytes of a and b are equal; non-zero otherwise. The loop body must touch every byte regardless of where the first difference is.

Rules

  • No early break or return inside the loop.
  • No branches whose condition depends on the data being compared (apart from the loop bound).
  • n == 0 → return 0 (vacuously equal).
  • NULL a or b (with n > 0) → return 1.

Hints

  1. (concept) acc |= ((unsigned char*)a)[i] ^ ((unsigned char*)b)[i]; then return acc; — non-zero on any mismatch.
  2. (common bug) Adding if (acc) break; to "go faster" — that's exactly the leak you were defending against.

Why this matters

Crypto's most common defence: comparing a tag with memcmp leaks where the mismatch is. The fix is three lines and an OR-fold.

Input format

Two const buffers and a byte count.

Output format

0 on equal, non-zero otherwise.

Constraints

No early exit. No data-dependent branching.

Starter code

#include <stddef.h>
int ct_memcmp(const void *a, const void *b, size_t n) {
    /* TODO */
    (void)a; (void)b; (void)n;
    return -1;
}

Common mistakes

Adding if (acc) break;. Returning int from a char * cast (signed widening). Forgetting the n==0 case.

Edge cases to handle

n == 0. NULL with n > 0. Bytes with the high bit set.

Complexity

O(n) — always exactly n iterations.

Background lessons

Up next

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.