You do not need constant-time comparisons of password hashes; to see why, reason through how you'd attack them.
An attack against an HMAC authenticator provides full, incremental control over the hash. No such control exists for a password hash; you can't step from "AAAAAA" to "AAAAAB" without a devastating break in the hash function itself. The capability to predict the input that generates "AAAAAB" implies a total failure of preimage resistance.
You can end up in a situation where timing leaks are relevant to password authentication. To do that, you need to design your own password storage system, and it needs to be badly flawed; for instance, you could literally use a general-purpose fast lookup hash and a chaining hash table. This is yet another reason to simply use bcrypt or scrypt to store passwords; doing so takes timing off the table.
In reality, though, you could just use a salted SHA1 hash (don't, though) and still be safe from this attack.
The scenario he's describing is:
1. A user submits a password (with no username) to authenticate.
2. The server looks up the password in a hash table. This involves:
a. Hashing the password to find the correct bucket.
b. Doing a string comparison against passwords stored in the bucket.
There are vulnerabilities in two places:1. Buckets containing more passwords will take longer to return a negative result because they will have to perform more string comparisons. (This irrelevant except in pathological cases.)
2. String comparisons with longer common prefixes will take longer to return a negative result.
This is a bad example for a couple reasons:
1. Password inputs are not compared directly to stored values in any real authentication system. They are hashed first. A timing attack here implies a preimage attack against the hash function.
2. Passwords are not global. Passwords are paired with a username. You don't get to time the comparison against every password in the system to determine common prefixes, for example.
A better example might have been API tokens, which are often stored in cleartext and fetched directly from the database.
I still don't follow. Good authentication tokens make extremely sparse use of their numeric domain; they are e.g. 128 bit numbers, of which only ~15-20 bits are needed. It doesn't look like there's enough information in the indices to incrementally attack them, which is how timing attacks work.
I buy that I'm just missing something here. I'm just looking for someone to reframe this issue in terms of what an attacker actually does.
The canonical example of the target you're referring to is a Java JSESSIONID cookie. So: what's the timing attack against JSESSIONID?
That said, I think we'd be talking of something like this for a web framework session storage attack:
Assumptions:
- A separate chained hash table implementation that doesn't use any kind of per-table randomization (e.g. like what Perl does for DOS prevention).
- A string comparison function that's not constant time.
What the attacker would do:
- Log in, observe session key.
- Generate on client side other tokens that would hash to the same bucket as the real session key
- Use a timing attack using the generated keys to determine the number of other session keys in the same hash bucket.
- Repeat with new logins until they find a bucket with exactly one other session key.
- Conduct a timing attack on the first character of the other session key in the bucket. (I.e. generate 256 fake session keys all hashing to the same bucket, each starting with a different byte).
- Conduct a timing attack on the second character.
- And so on.
- Once you've found a genuine session key, see whether it granted you any elevated rights.
Seems to me that the only thing that's needed is the ability to generate strings with an arbitrary prefix that hash to an arbitrary bucket. Without giving it too much thought, that seems pretty simple. I must be missing something here since you guys are finding this to be such an outlandish idea. Looking forward to hearing what it is.
Sure, it's usually poignant critique, but anyone who actually knows anything about this subject agrees that doing a non-constant-time-compare on what is essentially a random oracle leaks nothing of value to an attacker, and people are just parroting a catch phrase without understanding it. It's the evolution of the fan favorite "That's security through obscurity!" applied outside cryptography.
As far as I can tell, you're implying an attacker can determine a password using timing of hash comparisons. How is this any different (just more complicated) than a regular brute force attack?
If you did not have the requirement that password could be arbitrarily long (which seems like a bad idea anyway), you could just pad the password, then compare char by char, never stopping until the end, updating a boolean variable that says if the string is different at each step (character).
Also, this might not be a concern on the web where there's already quite a bit of variability in the response time.
If a particular function by design needs to have a throughput of at least 100 requests/sec, then you ensure that it has a throughput of exactly 100 rq/sec by calculating the response and then busywaiting until 10ms has elapsed - no matter if the actually neccessary things took 1 or 9 ms based on the input, caching and whatever else.
Alternatively, you use a secure random to state that this request will respond in exactly x ms - not when some calculations are finished. Again, you need to be sure that your calculations are guaranteed to finish before x, so you need to know the worst case time of your algorithm, but you don't need it to be constant time.
Determining the the worst case amount of time is far from trivial. What's the slowest amount of time it takes lookup table based AES to decrypt a block?
The only answer to that question is to measure it a bunch, then pick some percentile and hope your margin of safety is sufficient. Or you could use a constant time algorithm to start and rely less on hope.
And I also suggest a scheme that is about rounding to a fixed delay.
Counterexample: Cuckoo hashmap, at least for lookup.
Calculate both hashes. Index the table via each hash. Check both table indexes to see if either match.
The only timing information this leaks is the length of the input string, and that an adversary already knows.
Now, there will be some cache information potentially leaked, although there are ways around that too. But this is constant time disregarding cache, and is sublinear.
As for the question of "a nice constant-time comparison algorithm for (say) 160-bit values", it's relatively easy. Do a pairwise comparison of constant-size chunks of the value, mapping <, =, and > to 01, 10, and 11, respectively. Then recurse as necessary. Of course, this assumes you have a constant-time comparison function for a single chunk.
Since I think he only wants to check for equality, a constant time comparison of 160-bit values should be easy too. If you have a modern x86 machine with SSE4.1, the easiest might be to use _mm_cmpeq_epi64(vpcmpeqq), _mm_and_si128(pand), and _mm_testz_si128(ptest). With 256-bit vectors, it gets even simpler. But any scalar solution should be fine too as long as you can convince the compiler to do what you want.
And as for cache misses - what exactly is a cache miss going to tell you in this case? All it'll tell you is that that an input having a hash close to either hash of your input wasn't accessed recently. That, as far as I can tell, doesn't leak anything, assuming your passwords are salted.
The following should be constant-time relatively to the contents of its inputs:
// return -1 if x < y, else 0
static unsigned lt(unsigned x, unsigned y) {
return -((x - y) >> (sizeof(x)*8-1));
}
// return -1 if x != y, else 0
static unsigned ne(unsigned x, unsigned y) {
const unsigned d = (x - y) | (y - x);
return -(d >> (sizeof(d)*8-1));
}
// return 1 if x[0..n-1] is lexicographically less than y[0..n-1], else 0
int less_than(const unsigned char * x, const unsigned char * y, size_t n) {
unsigned flag = 0;
unsigned done = 0;
for(size_t i = 0; i < n; ++i) {
flag |= lt(x[i], y[i]) & ~done;
done = ne(x[i], y[i]);
}
return flag & 1;
}http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libc/string...
//quantize run duration to mitigate timing attacks
checkPasswordWrapper(...) {
result = checkPassword(...)
until (getMilliseconds() % 250) {
sleep(1) //Needs optimization
}
return result
}Before the hash lookup, calculate a set time into the future, say 300 ms. Then do the hash lookup. Then wait until the predetermined time and return the result.
Waking up a thread at a specific time is just real-time programming 101.
Another idea: if you have some context information that lets you identify that queries are coming from the same entity (same IP address, same tty, same operating system user, whatever), you can impose a shadow ban after 10 unsuccessful tries: fail even if there is a hash "hit", and randomize the times to feed the attacker junk timing data.
You can also run your lookup against uncacheable memory, so that the attacker can't detect hot keys. There's still some memory timing analysis available because of DRAM banking, though.
Security is a process :-)
There's still the problem of cache noise.
Edit: Actually, even if by coincidence you did get FOOBAZ on the same bucket as FOOBAR, you wouldn't be able to easily distinguish from a single comparison with 5 equal characters from multiple comparisons with smaller number of equal characters. Also, if you're getting that many collisions to the point that this is a problem, you've sized your hash table incorrectly.
The quest for a sublinear algorithm is futile in the face of caching. The solution is similar to the solution to branch-based timing attacks: don't branch, in code or in data. Every comparison must follow the same code path and the same memory path, and that means reading every element of the data structure every time you search.