Hot on the heels of my dives into authenticated encryption and key-driven cryptographic agility, I thought I’d lay out a sketch of what I think a simpler core JOSE/JWT spec might look like. We’ll take in misuse-resistant crypto and Macaroons along the way. And cats. But first, a brief diversion into JSON.
In Part I, we saw that authenticated encryption is usually the security goal you want in both the symmetric and public key settings. In Part II, we then looked at some ways of achieving public key authenticated encryption (PKAE), and discovered that it is not straightforward to build from separate signing and encryption methods, but it is relatively simple for Diffie-Hellman. In this final part, we will look at how existing standards approach the problem and how they could be improved.
In Part I, I made the argument that even when using public key cryptography you almost always want authenticated encryption. In this second part, we’ll look at how you can actually achieve public key authenticated encryption (PKAE) from commonly available building blocks. We will concentrate only on approaches that do not require an interactive protocol. (Updated 12th January 2019 to add a description of a NIST-approved key-agreement mode that achieves PKAE).
If you read or watch any recent tutorial on symmetric (or “secret key”) cryptography, one lesson should be clear: in 2018 if you want to encrypt something you’d better use authenticated encryption. This not only hides the content of a message, but also ensures that the message was sent by one of the parties that has access to the shared secret key and that it hasn’t been tampered with. It turns out that without these additional guarantees (integrity and authenticity), the contents of a message often does not remain secret for long either.
In the old days these properties were considered separate. If you wanted to encrypt you used an unauthenticated cipher, and if you wanted to authenticate you used a MAC. If you wanted both (and it became clear over time that you almost always did), then you had to combine the cipher and the MAC and hope that the combination was secure. Mostly, it wasn’t. You see, it turns out to be less than straightforward to combine a cipher and a MAC. There is one approach that is (almost) always secure: Encrypt-then-MAC (EtM). First you encrypt the message using the cipher and then you authenticate the encrypted ciphertext using the MAC. But it took quite a long time before that was well known, and a lot of mistakes were made along the way.
In order to prevent these mistakes, cryptographers adopted the combined goal of authenticated encryption and developed cipher modes that combined both functions in one, such as AES-GCM or ChaCha20-Poly1305. Such modes treat the problem as a unified whole rather than as separate pieces to be combined by the user. Now there are many high quality authenticated encryption modes, and so it is relatively easy to do the right thing.
Contrast this with the current situation for public key encryption. In this setting, the security goal is usually seen as confidentiality with ciphertext integrity (also known as IND-CCA security – indistinguishability under a chosen ciphertext attack). This goal ensures confidentiality and integrity, but without sender authentication. The receiver can be sure that the contents are private and that the message has not been tampered with, but they do not know who the sender is. For instance, in this (generally very good) article from Matthew Green, he argues that IND-CCA2 is the correct security definition in a public key setting (compared to authenticated encryption), because in the public key setting “I should be able to (safely) decrypt messages from anyone”. I disagree with this goal however. I argue that in almost all cases I want to know who sent me a message, and I want to make a trust decision based on that information.
Consider Green’s example of Apple’s iMessage service, or even email. I don’t actually want anybody at all to be able to send me a message, as mostly they just send me spam. Even in the rare case that I am willing to accept an email from anybody, I want to be able to positively identify who they are before I decide act on its contents. Even in the public key setting, I almost always want (sender) authentication as well as confidentiality and integrity. I want public key authenticated encryption!
I am slightly mischaracterising Prof. Green’s blog somewhat here. He is not actually arguing (I think) that authentication is not important, but rather arguing a more subtle point: that the combination of (chosen plaintext) confidentiality with sender authentication (in the form of signatures) is not sufficient to prevent chosen ciphertext attacks in the public key setting like it was in the symmetric setting, and that therefore we want IND-CCA2 security. I completely agree with this, but my point is that I still want sender authentication in 99% of cases. In other words, I still want authenticated encryption but rather than combining authentication with a simple CPA-secure cipher as I can in the symmetric setting, I need to combine authentication with a CCA-secure cipher in the public key setting.
These kinds of subtleties are why I would like cryptographers to focus more on authenticated encryption in the public key setting. As was the case for symmetric key crypto, combining authentication with public key encryption is far from straightforward. I have seen developers replace a symmetric authenticated encryption algorithm used for session cookies with RSA-OAEP, and be completely unaware that now anybody at all can now forge a valid session. They needed authentication but didn’t understand the security properties of the crypto. It is easy to see why developers get confused as many standards present symmetric authenticated encryption algorithms and public key encryption algorithms (without authentication) as if they were equivalent and interchangeable.
I said earlier that the security goal for public key encryption is almost always presented as IND-CCA security. There is one prominent exception: NaCl. If you’ve not head of NaCl (pronounced “salt”, geddit?), it is a well-respected cryptography library developed by Daniel Bernstein. NaCl clearly documents the security goal of its
crypto_box function as public key authenticated encryption. As a result, it is much easier to construct a secure system with NaCl than by trying to combine typical public key encryption and signature schemes.
In Part II, we will look at how to achieve public key authenticated encryption in practice, including how NaCl does it (and potential drawbacks). In Part III, we will then consider some changes to JOSE and other standards to clarify and improve the security properties on offer.
One of the criticisms of the JOSE/JWT standards is that they give an attacker too much flexibility, by allowing them to specify how a message should be processed. In particular, the standard “alg” header tells the recipient what cryptographic algorithm was used to sign or encrypt the contents. Letting the attacker chose the algorithm can lead to disastrous security vulnerabilities. Continue reading “Key-driven cryptographic agility”
Almost everyone hates Java’s checked exceptions. Even those that still use them will admit that they lead to a lot of boilerplate. Everyone has code along the following lines: Continue reading “A small tweak to checked exceptions”
If you need an unguessable random string (for a session cookie or access token, for example), it can be tempting to reach for a random UUID, which looks like this:
This is a 128-bit value formatted as 36 hexadecimal digits separated by hyphens. In Java and most other programming languages, these are very simple to generate:
import java.util.UUID; String id = UUID.randomUUID().toString();
Under the hood this uses a cryptographically secure pseudorandom number generator (CSPRNG), so the IDs generated are pretty unique. However, there are some downsides to using random UUIDs that make them less useful than they first appear. In this note I will describe them and what I suggest using instead.