Like most software systems, cryptographic toolkits are built in layers. In my experience many people, even software developers, don’t understand the difference among the various crypto layers or when and how to use them. When bugs arise in algorithms, protocols, or libraries, it’s important to know what layer those bugs are in, or we might incorrectly blame the lower layer for “letting” the higher layer do something wrong.
In brief, here’s my way of thinking about the layers of crypto, from least abstract to most abstract. In reality, it’s not actually this neat and tidy, but let’s take a stab at it:
- The Math: In case you didn’t know, crypto is math, and the math is good. As far as we know, and until there are quantum computers. At this layer, the application itself isn’t even a twinkle in a grad student’s eye.
- The Algorithm: This layer includes the alphabet soup of symmetric and asymmetric ciphers and hashing algorithms. They all work very differently and apply to different problems (e.g. AES, RSA, ECC, SHA2). I’m also including related algorithms in this layer. Like how keys themselves are generated (e.g. password-based key derivation functions, pseudo random number generators) and the cipher “modes” (e.g. ECB, GCM, CCM). These ciphers themselves are awesome! But hard to use right, particularly in combination with various ancillary functions and modes. In fact, they are merciless. They will let you make encrypted looking stuff that’s not secure at all. It’s usually not their fault, though, and it’s not the math’s fault.
- Example: The Signal protocol uses a variety of ciphers, including AES.
- The Algorithm Library: Now we get to the programming bit! Each programming language has a set of potentially non-overlapping combinations and implementations of those algorithms, so it’s sometimes hard to communicate across languages. Java, for example has its crypto libraries that include all the algorithms above, as implemented by e.g. BouncyCastle. At this level, you have to understand the algorithm itself in order to use it correctly. For instance, without some help, you probably will use AES wrong in Java.
- The Protocol: HTTPS, SMIME, JOSE/JWE/JWT, SAML, OAuth, Whisper, etc. The protocol is the application of a set of algorithms to a type of problem. Notice that this is a type of problem, not necessarily a specific application. Plus “protocols” span a very wide range of abstraction levels since they build on each-other. Protocols are often specified in Requests for Comment. RFCs are the Internet’s way of being almost detailed enough about something.
- The Protocol Library: You can’t actually use a protocol until someone writes a library for it. OpenSSL is a great example here because it’s a very widely deployed implementation of HTTPS. Many people use OpenSSL correctly, and many people use it badly; and many people use it for things besides HTTPS, which is why it appears multiple times in this list.
- The Application: The use of the protocol library to secure a part of the whole system. Note at this layer you must be sure that you’re actually using the protocol right or you’ll get security bugs. And sadly, with today’s tools you usually have to understand each layer down to the algorithm itself. That’s because your specific problem isn’t actually addressed by any of the layers between here and the algorithm.
Insecurity Happens Between Layers
At each layer in this stack, some developer somewhere can take some action that the previous layer didn’t stop them from taking. That’s just the reality of programming. In the space between 5 and 6, WhatsApp got a lot of bad press
for making different choices from Signal about how it does key rotation (even though it wasn’t a mistake or a back door). Similarly, people usually even mess up deployment of HTTPS.
Despite Java having implemented AES many years ago, its libraries (layer 3) are so vague that they’re super easy to use wrong
, and so everyone uses them wrong. In fact, most people skip layers 4 and 5 and just start hacking away on layer 6. When this happens, they barely know what security properties they’re looking for and it’s not going to be secure. Your above average programmer simply can’t pick up an AES library and use it to secure a piece of software. The bigger the conceptual distance between these layers, the bigger the mistakes.
The Missing Layers: Shrinking the conceptual distance
When it comes to Java, we saw an opportunity to be helpful and add a layer 4/5 with our AES library for strings
in Android. It’s very short, very opinionated about how to do things right, and it makes a lot of choices for you. It also can’t
be used for anything besides encrypting strings in Java using AES, unlike layer 3 which has the kitchen sink. That’s always the trade-off with climbing up the abstraction layers. It tries not to let the conceptual details of the algorithm leak all the way up to the application where they can be misused.
Similarly, and on a much larger scale, crypto_box from NaCL
combines a set of algorithms in a meaningful way to get what you probably actually want. PGP handles keys, signatures, and both symmetric and asymmetric ciphers. OpenSSL provide the crypto for a vast array of different protocols and applications, including email, HTTPS, files, and chat.
I like to point to Signal because they did a great job of really thinking through and owning
layers 4 through 6. They had specific application-layer goals and built them. That’s also what Tozny is doing with E3DB, our end-to-end encrypted database
Cryptol by our parent company Galois
is a programming language that shortens the conceptual distance between the math and the algorithm and provides the SAW workbench to prove the next step up. Amazon used this
for their TLS implementation with great results
This brings us back to JOSE
In a recent blog post, esteemed crypto guru Scott Arciszewski charged that “JOSE is a bad standard that everyone should avoid
.” His blog post has been updated a few times, but as of this writing, his current argument attacks the “standard” at different layers and conceptual gaps. In summary (and with massive amounts of interpretation on my part):
- Don’t use JWTs for trusted client-side session storage: In this point, he’s attacking the gap between 6 (client side session storage) and 4 (JWT). In fact, he’s arguing against doing client side session storage at all, so the mistake is perhaps just at 6.
- JWTs can be implemented with various crypto algorithms (from layer 2), which lets programmers choose them and attackers misuse the declared algorithm. Known attacks have happened between layers 4 and 5 where the spec apparently didn’t tell the library developers not to accept the “none” algorithm and make it the default, and not to let the attacker lie about what kind of key they have. Maybe that should have been obvious, but like I said, RFCs are the Internet’s way of being almost detailed enough about something.
- JWE leaves room for implementation errors by exposing a variety of encryption algorithms (from layer 2). The challenge here is that this suite of algorithms from layer 2 leaks all the way up to 6, and the vast majority of developers don’t know how to use layer 2 safely. This is a huge conceptual distance.
His criticisms and recommendations make it clear that he’s arguing to close the conceptual gap between 2 and 6. He argues JOSE is a bad “5” and a good “5” would offer combinations of ciphers and operations that are always mutually consistent. He knows what he’s talking about and has done a lot of work to close similar gaps. But,
his post is a long way from being convincing
about its claims.
JOSE is a solid standard. It’s been implemented very widely and successfully by awesome protocol engineers, cryptographers, and developers. But the sad fact remains: Crypto software for programmers sucks.
We need easy application-specific protocols
There are few good tools that close the most important gaps, and it’s been bad for a long time. But it’s getting better. libSodium
firmly sits between 3 and 4 and closes some of that distance. Toolkits like the Whisper protocol are fantastic for very specific applications because they close the gap all the way up to 5+. In my humble opinion, we need more and better protocols that do a good job solving specific problems at that level
so programmers can build better applications.
Tozny is building an end-to-end encrypted database for storing and sharing sensitive data. Take a look at our examples where you can implement end-to-end sharing of data in a few lines of code
, without listing a single crypto cipher. The goal is to let you store data for later retrieval on a demand or on a publish-subscribe basis, but to work at a simple name/value pair basis like a regular NoSQL database.
When we do a full release, you can use Project E3DB
for any type of data storage.
- Do you collect data about your users? You should be storing it encrypted at rest so the bad guys can’t get it. You really should already be doing this.
- Do you share data with third parties? Instead of sending them your data in the clear, make them read it encrypted at the source. It lets you stay in control and doesn’t encourage them to store their own plain text copy.
- Do you want your users to communicate end-to-end encrypted? Each party can have their own key and send JSON blobs to each-other without you or us being able to decrypt it.
- Do you want to segregate data among your servers? Ever heard of the Principle of Least Privilege? Project E3DB lets you create a JSON object store where only the services that need access to the data will get the keys to the data. That means that if a bad guy breaks into one part of your system, they don’t get everything!
We do use JWE standards in our implementation because we want to remain flexible about the ciphers we offer going forward. This is important for interfacing with crypto systems that are not under our complete control, but we narrow the scope of JWE significantly so that only secure combinations of ciphers can be used for their appropriate purpose.
E3DB is going to be awesome. Come try it out.