5

I am working on a cross platform (JS/iOS/Android) list manager application that persists data through a REST API and I want to ensure that any textual data is properly encrypted on the client side so that there's no way to decrypt the data on server side and in the unfortunate case that the database be stolen it does not worth spending any effort on trying to decrypt it.

Many months of research and trial and error has led me to decide that AES encryption in CBC mode of operation is the best choice for the purpose due to its strength and wide adoption on all platforms. I decided to do key derivation based on OpenSSL's similar algorithm so that I have a reliable command-line tool for testing against the accuracy of my implementation.

The only thing that's left is to somehow ensure the validity of the user-supplied encryption passphrase which will never make it to the server side directly. The best idea that has occurred to me so far is to decrypt several list items after login to test if they can be decrypted properly by applying HMAC hashing on the ciphertext, which in turn requires the storage of the HMAC hash on the server side.

I have a couple of questions:

  • is there any other way to securely ensure the validity of the passphrase provided after a successful login?
  • is it safe to re-use the same passphrase in AES CBC encryption as a secret key in HMAC hashing of the same encrypted data?

I've seen this TLS thread but I do not need the encryption between a typical client/server setup. In my case the client encrypts the data, stores in the cloud and next time either the same client or another client will decrypt it. Thus, handshakes and such in TLS make not much sense to me.

Note 1: I intentionally call it a "passphrase" above to emphasize it is different from the user password which is validated at log in and is stored as a hash within the user account.

Note 2: On the top of all that, requests will travel through HTTPS. The point of the above is not to ensure transport security but that to restrict data readability to the client side for numerous security benefits.

I appreciate any feedback. Thanks!

Norbert
  • 53
  • 5

3 Answers3

9

You are rolling your own cryptography. Don't roll your own crypto. Instead, I recommend that you use PGP or GPG, or use their format: namely, the OpenPGP file format.

You should not use the same key for both encryption and authentication. I don't recommend using the same key for both AES-CBC and for HMAC.

Instead, I suggest that you derive two keys from the master key. e.g., Kenc = AES(MK, 0), Kauth = AES(MK, 1), where MK is the master key (an AES key), and 0,1 are two different AES plaintexts. Now use Kenc as your encryption key for use with AES-CBC, and use Kauth as your authentication key for use with SHA1-HMAC or AES-CMAC.

Alternatively, you can use an authenticated encryption mode, such as EAX, CCM, GCM, or OCB. Authenticated encryption takes care of deriving keys from a single master key, and takes care of providing both authentication and encryption.

It sounds like you are also making another mistake: using a passphrase as your encryption key. You should try to avoid using passwords as encryption keys.

Is there any chance you can just use GPG conventional encryption? GPG has been carefully written and vetted to take care of all these issues.

D.W.
  • 98,420
  • 30
  • 267
  • 572
  • 2
    Excelent answer. I would just add that the authenticated encryption modes do require additional processing resources which may be a issue on mobile platforms. – this.josh Sep 12 '11 at 06:41
  • @D.W. I appreciate the detailed answer. I do not aim to roll my own crypto, actually it's the opposite. I also need however the encryption to work within a web browser on the client side so compiled libraries are not really an option. For this reason I chose the minimal subset of algos that I need, namely an encryption standard (AES CBC), a hash function (HMAC SHA256) and some sort of standard [key derivation](http://www.openssl.org/docs/crypto/EVP_BytesToKey.html). I thought running the test vectors and testing against the command-line OpenSSL client would provide enough credibility? – Norbert Sep 16 '11 at 19:32
  • @this.josh Thanks for pointing out processing resources as a decision factor for mobile platforms. – Norbert Sep 16 '11 at 19:34
  • @D.W. Also, I do not plan to use the same key, only the same passphrase for key derivation, which includes random salts for both generating the encryption key and generating the key used for hashing (separately). Does that clarification make it more acceptable from a security standpoint? – Norbert Sep 16 '11 at 19:38
  • I think I made a mistake in communicating the question itself. Rephrasing the question title. – Norbert Sep 16 '11 at 20:16
  • @Norbert, your revised question is too confusing for me to figure out what you are planning to do, but yes, it is OK to derive two keys from the same passphrase if you use PBKDF2 as Justice explains. It's better to use a truly random crypto key instead of something derived from a passphrase, but if you have to start with a passphrase, do it the way Justice explains. – D.W. Sep 16 '11 at 22:51
  • How do you do authenticated symmetric encryption with gpg? – Siyuan Ren Oct 17 '14 at 08:07
