-
Notifications
You must be signed in to change notification settings - Fork 55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
timingSafeEqual functionality #270
Comments
This would be a very welcome addition. |
I'd like to add this to the spec, but I haven't done any spec work this big before. Do you think this would be a good first bug? |
Hey 👋 Thanks for the proposal. While I think this addition could make sense for SubtleCrypto, it would seem to be out of scope for the new charter that the WebAppSec WG is planning to adopt, which is more focused on maintaining the API (making sure it stays secure, adding more modern algorithms, etc) rather than adding new features. In addition, there's some overlap here with Constant-Time WebAssembly, and there's some question whether low-level crypto operations (which rely on constant-time comparisons) may make more sense to implement in Web Assembly. For example, note that verifying an HMAC is already supported as a higher-level operation in Web Crypto. Do you have any other concrete use cases of a constant-time comparison function that aren't covered by Web Crypto already but that you believe would make more sense to implement in JavaScript than WebAssembly? Regardless of the above, before we specify this, there needs to be interest from implementors first. We could of course try to find out if there's interest, but with Constant-Time WebAssembly (hopefully) around the corner, IMHO this seems lower priority than the maintenance work that needs to happen, so personally I would wait for both of those to happen, and then re-evaluate whether this is needed. |
Thanks for the info, sounds good 👍 I needed the functionality server side in Nodejs. I ended up using the non standard My specific use case is that I generate a random password and send it to the client while storing a digest in my database. |
No, makes sense. Technically speaking, comparing hashes is probably less timing-sensitive than certain other operations, because with a strong key derivation function (such as Argon2 or scrypt), learning anything about the hash should not reveal anything about the password, unless the password is very weak. But nevertheless it's probably good practice for that case, yeah 👍 If both Node.js and Deno are interested in implementing this in Web Crypto, then it may be worth checking with the browsers whether they are potentially also interested in implementing it, to see if it would be worth standardizing. |
This sounds fine, assuming you also use a random salt for each stored digest :)
In general, I do believe that timing-safe comparisons are an essential component to any system that uses low-level cryptographic features and I am not generally opposed to adding it to WebCrypto. That being said, JavaScript is not good for using low-level cryptographic features without introducing a ton of side channels. For example, it turns out to be difficult just to test WebAssembly, for example, allows much better control over side channels than JavaScript and still cannot provide timing safety, which is the primary reason behind the constant-time WebAssembly proposal. The main use case of |
Just revisiting this. For Cloudflare Workers, we are looking at the possibility of implementing |
Alrighty 👍 Looking at Node.js's docs, just one comment I'd have regarding
I would perhaps suggest to instead restrict the input types, and do the same check that
(we could come up with a name for this set, if it makes things simpler, e.g. "integer array views").
Hmhm. Other than the above, I don't see any issues or risks for conflict, but having some spec text for this would probably be valuable 👍 If there's interest from other implementers, submitting it to the WICG might also make sense. |
+1 to include this functionality. In our use case, we use this auxiliary function in Typescript. However, there are no guarantees that operations are performed by the integer ALU rather than the floating point unit (FPU). |
+1 With the rise of Edge runtime, and Next.js middleware using said runtime, this is a required security API for handling, for example, static access tokens in endpoints. |
I've been looking deeper into this, and for the record, I'm a cryptography novice.
This seems to be a bigger issue in Node.js than expected, which is also highlighted in this JS library and made Deno use an approach with A similar concern has been raised with the Node.js team, and the "Double HMAC verification" pattern has been recommended. However, it turned out that this issue was a bug in |
If I understood your first link correctly, the issue is that in JavaScript, accessing a number might not be constant-time on 32-bit V8 (and possibly other JS engines), since it first has to check the first bit to see whether it's actually a number or a pointer. (I was actually under the impression that 64-bit JS engines do something similar, but I might be wrong.) But, I think for a native API like Web Crypto, this shouldn't be an issue, because the function should be able to read the bytes in the typed array and compare them directly, without needing to check whether they're pointers, since a typed array can only contain numbers. Even when passing in a In general, implementations of such a function in JS can't really be guaranteed to be constant-time, I think; so the implementation should always be done natively. |
@twiss, thanks for the elaboration!
I see. Would the "Double HMAC verification" pattern then be a constant-time alternative, since it doesn't rely on a bitwise comparison pattern?
I'd imagine that the HMAC comparison itself leaking length doesn't matter because the prefix of the double HMAC will constantly change. You cannot rely on the assumption that you've correctly guessed the first byte between A shower thought - would a WebAssembly solution guarantee constant-time? |
The pedantic answer would be no, there could still be timing differences, when you read the values to be HMAC'd, for example (if you implement the HMAC'ing in JS). That would probably be very difficult to exploit though, so it's probably "safe enough" (especially if you use Web Crypto for the HMAC), but it still seems safer to implement it entirely natively, IMHO.
WebAssembly is not guaranteed to be constant-time, no, though there is a proposal for constant-time WebAssembly instructions. |
I'm curious, how would the following leak time (pseudo-code)? const key = "abc" // some strong key
function compare(a, b){
return hmac(key, a) === hmac(key, b)
}
compare("abc", "abc") // true From my understanding as i written before, lets imagine:
Interesting, thanks for sharing! |
Well - depending on the JS engine, reading the values |
@twiss, I think I'm following now. I might not be able to wrap my head entirely around the situation, but I believe that even if there is a time-leak with reading a and b from memory, the variating time it takes for the "hmac double verification" to finish cancels this issue out, making it nearly impossible. Don't understand me wrong, I'm all for a native implementation! I'm just looking for an alternative that can be safely used in Browser/Edge environments until this spec has been released and implemented in Edge/Major Browsers. |
@twiss / @armfazh - I ended up writing a time safe comparison npm library that uses the "Double hmac verification" pattern with WebCrypto's HMAC, that could be used meanwhile. It's not perfect, but I'd imagine it to be a safer alternative to the existing libraries that rely on the bitwise XOR comparison. It runs "only" 60 times slower than Node.js' |
I'm thinking: why dose it have to be on the crypto at all and why should it only be able to deal with typed arrays? |
@jimmywarting The way |
@jimmywarting How would that work? The |
@larsqa Your comments made me realize that the current API kind of already does provide a way to do a timing-safe comparison, as follows: async function timingSafeEqual(bufferSource1, bufferSource2) {
const algorithm = { name: 'HMAC', hash: 'SHA-256' };
const key = await crypto.subtle.generateKey(algorithm, false, ['sign', 'verify']);
const hmac = await crypto.subtle.sign(algorithm, key, bufferSource1);
const equal = await crypto.subtle.verify(algorithm, key, hmac, bufferSource2);
return equal;
} Even though the spec doesn't say that the comparison of the HMACs needs to be done in constant-time, all implementations I checked (Chromium, Webkit and Gecko) do so; and of course the HMAC adds some safety even if the comparison is not done in constant-time. Perhaps we should additionally specify that this comparison must be done in constant-time, as a first step - that might also reduce the need for this method? (Although it might still be convenient, and would of course be faster if it doesn't have to do two HMACs.) |
Nodejs has the built in timingSafeEqual(a, b) function.
Are there any plans for getting this in SubtleCrypto as well?
The text was updated successfully, but these errors were encountered: