Image for post
Image for post

OPAQUE: One of the Great Advancements in Cybersecurity?

Why do we still store passwords on corporate servers?

Why do we still store hashed values of passwords on corporate servers?

Why do we still store hashed values and the salt on corporate servers?

Our fundamental problem is that Alice doesn’t want her password to be revealed to Eve, but Bob stores a hash of her password with a salt value. If Eve gets access to this, she basically just searches a dictionary of common passwords or brute force, and just adds on the salt value that he has used:

Image for post
Image for post

Increasingly we use ZKP (Zero-Knowledge Proofs) to prove that the Bob still has knowledge of his password. Another method is PAKE (password-authenticated key exchange) and which supports the hiding of a shared password within network communications. With this we can have a relatively weak shared password on either side, and then communicate to determine a strong shared key.

One of the most implemented PAKEs is SRP (Secure Remote Password) [here], and which is integrated into TLS and into a range of Apple products (such as with the iCloud Key Vault). But it is not the best PAKE around, and OPAQUE aims to provide the best methods around.

A perfect solution?

The perfect solution for our online world is for us to never store a user’s password or hashed version of the password on our servers, as there is no chance of an intruder finding out the password. Our data breach report which just say, that someone had stolen a whole lot of random numbers, and which can never be resolved back to anything that resembles their password.

So it seems impossible, but it’s actually easy. All we have to store is a salt value, and which is generated when the user registers their password. All that is stored is a completely random number:

Image for post
Image for post

Now Alice (the client) knows her password, and Bob (the server) only knows her salt. If Bob gets hacked, there is no way that an intruder will be able to know Alice’s password, as there is not even a hashed version on the server (Bob). Initially Alice registers with Bob, and generates a secret client key, and Bob gives her the server’s public key for a future key exchange.

The work was first published at EUROCRYPTO 2018 [here]:

Image for post
Image for post

and has now moved to being standardized by the Crypto Forum Research Group by Hugo Krawczyk [link]:

Image for post
Image for post

So how does Alice learn the salt, without actually determining what it is? For this we use an oblivious protocol named Diffie-Hellman Oblivious PRF (Pseudo-Random Function). This passes the salt value to Alice, but she cannot determine what the actual value is. The usage of the password registration process makes sure that there are public and private keys used to validate the generation of the shared key.

Image for post
Image for post

The client then receives a ciphertext which can only be decrypted within the right knowledge of the keys used in the registration. The decrypted is then checked against the key generated earlier, and the client with them know if the key is correct.

Implementation

Below is the basic method used, and where Bob generates a hash of his password to produce x. Alice and Bob agree on a generator value (g) and where they will use discrete logs. Bob then has a user ID (IDU), and generates a key pair (PrivU, PubU). He passes IDU, PubU, and g^x to Alice. Alice also has an ID (IDS), and a unqiue user key for Bob (y). She then create a key pair (PrivS, PubS). She then sends IDS, PubS, and g^y:

Image for post
Image for post

Bob computes d, and which is the hash of g^x and IDS, and Alice computes e, and which is the hash of g^y and IDU. Finally they compute the same key. Here is the working out:

Image for post
Image for post

Now let’s do some real code:

A sample gives:

User: user
Password: password
Private key: &{{cc4b7141398968db8156cad817aaef3f393daef269b86290034cb490f106099303e6f260f6
45e421fe688691cf26fffd106c2eb7ab794b30fc7697f5d8e39011 10001} 8a7af92245662775e38e79a15129
c5530df20a0cb82d0c62a259f46beb7534370405171472d46ac508980b12edf588f102571c58c5de1cd32040ca
56ae85d201 [cf8339f609a56d1b18ffc4603f70a5d09b29c8f9106b0809138780669c1f1d09 fc07bb095cf87
09c4a6f0da4e69076a0623808408191736aae68b12bf9fca4c9] {7c884226774f8194bb81f2b5d81ca045fa78
2f15d9568232003857443cf92711 bf5aa340ddace7db528c750091ea7797185bd0b021702d007fc18f5b8367b
639 1e769cdf16bc8626c94da6fd523137db266b34b6d8ef0854dab31234da63de07 []}}
Client session: &{c00000e400 c00000e3c0 70617373776f7264 200}
Session session: &{75736572 c00000e420 c00000e440}

User (user) has authenticated with password

User (user) has NOT authenticated with fred

The following registers a user. First we register a public and a private key for the user on the server (privS). Next, the user registers their username and password, and sends msg1 (and keep clientSession). The server receive msg1, and then applies its private key to the registration and sends back msg2 (and keeps serverSession). Next, the client receives msg2, and adds its own clientSession, to create msg3, and which is sent back for the server to store the user entity:

privS, _ := rsa.GenerateKey(rand.Reader, 512)clientSession, msg1, _ := opaque.PwRegInit(username, password, 512)serverSession, msg2, _ := opaque.PwReg1(privS, msg1)msg3, _ := opaque.PwReg2(clientSession, msg2)user := opaque.PwReg3(serverSession, msg3)

I have created a demo here:

Conclusions

Try not to store passwords on your server. Try not to store hashed versions on your server. Just store a random salt!

Written by

Professor of Cryptography. Serial innovator. Believer in fairness, justice & freedom. EU Citizen. Auld Reekie native. Old World Breaker. New World Creator.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store