7

You can use PBKDF2 with different salts to derive keys for the hashing and the encryption. The purpose of the PBKDF functions is deriving keys from pass(words|phrases). The salts should be generated using a cryptographically strong PRNG, and should be long (e.g., as long as your keys). Note that the salts for PBKDF2 are not necessarily secrets, so it's safe to send them to the server if necessary (at least the salt used as input in deriving the key used for HMAC).

yfeldblum
  • 2,807
  • 20
  • 13
1

Yes, you can use that pattern.

  • Providing a user with plaintext and an HMAC of that text does not reveal the HMAC's key.
  • Providing a user with plaintext does not allow them to derive the HMAC without knowing the key.
  • Providing a user an HMAC without a key does not allow them to test the HMAC against any text.

With those known factors, providing the user with an HMAC and cyphertext that use the same key gives that user nothing without their knowing the key. There is nothing there that could reduce the number of attempts required to defeat the encryption.

The substantive difference between this scheme and providing as simple hash of the plaintext is that one can't effectively attempt to brute force / rainbow table the hash (though the message is probably too long for that anyway).

There are other modes that encrypt a message in such a way to ensure authenticity. These are mentioned in the answers of others and may be more useful depending on your needs. I suggest investigating those also.

Jeff Ferland
  • 38,090
  • 9
  • 93
  • 171
  • 2
    If you provide the user with plaintext and a HMAC of that text *where the key is a passphrase*, then this might well reveal the passphrase. In particular, it enables dictionary attacks on the passphrase. Given that most users pick passphrases that don't have enough entropy to resist offline dictionary attack, using a passphrase as the key is not very safe. – D.W. Sep 11 '11 at 07:02
  • You can brute-force anything. If you give them an encrypted file where the key is the passphrase, same thing happens. Dictionary attacks don't need to absolutely confirm the data via a hash. – Jeff Ferland Sep 12 '11 at 14:25
  • no, it is not true that you can brute-force anything. For instance, you cannot brute-force a truly random AES key (not within your lifetime). More to the point, there are partial defenses against dictionary attacks on passphrases, as described in my answer. These defenses are not perfect, but they do help. – D.W. Sep 13 '11 at 06:55
  • Now you're arguing key strength, not information disclosure. The point of the question is whether that information causes an increased weakness; it does not. – Jeff Ferland Sep 13 '11 at 13:36
  • I tend to agree with @D.W. that providing the user with the plaintext and HMAC enables dictionary attacks on the key, however the key would be different than the one used for encryption as each key is a result of key derivation that differs in the salt at the very least. Also, I'd like to protect against database theft, in which case the attacker would only get access to the cyphertext and the hash and possibly the salts but not the plaintext nor the original passphrase used for deriving the keys. – Norbert Sep 16 '11 at 20:01
  • 1
    @Norbert, thank you for your kind words, however, I think you may be a bit confused about how this works. Your statement beginning "however ..." is not correct. The risk I'm talking about is that someone might iteratively try guesses at the passphrase, and for each guess, derive the corresponding decryption key (or MAC key) and do a trial decryption of the ciphertext to see if it looks valid (or check the MAC for validity). If this attack is successful, it recovers the original passphrase and all keys derived from this passphrase. – D.W. Sep 16 '11 at 22:54