cipher: Buffer data from gcry_cipher_authenticate in OCB mode.
authorWerner Koch <wk@gnupg.org>
Tue, 12 Apr 2016 09:11:35 +0000 (11:11 +0200)
committerWerner Koch <wk@gnupg.org>
Tue, 12 Apr 2016 09:12:16 +0000 (11:12 +0200)
* cipher/cipher-internal.h (gcry_cipher_handle): Add fields
aad_leftover and aad_nleftover to u_mode.ocb.
* cipher/cipher-ocb.c (_gcry_cipher_ocb_set_nonce): Clear
aad_nleftover.
(_gcry_cipher_ocb_authenticate): Add buffering and facor some code out
to ...
(ocb_aad_finalize): new.
(compute_tag_if_needed): Call new function.
* tests/basic.c (check_ocb_cipher_splitaad): New.
(check_ocb_cipher): Call new function.
(main): Also call check_cipher_modes with --ciper-modes.
--

It is more convenient to not require full blocks for
gcry_cipher_authenticate.  Other modes than OCB do this as well.

Note that the size of the context structure is not increased because
other modes require more context data.

Signed-off-by: Werner Koch <wk@gnupg.org>
cipher/cipher-internal.h
cipher/cipher-ocb.c
doc/gcrypt.texi
src/gcrypt.h.in
tests/basic.c

index 80e7c09..9fd1d91 100644 (file)
@@ -274,10 +274,16 @@ struct gcry_cipher_handle
          checksum of the data.  */
       unsigned char aad_sum[OCB_BLOCK_LEN];
 
+      /* A buffer to store AAD data not yet processed.  */
+      unsigned char aad_leftover[OCB_BLOCK_LEN];
+
       /* Number of data/aad blocks processed so far.  */
       u64 data_nblocks;
       u64 aad_nblocks;
 
+      /* Number of valid bytes in AAD_LEFTOVER.  */
+      unsigned char aad_nleftover;
+
       /* Length of the tag.  Fixed for now but may eventually be
          specified using a set of gcry_cipher_flags.  */
       unsigned char taglen;
index 6db1db3..92260d2 100644 (file)
@@ -1,5 +1,5 @@
 /* cipher-ocb.c -  OCB cipher mode
- * Copyright (C) 2015 g10 Code GmbH
+ * Copyright (C) 2015, 2016 g10 Code GmbH
  *
  * This file is part of Libgcrypt.
  *
@@ -211,6 +211,7 @@ _gcry_cipher_ocb_set_nonce (gcry_cipher_hd_t c, const unsigned char *nonce,
   c->marks.finalize = 0;
   c->u_mode.ocb.data_nblocks = 0;
   c->u_mode.ocb.aad_nblocks = 0;
+  c->u_mode.ocb.aad_nleftover = 0;
   c->u_mode.ocb.data_finalized = 0;
   c->u_mode.ocb.aad_finalized = 0;
 
@@ -235,10 +236,7 @@ _gcry_cipher_ocb_set_nonce (gcry_cipher_hd_t c, const unsigned char *nonce,
 
 /* Process additional authentication data.  This implementation allows
    to add additional authentication data at any time before the final
-   gcry_cipher_gettag.  The size of the data provided in
-   (ABUF,ABUFLEN) must be a multiple of the blocksize.  If a
-   non-multiple of the blocksize is used no further data may be passed
-   to this function.  */
+   gcry_cipher_gettag.  */
 gcry_err_code_t
 _gcry_cipher_ocb_authenticate (gcry_cipher_hd_t c, const unsigned char *abuf,
                                size_t abuflen)
