This was originally posted on a website I was developing over a year ago called Keenotes.
So let's say you are tasked with developing a mobile or desktop application that requires a user to create an account and later log in with it. So you happily design the GUI and write the database software and API, no problems. Your code is efficient and readable, and doesn't have any of the memory corruption bugs you've been taught to avoid. Perfection! But then, that one annoying guy on your team (the one who might wear a tin foil hat if it weren't passe) brings up security: "How secure are our users' authentication credentials? What if, for example, our database/proxy server gets hacked?"
What follows is a rough draft for a client-server user authentication algorithm that prevents any user's password from being transmitted in the plaintext. In essence, a user's long-term password is transformed into one-time passwords (hashed client-side then server-side) to prevent replay attacks and mitigate against password reuse by reducing the worst-case (a hacked server) from unauthorized plaintext recovery to bruteforcing a 512-bit Whirlpool HMAC key.
When setting up your database table for user credentials, you should accommodate enough space for the index, username, bcrypted final hash, and a 512-bit base32-encoded nonce in addition to any other fields you need (permission level, etc.).
int id; varchar(64) username; varchar(90) passHash; varchar(103) nonce;
- Client sends username to Server.
- Server responds with the bcrypt salt to use for the final hashing process, and the nonce to use for the Whirlpool HMAC.
- Client calculates the authentication hash:
Auth = bcrypt( WhirlpoolHMAC(msg = nonce, key = userPassword), salt)
- Client generates a new nonce (newNonce)
- Client signs nonce with password:
nextPW = WhirlpoolHMAC(msg = newNonce, key = userPassword)
- Client sends Auth, newNonce, and nextPW to Server.
- The server compares Auth with the bcrypt hash on file for this user. If mismatch, fail.
- If authentication is successful, server generates a new bcrypt salt.
- Server calculates and stores nextAuth = bcrypt(newNonce, newSalt) and newNonce
- Client is granted access to their resources on the server.
Strengths of this Algorithm
- The user's password is never transmitted over the wire; it is instead transformed into a one-time password using a salt and nonce stored on the server (supplied by the user's client during their last login).
- A hacker who obtains access to the database must bruteforce bcrypt hashes of Whirlpool HMACs to access a single attack
- The server never knows the user's plaintext password, so password reuse isn't as enormous of a threat.
- If implemented correctly, the server never delivers the code used to encrypt the password. (Desktop applications v. web applications)
Weaknesses of this Algorithm
- A malicious user capable of decrypting SSL/TLS traffic can still perform a replay attack by using the previously sent nextPW and recovering the server's salt (freely given when the username is supplied).
- The algorithm as described does not protect against MITM attacks that replace the nonce and/or the nextPW parameters. (SSL/TLS is a must here)
Never use any of the keys, salts, or nonces in this protocol for encrypting/decrypting server-side data. This system should only be used to provide a user access to the ciphertext; the actual encryption key should be derived separately from the user's plaintext password and encrypted/decrypted client-side. Thus, replay attacks will be useless to an attacker attempting to gain access to encrypted file lockers.
Strong public key cryptography (SSL/TLS or Curve25519 + AES-128 and Web of Trust/Convergence/Sovereign Keys) should be in place to ensure the client is speaking to the correct server and no third parties are intercepting the hashes, salts, and nonces.