In the previous post, I showed some code that compared two MAC values (binary buffers) and I mentioned that the manner in which I did that was bad.
Here is the code in question:
When you are looking at code that is used in a cryptographic context, you should be aware that any call that compares buffers (or strings) cannot short circuit. What do I mean by that? Let’s look at the implementation of those two functions:
Those two functions are doing the same thing, but in a very different manner. The issue with eql() is that it will stop at the first mismatch byte, while timingSafeEql() will always scan through the two buffers first and then return the result.
Why do we need that?
Well, the issue is that if I can time (and you can, even over the network) the duration of a call like that, I’ll be able to test various values until I match whatever secret value the code is comparing against. In this case, I don’t believe that the use of eql() is an actual problem. We tested that on the output of HMAC operation vs. the expected value. The caller has no way to control the HMAC computation and already knows what we are comparing against. I can’t think of any reason where that would be a problem. However, I’m not a cryptographer and any call to buffer comparison in crypto related code should use a constant time method.
For that matter, side channels are a huge worry in cryptography. AES, for example, is nearly impossible to implement in software at this point, because it is vulnerable to timings side channels and requires the hardware to help here. Other side channels include watching caches, power signatures and more. I don’t actually have much to say about this, except that when working with cryptography, even something as simple as multiplication is suspect, because it may not complete in constant time. As a good example of the problem, see this page.
More posts in "Badly implementing encryption" series:
- (24 Feb 2022) Part X-Additional data
- (23 Feb 2022) Part IX–SIV
- (22 Feb 2022) Part VIII–timings attacks and side channels
- (21 Feb 2022) Part VII–implementing authenticated encryption
- (18 Feb 2022) Part VI–malleable encryption
- (17 Feb 2022) Part V–nonce reuse
- (16 Feb 2022) Part IV–keyed hash function
- (15 Feb 2022) Part III–breaking your encryption apart
- (14 Feb 2022) Part II–breaking the code
- (11 Feb 2022) Part I