@@ -254,6 +252,32 @@ _gcry_cipher_ocb_authenticate (gcry_cipher_hd_t c, const unsigned char *abuf,
   /* Check correct usage and arguments.  */
   if (c->spec->blocksize != OCB_BLOCK_LEN)
     return GPG_ERR_CIPHER_ALGO;
+
+  /* Process remaining data from the last call first.  */
+  if (c->u_mode.ocb.aad_nleftover)
+    {
+      for (; abuflen && c->u_mode.ocb.aad_nleftover < OCB_BLOCK_LEN;
+           abuf++, abuflen--)
+        c->u_mode.ocb.aad_leftover[c->u_mode.ocb.aad_nleftover++] = *abuf;
+
+      if (c->u_mode.ocb.aad_nleftover == OCB_BLOCK_LEN)
+        {
+          c->u_mode.ocb.aad_nblocks++;
+
+          /* Offset_i = Offset_{i-1} xor L_{ntz(i)} */
+          buf_xor_1 (c->u_mode.ocb.aad_offset,
+                     ocb_get_l (c, l_tmp, c->u_mode.ocb.aad_nblocks),
+                     OCB_BLOCK_LEN);
+          /* Sum_i = Sum_{i-1} xor ENCIPHER(K, A_i xor Offset_i)  */
+          buf_xor (l_tmp, c->u_mode.ocb.aad_offset,
+                   c->u_mode.ocb.aad_leftover, OCB_BLOCK_LEN);
+          c->spec->encrypt (&c->context.c, l_tmp, l_tmp);
+          buf_xor_1 (c->u_mode.ocb.aad_sum, l_tmp, OCB_BLOCK_LEN);
+
+          c->u_mode.ocb.aad_nleftover = 0;
+        }
+    }
+
   if (!abuflen)
     return 0;
 
@@ -291,31 +315,56 @@ _gcry_cipher_ocb_authenticate (gcry_cipher_hd_t c, const unsigned char *abuf,
       abuflen -= OCB_BLOCK_LEN;
     }
 
-  /* Hash final partial block.  Note that we expect ABUFLEN to be
-     shorter than OCB_BLOCK_LEN.  */
-  if (abuflen)
+  /* Store away the remaining data.  */
+  for (; abuflen && c->u_mode.ocb.aad_nleftover < OCB_BLOCK_LEN;
+       abuf++, abuflen--)
+    c->u_mode.ocb.aad_leftover[c->u_mode.ocb.aad_nleftover++] = *abuf;
+  gcry_assert (!abuflen);
+
+  return 0;
+}
+
+
+/* Hash final partial AAD block.  */
+static void
+ocb_aad_finalize (gcry_cipher_hd_t c)
+{
+  unsigned char l_tmp[OCB_BLOCK_LEN];
+
+  /* Check that a nonce and thus a key has been set and that we have
+     not yet computed the tag.  We also skip this if the aad has been
+     finalized.  */
+  if (!c->marks.iv || c->marks.tag || c->u_mode.ocb.aad_finalized)
+    return;
+  if (c->spec->blocksize != OCB_BLOCK_LEN)
+    return;  /* Ooops.  */
+
+  /* Hash final partial block if any.  */
+  if (c->u_mode.ocb.aad_nleftover)
     {
       /* Offset_* = Offset_m xor L_*  */
       buf_xor_1 (c->u_mode.ocb.aad_offset,
                  c->u_mode.ocb.L_star, OCB_BLOCK_LEN);
       /* CipherInput = (A_* || 1 || zeros(127-bitlen(A_*))) xor Offset_*  */
-      buf_cpy (l_tmp, abuf, abuflen);
-      memset (l_tmp + abuflen, 0, OCB_BLOCK_LEN - abuflen);
-      l_tmp[abuflen] = 0x80;
+      buf_cpy (l_tmp, c->u_mode.ocb.aad_leftover, c->u_mode.ocb.aad_nleftover);
+      memset (l_tmp + c->u_mode.ocb.aad_nleftover, 0,
+              OCB_BLOCK_LEN - c->u_mode.ocb.aad_nleftover);
+      l_tmp[c->u_mode.ocb.aad_nleftover] = 0x80;
       buf_xor_1 (l_tmp, c->u_mode.ocb.aad_offset, OCB_BLOCK_LEN);
       /* Sum = Sum_m xor ENCIPHER(K, CipherInput)  */
       c->spec->encrypt (&c->context.c, l_tmp, l_tmp);
       buf_xor_1 (c->u_mode.ocb.aad_sum, l_tmp, OCB_BLOCK_LEN);
 
-      /* Mark AAD as finalized to avoid accidentally calling this
-         function again after a non-full block has been processed.  */
-      c->u_mode.ocb.aad_finalized = 1;
+      c->u_mode.ocb.aad_nleftover = 0;
     }
 
-  return 0;
+  /* Mark AAD as finalized so that gcry_cipher_ocb_authenticate can
+   * return an erro when called again.  */
+  c->u_mode.ocb.aad_finalized = 1;
 }
 
 
