Cryptographic Right Answers (2009)
daemonology.net
daemonology.net
I don't understand this part - if I'm not using SSL, what am I doing at this point?
You'll save yourself time and heartache if you just use SSL. Once every few years, someone will find a flaw in TLS, and once every few times that happens, that flaw will be relevant to your application. But on those rare occasions, you'll also have some of the smartest crypto implementors on the planet rushing to come up with the best fix, which is something you will never have with a custom protocol.
It is worth remembering here that the overwhelming majority of TLS security issues are relevant primarily to browsers, and rely on the BEAST exploit model, where attackers have extensive and fine-grained control over what plaintexts victims generate, due to Javascript.
If certificate processing code scares you --- also reasonable! --- and you're building a client/server backend system, do what enterprise software does: slurp the whole certificate into your code, fingerprint it with a hash, and verify it in a whitelist. Your TLS implementation will still verify the certificate, but you won't care; you'll rely on your whitelist for security instead.
What Colin wants to say in this document but couldn't at the time (because it didn't exist yet) and won't now (because of false modesty) is that he thinks you should use spiped instead of TLS. If he'd say that, I'd agree that was a reasonable choice.
From what your saying that SSL layer was not secure on it's own. Which is a valid concern, but not what I am talking about.
I've been considering writing aspiped though.
http://www.daemonology.net/blog/2009-06-24-encrypt-then-mac....
At the moment, the best-supported AEAD mode seems to be AES-GCM, which has received a fair bit of attention since then. Alternately, {Salsa20,ChaCha20}-Poly1305 has some good implementations, e.g.
and in TLS:
https://www.imperialviolet.org/2014/02/27/tlssymmetriccrypto...
Also, using Encrypt-then-MAC allows you to discard inauthentic messages without ever decrypting anything, which protects you from side channels in your block cipher.
An HMAC authenticator is conceptually very simple. You start with a secure hash (even MD5 still qualifies, barely, when we're talking about HMAC), and, given a "key" and a "message", apply the hash function twice: an inner hash of the key and the message, and an outer hash of the key and the inner hash (the inner and outer hashes are distinguished by an "inner pad" and "outer pad" constant). Doing this produces a secure message authenticator for a message assuming the hash and key is secure (with SHA3, you don't even need HMAC, and can create a secure authenticator out of a simple keyed hash).
You understand a secure hash, you understand what a keyed hash is (concatenate the key and the message and now only people who know the key can replicate that hash), and now you get what HMAC is.
GCM is CTR mode, like Colin says you should use with HMAC, plus a GHASH authenticator. GHASH is a little trickier to understand than HMAC, but you can get most of the way there by thinking of it as a thermonuclear CRC algorithm: it's (at root) polynomial multiplication and reduction.
The problem with GCM is that fast polynomial multiplication and reduction in software is table-driven. When software verifies a GHASH MAC, it is forced to look up elements in a table at locations determined by a secret. Those lookups end up configuring the system's cache; future memory references at addresses that collide with the secret table indices will be measurably faster than references that don't collide. Attackers can write software that profiles cache hits and from that information potentially recover secrets.
Modern implementations of GCM on X64 hardware get hardware-accelerated multiplication optimized for this operation, mitigating the table lookup problem. But GCM is still potentially a concern for platforms that don't have these instructions.
The plus side of GCM is that you'll never implement it yourself: it's gnarly enough that everyone uses one of a few library implementations of it. As a library interface, GCM is very nice: you're getting a "seal" and "unseal" operation that securely verifies authentication and then handles decryption, in the correct order of operations. On recent server hardware, that implementation is likely not to have side channel problems; the same is rarely true of HMAC implementations, which tend (annoyingly) to be hand-hacked, particularly for verification.
The "cool kids" replace GCM with Salsa20+Poly1305, which replaces AES-CTR with a native stream cipher (Salsa20, which is actually a hash core running in CTR mode) and a polynomial MAC that is very fast in secret-table-free software. If you use Nacl, that's what you're getting; it's something much closer to GCM than to AES-CTR+HMAC.
Disagree. Hashes and HMACs are very safe against side channel attacks. Heck, I don't think I could insert a side channel if I tried!
On a 1-10 scale of side channel attackiness, hashes and HMACs are 0, block ciphers are 3-4, and asymmetric crypto is 6-8.
When you compare the two HMACs using standard string comparison, you expose yourself to a timing attack because the algorithm will likely stop comparing after finding the first difference in the strings.
You've just punted the problem. How are you checking whether the result is zero?
The correct code (copy and paste from libcperciva):
/**
* crypto_verify_bytes(buf0, buf1, len):
* Return zero if and only if buf0[0 .. len - 1] and buf1[0 .. len - 1] are
* identical. Do not leak any information via timing side channels.
*/
uint8_t
crypto_verify_bytes(const uint8_t * buf0, const uint8_t * buf1, size_t len)
{
uint8_t rc = 0;
size_t i;
for (i = 0; i < len; i++)
rc = rc | (buf0[i] ^ buf1[i]);
return (rc);
}Of course, we're not just defending against surprises from the calling code, we're defending against compiler behavior too. We're trying to constrain its options so tightly that it has no choice but to emit a series of machine instructions that we know will run in constant time. If it weren't such a hassle, we'd write crypto_verify_bytes() in x86 assembly.
It'd be lovely if our languages had a way to express these constraints directly, instead of phrasing them with artificially small types and low-level logical operations.
What prevents a compiler from optimizing that function to non-constant time? For example, by inserting a bailout condition (rc == 255) into the loop? (I am aware that it probably wouldn't, but "probably" in cryptographic contexts isn't generally good enough...)
Worse than that, there's things compilers actually do in practice, like optimizing away
memset(sensitivedate, 0, sizeof(sensitivedata))
and you can't even protect yourself against that properly by using a (volatile *) cast since the compiler is perfectly within its rights to make extra copies of that data and even without optimization the memset will only zero one of the copies. (gcc in particular loves to copy data onto the stack, due to a broken register allocator.)I've been trying to convince some clang developers that we need to have a C language extension for "we're handling sensitive data here, don't optimize it in dangerous ways", but it's difficult to define exactly what optimizations could be problematic...
There's a reason I talk about this in my crypto-in-1-hour talk though. ;-)
Naturally, we agree that devs shouldn't implement curves themselves --- but then, neither should they implement RSA-PSS. The right answer is to use Nacl, which is curve-based, but who cares? You're not interacting with that level of the design.
The first of those statements does not follow from the second. I agree that there has been an increase in the usage of elliptic curves; but I've also seen a lot more attacks on elliptic curves recently than on RSA or DH(Z_p). Unless you have a particular need for small signatures (e.g., bitcoin) or maximal performance, I maintain that using elliptic curves is an unnecessary risk.
Curves also have a nice property in practice of tilting designs towards forward secrecy.
I'm not sure why you say this. DH has exactly the same PFS properties regardless of the group being used.
My claim about forward secrecy is easy to understand: ECC software doesn't encrypt directly, but RSA software easily can, and developers (for instance: Paul Kocher) give in to the temptation to use RSA encryption directly. RSA practically begs developers to create systems that aren't forward secure.
There is a lot of new attack research on ECC. But how much of that research implicates (a) common implementations of (b) the popular curves (NIST-Px, secpx, and Curve25519)? Inquiring minds, who have been implementing a lot of attack papers on artificially vulnerable curve software, want to know!
(Also, curve attacks that target DSA tend to be the fault of the DSA construction. I'm guessing this isn't the level at which you're thinking of curves being attack in the literature, though.)
As for the more theoretical research -- none of it has been relevant to widely deployed systems yet, but this is the nature of research. Elliptic curves have a lot of structure which is still being explored, and as long as people continue to find attacks against new variants, I'm not going to bet that they'll never find attacks against the widely deployed systems.
I put being wary of ECC right now in the same category as being wary of SHA256 in 2006. It's not broken but there's a worrying amount of progress on related systems.
With respect to theoretical attacks, it is of course impossible to know what the future brings. But as long as we're sticking with conservative prime fields, I think it's probably either going to be fine or all of ECC is dead. It's also not like integer factorization or discrete logs have reached some kind of complexity lower bound; it's certainly not impossible that we'll get a L(1/4) method in the future as well. I look forward to see how it plays out.
Not impossible, no. But the fact that people have spent much longer looking for improved integer factorization attacks without making progress makes me more optimistic that integer factorization will continue to hold up to further scrutiny.