I'm working on a proof-of-concept program to encrypt email with less difficulty for end-users with a process like:

To create an account:

  1. Generate a random salt and IV, and get a password from the user.
  2. Generate a 384-bit key with PBDKF2-HMAC-SHA512 (or maybe scrypt?).
  3. Split that into three 128-bit keys (k1, k2, k3).
  4. Generate an RSA key pair (for GPG).
  5. Encrypt the private key with k3.
  6. Generate an HMAC of the encrypted private key, using k2.
  7. Send the salt, IV, k1, number of iterations, public key, HMAC and encrypted private key to the server.

To log in:

  1. Request our salt and number of iterations from the server (maybe do something here to prevent leaking of email addresses).
  2. Regenerate k1, k2, k3 with PBKDF2.
  3. Send k1 to the server, which checks it against its stored version. This is used to prevent making the encrypted key public. Theoretically, this shouldn't matter, but it seems better to protect the encrypted data just in case.
  4. If k1 matches, the server sends back an AES-128 encrypted private key and IV, with an HMAC-SHA-1 MAC.
  5. Use k2 to verify the HMAC.
  6. Use k3 to decrypt the private key.

For future logins, we can simplify things since the server has the public key and we have the private key.

The idea is that even if the server is compromised, the only information it has is k1, the salt, the IV, and the encrypted private key, but to mess with the key, it needs k2, and to decrypt the key it needs k3.

