Skip to content

fix(otp): compare OTP codes in constant time#24

Closed
bytaesu wants to merge 1 commit into
mainfrom
taesu/oss-438-non-constant-time-comparison-of-totphotp-code-in-3010
Closed

fix(otp): compare OTP codes in constant time#24
bytaesu wants to merge 1 commit into
mainfrom
taesu/oss-438-non-constant-time-comparison-of-totphotp-code-in-3010

Conversation

@bytaesu

@bytaesu bytaesu commented Jun 13, 2026

Copy link
Copy Markdown
Member

verifyTOTP compared the attacker-supplied otp against the server-computed code with ===, which short-circuits on the first differing byte. Since generatedOTP is secret material derived from the HMAC over the shared secret, the comparison time leaks the matching-prefix length — a timing side channel for secret comparison in a low-level crypto primitive.

Changes

  • Add a constantTimeEqual helper that XOR-accumulates over all bytes and folds in the length difference, never early-returning on a length or byte mismatch.
  • Use it in verifyTOTP and iterate over all window candidates without returning on the first match, so neither the prefix length nor which window matched is leaked.

Tests

  • Correctness cases (equal/unequal, length mismatch, multibyte unicode).
  • A timing regression that compares mean comparison time for an early-mismatch vs. a late-mismatch candidate: it fails against the old short-circuiting === (observed ratio ~260x) and passes for the constant-time compare.

All existing OTP tests pass unchanged.

Replace the short-circuiting === comparison in verifyTOTP with a
constant-time byte-wise compare so the comparison time no longer leaks
the matching-prefix length of the secret OTP. Also iterate over all
window candidates without an early return to avoid leaking which window
matched.

Generated with [Linear](https://linear.app/better-auth/issue/OSS-438/non-constant-time-comparison-of-totphotp-code-in-verifytotp#agent-session-5c8cee8c)

Co-authored-by: linear-code[bot] <222613912+linear-code[bot]@users.noreply.github.com>
@bytaesu bytaesu closed this Jun 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant