Creating a client-side encryption system

Javier Marquez
PassPill Project
Published in
10 min readMar 12, 2018

--

Nowadays there are lots of cool online services that allow you to express yourself or create almost anything that may come to your mind. Many of them are apparently free, the only thing you need to give away for using them is your personal information.

Some companies trade that data to make money, others just keep it to offer you a better tool. It seems that we always need to give at least our email address to use an online service. Is it impossible to create an online app without knowing anything from the users?

No, it’s not impossible. There is one kind of apps called Zero Knowledge applications, that doesn’t need the user to provide any personal information in order to create an account. The key is encrypting the data in the client side in a way that the backend can’t access to the information to be stored.

In this article we are going to explain how a Zero Knowledge app would work in a simple way, for every developer, there is no need to be a cryptography expert to understand it. It will be a cloud system to store data from users and we won’t even know their usernames or email address.

Creating the backend

We want to store user data, but we don’t want to know what data is that. To be sure, our system must make impossible that we, or anybody else but the owner, can access to that information.

The way of taking our dirty hands out of user’s data is a good encryption. One of those considered secure, impossible to decrypt by brute force in a million years.

We can’t let our servers encrypt the data because we don’t want to have anything to do with that data apart from storing and returning it to the user. So it’s the user the one that needs to encrypt it and send it to us in a format that we can’t read. The data needs to be encrypted in the client side.

This is exactly the way the PassPill Project wants to store the user’s passwords. For us, user’s encrypted data is called “the pill”. A capsule of encrypted information that only the user know what it contains. This is how a pill look:

V2h5IGRvIHlvdSBhbHdheXMgd2FudCB0byBrbm93PyBJIHN3ZWFyIHRoYXQgd2UgZW5jcnlwdCB1c2VyJ3MgZGF0YSBtdWNoIGJldHRlciB0aGFuIHRoaXMgbWVzc2FnZSA6KQ0KDQpTaW5jZSB5b3UgYXJlIGhlcmUgSSdsbCB0YWtlIHRoZSBjaGFuY2UgdG8gc2F5IHRoYXQgd2UgaGF2ZSBhIFBhdHJlb24gY2FtcGFpZ24gd2hlcmUgeW91IGNhbiBzdXBwb3J0IFBhc3NQaWxsIHByb2plY3QgYW5kIGdldCBzb21lIGdvb2RpZXMuIFRoYW5rcyENCg0KaHR0cHM6Ly93d3cucGF0cmVvbi5jb20vcGFzc3BpbGxwcm9qZWN0

Do you know what’s inside? I don’t.

Our mission is not to know, but just to store it for the users and give it back to them when they request it. From now we will represent “the pill” in a simpler way:

1.1 Morpheus, we have chosen the Red Pill.

The problem is: how do we know what pill belongs to what user? Looks like we need at least an username (speaking in authentication terms, otherwise it can be better called the pillname) to associate to the pill. We don’t want to give the pill to anyone that claims it either, we will need a password too to make sure that the user requesting has the rights of getting it.

Usernames and passwords make us look like we are starting to store personal information, like if we were not trustable anymore. There are 2 facts that will convince our users that they are using our tool anonymously:

- The user will not send their plain username and password, but hashes of them. For the server is impossible to decrypt those hashes to get the real credentials.

- Those hashes are not used to encrypt or decrypt the pill, so even if they are intercepted, the pill content will still be safe.

Storing the pill

The server will then receive a usernameHash, a passwordHash, and a pill that is already encrypted.

The steps for storing the pill are the following:

  • Concatenate the usernameHash and passwordHash in a single string and hash them together to create the userAuth token.
2.1 The userAuth token will be used to a authenticate the requests.
  • Concatenate the userAuth and the pill to get the authPill.
2.2 The authPill is what we store.
  • Store the authPill using the usernameHash as the key.
2.3 Look all those pills, beautifully labeled with their own usernameHash

Where to store the authPills is up to you. You can use a key/value storage system, a relational database table or even a file, using the usernameHash as the file name.

As part of the PassPill Project we will be writing articles about different strategies for storing the data, using most popular cloud infrastructure platforms.

Retrieving the pill

Things are doing well, we have a good bunch of pills stored in our backend. But how can their owners get them back? To get a pill back from the server, the user needs to provide the usernameHash and the passwordHash again.

Thes server then will follow these steps:

  • Use the usernameHash to get the authPill.
  • Exactly as the step 1 of the storing process (see figure 2.1), concatenate the usernameHash and passwordHash in a single string and hash them together to create the providedUserAuth token.
3.1 This is the user authentication token.
  • Compare if the providedUserAuth matches the beginning of the authPill.
3.2 If those two match this user has permission to get the pill.
  • If the tokens matched, strip the userAuth from the authPill getting the raw pill, and return it to the user.

That’s all it takes to create our ignorant server. I hope you could follow the article so far because it gets far more complicated in the front-end. Let’s have a look at it :)

Creating the front-end

So far we have designed a storage system. By itself is not private nor secure, it receives some credentials and store or retrieve data using them, but if the credentials are not hashed or the data is not encrypted we can perfectly know what and whose is the information.

Let’s design then the most important part to make our system private and secure: the front-end. When we say front-end we mean a web site, a mobile app or whatever way users can introduce their credentials to get his data.

As an example, imagine that our username is myName, our password is myPassword and our data is mySecret. Not really secure credentials, don’t you think?

Our system will make them stronger, but remember that using a password like that is not the best idea if you want to keep your data safe.

Knowing our tools

To encrypt and decrypt the data we are going to use the AES256 algorithm. AES256 takes an encryption key, like our password, and uses it to create an encrypted version of our data. The same key is used to recover the plain data again. We can see it like password protecting a file of our computer, in fact, it’s the algorithm used by Apple’s filevault or Android’s filesystem encryption.

AES256 is secure in the sense that there is no way of knowing the content of an encrypted text if we don’t know the key, but it is really fast encrypting and decrypting data and an attacker can try a lot of keys per second trying to discover ours. This fact makes the AES algorithm as secure as our key, since our password is easily predictable we are not in a good position.

Fortunately, our frontend won’t be using our plain password as the encryption key, we will make things hard for the attackers.

By hashing our password with an algorithm called bcrypt will be creating a secure key. Differently to other hashing algorithms, bcrypt is designed to be slow, it’s really expensive in terms of computational power, specially memory. We can make it as slow as we want and that’s good because if an attacker want to try many passwords, it will need to hash everyone of them beforehand.

Imagine that hashing our password into the encryption key takes 1 second, that’s asumible for our users, we can show a cool “decrypting…” animation to them in the meanwhile. For attackers is horrible, only 1 key can be tested every second if they want to try most common passwords.

Encrypting the data

Bcrypt takes our password and, using a random string called salt, applies some mathematical magic to hash it, to convert it in something different that will be our encryption key. It can iterate and apply the same mathematical operations once and again making is execution as slow (secure) as needed.

If every user have a different salt, attackers can’t create a dictionary of common password hashes that can work for everyone.

This way, a big part of the strength of our system resides in the process of generating the encryption key. That makes our myPassword not to look that bad. Anyway, it’s one of the worst passwords you can have, don’t use it.

The front-end app follows the steps below to create the pill:

  • If it’s the first time we encrypt the data we need to generate a random salt.
4.1 Two users should never have the same salt.
  • Use bcrypt with the user’s salt and the password to generate the encryption key.
4.2 The key is what we are going to use to encrypt the data.
  • Use AES with the key to encrypt the data, generating the encryptedData.
4.3 Encrypt the data using the key derived from the password.
  • Concatenate the salt and the encryptedData to generate the pill.
4.4 Now we know what’s inside our pills, even if we can’t access to it.

Finally, we have completed the recipe to cook our pill, the ingredients were just your password, some salt and the data we wanted to store.

The salt is stored in the pill, so we will need to actually fetch the pill from the server in order to generate the key that decrypt it.

In the server side we know the salt, but we don’t know the password so we can’t access to the information either.

Sending and fetching the pill

The pill can be sent to the server now. Imagine that somebody intercepts the communication at that moment. We should always use secure SSL connections for the communication between the back and the front-end but, for demo purposes, imagine that some hacker intercept our plain communication.

They will see our userNameHash, passwordHash and the pill. If they can decrypt the passwordHash, they will be able to access to the data, so we need a secure hash that can’t be easily broken.

Using algorithms like MD5 or SHA1 for the password hashing is like send them in plain format. They are vulnerable to attacks and there are some websites that contains dictionaries to decrypt billions of hashes in the blink of an eye. This one for example.

As we have seen before, bcrypt is a better option to securely create a hash our password. It requires a salt that we will generated from our userName and password .

5.1 SHA256 is another hashing algorithm, one without known vulnerabilities

That slice operation after the hashing is because we just need 22 characters for the salt. A prefix that define options for the bcrypt algorithm is needed to complete the salt, we won’t cover it here but you have a great explanation in this stack overflow response.

Bcrypt use to store the salt along with the hash, but since our salt is predictable and we never try to rerun the hashing in the server side, we don’t need it and we will strip it from our passwordHash:

5.2 The salt is appended to the beginning of the hash, that slice(-40) get rid of it.

The last piece for our users’ authentication is the usernameHash. It’s not the critical part of our security, so a SHA256 hashing will be enough:

5.3 The last piece to claim that the pill is ours

SHA256 returns a string with a hexadecimal number of 64 characters. If we feel that it’s too long for being used as the key for storing the pill, we can convert it to base32 or base64 to make it shorter. We can even use a part of it, we will have a slightly bigger chance of 2 usernames having the same hash, but it’s safe if our server responds that the pill already exist when trying to create the second one.

Decrypting the data

So far, we’ve been able to encrypt the data and send it to the server with our hashed credentials. The server has stored our data and using our usernameHash and passwordHash we can get it back.

The server send us the pill and we need to decrypt it. Do you remember the figure 4.4? The pill is compound by the salt and the encryptedData , with the password provided by the user we have everything that we need to reverse the encryption process:

6.1 Decryption process using the parts we saw in the encryption

As you can see, AES256 is a symmetric key algorithm, using the same key with the encrypted data returns the original data again.

Most cryptography articles are written for experts but I wanted this one to be understood by any programmer. Some steps are explained in a very simplistic way but I hope you got the point and take it as a starting point for understanding this kind of systems.

This article is part of the PassPill Project that wants to share the whole development of a web app. In fact, it describes how PassPill encrypts the information. I am not a cryptography expert, so any advices on how to improve the encryption system is appreciated!

If you liked the post, don’t forget to follow our publication and @passpillio in twitter in order not to miss any update!

--

--