When I described the architecture of the new re-built-from-scratch Screensaver Ninja, I talked about end to end encryption but I didn’t go into a lot of detail. Let’s do that now.
First, let me define what I mean by end to end encryption. It means that the data that is protected will be encrypted from the time it leaves the computer that generates it until the time it arrives at the computer that consumes it. When the data is in my server, at rest, it’s encrypted.
This is different from SSL or TLS which only encrypts the information while transferring from your computer to the server but then the server actually decrypts it and if it stores it, it stores decrypted. Both modes of encryption are important and have different uses and Screensaver Ninja will use both but this article is about the former.
Not all the data will be encrypted, only the critical parts, such as the URL of the sites you added, the cookies used to access those sites, as well as any other sensitive information that might be added in the future, such as credentials, scripts, CSS, etc. Particularly I’m not protecting: how many sites you have, for how long you show them, in which machines you show them. Should my servers become compromised, that data won’t give an attacker access to the websites you are showing so, they are less critical than cookies and credentials.
Back to Screensaver Ninja
In Screensaver Ninja, each account will have to be two symmetric keys (generated randomly). The first one is the master key which is used to encrypt everything and the second one is the displayer key, which is used to encrypt the screenshots for the displayers. The whole system will look like this:
The configurator and renderers will contain both, the master and the displayer keys while the displayers will contain only the displayer key. Your URLs, cookies, credentials and other sensitive material will be encrypted only with the master key so the displayers will never gain access to it. The renders will generate the screenshots and send them to the displayers encrypted with the displayer key.
These two keys will never be placed on the hosted server in plain text, they’ll be encrypted. Each user, renderer and displayer will have a public/private key pair that will be used to encrypt the master and displayer keys. All public keys will be uploaded to the hosted server but the renderers’ and displayers’ private keys will remain in their respective computers.
The users’ private key will be uploaded to the server so the user doesn’t depend on a single machine to operate Screensaver Ninja but it’ll be first encrypted with a key generated from the user’s password.
From that architecture, we can design the registration process that looks like this:
In that sequence diagram you can see the generation of the random public/private key pair that belongs to the user as well as the generation of random master and displayer keys.
It is important to note that the password never leaves the user’s computer. It is used to encrypt the private key (after applying PBKDF2 to it) and it’s also hashed before being sent to the hosted server with bcrypt.
PBKDF2 stands for Password-Based Key Derivation Function 2 and it turns a password into a piece of seemingly random data suitable for use as a key. Running it for so many iterations makes the process slow, which makes an brute force attack unbearably slow.
Bcrypt is a one way hashing mechanism used to store passwords so that the password can be verified but not reobtain. It’s the currently recommended solution to store passwords on a server so that should the database be exposed, nobody will be able to know what the passwords were. In the case of Screensaver Ninja, the password is hashed before leaving the user’s computer to avoid exposing it to the hosted server in any way and then again in the server for storage, in what I dubbed over hashed password, to avoid replay attacks should the database become exposed in any way.
The process of logging in is not as complicated as registration but it’s still more complex than your average application.
The user as usual enters the email and password but the first step for the configurator app is to request the salt used before to hash the password. This salt is random so we need to get the stored one in the server. With this salt, we can hash the password using bcrypt while in the client and send that for authentication.
The server verifies the hashed password matches the over hashed password in the server. If it does, then it considers the user logged in and sends the session cookie and the user’s data including the encrypted private key. This key has to be decrypted with the previously entered password which has to be processed with PBKDF2 the same way it was done while registering.
Even if an attacker somehow manages to log in by deceiving my servers somehow, the private key is still encrypted and without the password they won’t get far.
Once the private key is decrypted, the configurator requests all accounts. Note that Screensaver Ninja will be a multi-account application, so you can operate several accounts from a single email address. Each of these accounts comes with a master and a displayer key which is encrypted with the public key, so it proceeds to decrypt each of them with the private key. Once done, you are fully logged in and in business.
For clarification, the master and displayer keys are stored encrypted several times, once for each user, displayer and renderer, because each of them will have a different public/private key pair.
Changing the password
Changing the password has an added complexity similar to logging in. In this sequence diagram we assume the user is already logged in and wishes to change the password:
Both old and new passwords need to be hashed on the user’s computer before being sent and then the process is similar to your standard web application. Old password is verified, if it matches, new password is stored in the database.
The big difference here is that the private key of the user is encrypted with the password, so, a new version encrypted with the new password is sent to the server and it is stored in the database should new old password match.
Conclusion so far
For most web applications, I generally just use a library that implements authentication. It this case that is not possible as the system has very particular needs. I searched around for ready-made implementations of this and I couldn’t find anything. This meant spending a considerable amount of resources in building one and there were many iterations.
When it comes to security related code, the best way to do it is not to do it. It’s to rely on widely used and scrutinized libraries. What I’m doing here actually makes me uncomfortable. I have asked a few of my friends to review this design and that pushed me to do several iterations, improving the system and simplifying it.
Once I get some revenue from the project (or investment, who knows), I’ll hire a security expert to go over the whole thing again, both the design as well as the code implementing it. And if someone wants to see the code, I’m happy to post it online.