+
 /* Checksumming for encrypt and decrypt.  */
 static void
 ocb_checksum (unsigned char *chksum, const unsigned char *plainbuf,
@@ -507,6 +556,7 @@ compute_tag_if_needed (gcry_cipher_hd_t c)
 {
   if (!c->marks.tag)
     {
+      ocb_aad_finalize (c);
       buf_xor_1 (c->u_mode.ocb.tag, c->u_mode.ocb.aad_sum, OCB_BLOCK_LEN);
       c->marks.tag = 1;
     }
index c710765..a78c5fd 100644 (file)
@@ -1883,7 +1883,7 @@ the end of the input data. This is done with:
 Set a flag in the context to tell the encrypt and decrypt functions
 that their next call will provide the last chunk of data.  Only the
 first call to this function has an effect and only for modes which
-support it.  Checking the error in in general not necessary.  This is
+support it.  Checking the error is in general not necessary.  This is
 implemented as a macro.
 @end deftypefun
 
index c269621..bd25d1b 100644 (file)
@@ -1065,7 +1065,7 @@ gcry_error_t gcry_cipher_checktag (gcry_cipher_hd_t hd, const void *intag,
                                                      (oid), 0);
 
 /* Indicate to the encrypt and decrypt functions that the next call
-   provides the final data.  Only used with some modes. e */
+   provides the final data.  Only used with some modes.  */
 #define gcry_cipher_final(a) \
             gcry_cipher_ctl ((a), GCRYCTL_FINALIZE, NULL, 0)
 
index 1a7d3cb..4940f6a 100644 (file)
@@ -3596,6 +3596,236 @@ check_ocb_cipher_largebuf (int algo, int keylen, const char *tagexpect)
 
 
 static void
+check_ocb_cipher_splitaad (void)
+{
+  const char t_nonce[] = ("BBAA9988776655443322110D");
+  const char t_plain[] = ("000102030405060708090A0B0C0D0E0F1011121314151617"
+                          "18191A1B1C1D1E1F2021222324252627");
+  const char t_ciph[]  = ("D5CA91748410C1751FF8A2F618255B68A0A12E093FF45460"
+                          "6E59F9C1D0DDC54B65E8628E568BAD7AED07BA06A4A69483"
+                          "A7035490C5769E60");
+  struct {
+    const char *aad0;
+    const char *aad1;
+    const char *aad2;
+    const char *aad3;
+  } tv[] = {
+    {
+      "000102030405060708090A0B0C0D0E0F"
+      "101112131415161718191A1B1C1D1E1F2021222324252627"
+    },
+    {
+      "000102030405060708090A0B0C0D0E0F",
+      "101112131415161718191A1B1C1D1E1F",
+      "2021222324252627"
+    },
+    {
+      "000102030405060708090A0B0C0D0E0F",
+      "1011121314151617",
+      "18191A1B1C1D1E1F",
+      "2021222324252627"
+    },
+    {
+      "000102030405060708090A0B0C0D0E0F",
+      "101112131415161718191A1B1C1D1E1F",
+      "20",
+      "21222324252627"
+    },
+    {
+      "000102030405060708090A0B0C0D0E0F",
+      "101112131415161718191A1B1C1D1E1F",
+      "2021",
+      "222324252627"
+    },
+    {
+      "000102030405060708090A0B0C0D0E0F",
+      "101112131415161718191A1B1C1D1E1F",
+      "202122",
+      "2324252627"
+    },
+    {
+      "000102030405060708090A0B0C0D0E0F",
+      "101112131415161718191A1B1C1D1E1F",
+      "20212223",
+      "24252627"
+    },
+    {
+      "000102030405060708090A0B0C0D0E0F",
+      "101112131415161718191A1B1C1D1E1F",
+      "2021222324",
+      "252627"
+    },
+    {
+      "000102030405060708090A0B0C0D0E0F",
+      "101112131415161718191A1B1C1D1E1F",
+      "202122232425",
+      "2627"
+    },
+    {
+      "000102030405060708090A0B0C0D0E0F",
+      "101112131415161718191A1B1C1D1E1F",
+      "20212223242526"
+      "27"
+    },
+    {
+      "000102030405060708090A0B0C0D0E0F",
+      "1011121314151617",
+      "18191A1B1C1D1E1F2021222324252627"
+    },
+    {
+      "00",
+      "0102030405060708090A0B0C0D0E0F",
+      "1011121314151617",
+      "18191A1B1C1D1E1F2021222324252627"
+    },
+    {
+      "0001",
+      "02030405060708090A0B0C0D0E0F",
+      "1011121314151617",
+      "18191A1B1C1D1E1F2021222324252627"
+    },
+    {
+      "000102030405060708090A0B0C0D",
+      "0E0F",
+      "1011121314151617",
+      "18191A1B1C1D1E1F2021222324252627"
+    },
+    {
+      "000102030405060708090A0B0C0D0E",
+      "0F",
+      "1011121314151617",
+      "18191A1B1C1D1E1F2021222324252627"
+    },
+    {
+      "000102030405060708090A0B0C0D0E",
+      "0F101112131415161718191A1B1C1D1E1F20212223242526",
+      "27"
+    }
+  };
+
+  gpg_error_t err = 0;
+  gcry_cipher_hd_t hde;
+  unsigned char out[MAX_DATA_LEN];
+  unsigned char tag[16];
+  int tidx;
+  char *key, *nonce, *ciph, *plain;
+  size_t keylen, noncelen, ciphlen, plainlen;
+  int i;
+
+  /* Convert to hex strings to binary.  */
+  key   = hex2buffer ("000102030405060708090A0B0C0D0E0F", &keylen);
+  nonce = hex2buffer (t_nonce, &noncelen);
+  plain = hex2buffer (t_plain, &plainlen);
+  ciph  = hex2buffer (t_ciph, &ciphlen);
+
+  /* Check that our test vectors are sane.  */
+  assert (plainlen <= sizeof out);
+  assert (16 <= ciphlen);
+  assert (16 <= sizeof tag);
+
+  for (tidx = 0; tidx < DIM (tv); tidx++)
+    {
+      char *aad[4];
+      size_t aadlen[4];
+
+      if (verbose)
+        fprintf (stderr, "    checking OCB aad split (tv %d)\n", tidx);
+
+      aad[0] = tv[tidx].aad0? hex2buffer (tv[tidx].aad0, aadlen+0) : NULL;
+      aad[1] = tv[tidx].aad1? hex2buffer (tv[tidx].aad1, aadlen+1) : NULL;
+      aad[2] = tv[tidx].aad2? hex2buffer (tv[tidx].aad2, aadlen+2) : NULL;
+      aad[3] = tv[tidx].aad3? hex2buffer (tv[tidx].aad3, aadlen+3) : NULL;
+
+      err = gcry_cipher_open (&hde, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_OCB, 0);
+      if (err)
+        {
+          fail ("cipher-ocb-splitadd, gcry_cipher_open failed: %s\n",
+                gpg_strerror (err));
+          return;
+        }
+
+      err = gcry_cipher_setkey (hde, key, keylen);
+      if (err)
+        {
+          fail ("cipher-ocb-splitaad, gcry_cipher_setkey failed: %s\n",
+                gpg_strerror (err));
+          gcry_cipher_close (hde);
+          return;
+        }
+
+      err = gcry_cipher_setiv (hde, nonce, noncelen);
+      if (err)
+        {
+          fail ("cipher-ocb-splitaad, gcry_cipher_setiv failed: %s\n",
+                gpg_strerror (err));
+          gcry_cipher_close (hde);
+          return;
+        }
+
+      for (i=0; i < DIM (aad); i++)
+        {
+          if (!aad[i])
+            continue;
+          err = gcry_cipher_authenticate (hde, aad[i], aadlen[i]);
+          if (err)
+            {
+              fail ("cipher-ocb-splitaad,"
+                    " gcry_cipher_authenticate failed (tv=%d,i=%d): %s\n",
+                    tidx, i, gpg_strerror (err));
+              gcry_cipher_close (hde);
+              return;
+            }
+        }
+
+      err = gcry_cipher_final (hde);
+      if (!err)
+        err = gcry_cipher_encrypt (hde, out, MAX_DATA_LEN, plain, plainlen);
+      if (err)
+        {
+          fail ("cipher-ocb-splitaad, gcry_cipher_encrypt failed: %s\n",
+                gpg_strerror (err));
+          gcry_cipher_close (hde);
+          return;
+        }
+
+      /* Check that the encrypt output matches the expected cipher
+         text without the tag (i.e. at the length of plaintext).  */
+      if (memcmp (ciph, out, plainlen))
+        {
+          mismatch (ciph, plainlen, out, plainlen);
+          fail ("cipher-ocb-splitaad, encrypt data mismatch\n");
+        }
+
+      /* Check that the tag matches TAGLEN bytes from the end of the
+         expected ciphertext.  */
+      err = gcry_cipher_gettag (hde, tag, 16);
+      if (err)
+        {
+          fail ("cipher-ocb-splitaad, gcry_cipher_gettag failed: %s\n",
+                gpg_strerror (err));
+        }
+      if (memcmp (ciph + ciphlen - 16, tag, 16))
+        {
+          mismatch (ciph + ciphlen - 16, 16, tag, 16);
+          fail ("cipher-ocb-splitaad, encrypt tag mismatch\n");
+        }
+
+
+      gcry_cipher_close (hde);
+      xfree (aad[0]);
+      xfree (aad[1]);
+      xfree (aad[2]);
+      xfree (aad[3]);
+    }
+
+  xfree (nonce);
+  xfree (ciph);
+  xfree (plain);
+  xfree (key);
+}
+
+
+static void
 check_ocb_cipher (void)
 {
   /* Check OCB cipher with separate destination and source buffers for
@@ -3636,6 +3866,9 @@ check_ocb_cipher (void)
   check_ocb_cipher_largebuf(GCRY_CIPHER_SERPENT256, 32,
                            "\xe7\x8b\xe6\xd4\x2f\x7a\x36\x4c"
                            "\xba\xee\x20\xe2\x68\xf4\xcb\xcc");
+
+  /* Check that the AAD data is correctly buffered.  */
+  check_ocb_cipher_splitaad ();
 }
 
 
@@ -9128,7 +9361,10 @@ main (int argc, char **argv)
       if (pubkey_only)
         check_pubkey ();
       else if (cipher_modes_only)
-        check_ciphers ();
+        {
+          check_ciphers ();
+          check_cipher_modes ();
+        }
       else if (!selftest_only)
         {
           check_ciphers ();