5

I have a short message that’s exactly 128 bits of length. The message serves as a time-limited authorization token for a database. It contains a permission profile ID (64 bits) and an issue timestamp (64 bit double). The key is known only to the issuing server, but the content may actually be given to the user in plaintext (though having it hidden is preferred during transport, as an additional security measure), and in general not impossible to guess.

I intend to encrypt it using AES-128 (there is no need for IV/nonce as each timestamp is assured to be unique - by having a sub-millisecond resolution). I’m looking for a fast way to authenticate it, considering a single server would be expected to decrypt and verify up to tens of thousands of these tokens per second, with a marginal performance impact. I considered using an HMAC but looked for an alternative that’s faster and preferably relies solely on AES (AES-NI Instruction could significantly accelerate computations).

I came up with the simple idea of encrypting the plaintext twice with two different keys (either completely unrelated or derived from a master key using a salted hash or a KDF) and concatenating it to a 256 bit result, which looks like this:

E_K1(Plaintext) || E_K2(Plaintext)

As far as I can tell, this seems secure, and verification would be faster in practice than when using an HMAC - simply decrypt both and verify that both plaintexts match. Additionally, since maintaining two unrelated (or securely derived) keys would add some complexity, but would only introduce a single bit of strength, I’m also considering simply using MasterKey and [the first 128 bits of] SHA1(MasterKey) as the two keys.

Is this as secure as I think it is? Could this even be (marginally) more secure than using a HMAC? - since for every random guess of the first block there would exactly one corresponding second block that would decrypt to the same plaintext (i.e. no possibility of a collision since block ciphers have a one-to-one mapping between plaintexts and ciphertexts for a single block).


Note the above method works only with block ciphers and is equivalent to CBC-MAC with the special case of having only one block, so the resulting tag can be decrypted directly instead of decrypting the ciphertext and the re-encrypting the resulting plaintext with the authentication key.


Considering this further: it may actually be safe to authenticate the ciphertext itself so the second block could be an encryption of the first one. e.g.:

E_K1(Plaintext)||E_K2(Ciphertext)

(not sure if a second key is needed in this case [though it seems to be a good safety measure], but some caution needs to be exercised since with some ciphers, it may be the case that E(E(Plaintext)) = Plaintext)

Validation would be twice as fast, since only one encryption/decryption operation is needed to determine if the first block is authentic.


Although not based solely on AES (the original requirement), UMAC/VMAC, although relatively new techniques, may prove competitive or better in performance, but I'm relatively new to them so some studying is still needed. Using a GMAC (the authentication code used with GCM mode) is also an option, but I'm not sure it'll give better performance than UMAC/VMAC.


Conclusions (based on my own research and suggestions of the commenters):

  • MAC functions based on universal hashing (UMAC, VMAC, Poly1305) are very fast but require a nonce for every individual MAC, and GMAC requires an IV, thus these are probably not appropriate in this particular case, where having a short total length of transmitted payload is essential (it is not clear to me whether the nonce/IV can somehow be safely derived in this case, but that operation by itself would probably be quite expensive)

  • HMAC-SHA is currently a bit slow (as it performs two hash operations per call) but may be accelerated by sophisticated caching of its internal state - though this optimization may be difficult or time-consuming to implement in practice. A future hardware accelerated instruction set for SHA-1/SHA-256 is planned for release with the Skylake architecture, and may change the situation.

  • CBC-MAC (a more generalized algorithm than described by the original proposal) based on AES would be hardware accelerated and perform great, especially when it's applied to the ciphertext, so it seems like the best solution so far.

