End-to-end encryption, otherwise known as E2EE, is an incredibly important aspect of modern application security. It’s not only necessary to make sure that the data in our application is secure, but also to make sure that the data we send and receive is as well. Users rightfully expect that the data they are entrusting you with is kept safe with you and protected from anyone who shouldn’t see it as much as possible. End-to-end encryption is one of the best tools in our toolbox to make sure that this happens. In this article, we give you a head start on how to implement a modern way of end-to-end encryption into your app.
End-to-end encryption is a broad topic with many different strategies and implementations, but generally speaking, we can narrow the topic down to some form of data that is encrypted at a location and is able to travel to an endpoint location without being decrypted or revealed in the process. The starting point is usually a user’s device and the endpoint is usually the device of that same user or someone they want to share data with. It’s important to note that when we say the device isn’t decrypted en route, we don’t just mean that it manages to get there without being decrypted, we also mean that the tools or keys to decrypt it never leave the devices that are able to do that decryption. Even if I got my hands on data that you had sent to someone else, it would never be more than gibberish to me.
What Kind of App Can Use End-to-end encryption?
Let’s look at an example to illustrate some of the important aspects of end-to-end encryption and walk through some of the problems that it solves and how we can make it work for us.
Let’s imagine that Quickbird is considering developing a shopping list app. It’s important to him that he builds a server component around it so that he can sync his shopping list and add to it from his computer at work and also see it when he gets to the grocery store on his phone or maybe even on his cellular connected watch.
But there’s a problem. He eats a lot of candy and he might have to share it with his roommates in the birdhouse if they knew he had it. That wouldn’t be the worst thing, but he’d certainly prefer to keep it all for himself! As we all should know, if someone else has physical access to a device, it should not be considered secure, and in this case, his roommates live in the same house as his server, so we cannot consider it secure from them.
This means that the best way to make sure they can’t see his shopping list and when he chooses to buy candy is to make sure the server doesn’t know either. We have a great candidate app for implementing end-to-end encryption and keeping his candy – oops, I mean data – secure.
The app should look something like this:
Problems to Solve
Quickbird does have a few considerations to make when he chooses his encryption strategy. As I said before, it’s important to him that he can still access his encrypted data from multiple devices rather than being locked into the device he created it on.
It’s also important to him that if he were to lose his device or somehow have a problem with it, he can still access my data. Additionally, he should be able to see the benefits that we would normally get from adding end-to-end encryption to an app, no matter what version of it is used. That means that a partial list of the advantages he should see are:
- Only he can read the data
- Even if someone has access to the server, the information they can see will be useless to them and protect the user’s privacy, even from Quickbird, the developer.
- All of his devices are supported (PC, Phone, Watch)
- A way to recover his shopping list, in case he loses his devices
- It should be convenient and easy to use.
Let’s start working through these problems one by one.
Pieces of the Process
The first instinct that our feathery friend might have is to randomize a password or key of some kind to protect his user’s data. This would have the advantage of giving the recoverability feature that he desires. The user would only need to enter their information again on another device and then the key would be usable in both places. Unfortunately, as our article about passkeys points out, users are inherently bad at remembering and managing passwords, especially when that data is randomized and meaningless to them.
Knowing that he can use something similar. Rather than generating an entirely randomized key, Quickbird can begin the process with a human-readable passcode which can be treated like a password the user is familiar with, maybe even stored in a password manager.
One option here might be to use the password of the user or some other data linked to the account, but most often this kind of data is known to change and humans are really very bad at randomization, even when we do our best.
The candy information will remain much safer by generating a passcode with a proven standard to get the desired results. One standard already exists in the form of the BIP39.
BIP39 originated in the Bitcoin world and is the de facto standard for generating and managing encryption keys in cryptocurrency wallets. It generates randomized data which is then represented by a mnemonic phrase, which, for our purposes, means a human-readable string of words. For example, we might end up with one of these phrases:
narrow swing either holiday own rice nothing guitar fitness carpet public session object ankle kitchen
note fame mother rare uncle join delay toddler collect dove state siege series leaf sample
candy sweets toffee gum caramel marshmallow sugar desert bonbon honey delicious syrupy treat crunchy chocolate
It may not be as convenient as a password might be, but pretty difficult to guess and not the worst thing in the world to re-type if someone gets a new device and want to set up their shopping list app on it. The phrase is created by generating cryptographically secure bits, in this case, 128 bits, calculating a checksum of 4 bits and adding them to the end, giving us a total of 132. The bits are then split up into 12 segments of 11 bits each and those values are used to choose words from a wordlist. This creates a much more random sequence of words (and characters) than the average person or bird roommate would manage on their own.
The next step in the process will be to get a key from our passcode. This will be what we call the ‘Master Key’.
The master key has a few requirements we need from it. First of all, it needs to be deterministic. If Quickbird uses the same passcode on another device, he should generate the same master key with it. Beyond that, it should never leave the device it is generated on. We can even take that a step further and avoid storing it on any device at all. There’s no reason, other than an imperceptible performance blip, to ever store the master key on the device rather than just generating it when we need to.
So, if the user has a consistent master key that can be generated on any device that they manually share the passcode with, they should actually be able to encrypt their data, send it to the server, and decrypt it when they receive it again, right? Well, that’s technically true, but there are a couple of not-so-user-friendly problems to deal with here.
One aspect of secure encryption is key rotation. That means re-randomizing and re-generating keys regularly. For instance, to prevent someone who got access to the data from running a very long brute force attack where they have all the time in the world. Right now though, the key isn’t randomized. It’s based entirely on our passcode, which means that Quickbird, as a user, would need to be responsible for creating a new passcode and replacing it on all the devices he uses. To reiterate a point from earlier, human beings and birds are not wonderful at keeping ourselves digitally secure and the odds are that Quickbird would be an unreliable mechanism for this.
So let’s introduce what we’ll call a shared key. This key will be totally randomized, meaning it can be re-randomized. It will be stored on Quickbird’s server and shared with every device a user logs in with so that every device is always using the same key to encrypt and decrypt its data. Since it’ll be leaving the user’s device, Quickbird needs to make sure that it leaves encrypted and that nothing can decrypt it until it reaches its proper destination. That is what he will use as the master key. Its only purpose will be to encrypt and decrypt the shared key on a user’s device.
Now Quickbird has a key that can be decrypted, decrypt its data, regenerated, re-encrypt the data it protects with the new version, and re-encrypted to be shared along with the data it protects.
Bringing It All Together
Let’s go back over what Quickbird needed to accomplish with his solution.
- Only the user can access their information, even if someone intercepts it.
- Even if someone has access to the server, the information they can see will be useless to them and protect the user’s privacy, even from Quickbird, as the developer.
- Increased user trust as a result of their data safety.
- Multiple device support.
- A recovery pathway, as the result of a lost or stolen device.
- Very little interference for the user, in spite of all of the advantages they’ve gained. All that is asked of them is to keep record of their passcode and enter it in the event of a device change or other data issue.
Our blue mascot has now guaranteed that only the user has access to their information. If someone were to listen to network traffic to intercept the data, what they might receive would be the shared key, in an encrypted form and the encrypted data. They would be unable to decrypt the shared key and then could not use that to decrypt the data.
Similarly, if one of Quickbird’s roommates were to get access to his server, they would only see a jumble of encrypted data. It’s like staring at a lock with no key in sight.
We can now reasonably assume that Quickbird’s users will trust their data with him more, knowing that he has implemented a system to keep their data out of the hands of anyone besides themselves.
Those users can now use all of the devices they’d like, simply by logging in and entering a passcode to that they have access. The same applies to a user losing their device and wanting to a way to see their shopping (or candy) list. All of this adds very little overhead for a user to understand or work with and that means that the bird has achieved simplicity for his users as well.
Word of warning
There are a few important things to remember here. First, it is nearly always useful to remember that you should almost never implement your own cryptography or try to create your own standard. The best rule is to always use a well-known and trusted library that has been extensively peer-reviewed. There is a lot of expertise, study, and research that goes into topics that you might assume are simple, like cryptographically secure randomization and mathematics.
With everything Quickbird has put in place, he should be able to sneak home candy to feed his sugar addiction, without his roommates knowing to look for it at all. His only concern can now be about them noticing his growing weight.
Like all encryption, none of this is a one size fits all approach. It’s merely one strategy to accomplish a specific set of goals, even if that goal is as important as keeping my candy safe. If the topic is new to you then this can serve as a starting point to begin delving into topics like symmetric vs asymmetric encryption, Diffie–Hellman key exchange, Bob and Alice, and the various types of attacks and vulnerabilities of different strategies.
If you are interested in other ways how to secure your iOS app check out our article about best security practices on iOS.
Did you enjoy this article? Tell us your opinion on Twitter! And if you have any open questions, thoughts, or ideas, feel free to get in touch with us! Thanks for reading! 🙏
Are you a Software Developer?
Do you want to work with people that care about good software engineering?
Join our team in Munich