4

I need to send authenticated ciphered messages by using a single password. Reusing the same (derived) key for the block cipher and the HMAC is not a good practice, I know.

My initial idea is to derive two different keys from the password in order to apply a encrypt-then-MAC scheme:

Key1 = PBKDF2(passwd, SALT1, ITERATIONS1)

Key2 = PBKDF2(passwd, SALT2, ITERATIONS2)

Let M be the plaintext, the message sent is:

AES-CTRKey1(M) || HMAC-SHA256Key2(AES-CTRKey1(M))

SALT1, SALT2, ITERATIONS1, ITERATIONS2, and the IV (counter) are also attached.

Do you find any vulnerability in this scheme?

It looks good to me, but I’d like to know your opinion.

I know that AES in CCM mode (counter with CBC-MAC) is an alternative.

SilverlightFox
  • 33,408
  • 6
  • 67
  • 178

1 Answers1

4

Here are some weaknesses (the first one is the most serious):

  • If you do not include the encryption IV in the input to HMAC, then attackers could modify the IV and induce a corresponding change in the message without being detected. Similarly, the attacker could modify SALT1 or ITERATIONS1; the HMAC would still match, but you would get junk upon decryption. For encrypt-then-MAC, you MUST compute the MAC over everything that contributes to the decryption. In your description, the HMAC input should be an encoding of a structure containing the encrypted data itself, but also the IV, SALT1, ITERATIONS1, and any symbolic identifier for the used algorithms (in case you want to make some provisions for algorithm agility).

  • PBKDF2 is a password hashing function. It is made slow with a lot of iterations. Since slowness impacts attacker and defender alike, you cannot set the iteration count arbitrarily high. In your description, the defender must use two PBKDF2 invocations in normal usage, thus paying for both; whereas the attacker can run his dictionary attack on either. So the cost for the defender is ITERATIONS1+ITERATIONS2, whereas the cost for the attacker is only the smaller of the two. This is an advantage to the attacker.

  • PBKDF2 is not the best in class; it can be executed very efficiently on a GPU. Bcrypt would be better (see this answer).

  • You are relying on PBKDF2 behaving like a sort of "random oracle" with regards to the salt value. It so happens that in the case of PBKDF2, this is plausible (PBKDF2 is a chain of nested HMAC, starting with the salt). However, this is not a much studied property and you cannot count on it for just any password-based key derivation scheme. It would be safer to use cryptographic primitives the way they were intended to, because security comes from scrutiny, and exotic usage of algorithm has not, by definition, been extensively reviewed by cryptographers.

A better scheme would run one password hashing function (say, bcrypt), with one salt and one iteration count; and it would then expand the result with a Key Derivation Function (a fast one, e.g. HKDF) into a sequence of bytes that you would split into an encryption key, and a MAC key. This is, incidentally, how things are done in SSL. If you are in a hurry and don't want to implement an extra KDF, you can use in many cases a simple hash: compute SHA-256 on the bcrypt output, yielding 256 bits; the first 128 bits are for the encryption, the other half for the MAC.


In all generality, it is preferable to use authenticated encryption. CCM is an early attempt at an AE mode; better choices are EAX and GCM. The latter is fast becoming a standard for such things, and has been blessed by NIST. It is supports in SSL/TLS (since TLS-1.2) and is available in some widespread libraries (e.g. OpenSSL).

Tom Leek
  • 168,808
  • 28
  • 337
  • 475
  • Thanks for the quick response! You are right, the IV and the rest of parameters must to be included in the HMAC input, I missed this detail in my incomplete description. Efficiency (second point) is not a problem in this case, because these messages will be very infrequent. I prefer to use a very expensive function in order to avoid brute force and dictionary attacks. – Winston Wolfe Oct 24 '14 at 19:51
  • <> This is a good idea. In fact, while reading your response, I’ve just noticed that I planned to do that at some point (months ago) and I forgot about that (too many concurrent projects XD). I will do some research on GCM, I have never used it. We need to implement that in Java/Android. Any suggestion? The standard Java 8 implementation seems to be [buggy](http://blog.philippheckel.com/2014/03/01/cipherinputstream-for-aead-modes-is-broken-in-jdk7-gcm/). Thanks, again. – Winston Wolfe Oct 24 '14 at 19:55