I'm writing a client application that wants to store some secret information with a storage service. The client has to authenticate the user with the service and the service should not be able to decode the information that it stores for the user. Authentication against the service has to be based on username - password combination (I also can't use local storage to store encryption keys on the client).

I want to minimize the information the user has to memorize (willing to sacrifice some security but not too much for this). Ideally just username plus 1 password. Is there a well understood cryptographic scheme that allows me to do that?

If not how well would the following work (from a security perspective):

  • Client app asks user for username (U) and password (P) .
  • Using cryptographic hash H(), the client registers the user with the user name U and password H(P).
  • Client then encrypts the secret that is to be stored using some password based encryption (e.g. PBKDF2) using P.
  • Client then authenticates with the service using U and H(P) and uploads the secret information encrypted with P.

The thinking being that the server will only ever see H(P) and thus not be able to decrypt the secret (which requires knowledge of P) and also H(P) being a reasonable password for the service.


3 Answers 3


If you want to keep the crypto simple and mostly on the server side (which may be a good choice e.g. if you're developing a web service), you could use the following scheme:

  • To log in, the client sends the password $P$ to the server in clear, preferably over a secure channel (e.g. HTTPS).

  • The server derives a "master key" $K_0$ from $P$ using a key-stretching KDF (e.g. PBKDF2 or scrypt) and a salt $S$ stored in the database. The length of $K_0$ should ideally match the natural output length of the KDF (e.g. 256 bits for scrypt or PBKDF2-HMAC-SHA256), and should be at least 128 bits.

  • The server uses a fast KDF (such as HKDF; note that extraction part of HKDF can be safely skipped, since $K_0$ is already a PRK suitable for direct expansion) to derive a verification key (i.e. a "password hash") $V$ of, say, 128 bits, which is compared against the corresponding value in the database to tell if the password is correct. If it is, the server can use the same KDF to also derive additional key material, such as any number of file encryption keys $K_i$, from $K_0$. After all necessary keys are derived, $K_0$ can and should be discarded.

Where desired and practical, the computationally intensive part of deriving $K_0$ from $P$ may equally well be performed on the client side. However, $K_0$ should still be transmitted to the server over a secure channel, since under such a protocol it is still effectively a "password-equivalent authenticator".

Under this scheme, the server only stores $S$ and $V$ (and, of course, the encrypted files and any associated material such as IVs), which are not sufficient to recover $K_0$ or any of the file encryption keys. A brute force attack on $P$ should be impractical due to the slow KDF used to derive $K_0$ from it; a similar attack on $K_0$ itself is infeasible due to the size of the keyspace.

The major and obvious weakness of this scheme, of course, is that a malicious or compromised server can still obtain $K_0$, and thus also any file encryption keys, when the user logs in. Thus, while the user is safe from server compromises as long as they don't actively use the service, they do need to trust that the server is not currently compromised at the moment when they log in.

As long as the actual file encryption is done on the server, the weakness described above is unavoidable. If this is not required, the obvious alternative is to perform the file encryption entirely on the client side (for which essentially the same key derivation process as described above may be used, just all done on the client) and in full control of the client (i.e. no running server-supplied crypto code on the client), and simply treating the server as a dumb file storage device. The user can then be securely authenticated to the server using an augmented PAKE protocol such as SRP, possibly (but not necessarily) using the same password as for deriving the encryption keys.

Edit: ps. Even if the server can't be made to support SRP or some other augmented PAKE protocol (e.g. if the server runs some pre-existing software that you can't modify), a custom client could still securely store encrypted files on it by deriving both the file encryption keys and the server "password" (e.g. a 128-bit key, ASCII85 encoded for interoperability) from the user's actual password using a KDF as described above. No matter how insecure the server may be, a compromise of the derived password will, at worst, allow an attacker to log into the server as the user, but it won't compromise the user's actual password or file encryption keys.

One difficulty here is that, without help from the server, there may not be any convenient place to store a salt for PBKDF2 / scrypt. One way to work around that issue could be to derive the salt e.g. from the username and the server name; this is not ideal, but it's good enough for most purposes, especially if you also use a random per-file salt (stored as part of the encrypted file) when deriving each file encryption key from the master key.

