When the client authenticates with our service, he is issued an opaque "token". This token includes the information, that identifies the client and some other information about the client ("payload"). The payload is used by the service, when the client passes the token back to the service on subsequent requests. The payload should not be disclosed to the client, so not only integrity must be maintained, but confidentiality too.

Here's what I was going to use for generating this token: - HMAC-SHA256 for integrity - AES in CBC mode for confidentiality

So, given payload P, keys K1 and K2 to generate the "token" I

  • pad P to a multiple of 16
  • generate IV of length 16
  • encrypt P using AES with K1 and IV
  • concatenate IV and encrypted P
  • get a digest of the result (IV + encrypted P) using HMAC-SHA256 with K2 as a key
  • concatenate digest with the result

Since IV and digest are of known length, no separators are needed. The padding will probably be done with spaces, since the format used for payload does not give significance to trailing spaces.

I must say, that I am not very much into security, so I might be missing some very obvious things. Anyway, the questions:

  • Doing this seems very much like "rolling my own crypto". Am I reinventing the wheel here?
  • Is there any added security in using separate K1 and K2, given that they will be stored "side-by-side"? If not, it will certainly be more convenient for me to use the same key
  • ... well, did I miss anything?

Well, you are reinventing the wheel, but at least you are using a circular shape for that, which is better than what most wheel-inventors out there do. And, frankly, there is a bit of a shortage of immediately usable high-quality wheels.

You use CBC with an IV (which must be generated with a strong PRNG), and that's good. You apply HMAC on the encrypted data, and that's good. You do not forget to include the IV in the input to HMAC, which would have been the classic mistake of encrypt-then-MAC. And you are using distinct keys for the encryption and the MAC, which is nice. So, really, not bad at all.

You may want to consider the following points:

  • In your format, there is no indication of what algorithms are used, so there is no algorithm agility: if tomorrow you want to switch to another encryption algorithm, you cannot do it without breaking compatibility with existing payloads (unless you play with the total length). For clean algorithm agility, you may want to add a header: a single byte with a conventional value, e.g. always 0x01 (subsequent versions of the payload format would use other values). Important note: if you do that, then do not forget to add the header byte in the HMAC input as well.

  • If having two keys K1 and K2 is inconvenient for you, you may want to store a single key K, and derive keys K1 and K2 from K. As long as the derivation process is one-way and uniform, this will be fine. We want the encryption key and the MAC key to be distinct so as to avoid nasty interactions between the two algorithms; no such interactions are known in the case of AES-CBC and HMAC/SHA-256, but "better safe than sorry". In your case, just hash K with SHA-256, use the first half (128 bits) for K1, the second half (128 bits) for K2.

  • There are newer authenticated encryption modes which combine encryption and integrity check, and do all the hard work for you. In particular, GCM and EAX are consider secure, and are patent-free. They offer some bonuses: no need for a random IV (a non-repeating IV such as a counter is sufficient), no need for padding (they rely on CTR mode internally), only one key (any "expansion" is handled internally).

  • If a user can obtain several payloads, then he can switch them and replay old payloads. Depending on your context, this may or may not be a problem for you.

  • If keys are not user-specific, then colluding users could swap payloads. Depending on your context, this may or may not be a problem for you.

  • Neat idea about the keys! Re authenticated encryption: yes, I've read about those modes, but I am using a library for the actual crypto, that does not support authenticated encryption modes. Payload replaying is not a problem in my context. If the user gets a hold of another user's payload, he gets to act on his behalf, that is correct and the workflow I've described in my question is not meant to be responsible for protection against these kind of things. Thanks a lot for the detailed answer! – shylent Feb 20 '13 at 13:40
  • As a side note, as I understand, the "version" header only signifies the version of the payload, that is whatever is included in the HMAC input, but not the details of the actual signing (function used and so on). – shylent Feb 20 '13 at 15:00
  • By "version header" I mean a byte which, when seen by the code which does the decryption, makes it think "that header byte has value 0x01, so this blob is encrypted with AES-CBC and protected with HMAC/SHA-256". This leaves you room for defining later on blobs with header byte 0x02, which are encrypted with (say) RC6 and protected with HMAC/SHA-512, or anything else. With header bytes, the same code will be able to decrypt both kinds of payloads. (But it is then important to protect the header byte with the HMAC, because otherwise the attacker could change it). – Thomas Pornin Feb 20 '13 at 15:10
  • To chime in here, it is typical practice to use separate keys for different cryptographic primitives. That means that you're doing what is expected and believed correct by using distinct keys for your confidentiality and integrity. Algorithm agility may well be crucial, as it may be the case that AES-CBC or HMAC-SHA-256 becomes broken in the future, then you have a much cleaner migration strategy. You can even being to migrate as algorithms show weakness. – Tinned_Tuna Feb 20 '13 at 15:15
  • @Thomas Pornin Hold on, how can the header byte tell me, that the message is protected with whatever HMAC, if it itself is protected by that same HMAC? To know which HMAC must be used I should look at the header, but before looking at the header I must check its integrity, which is protected by HMAC. Which I will know only by looking at the header. Seems like a chicken-and-egg thing to me. – shylent Feb 20 '13 at 15:21

The padding will probably be done with spaces, since the format used for payload does not give significance to trailing spaces.

It's likely that you'll be better off using a standard padding scheme then inventing your own. For example, padding to of n bytes by using a n repeating bytes of n, or adding a dummy pad block if it's already of a suitable length.

  • Guess, I should after all. To be honest, the dummy block part turned me off a bit, but then again it is a great scheme, because it involves no special cases. – shylent Feb 20 '13 at 15:57
  • 1
    To avoid padding altogether, use a padding-less mode like CTR instead of CBC. – Thomas Pornin Feb 20 '13 at 19:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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