5

In an implementation of AEAD_AES_128_GCM, if the AAD is defined as a "zero-length octet sequence", is it necessary to call the encryption code that adds an AAD at all? In particular, does an OpenSSL/LibreSSL libcrypto client need the EVP_En/DecryptUpdate invocation that adds the AAD, if its length is 0?

I'm working on an implementation of RFC 8188, a protocol for encrypting HTTP messages with AEAD_AES_128_GCM, which has this to say:

   The additional data passed to each invocation of AEAD_AES_128_GCM is
   a zero-length octet sequence.

Currently my test code (just in a mockup stage) has this:

static const unsigned char *aad = (const void *)&aad;
static const int aad_len = 0;

#if 0 /* XXX not sure if this is necessary, examples work with & without */ if (EVP_DecryptUpdate(&ctx, NULL, &len, aad, aad_len) != 1) { ERR("decryption: adding aad"); return -1; } #endif

The resulting code produces the expected decryptions and intermediate values for the examples in chapter 3 of the RFC. As you can see, this part is #if 0-d out, so in fact nothing is done to add an AAD.

Since this may seem like a remarkably silly question (already I'm pre-emptively embarrassed), let me explain why I'm asking:

  • The RFC has all of two examples, both of which decrypt ciphertext to "I am the walrus" (with two different record sizes). So the "it works" result may be a lucky accident of sparse test data.

  • Since the RFC goes to the trouble of saying "zero-length octet sequence", rather than something like "there is no AAD", it feels like there might be a reason for putting it that way.

  • An assumption on my part, a crypto layman, that "it's zero-length so I don't have to do anything" feels like the sort of thing that leads to errors and insecurity in code that uses cryptography. Operations on zero-length structures don't always mean the same thing as "do nothing" (consider SHA hashes of the empty string).

So I thought I'd ask others if there might be more going on here than meets the eye.

slimhazard
  • 53
  • 3

1 Answers1

4

AES-GCM does not support ‘no AAD’ as distinct from ‘zero-length AAD’. See NIST SP800-38D for details of how it works internally: the AAD is padded with zeros so it is a multiple of 128 bits long, and the length is hashed in at the end. If the AAD is zero-length, then zero bits of padding are added and no time is spent hashing any AAD or AAD padding.

  1. Let $u = 128\cdot\lceil\operatorname{len}(C)/128\rceil - \operatorname{len}(C)$ and let $v = 128\cdot\lceil\operatorname{len}(A)/128\rceil - \operatorname{len}(A)$.
  2. Define a block, $S$, as follows: $$S = \operatorname{GHASH}_H(A \mathbin\| \mathtt 0^v \mathbin\| C \mathbin\| \mathtt 0^u \mathbin\| [\operatorname{len}(A)]_{64} \mathbin\| [\operatorname{len}(C)]_{64}).$$

In Steps 4 and 5, the AAD and the ciphertext are each appended with the minimum number of ‘$\mathtt 0$’ bits, possibly none, so that the bit lengths of the resulting strings are multiples of the block size.

—NIST SP800-90A, §7.1 ‘Algorithm for the Authenticated Encryption Function’, p. 15 (emphasis added)

Note that if the AAD $A$ is empty, then $\operatorname{len}(A) = \lceil0/128\rceil = 0$, so $\lceil\operatorname{len}(A)/128\rceil = 0$, and thus the number of ‘$\mathtt 0$’ bits appended is $$v = 128\cdot\lceil\operatorname{len}(A)/128\rceil - \operatorname{len}(A) = 128\cdot0 - 0 = 0.$$ In other words, if $A$ is empty, then the prefix $A \mathbin\| \mathtt 0^v$ is also empty.

But it is a good intuition to wonder whether ‘no AAD’ and ‘zero-length AAD’ are distinct! There are many applications that do make such distinctions, often to the advantage of adversaries or at least to the frustration of legitimate users.

Squeamish Ossifrage
  • 48,392
  • 3
  • 116
  • 223
  • Hmm, OK to be honest I wouldn't have read the NIST document that way (I would have thought it's saying that it really does fill out all of the 0 bits, append length 0 as well as the same construct with the ciphertext, and GHASH the result). But like I said, I'm not the type who routinely reads NIST specs.I'm coming around to removing the #if 0, if nothing else to make it explicit in the code what's happening and that it's by choice. – slimhazard Sep 02 '19 at 17:41
  • Thanks for the clarification. I had thought "possibly none" refers to the case when AAD/ciphertext fit right into the block size, so no padding is necessary. (But that's not what my question about anyway.) – slimhazard Sep 03 '19 at 15:46