I've written before that revealing the length of your password is not really that big a deal. See my answer here.
What does a timingSafeEqual
do? Well, most language's string equality behaves like this:
bool equal(strA, strB) {
assert(len(strA) == len(strB));
for (i < len(strA)) {
if (strA[i] != strB[i]) return false;
}
return true;
}
That means that it will start from the beginning of the strings and "fail out" when it finds the first character that doesn't match. For example, equal("aaa", "bbb")
will fail out on the first loop while equal("aaa","abb")
will fail out on the second loop. A careful attacker can detect this timing difference and brute-force your password one character at a time.
On the other hand, a timingSafeEqual
will always do exactly the same number of operations regardless of what it finds, for example it would be closer to:
bool timingSafeEqual(strA, strB) {
assert(len(strA) == len(strB));
bool match = true;
for (i < len(strA)) {
match ^= strA[i] == strB[i];
}
return match;
}
(you need to be a bit careful when writing the assignment line to make sure your compiler doesn't get clever and remove no-op assignments, cause then you're back to having a timing difference.)
Final note, if you're doing things properly and comparing two salted password hashes then it doesn't really matter whether you use a timing-invariant comparison or not because an attacker learning your password hash doesn't really help them.