In fact, even if you do store the salt on the server, it may still be a good idea to append the username and server name to it, since it can protect against some theoretical attacks (where a compromised server lies about the salt to find out if two users have the same password) as well as, more practically, against poorly implemented salt generation.

  • $\begingroup$ I think the scheme in your last paragraph is better, given the requirement from the question that "the service should not be able to decode the information that it stores for the user". That scheme (from the last paragraph) seems like a good one to me! $\endgroup$
    – D.W.
    Aug 7, 2013 at 12:56
  • $\begingroup$ @D.W.: Agreed, the second scheme is better, if it's feasible. However, then you're no longer really implementing an encrypted file storage server, but simply a generic file storage server with secure password authentication. $\endgroup$ Aug 7, 2013 at 17:36
  • $\begingroup$ I think that's it. SRP would be nice but there don't seem too many implementations out there (and implementing ourselves is too work intensive and fraught with risk of compromising security by screwing it up). Server stored salt with username + salt + password for authentication seems to be the way to go. It still allows attackers to guess usernames (as I need a method to get the salt for a user name without authentication) but that's probably acceptable. $\endgroup$
    – user12889
    Aug 7, 2013 at 22:57
  • $\begingroup$ @user12889: If you don't want to allow username sniffing, you could always serve a dummy salt (such as an HMAC of the username using a fixed key) for any unknown usernames. Just try to avoid timing attacks (e.g. by always doing both a DB lookup for the salt and the HMAC calculation for any username before deciding which one to send). That said, generally one ought to assume usernames to be public information anyway; protecting them against guessing may slow down some attacks, but you shouldn't rely on it for any real security. That also means it's OK to leak them if you need to. $\endgroup$ Aug 8, 2013 at 9:01

Store the data on the server, encrypted under a cryptographic key that is stored on the client.

If the user might use multiple clients, you'll need a key management scheme to get that key onto all of the user's clients. That's the hard part. There are no easy answers.

Encrypting data with a key derived from a password typically tends to be insecure, in practice, given how humans work. Search on the IT Security Stack Exchange site for explanations of why.

  • $\begingroup$ I understand the limitations of password based encryption - i don't have a choice in this case. What my questions is about, is whether the proposed scheme is worse than password based encryption is at best of times. $\endgroup$
    – user12889
    Aug 7, 2013 at 4:30
  • $\begingroup$ @user12889, can you edit your question to indicate that fact? That wasn't clear from the question. We want questions to be self-contained (people shouldn't need to read comments to understand your intent), so I hope you can edit your question. $\endgroup$
    – D.W.
    Aug 7, 2013 at 4:33

Your proposal involves the server storing $H(P)$, and the client authenticating by sending $H(P)$ to the server in cleartext. This has multiple problems:

  • For authentication purposes, $H(P)$ essentially becomes the user's credential. The server stores this credential in cleartext. That's less than ideal, because if the server's database is leaked, the attacker now has all of the user credentials and can log in as other useres.

  • You should not store a hashed password on the server, using standard cryptographic hash functions. Standard hash functions are much too fast (and they lack salt). It would be too easy for an attacker who gains access to $H(P)$ to recover the password $P$.

What is the purpose of having the user authenticate? It might not be necessary.

If authenticating the user is necessary, one approach is for the client to authenticate to the server using PAKE (password-authenticated key exchange). Then, encrypt data on the client side using a cryptographic key derived from the user's password, and send it to the server over the PAKE-authenticated connection. The derivation function should use PBKDF with a random nonce/salt. If you do this, it's OK to reuse the same password for both functions (just don't reuse the same random nonce for both generating the encryption key and for storing the user's hashed password on the server for PAKE). You can find extensions to TLS that support PAKE, so the client would connect to the server using TLS-PAKE and authenticate using the PAKE protocol. This kind of scheme has been used before, e.g., in password-sync services (I think Mozilla used something like this, though I'm not 100% certain).

  • $\begingroup$ The server doesn't need to store H(P). I would expect it to treat it like any other user password and store some hashed version of it, i.e. store something like H'(H(P)). $\endgroup$
    – user12889
    Aug 7, 2013 at 4:46
  • $\begingroup$ @user12889, that's not enough. 1. If $H,H'$ is a standard hash functions, it'd be too easy for an attacker to recover $P$ from that, since standard hash functions are too fast. 2. The user still sends $H(P)$ to the server, so if the server is hacked, an attacker learns $H(P)$ and then may be able to recover $P$ (if $H$ is a fast hash). This is bad, because users often reuse the same password on other sites, and that means a successful security breach of your site may adversely affect the user's security on other sites. I suggest reading standard resources on password hashing. $\endgroup$
    – D.W.
    Aug 7, 2013 at 4:48
  • $\begingroup$ Isn't the risk of learning P from knowing H(P) the same as it would be for any service that stores password hashes for authentication? $\endgroup$
    – user12889
    Aug 7, 2013 at 4:51
  • $\begingroup$ Regarding PAKE, thanks for the pointer, haven't heard of it before. Am I right to assume that it does not require the client to disclose the password to the server (otherwise it would be useless as password for subsequent PBKDF). $\endgroup$
    – user12889
    Aug 7, 2013 at 4:54
  • $\begingroup$ @user12889, Regarding your first question, services should not be storing password hashes that are computed using a standard cryptographic hash function; this is standard advice. I recommend that you read standard material on password hashing. There's lots of good stuff on IT Security.Stack Exchange on password hashing, and on PAKE (see also Wikipedia): I encourage you to search there and read up on these topics before asking too many more follow-up questions. $\endgroup$
    – D.W.
    Aug 7, 2013 at 5:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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