Anon2000
  • 341
  • 1
  • 9
  • SHA1(MasterKey) has a different length from AES keys. $;$ –  May 15 '15 at 10:20
  • Of course, I meant, to truncate it to the first 128 bits (at least that what was implied, I'll fix it). – Anon2000 May 15 '15 at 10:26
  • If you have any accelerated hashing functions, I'd check to see if using one of those with the HMAC construction is faster than using AES as a MAC. Note that you can pre-compute both the inner and outer padded hashes in the HMAC and copy those (e.g. the hash context) rather than re-process the HMAC key each time (I'm assuming you can restore a state even with hardware accelerated hashing). – Steve Peltz May 15 '15 at 18:47
  • If you are validating tens of thousands of these per second, are you certain your assumption holds that sub-millisecond resolution is good enough to guarantee uniqueness? – Stephen Touset May 15 '15 at 19:48
  • If you can spare a single bit out of the plaintext (say, the leftmost of the 128 bits is always 0 in a valid plaintext), you can have authenticated encryption with a single key: make the cryptogram $E_K(plaintext)|E_K(plaintext+2^{127})$ and make the obvious checks of the 129 constrained bits on the receiver side. If size is an issue, the second block can be truncated. – fgrieu May 15 '15 at 20:17
  • @StevePeltz: I'm aware of the upcoming SHA-1 hardware support such as the Intel SHA extensions which should arrive with Skylake (so it isn't currently possible to test in practice). I guess it may change the picture when it's available. Your second suggestion of restoring the internal context is very interesting. It seems to go deep into the inner working of the hash function itself. It might be possible even with OpenSSL (though I'm not sure) but seems to be some amount of research and work to make possible. I'll definitely check that out! – Anon2000 May 15 '15 at 21:19
  • 2
    It sounds to me that CBC-MAC is a perfect fit for you. No need for special constructs. CMAC is safe for variable length messages but it is more complex; it should not be needed for these messages. – Maarten Bodewes May 16 '15 at 00:54
  • @StephenTouset The timestamps are Unix millisecond timestamps stored in an 64 bit IEEE754 FP container. An exact count gave 4,096 possible values per millisecond (since the values are in the trillions the fractional resolution isn't very big). The tokens would be mostly given for several minutes of use, so they would be very unlikely to collide (even if granted concurrently by different servers). Even if two tokens do collide, that would not be a security risk but simply imply the two users would share the same usage limits (request count/bandwidth) for a couple of minutes. – Anon2000 May 16 '15 at 05:01
  • @fgrieu: Interesting suggestion, thanks, though with my limited understanding of cryptography I feel this seems to put a higher expectation on the block-cipher to give out high quality pseudorandom permutations (for a single key, and changing a single bit). Having different, but mutually-derived, keys isn't really a big "hassle" in practice and gives some measure of safety, it seems, even for use with possibly weaker ciphers. – Anon2000 May 16 '15 at 05:15
  • @MaartenBodewes: I'm looking into CBC-MAC (and plan to investigate UMAC and VMAC as well) but haven't really got through all the subtleties of it yet. If I understand correctly it should be safe for a fixed length message. It seems like what I suggested may actually be equivalent in practice to the special case of CBC-MAC when there is only one block? and since there is only one it is simply possible to decrypt the tag directly, instead of decrypting the ciphertext and re-encrypting with the key used for authentication? (this also allows parallel execution, which may add some performance). – Anon2000 May 16 '15 at 06:52
  • @user1636512: your feeling is right (the adversary gets twice as much plaintext with the same key); yet modern ciphers like AES are perfectly fit for this. – fgrieu May 16 '15 at 08:32
  • OpenSSL hash contexts store the state, copying the structure saves/restores the state. With pre-keying, the only overhead of HMAC is copying that structure twice, processing the output length of the hash once, plus one additional finalize operation (which for many hashes is simply copying out the internal state). – Steve Peltz May 16 '15 at 19:15
  • @StevePeltz Thanks for the information, though currently I'm using the OpenSSL functionality available through Node.js, which as far as I've investigated, doesn't allow access to hash contexts. One thing that comes to mind though, is whether there's a need for the HMAC construction at all. I mean - since the message is of fixed, known length, it may not be susceptible to a length-extension attack. Could H(key||message) actually be safe in this case? (Wikipedia seems to imply this may be the case) – Anon2000 May 16 '15 at 19:57
  • Also, How/When do I use HMAC? has some good discussion and further links (see in particular Why is H(k||x) not a secure MAC construction?) – Steve Peltz May 16 '15 at 21:52
  • @StevePeltz: The thing is, the only weakness (though very severe) of H(key||message) that is described is that when message is of unknown length, it is possible to create a valid HMAC for a message of a longer length, by padding up to the hash block size and appending additional data (though SHA-3 is immune to this). In this particular case, the message and key are always exactly 128 bit of length, which means that key||message would occupy half of a block of SHA256 (which is 512 bits). This also makes it incredibly hard to believe it would be insecure in some way. – Anon2000 May 17 '15 at 07:29
  • I posted a separate question for this particular issue. – Anon2000 May 17 '15 at 09:33

1 Answers1

3

Your scheme is secure under the assumption that AES is a pseudo-random permutation (PRP): In fact, message authentication codes (MACs) are theoretically modelled as pseudo-random functions (PRFs), and any PRP is also a PRF. Therefore, your usage of single-block AES essentially is a MAC.

In the case that only a very small subset of the possible blocks is "valid" (for instance, when you check that a timestamp is no older than a few minutes or so), you may even drop the second encrypted block: If the probability that an attacker guesses the ciphertext corresponding to a valid block is already negligible, an additional MAC is not needed. Note that this depends strongly on your specific use case and is not a general security recommendation.

yyyyyyy
  • 12,081
  • 4
  • 47
  • 68
  • I considered using only a single block, but eventually decided against it. Most of the entropy would actually derive from the permission ID (about 44 bits for a million possible valid values). Timestamps, however, could be set to the future, or the allowed range could be very long. I had an exact calculation (that takes FP accuracy in consideration), based on a 100 year time range yielded about 7.158 bits of entropy. – Anon2000 May 15 '15 at 10:50
  • I'm also realizing this may only work with a block cipher. As in a stream cipher it would be possible to make very slight modifications to one of the blocks (for example, tamper with an existing token to modify its content in some way). And relatively easy to guess matches on the second blocks. As for multi-block approach like CBC I haven't really considered it (I'm not by any means a cryptographer!) – Anon2000 May 15 '15 at 10:58
  • Yes, this would absolutely not work with a stream cipher, as flipping the same bit in both halves would result in the same plaintext output. – Stephen Touset May 15 '15 at 19:48
  • Any ideas about using this with CBC? could this work for [two copies] of several chained blocks? I haven't really spent much time considering that.. (I'm not sure though whether in this case it would actually have as many practical benefits though..). – Anon2000 May 15 '15 at 20:53
  • You could use this with a streaming cipher as the first encryption only. If you're going to encrypt multiple blocks, the case for using a MAC instead of encrypting the blocks a second time is much stronger from a speed standpoint. – Steve Peltz May 15 '15 at 23:11
  • @StevePeltz ...and from a security standpoint: Encrypting the blocks by themselves corresponds to authenticating each block individually (not the whole message at once), hence an attacker may reorder them as he pleases. Unless there is only one block, this breaks unforgeability. – yyyyyyy May 16 '15 at 08:07
  • The context of my last comment was unclear, I think. I wasn't suggesting encrypting each block in ECB mode, I was responding to the question about using CBC mode if the message is more than one block. If both encryptions use CBC doesn't that prevent block re-ordering or length extension attacks? The last block of each encrypted message is a CBC-MAC of the same plaintext. – Steve Peltz May 16 '15 at 19:02
  • @StevePeltz I'm sorry, that was indeed unclear to me. Concerning "The last block of each encrypted message is a CBC-MAC of the same plaintext.": This is true only if the initialization vector is fixed! Otherwise, an attacker may forge valid tags for messages with a modified first block by XORing differentials into the IV — for this reason, CBC-MAC is defined to always use a zero IV. The same problem applies to the construction in question; so one either has to use a fixed IV (breaking CPA resistance) or somehow protect the IVs from tampering (but we already need a MAC for this). – yyyyyyy May 17 '15 at 16:35
  • He was already using an IV of zero with one-block messages, given the same assumption (all messages unique) isn't adding a non-zero IV unnecessary? I perhaps shouldn't have assumed that's what he meant. Or, even if using an IV, couldn't you use the same IV for both (since it's with a different key)? It's still unpredictable at the time the message is generated, and the two encryptions still have same property of a CBC-MAC on each other. – Steve Peltz May 17 '15 at 18:41