Since the client only needs to decrypt the key once (and can then store it in their operating system's keyring), the number of iterations could be really high.

Am I correct in assuming that this would protect clients against a compromised server? I think the only attack vectors left would be hacking the client, brute forcing the password (which should be incredibly difficult, since we can use a lot of iterations of PBKDF2), and rubber-hose cryptanalysis.

EDIT: More specifically, this is meant to protect a user's password, private key and encrypted emails from an attacker, even if that attacker is working with the owner of the server. Unencrypted emails are basically impossible to protect, since an attacker in control of the server could just save them all off as they come in. Social engineering and rubber hose cryptanalysis are relevant, but my goal was mainly to reduce the possible attacks to ones that can't be kept secret.

  • Could you explain a bit more how the overarching concept works? Specifically: what is the server provided key used for?; do sender and recipient share a password?; what is the purpose of sending k1 to the server (it doesn't seem to use it)?
    – Michael
    Jun 22, 2013 at 21:09
  • @Michael The key is a GPG/PGP private key (used to encrypt or sign emails). We use k1 to "log in", so we don't give out all of the information to anyone who asks. This should make brute-force attacks slower for attacks who don't have access to the database. Jun 22, 2013 at 21:12

2 Answers 2


There's a couple of problems:

1 - In the log in sequence - step #4 - you are sending k1 for verification at the server. The server does not have a copy of k1. According to the account creation thread, step 3 (generate k1, k2, k3) occurred presumably client side, and were not part of the server transmission (step 7)

2 - If you store k1, then it's effectively a password - repetition of the k1 value is all that is required to get to login step 4.

3 - The thing that is standing between the attacker and the ability to imitate the user is the AES 128 bit encryption of the private key. It's stored on the server, and so if the server is cracked the encrypted key is obtainable. If you also offer k1, you might offer the ability to get added data. There's basically two possible calculations that the attacker can compute at until he finds a winning combo:

  • AES decrypt( test-k3, encrypted private key) = creates a key that can decrypt any encrypted email.
  • key gen with PBDKF2-HMAC-SHA512 (salt, # interations, test-password) = k1 (known), k2 (unknown), k3 (unknown)

Now we're into the realm where the question is "how easy are those things?" and I really don't know. That's a good question for crypto.SE - we're into the world of cryptography difficulty, which isn't my strong suit.

4 - don't negate the value of watching transmissions and colocation of servers. If the attacher has hacked the account/login server, what else is located on that server? If it's also serving email, or post-authentication privileged user transations - your problem isn't the steps above, it's the other high-value assets. Also - watching when a user logs in, and their general pattern of behavior, trials and errors will give enough social engineering information that the annoyingly mathematical part of cryptographic hacking is irrelevant. If I can break the server, call the user and apologize, and get him to tell me his password over the phone, the work of hacking the crypto is obsolete.

  • k1 is supposed to be stored on the server too, and it's meant to be used as a password. The idea is that the encryption should be strong enough on its own, but it's helpful if we can keep it secret too, "just in case". Assuming the communication takes place over TLS, then leaks of k1 don't hurt the security of the protocol at all, since if the leak is on the client side, we've lost completely and the attacker can just steal all of the keys, and if it's on the server side, then they already have access to the encrypted key, so k1 is useless. Jul 24, 2013 at 16:07

Some things to think about:

  • What are your identified attack vectors? Alternately stated, what specific types of compromise to your information security are you trying to prevent?
  • Your scheme is very complex. The more complex a scheme, the more moving parts it has, then the more possible weaknesses it has and the easier it is to gum up the works. Is there an easier way to protect yourself from your identified attack vectors?
  • The limiting factor here is your user's password strength, and his susceptibility to social engineering (doesn't have to be rubber-hose cryptography). These things are the weak link in the chain, and so all you can do is make sure that's the only weak link (so it's not your fault an attacker got in).

More specifically to the technical aspects of this scheme:

  • Having a separate MAC is a weakness; because checking it isn't integrated with the decryption of the private key, depending on your cipher mode an attacker could possibly hijack your client software as a decryption oracle to recover the private key. Consider using an authenticated cipher mode which integrates checking the HMAC with decrypting the message.
  • Corollary to the above, there's little value in using a different key to compute the HMAC than you used to encrypt the ciphertext you're digesting. An attacker who can change data in your DB can foul up the login (enabling social engineering by posing as you to "recover" the account) by scrambling either the HMAC or the key, no secrets necessary. Someone trying to obtain the private key won't care about the HMAC at all, since it's not necessary to crack the HMAC to crack the encrypted key. So, consider combining k2 and k3 into a single 256-bit key that will encrypt the private RSA key using an authenticated mode.
  • The server does no work in this scheme beyond matching data, and you didn't mention securing the traffic between client and server. The scheme is therefore exploitable to obtain a data dump by observing the incoming login requests and replaying them, without having to know any secrets used to generate the password hash (k1) being transmitted. Consider requiring a secure connection between client and server to mitigate this.
  • I was using a separate key for the HMAC because this answer says it's generally not a good idea to reuse keys, since there's a possibility that it will cause problems. Using an authenticated cipher mode does sound better though. Jul 24, 2013 at 17:01
  • Using TLS is also a good idea. The reason the server does so little is that the protocol is meant to protect against compromised servers, even if the server's owner is working with the attacker. If the owner of the server goes to the dark side, they can steal the unencrypted emails, but they shouldn't have the users' passwords or encrypted emails, or any way to get them besides brute-force or social engineering attacks. Jul 24, 2013 at 17:07
  • Well, the server could still hash k1 again and store that, meaning that an attacker could obtain a list of usernames and k1 hashes and still wouldn't have what they needed to obtain the rest of the data via the service endpoint. If you can't trust the server, you lose; if the server's owner goes over to the dark side then social engineering is trivial, because the guy calling you really is the person who should be resolving any user login issues.
    – KeithS
    Jul 24, 2013 at 17:16
  • The server can store k1 however it wants, it's not important to the protocol. I'm assuming that if someone can get access to k1, then they can probably get access to the encrypted key, so they don't need k1. I'm not sure there's anything I could do protocol-wise to protect against social engineering. At least people who know what they're doing would know better than to give their password to someone. Jul 24, 2013 at 17:19
  • This is why PGP implementations typically stand alone; if you could trust your e-mail server to secure your messages properly, you wouldn't need PGP. Giving your PGP private key to the e-mail server for safe-keeping, even encrypted, takes the responsibility out of your hands.
    – KeithS
    Jul 24, 2013 at 17:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .