Search code examples
encryptioncryptographytheory

remotely stored password protected private key


I was wondering if there is a protocol for this.

The idea is to have some encrypted data on a server.

Now I would like to find a protocol that fulfills the following requirements.

  1. Since actively managing keys is beyond what most users are willing to put up with, a password protected key is stored on the server.

  2. The server should not be able to decrypt the data. I.e. can not learn the password

  3. Third parties should not be able to access the password protected key because they don't know the password.

  4. There should only be one password. The problem is trivial to solve with 2 passwords. One to download the protected key and one to unprotect the key

The solution might involve some kind of zero-knowledge proof of knowledge of the password.


Solution

  • There is no problem generating multiple keys and authentication hashes from the same password. You shouldn't be sending the real password to the server in any case. You want to send it through PBKDF2 first. My usual recommendation for authentication looks like this:

    salt = static-per-app-token + username
    key = PBKDF2(salt, password, > 10000 iterations)
    send key to server
    on server: finalkey = SHA512(key) // This step keeps someone who steals your auth database from logging in
    compare finalkey to database
    

    Given that procedure, you can just modify the PBKDF2 step to generate a key twice as long as you need. The top X bytes are for authenticating, the bottom X bytes are for decryption. PKBDF2 can create as many bytes as you want, and having half the bytes doesn't help you figure out the other half.

    For your specific problem, this is probably the most convenient and best performing. You could also do as you suggest, and use two different salts for PBKDF2. That is also fine. It just takes twice as long to compute. If that's not a problem, then it's a very simple solution and may be even more convenient depending on your code.

    But just to take the discussion another step, let's say you didn't have a password. You had a (random) key, and you wanted to create independent keys from that. Or you have the output of PBKDF2 already, and want to expand it into more keys. In that case, you can expand an key using HKDF. The process is simple (|| means "concatenate"):

    prk = psuedorandom key (your input key)
    info = some-random-token || username
    T(0) = empty string (zero length)
    T(1) = HMAC-Hash(PRK, T(0) | info | 0x01)
    T(2) = HMAC-Hash(PRK, T(1) | info | 0x02)
    T(3) = HMAC-Hash(PRK, T(2) | info | 0x03)    ...
    

    You keep building T(n) until you have enough bytes for all your keys. You glue all the T(n) results together, and slice it up to get your keys out. This is much faster than PBKDF2, and can be applied to the output of PBKDF2 if you want.

    Take a look at the RNCryptor v4 spec for an example of a process for expanding a single password into a bunch of different keys and random values.

    Passwords are still horrible, and none of this can fix badly chosen passwords, but that at least stacks the cards in your favor if the password is reasonably hard to guess.