I'm building an application with the following requirements:

  1. A client logs in with email and password. On the server, bcrypt(password) is computed and compared to what is stored in the database.
  2. If authentication is successful, the server sends a block of encrypted data that will later be decrypted on the client. (At some point in the past, the data was encrypted by the client and sent to the server.)
  3. The password for the encrypted data must never be sent to the server, so I can't use the same password for authentication and encryption/decryption.

One solution that would fulfill these requirements is to just use two passwords: one for authentication and one for encryption/decryption of the data. However, users will need to remember two passwords and could mix them up, accidentally sending the decryption password to the server and violating the third requirement.

An alternative that requires only one password: use the password for encryption/decryption and bcrypt(password) for authentication. That way, the database would store bcrypt(bcrypt(password)) and the server would never handle the encryption/decryption password.

Is that a reasonable thing to do or am I overlooking a security issue? The additional computation/login time caused by computing two hashes wouldn't be a problem.


The more appropriate solution for this kind of thing is a prefix.

For example:

  • Authentication key = bcrypt("AUTHENTICATION" + $password)
  • Encryption key = bcrypt("ENCRYPTION" + $password)

However, I should note that BCrypt isn't really designed to generate cryptographic keys. It is a password hashing function, not a key derivation function, and their properties are different. You could probably get away with it, but I would suggest using PBKDF2 or a similar KDF for generating the encryption key. At this point the use of a prefix is redundant, as you're using two entirely different algorithms.

| improve this answer | |
  • Do you mean that the authentication key should be generated by the client? Otherwise, the server would receive the plain text password together with the prefix. – chrisklaussner Jan 1 '17 at 17:22
  • @chrisklaussner It was a simplistic method. My advice would be to use an SRP scheme which doesn't reveal the password to the server. Jeffery's answer below explains one way of doing that. – Polynomial Jan 1 '17 at 19:58

I work for AgileBits, the makers of 1Password and I will describe what we do to live up to our name of 1 password. I will actually give you a simplified version of it, because there is other stuff we do in our key derivation that isn't relevant to this question.

The two keys that our clients are deriving from one master password are something we call a Master Unlock Key (MUK) which is the key with which the secret part of a user's public key pair is encrypted. The other key is the SRP-x to be used in the Secure Remote Password authentication protocol. Both are 256 bits.

We start with two different salts, I will call one salt_auth and the other salt_enc. And we have two different version strings; let's say that they are "v1-auth" and "v1-enc".

For deriving the SRP-x

so now in very pseudo code

deriveKey(salt, password, version, email, accountID)
    s <- HMAC-SHA256(salt, version + username)
    k <- PBKDF2-HMAC-SHA256(password, s, 100000)
    k <- HMAC-SHA256(k, version + accountID)
    return k

Now not all of those steps are necessary. But there are two differences in input to this when deriving either the SRP-x (for authentication) or the master unlock key (for encryption). The salt, and the version.

(There are other reasons we bind the username and email address into the key derivation that aren't relevant to this discussion. Also we use HKDF instead of HMAC-SHA256 so that we have something more adaptable to different key sizes.

Use PBKDF2 with care

Whatever you do, do not use one run of PBKDF2 to derive two keys. For example do not use PBKDF2 to generate, say, a 256 bit key which you split into two 128 bit keys. Bad things can happen if you do that.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.