# T-SQL Constant Time Comparison

Explanation is in the function. Please point out any egregious errors:

create function api.ConstantTimeCompare (
@x varbinary(8000) = 0x00
, @y varbinary(8000) = 0x00
)
/*
What? Compares two variable length binary strings byte by byte in constant time.

When? Cryptography.

Why? Given the strings 0x1234 and 0x1213 a simple equality comparison will stop processing after
it hits the first mismatch at byte position 3. An opponent who only knows @x can exploit this
feature to guess @y by trying thousands of comparisons and gathering statistics on how long
each one took.

In other words, assuming that it takes 100 milliseconds to compare at 16 byte string an
opponent can rely on the fact that string that only takes 15 milliseconds to process is a
worse guess than ones that take more than 15 milliseconds. A 50 millisecond comparison in
this scenario means that @x is a 50% match of @y (from byte 1-8).

How? Set up a loop based on the length of @x and perform a byte by byte XOR. If @result is still 0
after all iterations then @x is equal to @y. If the comparison is meant to be secure then the
untrusted string should be @x and the trusted string should be @y.
*/
returns bit
with schemabinding
as
begin;
declare @i int = 0;
declare @l int = DataLength(@x);
declare @result int = @l - DataLength(@y);

while (@i < @l)
begin;
set @result = ((@result ^ Substring(@x, @i, 1)) ^ Substring(@y, @i, 1));
set @i = @i + 1;
end;

set @result = case @result when 0 then 1 else 0 end;

return @result;
end;

• Please don't update the code after receiving answers. It's confusing to read answers that don't match the code. You can ask a new follow up question if you wish, but I would also recommend explaining the what & why. That context is invaluable to reviewers, and there fore you. By leaving it out, you're robbing yourself of good answers. Oct 22, 2015 at 23:54

The general approach is sound, but the xor-ing is unnecessarily complex and actually causes it to give an incorrect answer in some cases. If I've worked through it correctly, it will report that the empty string is equal to the string with the single byte 1. The simple approach would be to just increment @result when the strings differ at position @i.
There's another concern that I'm not qualified to comment on for T-SQL, but that I know applies to C# or C++: the algorithm that you've specified should be constant-time, but that is before it gets run through any compilers or optimizers. If you switch to an algorithm that increments a counter, it's conceivable that an optimizer would realize that once @result is not zero the return value is 1, defeating your efforts.