# `range/constant-time-compare.ts` ## Purpose `constantTimeEqualsHex` compares two hex suffixes without early-exit on the first mismatching character. It is used by the range-API client to verify whether any line in the response matches the input password's 35-char suffix, so a timing-side-channel attacker cannot infer which slot the real suffix lands in. The instrumented variant (`constantTimeEqualsHexInstrumented`) returns the byte-XOR count for test assertions. ## Features - Branch-free byte loop using OR-accumulator over XOR pairs. - Touches every byte of the input — no length-based shortcut beyond the initial length-mismatch check. - ASCII-only (uppercase hex) inputs guarantee `charCodeAt` is single-byte, keeping per-character cost predictable. - Test instrumentation exposed without changing the hot path. ## Usage: Basic Example ```ts import { constantTimeEqualsHex } from "@suaveplan/hibp"; if (constantTimeEqualsHex(line.suffix, target)) { /* matched */ } ``` ## Usage: Advanced Example ```ts import { constantTimeEqualsHexInstrumented } from "@suaveplan/hibp"; const { equal, bytesTouched } = constantTimeEqualsHexInstrumented(a, b); if (!equal && bytesTouched !== a.length) { throw new Error("comparator regressed to early-exit"); } ``` ## API Reference - `constantTimeEqualsHex(a: string, b: string): boolean`. - `constantTimeEqualsHexInstrumented(a, b): { equal, bytesTouched }`. ## Implementation Notes JavaScript does not expose a true constant-time string compare, but the loop pattern above is the canonical approximation. The OR-accumulator trick converts every byte XOR into a single boolean test at the end, eliminating the data-dependent branch that an `if (a[i] !== b[i]) return false` would introduce. The length-mismatch check leaks only the public fact that the two strings differ in length, which is never secret in this protocol. The test suite asserts the wall-clock mean delta between equal and unequal pairs stays within tolerance over 10 000 samples; the more rigorous proof is the bytes-touched invariant.