[PATCH 2/3] Add EAX mode

Jussi Kivilinna jussi.kivilinna at iki.fi
Sat Jan 20 21:04:50 CET 2018


* cipher/Makefile.am: Add 'cipher-eax.c'.
* cipher/cipher-cmac.c (cmac_write): Rename to ...
(_gcry_cmac_write): ... this; Take CMAC context as new input
parameter; Return error code.
(cmac_generate_subkeys): Rename to ...
(_gcry_cmac_generate_subkeys): ... this; Take CMAC context as new
input parameter; Return error code.
(cmac_final): Rename to ...
(_gcry_cmac_final): ... this; Take CMAC context as new input
parameter; Return error code.
(cmac_tag): Take CMAC context as new input parameter.
(_gcry_cmac_reset): New.
(_gcry_cipher_cmac_authenticate): Remove duplicate tag flag check;
Adapt to changes above.
(_gcry_cipher_cmac_get_tag): Adapt to changes above.
(_gcry_cipher_cmac_check_tag): Ditto.
(_gcry_cipher_cmac_set_subkeys): Ditto.
* cipher-eax.c: New.
* cipher-internal.h (gcry_cmac_context_t): New.
(gcry_cipher_handle): Update u_mode.cmac; Add u_mode.eax.
(_gcry_cmac_write, _gcry_cmac_generate_subkeys, _gcry_cmac_final)
(_gcry_cmac_reset, _gcry_cipher_eax_encrypt, _gcry_cipher_eax_decrypt)
(_gcry_cipher_eax_set_nonce, _gcry_cipher_eax_authenticate)
(_gcry_cipher_eax_get_tag, _gcry_cipher_eax_check_tag)
(_gcry_cipher_eax_setkey): New prototypes.
* cipher/cipher.c (_gcry_cipher_open_internal, cipher_setkey)
(cipher_reset, cipher_encrypt, cipher_decrypt, _gcry_cipher_setiv)
(_gcry_cipher_authenticate, _gcry_cipher_gettag, _gcry_cipher_checktag)
(_gcry_cipher_info): Add EAX mode.
* doc/gcrypt.texi: Add EAX mode.
* src/gcrypt.h.in (GCRY_CIPHER_MODE_EAX): New.
* tests/basic.c (_check_gcm_cipher, _check_poly1305_cipher): Constify
test vectors array.
(_check_eax_cipher, check_eax_cipher): New.
(check_ciphers, check_cipher_modes): Add EAX mode.
* tests/bench-slope.c (bench_eax_encrypt_do_bench)
(bench_eax_decrypt_do_bench, bench_eax_authenticate_do_bench)
(eax_encrypt_ops, eax_decrypt_ops, eax_authenticate_ops): New.
(cipher_modes): Add EAX mode.
* tests/benchmark.c (cipher_bench): Add EAX mode.
--

Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>
---
 0 files changed

diff --git a/cipher/Makefile.am b/cipher/Makefile.am
index bba815bbe..6e6c5ac03 100644
--- a/cipher/Makefile.am
+++ b/cipher/Makefile.am
@@ -44,7 +44,7 @@ cipher.c cipher-internal.h \
 cipher-cbc.c cipher-cfb.c cipher-ofb.c cipher-ctr.c cipher-aeswrap.c \
 cipher-ccm.c cipher-cmac.c cipher-gcm.c cipher-gcm-intel-pclmul.c \
   cipher-gcm-armv8-aarch32-ce.S cipher-gcm-armv8-aarch64-ce.S \
-cipher-poly1305.c cipher-ocb.c cipher-xts.c \
+cipher-poly1305.c cipher-ocb.c cipher-xts.c cipher-eax.c \
 cipher-selftest.c cipher-selftest.h \
 pubkey.c pubkey-internal.h pubkey-util.c \
 md.c \
diff --git a/cipher/cipher-cmac.c b/cipher/cipher-cmac.c
index da3ef7592..30567b7fc 100644
--- a/cipher/cipher-cmac.c
+++ b/cipher/cipher-cmac.c
@@ -1,5 +1,5 @@
 /* cmac.c - CMAC, Cipher-based MAC.
- * Copyright (C) 2013 Jussi Kivilinna <jussi.kivilinna at iki.fi>
+ * Copyright (C) 2013,2018 Jussi Kivilinna <jussi.kivilinna at iki.fi>
  *
  * This file is part of Libgcrypt.
  *
@@ -33,8 +33,9 @@
   (burn) = (burn) > __nburn ? (burn) : __nburn; } while (0)
 
 
-static void
-cmac_write (gcry_cipher_hd_t c, const byte * inbuf, size_t inlen)
+gcry_err_code_t
+_gcry_cmac_write (gcry_cipher_hd_t c, gcry_cmac_context_t *ctx,
+		  const byte * inbuf, size_t inlen)
 {
   gcry_cipher_encrypt_t enc_fn = c->spec->encrypt;
   const unsigned int blocksize = c->spec->blocksize;
@@ -42,31 +43,37 @@ cmac_write (gcry_cipher_hd_t c, const byte * inbuf, size_t inlen)
   unsigned int burn = 0;
   unsigned int nblocks;
 
+  if (ctx->tag)
+    return GPG_ERR_INV_STATE;
+
   /* Tell compiler that we require a cipher with a 64bit or 128 bit block
    * length, to allow better optimization of this function.  */
   if (blocksize > 16 || blocksize < 8 || blocksize & (8 - 1))
-    return;
+    return GPG_ERR_INV_CIPHER_MODE;
 
-  if (!inlen || !inbuf)
-    return;
+  if (!inbuf)
+    return GPG_ERR_INV_ARG;
+
+  if (inlen == 0)
+    return 0;
 
   /* Last block is needed for cmac_final.  */
-  if (c->unused + inlen <= blocksize)
+  if (ctx->mac_unused + inlen <= blocksize)
     {
-      for (; inlen && c->unused < blocksize; inlen--)
-        c->lastiv[c->unused++] = *inbuf++;
-      return;
+      for (; inlen && ctx->mac_unused < blocksize; inlen--)
+        ctx->macbuf[ctx->mac_unused++] = *inbuf++;
+      return 0;
     }
 
-  if (c->unused)
+  if (ctx->mac_unused)
     {
-      for (; inlen && c->unused < blocksize; inlen--)
-        c->lastiv[c->unused++] = *inbuf++;
+      for (; inlen && ctx->mac_unused < blocksize; inlen--)
+        ctx->macbuf[ctx->mac_unused++] = *inbuf++;
 
-      buf_xor (c->u_iv.iv, c->u_iv.iv, c->lastiv, blocksize);
-      set_burn (burn, enc_fn (&c->context.c, c->u_iv.iv, c->u_iv.iv));
+      buf_xor (ctx->u_iv.iv, ctx->u_iv.iv, ctx->macbuf, blocksize);
+      set_burn (burn, enc_fn (&c->context.c, ctx->u_iv.iv, ctx->u_iv.iv));
 
-      c->unused = 0;
+      ctx->mac_unused = 0;
     }
 
   if (c->bulk.cbc_enc && inlen > blocksize)
@@ -74,7 +81,7 @@ cmac_write (gcry_cipher_hd_t c, const byte * inbuf, size_t inlen)
       nblocks = inlen / blocksize;
       nblocks -= (nblocks * blocksize == inlen);
 
-      c->bulk.cbc_enc (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks, 1);
+      c->bulk.cbc_enc (&c->context.c, ctx->u_iv.iv, outbuf, inbuf, nblocks, 1);
       inbuf += nblocks * blocksize;
       inlen -= nblocks * blocksize;
 
@@ -83,8 +90,8 @@ cmac_write (gcry_cipher_hd_t c, const byte * inbuf, size_t inlen)
   else
     while (inlen > blocksize)
       {
-        buf_xor (c->u_iv.iv, c->u_iv.iv, inbuf, blocksize);
-        set_burn (burn, enc_fn (&c->context.c, c->u_iv.iv, c->u_iv.iv));
+        buf_xor (ctx->u_iv.iv, ctx->u_iv.iv, inbuf, blocksize);
+        set_burn (burn, enc_fn (&c->context.c, ctx->u_iv.iv, ctx->u_iv.iv));
         inlen -= blocksize;
         inbuf += blocksize;
       }
@@ -93,16 +100,18 @@ cmac_write (gcry_cipher_hd_t c, const byte * inbuf, size_t inlen)
   if (inlen == 0)
     BUG ();
 
-  for (; inlen && c->unused < blocksize; inlen--)
-    c->lastiv[c->unused++] = *inbuf++;
+  for (; inlen && ctx->mac_unused < blocksize; inlen--)
+    ctx->macbuf[ctx->mac_unused++] = *inbuf++;
 
   if (burn)
     _gcry_burn_stack (burn + 4 * sizeof (void *));
+
+  return 0;
 }
 
 
-static void
-cmac_generate_subkeys (gcry_cipher_hd_t c)
+gcry_err_code_t
+_gcry_cmac_generate_subkeys (gcry_cipher_hd_t c, gcry_cmac_context_t *ctx)
 {
   const unsigned int blocksize = c->spec->blocksize;
   byte rb, carry, t, bi;
@@ -117,7 +126,7 @@ cmac_generate_subkeys (gcry_cipher_hd_t c)
   /* Tell compiler that we require a cipher with a 64bit or 128 bit block
    * length, to allow better optimization of this function.  */
   if (blocksize > 16 || blocksize < 8 || blocksize & (8 - 1))
-    return;
+    return GPG_ERR_INV_CIPHER_MODE;
 
   if (MAX_BLOCKSIZE < blocksize)
     BUG ();
@@ -127,7 +136,7 @@ cmac_generate_subkeys (gcry_cipher_hd_t c)
   burn = c->spec->encrypt (&c->context.c, u.buf, u.buf);
 
   /* Currently supported blocksizes are 16 and 8. */
-  rb = blocksize == 16 ? 0x87 : 0x1B /*blocksize == 8 */ ;
+  rb = blocksize == 16 ? 0x87 : 0x1B /* blocksize == 8 */ ;
 
   for (j = 0; j < 2; j++)
     {
@@ -139,93 +148,113 @@ cmac_generate_subkeys (gcry_cipher_hd_t c)
           t = carry | (bi << 1);
           carry = bi >> 7;
           u.buf[i] = t & 0xff;
-          c->u_mode.cmac.subkeys[j][i] = u.buf[i];
+          ctx->subkeys[j][i] = u.buf[i];
         }
       u.buf[blocksize - 1] ^= carry ? rb : 0;
-      c->u_mode.cmac.subkeys[j][blocksize - 1] = u.buf[blocksize - 1];
+      ctx->subkeys[j][blocksize - 1] = u.buf[blocksize - 1];
     }
 
   wipememory (&u, sizeof (u));
   if (burn)
     _gcry_burn_stack (burn + 4 * sizeof (void *));
+
+  return 0;
 }
 
 
-static void
-cmac_final (gcry_cipher_hd_t c)
+gcry_err_code_t
+_gcry_cmac_final (gcry_cipher_hd_t c, gcry_cmac_context_t *ctx)
 {
   const unsigned int blocksize = c->spec->blocksize;
-  unsigned int count = c->unused;
+  unsigned int count = ctx->mac_unused;
   unsigned int burn;
   byte *subkey;
 
   /* Tell compiler that we require a cipher with a 64bit or 128 bit block
    * length, to allow better optimization of this function.  */
   if (blocksize > 16 || blocksize < 8 || blocksize & (8 - 1))
-    return;
+    return GPG_ERR_INV_CIPHER_MODE;
 
   if (count == blocksize)
-    subkey = c->u_mode.cmac.subkeys[0];        /* K1 */
+    subkey = ctx->subkeys[0];        /* K1 */
   else
     {
-      subkey = c->u_mode.cmac.subkeys[1];      /* K2 */
-      c->lastiv[count++] = 0x80;
+      subkey = ctx->subkeys[1];      /* K2 */
+      ctx->macbuf[count++] = 0x80;
       while (count < blocksize)
-        c->lastiv[count++] = 0;
+        ctx->macbuf[count++] = 0;
     }
 
-  buf_xor (c->lastiv, c->lastiv, subkey, blocksize);
+  buf_xor (ctx->macbuf, ctx->macbuf, subkey, blocksize);
 
-  buf_xor (c->u_iv.iv, c->u_iv.iv, c->lastiv, blocksize);
-  burn = c->spec->encrypt (&c->context.c, c->u_iv.iv, c->u_iv.iv);
+  buf_xor (ctx->u_iv.iv, ctx->u_iv.iv, ctx->macbuf, blocksize);
+  burn = c->spec->encrypt (&c->context.c, ctx->u_iv.iv, ctx->u_iv.iv);
   if (burn)
     _gcry_burn_stack (burn + 4 * sizeof (void *));
 
-  c->unused = 0;
+  ctx->mac_unused = 0;
+
+  return 0;
 }
 
 
 static gcry_err_code_t
-cmac_tag (gcry_cipher_hd_t c, unsigned char *tag, size_t taglen, int check)
+cmac_tag (gcry_cipher_hd_t c, gcry_cmac_context_t *ctx,
+	  unsigned char *tag, size_t taglen, int check)
 {
+  gcry_err_code_t ret;
+
   if (!tag || taglen == 0 || taglen > c->spec->blocksize)
     return GPG_ERR_INV_ARG;
 
-  if (!c->u_mode.cmac.tag)
+  if (!ctx->tag)
     {
-      cmac_final (c);
-      c->u_mode.cmac.tag = 1;
+      ret = _gcry_cmac_final (c, ctx);
+      if (ret != 0)
+	return ret;
+
+      ctx->tag = 1;
     }
 
   if (!check)
     {
-      memcpy (tag, c->u_iv.iv, taglen);
+      memcpy (tag, ctx->u_iv.iv, taglen);
       return GPG_ERR_NO_ERROR;
     }
   else
     {
-      return buf_eq_const (tag, c->u_iv.iv, taglen) ?
+      return buf_eq_const (tag, ctx->u_iv.iv, taglen) ?
         GPG_ERR_NO_ERROR : GPG_ERR_CHECKSUM;
     }
 }
 
 
+void
+_gcry_cmac_reset (gcry_cmac_context_t *ctx)
+{
+  char tmp_buf[sizeof(ctx->subkeys)];
+
+  /* Only keep subkeys when reseting context. */
+
+  buf_cpy (tmp_buf, ctx->subkeys, sizeof(ctx->subkeys));
+  memset (ctx, 0, sizeof(*ctx));
+  buf_cpy (ctx->subkeys, tmp_buf, sizeof(ctx->subkeys));
+  wipememory (tmp_buf, sizeof(tmp_buf));
+}
+
+
 gcry_err_code_t
 _gcry_cipher_cmac_authenticate (gcry_cipher_hd_t c,
                                 const unsigned char *abuf, size_t abuflen)
 {
   if (abuflen > 0 && !abuf)
     return GPG_ERR_INV_ARG;
-  if (c->u_mode.cmac.tag)
-    return GPG_ERR_INV_STATE;
   /* To support new blocksize, update cmac_generate_subkeys() then add new
      blocksize here. */
   if (c->spec->blocksize != 16 && c->spec->blocksize != 8)
     return GPG_ERR_INV_CIPHER_MODE;
 
-  cmac_write (c, abuf, abuflen);
-
-  return GPG_ERR_NO_ERROR;
+  return _gcry_cmac_write (c, &c->u_mode.cmac, abuf, abuflen);
 }
 
 
@@ -233,7 +262,7 @@ gcry_err_code_t
 _gcry_cipher_cmac_get_tag (gcry_cipher_hd_t c,
                            unsigned char *outtag, size_t taglen)
 {
-  return cmac_tag (c, outtag, taglen, 0);
+  return cmac_tag (c, &c->u_mode.cmac, outtag, taglen, 0);
 }
 
 
@@ -241,13 +270,11 @@ gcry_err_code_t
 _gcry_cipher_cmac_check_tag (gcry_cipher_hd_t c,
                              const unsigned char *intag, size_t taglen)
 {
-  return cmac_tag (c, (unsigned char *) intag, taglen, 1);
+  return cmac_tag (c, &c->u_mode.cmac, (unsigned char *) intag, taglen, 1);
 }
 
 gcry_err_code_t
 _gcry_cipher_cmac_set_subkeys (gcry_cipher_hd_t c)
 {
-  cmac_generate_subkeys (c);
-
-  return GPG_ERR_NO_ERROR;
+  return _gcry_cmac_generate_subkeys (c, &c->u_mode.cmac);
 }
diff --git a/cipher/cipher-eax.c b/cipher/cipher-eax.c
new file mode 100644
index 000000000..1ce479755
--- /dev/null
+++ b/cipher/cipher-eax.c
@@ -0,0 +1,248 @@
+/* cipher-eax.c  -  EAX implementation
+ * Copyright (C) 2018 Jussi Kivilinna <jussi.kivilinna at iki.fi>
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser general Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "g10lib.h"
+#include "cipher.h"
+#include "bufhelp.h"
+#include "./cipher-internal.h"
+
+
+gcry_err_code_t
+_gcry_cipher_eax_encrypt (gcry_cipher_hd_t c,
+                          byte *outbuf, size_t outbuflen,
+                          const byte *inbuf, size_t inbuflen)
+{
+  gcry_err_code_t err;
+
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+  if (c->marks.tag)
+    return GPG_ERR_INV_STATE;
+
+  if (!c->marks.iv)
+    {
+      err = _gcry_cipher_eax_set_nonce (c, NULL, 0);
+      if (err != 0)
+	return err;
+    }
+
+  err = _gcry_cipher_ctr_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+  if (err != 0)
+    return err;
+
+  return _gcry_cmac_write (c, &c->u_mode.eax.cmac_ciphertext, outbuf, inbuflen);
+}
+
+
+gcry_err_code_t
+_gcry_cipher_eax_decrypt (gcry_cipher_hd_t c,
+                          byte *outbuf, size_t outbuflen,
+                          const byte *inbuf, size_t inbuflen)
+{
+  gcry_err_code_t err;
+
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+  if (c->marks.tag)
+    return GPG_ERR_INV_STATE;
+
+  if (!c->marks.iv)
+    {
+      err = _gcry_cipher_eax_set_nonce (c, NULL, 0);
+      if (err != 0)
+	return err;
+    }
+
+  err = _gcry_cmac_write (c, &c->u_mode.eax.cmac_ciphertext, inbuf, inbuflen);
+  if (err != 0)
+    return err;
+
+  return _gcry_cipher_ctr_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+}
+
+
+gcry_err_code_t
+_gcry_cipher_eax_authenticate (gcry_cipher_hd_t c,
+                               const byte * aadbuf, size_t aadbuflen)
+{
+  gcry_err_code_t err;
+
+  if (c->marks.tag)
+    return GPG_ERR_INV_STATE;
+
+  if (!c->marks.iv)
+    {
+      err = _gcry_cipher_eax_set_nonce (c, NULL, 0);
+      if (err != 0)
+	return err;
+    }
+
+  return _gcry_cmac_write (c, &c->u_mode.eax.cmac_header, aadbuf, aadbuflen);
+}
+
+
+gcry_err_code_t
+_gcry_cipher_eax_setkey (gcry_cipher_hd_t c)
+{
+  gcry_err_code_t err;
+
+  err = _gcry_cmac_generate_subkeys (c, &c->u_mode.eax.cmac_header);
+  if (err != 0)
+    return err;
+
+  buf_cpy (c->u_mode.eax.cmac_ciphertext.subkeys,
+	   c->u_mode.eax.cmac_header.subkeys,
+	   sizeof(c->u_mode.eax.cmac_header.subkeys));
+
+  return 0;
+}
+
+
+gcry_err_code_t
+_gcry_cipher_eax_set_nonce (gcry_cipher_hd_t c, const byte *nonce,
+			    size_t noncelen)
+{
+  gcry_cmac_context_t nonce_cmac;
+  unsigned char initbuf[MAX_BLOCKSIZE];
+  gcry_err_code_t err;
+
+  c->marks.iv = 0;
+  c->marks.tag = 0;
+
+  _gcry_cmac_reset (&c->u_mode.eax.cmac_header);
+  _gcry_cmac_reset (&c->u_mode.eax.cmac_ciphertext);
+
+  /* Calculate nonce CMAC */
+
+  memset(&nonce_cmac, 0, sizeof(nonce_cmac));
+  memset(&initbuf, 0, sizeof(initbuf));
+
+  buf_cpy (&nonce_cmac.subkeys, c->u_mode.eax.cmac_header.subkeys,
+	   sizeof(c->u_mode.eax.cmac_header.subkeys));
+
+  err = _gcry_cmac_write (c, &nonce_cmac, initbuf, c->spec->blocksize);
+  if (err != 0)
+    return err;
+
+  if (noncelen != 0)
+    {
+      err = _gcry_cmac_write (c, &nonce_cmac, nonce, noncelen);
+      if (err != 0)
+        return err;
+    }
+
+  err = _gcry_cmac_final (c, &nonce_cmac);
+  if (err != 0)
+    return err;
+
+  buf_cpy (c->u_iv.iv, nonce_cmac.u_iv.iv, MAX_BLOCKSIZE);
+  buf_cpy (c->u_ctr.ctr, nonce_cmac.u_iv.iv, MAX_BLOCKSIZE);
+
+  wipememory (&nonce_cmac, sizeof(nonce_cmac));
+
+  /* Prepare header CMAC */
+
+  initbuf[c->spec->blocksize - 1] = 1;
+  err = _gcry_cmac_write (c, &c->u_mode.eax.cmac_header, initbuf,
+			  c->spec->blocksize);
+  if (err != 0)
+    return err;
+
+  /* Prepare ciphertext CMAC */
+
+  initbuf[c->spec->blocksize - 1] = 2;
+  err = _gcry_cmac_write (c, &c->u_mode.eax.cmac_ciphertext, initbuf,
+			  c->spec->blocksize);
+  if (err != 0)
+    return err;
+
+  c->marks.iv = 1;
+  c->marks.tag = 0;
+
+  return 0;
+}
+
+
+static gcry_err_code_t
+_gcry_cipher_eax_tag (gcry_cipher_hd_t c,
+                      byte *outbuf, size_t outbuflen, int check)
+{
+  gcry_err_code_t err;
+
+  if (!c->marks.tag)
+    {
+      err = _gcry_cmac_final (c, &c->u_mode.eax.cmac_header);
+      if (err != 0)
+	return err;
+
+      err = _gcry_cmac_final (c, &c->u_mode.eax.cmac_ciphertext);
+      if (err != 0)
+	return err;
+
+      buf_xor_1 (c->u_iv.iv, c->u_mode.eax.cmac_header.u_iv.iv, MAX_BLOCKSIZE);
+      buf_xor_1 (c->u_iv.iv, c->u_mode.eax.cmac_ciphertext.u_iv.iv,
+		 MAX_BLOCKSIZE);
+
+      _gcry_cmac_reset (&c->u_mode.eax.cmac_header);
+      _gcry_cmac_reset (&c->u_mode.eax.cmac_ciphertext);
+
+      c->marks.tag = 1;
+    }
+
+  if (!check)
+    {
+      if (outbuflen > c->spec->blocksize)
+        outbuflen = c->spec->blocksize;
+
+      /* NB: We already checked that OUTBUF is large enough to hold
+       * the result or has valid truncated length.  */
+      memcpy (outbuf, c->u_iv.iv, outbuflen);
+    }
+  else
+    {
+      /* OUTBUFLEN gives the length of the user supplied tag in OUTBUF
+       * and thus we need to compare its length first.  */
+      if (!(outbuflen <= c->spec->blocksize)
+          || !buf_eq_const (outbuf, c->u_iv.iv, outbuflen))
+        return GPG_ERR_CHECKSUM;
+    }
+
+  return 0;
+}
+
+
+gcry_err_code_t
+_gcry_cipher_eax_get_tag (gcry_cipher_hd_t c, unsigned char *outtag,
+                          size_t taglen)
+{
+  return _gcry_cipher_eax_tag (c, outtag, taglen, 0);
+}
+
+gcry_err_code_t
+_gcry_cipher_eax_check_tag (gcry_cipher_hd_t c, const unsigned char *intag,
+                            size_t taglen)
+{
+  return _gcry_cipher_eax_tag (c, (unsigned char *) intag, taglen, 1);
+}
diff --git a/cipher/cipher-internal.h b/cipher/cipher-internal.h
index 8c897d7b5..a0ede5e03 100644
--- a/cipher/cipher-internal.h
+++ b/cipher/cipher-internal.h
@@ -109,6 +109,25 @@ typedef union
 } cipher_context_alignment_t;
 
 
+/* Storage structure for CMAC, for CMAC and EAX modes. */
+typedef struct {
+  /* The initialization vector. Also contains tag after finalization. */
+  union {
+    cipher_context_alignment_t iv_align;
+    unsigned char iv[MAX_BLOCKSIZE];
+  } u_iv;
+
+  /* Subkeys for tag creation, not cleared by gcry_cipher_reset. */
+  unsigned char subkeys[2][MAX_BLOCKSIZE];
+
+  /* Space to save partial input lengths for MAC. */
+  unsigned char macbuf[MAX_BLOCKSIZE];
+
+  int mac_unused;  /* Number of unprocessed bytes in MACBUF. */
+  unsigned int tag:1; /* Set to 1 if tag has been finalized.  */
+} gcry_cmac_context_t;
+
+
 /* The handle structure.  */
 struct gcry_cipher_handle
 {
@@ -197,7 +216,7 @@ struct gcry_cipher_handle
 
       unsigned char s0[GCRY_CCM_BLOCK_LEN];
 
-      unsigned int nonce:1;/* Set to 1 if nonce has been set.  */
+      unsigned int nonce:1; /* Set to 1 if nonce has been set.  */
       unsigned int lengths:1; /* Set to 1 if CCM length parameters has been
                                  processed.  */
     } ccm;
@@ -217,12 +236,16 @@ struct gcry_cipher_handle
     } poly1305;
 
     /* Mode specific storage for CMAC mode. */
+    gcry_cmac_context_t cmac;
+
+    /* Mode specific storage for EAX mode. */
     struct {
-      unsigned int tag:1; /* Set to 1 if tag has been finalized.  */
+      /* CMAC for header (AAD). */
+      gcry_cmac_context_t cmac_header;
 
-      /* Subkeys for tag creation, not cleared by gcry_cipher_reset. */
-      unsigned char subkeys[2][MAX_BLOCKSIZE];
-    } cmac;
+      /* CMAC for ciphertext. */
+      gcry_cmac_context_t cmac_ciphertext;
+    } eax;
 
     /* Mode specific storage for GCM mode. */
     struct {
@@ -236,7 +259,6 @@ struct gcry_cipher_handle
       unsigned char macbuf[GCRY_CCM_BLOCK_LEN];
       int mac_unused;  /* Number of unprocessed bytes in MACBUF. */
 
-
       /* byte counters for GCM */
       u32 aadlen[2];
       u32 datalen[2];
@@ -309,7 +331,6 @@ struct gcry_cipher_handle
          processed.  */
       unsigned int data_finalized:1;
       unsigned int aad_finalized:1;
-
     } ocb;
 
     /* Mode specific storage for XTS mode. */
@@ -406,6 +427,42 @@ gcry_err_code_t _gcry_cipher_ccm_check_tag
                  const unsigned char *intag, size_t taglen);
 
 
+/*-- cipher-cmac.c --*/
+gcry_err_code_t _gcry_cmac_generate_subkeys
+/*           */ (gcry_cipher_hd_t c, gcry_cmac_context_t *ctx);
+gcry_err_code_t _gcry_cmac_write
+/*           */ (gcry_cipher_hd_t c, gcry_cmac_context_t *ctx,
+		 const byte * inbuf, size_t inlen);
+gcry_err_code_t _gcry_cmac_final
+/*           */ (gcry_cipher_hd_t c, gcry_cmac_context_t *ctx);
+void _gcry_cmac_reset (gcry_cmac_context_t *ctx);
+
+
+/*-- cipher-eax.c --*/
+gcry_err_code_t _gcry_cipher_eax_encrypt
+/*           */   (gcry_cipher_hd_t c,
+                   unsigned char *outbuf, size_t outbuflen,
+                   const unsigned char *inbuf, size_t inbuflen);
+gcry_err_code_t _gcry_cipher_eax_decrypt
+/*           */   (gcry_cipher_hd_t c,
+                   unsigned char *outbuf, size_t outbuflen,
+                   const unsigned char *inbuf, size_t inbuflen);
+gcry_err_code_t _gcry_cipher_eax_set_nonce
+/*           */   (gcry_cipher_hd_t c,
+                   const unsigned char *nonce, size_t noncelen);
+gcry_err_code_t _gcry_cipher_eax_authenticate
+/*           */   (gcry_cipher_hd_t c,
+                   const unsigned char *aadbuf, size_t aadbuflen);
+gcry_err_code_t _gcry_cipher_eax_get_tag
+/*           */   (gcry_cipher_hd_t c,
+                   unsigned char *outtag, size_t taglen);
+gcry_err_code_t _gcry_cipher_eax_check_tag
+/*           */   (gcry_cipher_hd_t c,
+                   const unsigned char *intag, size_t taglen);
+gcry_err_code_t _gcry_cipher_eax_setkey
+/*           */   (gcry_cipher_hd_t c);
+
+
 /*-- cipher-gcm.c --*/
 gcry_err_code_t _gcry_cipher_gcm_encrypt
 /*           */   (gcry_cipher_hd_t c,
diff --git a/cipher/cipher.c b/cipher/cipher.c
index 18b25911a..1bef766cb 100644
--- a/cipher/cipher.c
+++ b/cipher/cipher.c
@@ -420,6 +420,7 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
       case GCRY_CIPHER_MODE_CTR:
       case GCRY_CIPHER_MODE_AESWRAP:
       case GCRY_CIPHER_MODE_CMAC:
+      case GCRY_CIPHER_MODE_EAX:
       case GCRY_CIPHER_MODE_GCM:
 	if (!spec->encrypt || !spec->decrypt)
 	  err = GPG_ERR_INV_CIPHER_MODE;
@@ -688,7 +689,11 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, size_t keylen)
       switch (c->mode)
         {
         case GCRY_CIPHER_MODE_CMAC:
-          _gcry_cipher_cmac_set_subkeys (c);
+          rc = _gcry_cipher_cmac_set_subkeys (c);
+          break;
+
+        case GCRY_CIPHER_MODE_EAX:
+          rc = _gcry_cipher_eax_setkey (c);
           break;
 
         case GCRY_CIPHER_MODE_GCM:
@@ -782,8 +787,12 @@ cipher_reset (gcry_cipher_hd_t c)
   switch (c->mode)
     {
     case GCRY_CIPHER_MODE_CMAC:
-      /* Only clear 'tag' for cmac, keep subkeys. */
-      c->u_mode.cmac.tag = 0;
+      _gcry_cmac_reset(&c->u_mode.cmac);
+      break;
+
+    case GCRY_CIPHER_MODE_EAX:
+      _gcry_cmac_reset(&c->u_mode.eax.cmac_header);
+      _gcry_cmac_reset(&c->u_mode.eax.cmac_ciphertext);
       break;
 
     case GCRY_CIPHER_MODE_GCM:
@@ -929,6 +938,10 @@ cipher_encrypt (gcry_cipher_hd_t c, byte *outbuf, size_t outbuflen,
       rc = GPG_ERR_INV_CIPHER_MODE;
       break;
 
+    case GCRY_CIPHER_MODE_EAX:
+      rc = _gcry_cipher_eax_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+      break;
+
     case GCRY_CIPHER_MODE_GCM:
       rc = _gcry_cipher_gcm_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
       break;
@@ -1060,6 +1073,10 @@ cipher_decrypt (gcry_cipher_hd_t c, byte *outbuf, size_t outbuflen,
       rc = GPG_ERR_INV_CIPHER_MODE;
       break;
 
+    case GCRY_CIPHER_MODE_EAX:
+      rc = _gcry_cipher_eax_decrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+      break;
+
     case GCRY_CIPHER_MODE_GCM:
       rc = _gcry_cipher_gcm_decrypt (c, outbuf, outbuflen, inbuf, inbuflen);
       break;
@@ -1158,6 +1175,10 @@ _gcry_cipher_setiv (gcry_cipher_hd_t hd, const void *iv, size_t ivlen)
         rc = _gcry_cipher_ccm_set_nonce (hd, iv, ivlen);
         break;
 
+      case GCRY_CIPHER_MODE_EAX:
+        rc =  _gcry_cipher_eax_set_nonce (hd, iv, ivlen);
+        break;
+
       case GCRY_CIPHER_MODE_GCM:
         rc =  _gcry_cipher_gcm_setiv (hd, iv, ivlen);
         break;
@@ -1226,6 +1247,10 @@ _gcry_cipher_authenticate (gcry_cipher_hd_t hd, const void *abuf,
       rc = _gcry_cipher_cmac_authenticate (hd, abuf, abuflen);
       break;
 
+    case GCRY_CIPHER_MODE_EAX:
+      rc = _gcry_cipher_eax_authenticate (hd, abuf, abuflen);
+      break;
+
     case GCRY_CIPHER_MODE_GCM:
       rc = _gcry_cipher_gcm_authenticate (hd, abuf, abuflen);
       break;
@@ -1263,6 +1288,10 @@ _gcry_cipher_gettag (gcry_cipher_hd_t hd, void *outtag, size_t taglen)
       rc = _gcry_cipher_cmac_get_tag (hd, outtag, taglen);
       break;
 
+    case GCRY_CIPHER_MODE_EAX:
+      rc = _gcry_cipher_eax_get_tag (hd, outtag, taglen);
+      break;
+
     case GCRY_CIPHER_MODE_GCM:
       rc = _gcry_cipher_gcm_get_tag (hd, outtag, taglen);
       break;
@@ -1300,6 +1329,10 @@ _gcry_cipher_checktag (gcry_cipher_hd_t hd, const void *intag, size_t taglen)
       rc = _gcry_cipher_cmac_check_tag (hd, intag, taglen);
       break;
 
+    case GCRY_CIPHER_MODE_EAX:
+      rc = _gcry_cipher_eax_check_tag (hd, intag, taglen);
+      break;
+
     case GCRY_CIPHER_MODE_GCM:
       rc = _gcry_cipher_gcm_check_tag (hd, intag, taglen);
       break;
@@ -1501,6 +1534,10 @@ _gcry_cipher_info (gcry_cipher_hd_t h, int cmd, void *buffer, size_t *nbytes)
               *nbytes = h->u_mode.ccm.authlen;
               break;
 
+            case GCRY_CIPHER_MODE_EAX:
+              *nbytes = h->spec->blocksize;
+              break;
+
             case GCRY_CIPHER_MODE_GCM:
               *nbytes = GCRY_GCM_BLOCK_LEN;
               break;
diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi
index 78315052b..ccb4b820b 100644
--- a/doc/gcrypt.texi
+++ b/doc/gcrypt.texi
@@ -1722,6 +1722,12 @@ value is automatically incremented after each call of
 Auto-increment allows avoiding need of setting IV between processing
 of sequential data units.
 
+ at item  GCRY_CIPHER_MODE_EAX
+ at cindex EAX, EAX mode
+EAX is an Authenticated Encryption with Associated Data (AEAD) block cipher
+mode by Bellare, Rogaway, and Wagner (see
+ at uref{http://web.cs.ucdavis.edu/~rogaway/papers/eax.html}).
+
 @end table
 
 @node Working with cipher handles
@@ -1752,12 +1758,13 @@ with some algorithms - in particular, stream mode
 Poly1305 AEAD mode (@code{GCRY_CIPHER_MODE_POLY1305}) only works with
 ChaCha20 stream cipher. The block cipher modes
 (@code{GCRY_CIPHER_MODE_ECB}, @code{GCRY_CIPHER_MODE_CBC},
- at code{GCRY_CIPHER_MODE_CFB}, @code{GCRY_CIPHER_MODE_OFB} and
- at code{GCRY_CIPHER_MODE_CTR}) will work with any block cipher
-algorithm.  GCM mode (@code{GCRY_CIPHER_MODE_CCM}), CCM mode
-(@code{GCRY_CIPHER_MODE_GCM}), OCB mode (@code{GCRY_CIPHER_MODE_OCB}),
-and XTS mode (@code{GCRY_CIPHER_MODE_XTS}) will only work
-with block cipher algorithms which have the block size of 16 bytes.
+ at code{GCRY_CIPHER_MODE_CFB}, @code{GCRY_CIPHER_MODE_OFB},
+ at code{GCRY_CIPHER_MODE_CTR} and @code{GCRY_CIPHER_MODE_EAX}) will work
+with any block cipher algorithm.  GCM mode
+(@code{GCRY_CIPHER_MODE_CCM}), CCM mode (@code{GCRY_CIPHER_MODE_GCM}),
+OCB mode (@code{GCRY_CIPHER_MODE_OCB}), and XTS mode
+(@code{GCRY_CIPHER_MODE_XTS}) will only work with block cipher
+algorithms which have the block size of 16 bytes.
 
 The third argument @var{flags} can either be passed as @code{0} or as
 the bit-wise OR of the following constants.
diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in
index 1eb3d7c0f..83f94b687 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -971,7 +971,8 @@ enum gcry_cipher_modes
     GCRY_CIPHER_MODE_POLY1305 = 10,  /* Poly1305 based AEAD mode. */
     GCRY_CIPHER_MODE_OCB      = 11,  /* OCB3 mode.  */
     GCRY_CIPHER_MODE_CFB8     = 12,  /* Cipher feedback (8 bit mode). */
-    GCRY_CIPHER_MODE_XTS      = 13  /* XTS mode.  */
+    GCRY_CIPHER_MODE_XTS      = 13,  /* XTS mode.  */
+    GCRY_CIPHER_MODE_EAX      = 14   /* EAX mode.  */
   };
 
 /* Flags used with the open function. */
diff --git a/tests/basic.c b/tests/basic.c
index c2b42082a..c883eb39f 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -1347,7 +1347,7 @@ check_ofb_cipher (void)
 static void
 _check_gcm_cipher (unsigned int step)
 {
-  struct tv
+  static const struct tv
   {
     int algo;
     char key[MAX_DATA_LEN];
@@ -1890,10 +1890,543 @@ check_gcm_cipher (void)
 }
 
 
+static void
+_check_eax_cipher (unsigned int step)
+{
+  static const struct tv
+  {
+    int algo;
+    char key[MAX_DATA_LEN];
+    char nonce[MAX_DATA_LEN];
+    int noncelen;
+    unsigned char header[MAX_DATA_LEN];
+    int headerlen;
+    unsigned char plaintext[MAX_DATA_LEN];
+    int inlen;
+    char out[MAX_DATA_LEN];
+    char tag[MAX_DATA_LEN];
+    int taglen;
+    int should_fail;
+  } tv[] =
+    {
+      /* Test vectors from http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf */
+      { GCRY_CIPHER_AES,
+        "\x23\x39\x52\xDE\xE4\xD5\xED\x5F\x9B\x9C\x6D\x6F\xF8\x0F\xF4\x78",
+        "\x62\xEC\x67\xF9\xC3\xA4\xA4\x07\xFC\xB2\xA8\xC4\x90\x31\xA8\xB3", 16,
+        "\x6B\xFB\x91\x4F\xD0\x7E\xAE\x6B", 8,
+        "",
+        0,
+        "",
+        "\xE0\x37\x83\x0E\x83\x89\xF2\x7B\x02\x5A\x2D\x65\x27\xE7\x9D\x01", 16,
+        0
+      },
+      { GCRY_CIPHER_AES,
+        "\x91\x94\x5D\x3F\x4D\xCB\xEE\x0B\xF4\x5E\xF5\x22\x55\xF0\x95\xA4",
+        "\xBE\xCA\xF0\x43\xB0\xA2\x3D\x84\x31\x94\xBA\x97\x2C\x66\xDE\xBD", 16,
+        "\xFA\x3B\xFD\x48\x06\xEB\x53\xFA", 8,
+        "\xF7\xFB",
+        2,
+        "\x19\xDD",
+        "\x5C\x4C\x93\x31\x04\x9D\x0B\xDA\xB0\x27\x74\x08\xF6\x79\x67\xE5", 16,
+        0
+      },
+      { GCRY_CIPHER_AES,
+        "\x01\xF7\x4A\xD6\x40\x77\xF2\xE7\x04\xC0\xF6\x0A\xDA\x3D\xD5\x23",
+        "\x70\xC3\xDB\x4F\x0D\x26\x36\x84\x00\xA1\x0E\xD0\x5D\x2B\xFF\x5E", 16,
+        "\x23\x4A\x34\x63\xC1\x26\x4A\xC6", 8,
+        "\x1A\x47\xCB\x49\x33",
+        5,
+        "\xD8\x51\xD5\xBA\xE0",
+        "\x3A\x59\xF2\x38\xA2\x3E\x39\x19\x9D\xC9\x26\x66\x26\xC4\x0F\x80", 16,
+        0
+      },
+      { GCRY_CIPHER_AES,
+        "\xD0\x7C\xF6\xCB\xB7\xF3\x13\xBD\xDE\x66\xB7\x27\xAF\xD3\xC5\xE8",
+        "\x84\x08\xDF\xFF\x3C\x1A\x2B\x12\x92\xDC\x19\x9E\x46\xB7\xD6\x17", 16,
+        "\x33\xCC\xE2\xEA\xBF\xF5\xA7\x9D", 8,
+        "\x48\x1C\x9E\x39\xB1",
+        5,
+        "\x63\x2A\x9D\x13\x1A",
+        "\xD4\xC1\x68\xA4\x22\x5D\x8E\x1F\xF7\x55\x93\x99\x74\xA7\xBE\xDE", 16,
+        0
+      },
+      { GCRY_CIPHER_AES,
+        "\x35\xB6\xD0\x58\x00\x05\xBB\xC1\x2B\x05\x87\x12\x45\x57\xD2\xC2",
+        "\xFD\xB6\xB0\x66\x76\xEE\xDC\x5C\x61\xD7\x42\x76\xE1\xF8\xE8\x16", 16,
+        "\xAE\xB9\x6E\xAE\xBE\x29\x70\xE9", 8,
+        "\x40\xD0\xC0\x7D\xA5\xE4",
+        6,
+        "\x07\x1D\xFE\x16\xC6\x75",
+        "\xCB\x06\x77\xE5\x36\xF7\x3A\xFE\x6A\x14\xB7\x4E\xE4\x98\x44\xDD", 16,
+        0
+      },
+      { GCRY_CIPHER_AES,
+        "\xBD\x8E\x6E\x11\x47\x5E\x60\xB2\x68\x78\x4C\x38\xC6\x2F\xEB\x22",
+        "\x6E\xAC\x5C\x93\x07\x2D\x8E\x85\x13\xF7\x50\x93\x5E\x46\xDA\x1B", 16,
+        "\xD4\x48\x2D\x1C\xA7\x8D\xCE\x0F", 8,
+        "\x4D\xE3\xB3\x5C\x3F\xC0\x39\x24\x5B\xD1\xFB\x7D",
+        12,
+        "\x83\x5B\xB4\xF1\x5D\x74\x3E\x35\x0E\x72\x84\x14",
+        "\xAB\xB8\x64\x4F\xD6\xCC\xB8\x69\x47\xC5\xE1\x05\x90\x21\x0A\x4F", 16,
+        0
+      },
+      { GCRY_CIPHER_AES,
+        "\x7C\x77\xD6\xE8\x13\xBE\xD5\xAC\x98\xBA\xA4\x17\x47\x7A\x2E\x7D",
+        "\x1A\x8C\x98\xDC\xD7\x3D\x38\x39\x3B\x2B\xF1\x56\x9D\xEE\xFC\x19", 16,
+        "\x65\xD2\x01\x79\x90\xD6\x25\x28", 8,
+        "\x8B\x0A\x79\x30\x6C\x9C\xE7\xED\x99\xDA\xE4\xF8\x7F\x8D\xD6\x16\x36",
+        17,
+        "\x02\x08\x3E\x39\x79\xDA\x01\x48\x12\xF5\x9F\x11\xD5\x26\x30\xDA\x30",
+        "\x13\x73\x27\xD1\x06\x49\xB0\xAA\x6E\x1C\x18\x1D\xB6\x17\xD7\xF2", 16,
+        0
+      },
+      { GCRY_CIPHER_AES,
+        "\x5F\xFF\x20\xCA\xFA\xB1\x19\xCA\x2F\xC7\x35\x49\xE2\x0F\x5B\x0D",
+        "\xDD\xE5\x9B\x97\xD7\x22\x15\x6D\x4D\x9A\xFF\x2B\xC7\x55\x98\x26", 16,
+        "\x54\xB9\xF0\x4E\x6A\x09\x18\x9A", 8,
+        "\x1B\xDA\x12\x2B\xCE\x8A\x8D\xBA\xF1\x87\x7D\x96\x2B\x85\x92\xDD"
+        "\x2D\x56",
+        18,
+        "\x2E\xC4\x7B\x2C\x49\x54\xA4\x89\xAF\xC7\xBA\x48\x97\xED\xCD\xAE"
+        "\x8C\xC3",
+        "\x3B\x60\x45\x05\x99\xBD\x02\xC9\x63\x82\x90\x2A\xEF\x7F\x83\x2A", 16,
+        0
+      },
+      { GCRY_CIPHER_AES,
+        "\xA4\xA4\x78\x2B\xCF\xFD\x3E\xC5\xE7\xEF\x6D\x8C\x34\xA5\x61\x23",
+        "\xB7\x81\xFC\xF2\xF7\x5F\xA5\xA8\xDE\x97\xA9\xCA\x48\xE5\x22\xEC", 16,
+        "\x89\x9A\x17\x58\x97\x56\x1D\x7E", 8,
+        "\x6C\xF3\x67\x20\x87\x2B\x85\x13\xF6\xEA\xB1\xA8\xA4\x44\x38\xD5"
+        "\xEF\x11",
+        18,
+        "\x0D\xE1\x8F\xD0\xFD\xD9\x1E\x7A\xF1\x9F\x1D\x8E\xE8\x73\x39\x38"
+        "\xB1\xE8",
+        "\xE7\xF6\xD2\x23\x16\x18\x10\x2F\xDB\x7F\xE5\x5F\xF1\x99\x17\x00", 16,
+        0
+      },
+      { GCRY_CIPHER_AES,
+        "\x83\x95\xFC\xF1\xE9\x5B\xEB\xD6\x97\xBD\x01\x0B\xC7\x66\xAA\xC3",
+        "\x22\xE7\xAD\xD9\x3C\xFC\x63\x93\xC5\x7E\xC0\xB3\xC1\x7D\x6B\x44", 16,
+        "\x12\x67\x35\xFC\xC3\x20\xD2\x5A", 8,
+        "\xCA\x40\xD7\x44\x6E\x54\x5F\xFA\xED\x3B\xD1\x2A\x74\x0A\x65\x9F"
+        "\xFB\xBB\x3C\xEA\xB7",
+        21,
+        "\xCB\x89\x20\xF8\x7A\x6C\x75\xCF\xF3\x96\x27\xB5\x6E\x3E\xD1\x97"
+        "\xC5\x52\xD2\x95\xA7",
+        "\xCF\xC4\x6A\xFC\x25\x3B\x46\x52\xB1\xAF\x37\x95\xB1\x24\xAB\x6E", 16,
+        0
+      },
+      /* Negative test for bad tag. */
+      { GCRY_CIPHER_AES,
+        "\x23\x39\x52\xDE\xE4\xD5\xED\x5F\x9B\x9C\x6D\x6F\xF8\x0F\xF4\x78",
+        "\x62\xEC\x67\xF9\xC3\xA4\xA4\x07\xFC\xB2\xA8\xC4\x90\x31\xA8\xB3", 16,
+        "\x6B\xFB\x91\x4F\xD0\x7E\xAE\x6B", 8,
+        "",
+        0,
+        "",
+        "\x00\x37\x83\x0E\x83\x89\xF2\x7B\x02\x5A\x2D\x65\x27\xE7\x9D\x01", 16,
+        1
+      },
+      /* Test vectors from libtomcrypt. */
+      {
+        GCRY_CIPHER_AES,
+        "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+        "", 0,
+        "", 0,
+        "",
+        0,
+        "",
+        "\x9a\xd0\x7e\x7d\xbf\xf3\x01\xf5\x05\xde\x59\x6b\x96\x15\xdf\xff", 16,
+        0
+      },
+      {
+        GCRY_CIPHER_AES,
+        "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+        "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 16,
+        "", 0,
+        "",
+        0,
+        "",
+        "\x1c\xe1\x0d\x3e\xff\xd4\xca\xdb\xe2\xe4\x4b\x58\xd6\x0a\xb9\xec", 16,
+        0
+      },
+      {
+        GCRY_CIPHER_AES,
+        "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+        "", 0,
+        "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 16,
+        "",
+        0,
+        "",
+        "\x3a\x69\x8f\x7a\x27\x0e\x51\xb0\xf6\x5b\x3d\x3e\x47\x19\x3c\xff", 16,
+        0
+      },
+      {
+        GCRY_CIPHER_AES,
+        "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+        "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 16,
+        "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 16,
+        "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+        "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
+        32,
+        "\x29\xd8\x78\xd1\xa3\xbe\x85\x7b\x6f\xb8\xc8\xea\x59\x50\xa7\x78"
+        "\x33\x1f\xbf\x2c\xcf\x33\x98\x6f\x35\xe8\xcf\x12\x1d\xcb\x30\xbc",
+        "\x4f\xbe\x03\x38\xbe\x1c\x8c\x7e\x1d\x7a\xe7\xe4\x5b\x92\xc5\x87", 16,
+        0
+      },
+      {
+        GCRY_CIPHER_AES,
+        "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+        "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e", 15,
+        "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d", 14,
+        "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+        "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c",
+        29,
+        "\xdd\x25\xc7\x54\xc5\xb1\x7c\x59\x28\xb6\x9b\x73\x15\x5f\x7b\xb8"
+        "\x88\x8f\xaf\x37\x09\x1a\xd9\x2c\x8a\x24\xdb\x86\x8b",
+        "\x0d\x1a\x14\xe5\x22\x24\xff\xd2\x3a\x05\xfa\x02\xcd\xef\x52\xda", 16,
+        0
+      },
+    };
+
+  gcry_cipher_hd_t hde, hdd;
+  unsigned char out[MAX_DATA_LEN];
+  unsigned char tag[16];
+  int i, keylen;
+  gcry_error_t err = 0;
+  size_t pos, poslen, taglen2;
+  int byteNum;
+
+  if (verbose)
+    fprintf (stderr, "  Starting EAX checks.\n");
+
+  for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++)
+    {
+      if (gcry_cipher_test_algo (tv[i].algo) && in_fips_mode)
+        {
+          if (verbose)
+            fprintf (stderr, "  algorithm %d not available in fips mode\n",
+		     tv[i].algo);
+          continue;
+        }
+
+      if (verbose)
+        fprintf (stderr, "    checking EAX mode for %s [%i]\n",
+                 gcry_cipher_algo_name (tv[i].algo),
+                 tv[i].algo);
+      err = gcry_cipher_open (&hde, tv[i].algo, GCRY_CIPHER_MODE_EAX, 0);
+      if (!err)
+        err = gcry_cipher_open (&hdd, tv[i].algo, GCRY_CIPHER_MODE_EAX, 0);
+      if (err)
+        {
+          fail ("aes-eax, gcry_cipher_open failed: %s\n", gpg_strerror (err));
+          return;
+        }
+
+      keylen = gcry_cipher_get_algo_keylen(tv[i].algo);
+      if (!keylen)
+        {
+          fail ("aes-eax, gcry_cipher_get_algo_keylen failed\n");
+          return;
+        }
+
+      err = gcry_cipher_setkey (hde, tv[i].key, keylen);
+      if (!err)
+        err = gcry_cipher_setkey (hdd, tv[i].key, keylen);
+      if (err)
+        {
+          fail ("aes-eax, gcry_cipher_setkey failed: %s\n",
+                gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      err = gcry_cipher_setiv (hde, tv[i].nonce, tv[i].noncelen);
+      if (!err)
+        err = gcry_cipher_setiv (hdd, tv[i].nonce, tv[i].noncelen);
+      if (err)
+        {
+          fail ("aes-eax, gcry_cipher_setiv failed: %s\n",
+                gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      err = gcry_cipher_info (hde, GCRYCTL_GET_TAGLEN, NULL, &taglen2);
+      if (err)
+        {
+          fail ("cipher-eax, gcryctl_get_taglen failed (tv %d): %s\n",
+                i, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+      if (taglen2 != 16)
+        {
+          fail ("cipher-eax, gcryctl_get_taglen returned bad length"
+                " (tv %d): got=%zu want=%d\n",
+                i, taglen2, 16);
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      for (pos = 0; pos < tv[i].headerlen; pos += step)
+        {
+          poslen = (pos + step < tv[i].headerlen) ?
+                    step : tv[i].headerlen - pos;
+
+          err = gcry_cipher_authenticate(hde, tv[i].header + pos, poslen);
+          if (err)
+            {
+              fail ("aes-eax, gcry_cipher_authenticate (%d) (%lu:%d) failed: "
+                    "%s\n", i, (unsigned long) pos, step, gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+          err = gcry_cipher_authenticate(hdd, tv[i].header + pos, poslen);
+          if (err)
+            {
+              fail ("aes-eax, de gcry_cipher_authenticate (%d) (%lu:%d) failed: "
+	            "%s\n", i, (unsigned long) pos, step, gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+        }
+
+      for (pos = 0; pos < tv[i].inlen; pos += step)
+        {
+          poslen = (pos + step < tv[i].inlen) ? step : tv[i].inlen - pos;
+
+          err = gcry_cipher_encrypt (hde, out + pos, poslen,
+                                     tv[i].plaintext + pos, poslen);
+          if (err)
+            {
+              fail ("aes-eax, gcry_cipher_encrypt (%d) (%lu:%d) failed: %s\n",
+                    i, (unsigned long) pos, step, gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+        }
+
+      if (memcmp (tv[i].out, out, tv[i].inlen))
+        fail ("aes-eax, encrypt mismatch entry %d (step %d)\n", i, step);
+
+      for (pos = 0; pos < tv[i].inlen; pos += step)
+        {
+          poslen = (pos + step < tv[i].inlen) ? step : tv[i].inlen - pos;
+
+          err = gcry_cipher_decrypt (hdd, out + pos, poslen, NULL, 0);
+          if (err)
+            {
+              fail ("aes-eax, gcry_cipher_decrypt (%d) (%lu:%d) failed: %s\n",
+                    i, (unsigned long) pos, step, gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+        }
+
+      if (memcmp (tv[i].plaintext, out, tv[i].inlen))
+        fail ("aes-eax, decrypt mismatch entry %d (step %d)\n", i, step);
+
+      taglen2 = tv[i].taglen ? tv[i].taglen : 16;
+
+      err = gcry_cipher_gettag (hde, out, taglen2);
+      if (err)
+        {
+          if (tv[i].should_fail)
+            goto next_tv;
+
+          fail ("aes-eax, gcry_cipher_gettag(%d) failed: %s\n",
+                i, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      if ((memcmp (tv[i].tag, out, taglen2) != 0) ^ tv[i].should_fail)
+        fail ("aes-eax, encrypt tag mismatch entry %d\n", i);
+
+      err = gcry_cipher_checktag (hdd, tv[i].tag, taglen2);
+      if (err)
+        {
+          if (tv[i].should_fail)
+            goto next_tv;
+
+          fail ("aes-eax, gcry_cipher_checktag(%d) failed: %s\n",
+                i, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      err = gcry_cipher_reset(hde);
+      if (!err)
+        err = gcry_cipher_reset(hdd);
+      if (err)
+        {
+          fail ("aes-eax, gcry_cipher_reset (%d) failed: %s\n",
+                i, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      /* gcry_cipher_reset clears the IV */
+      err = gcry_cipher_setiv (hde, tv[i].nonce, tv[i].noncelen);
+      if (!err)
+        err = gcry_cipher_setiv (hdd, tv[i].nonce, tv[i].noncelen);
+      if (err)
+        {
+          fail ("aes-eax, gcry_cipher_setiv failed: %s\n",
+                gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      /* this time we authenticate, encrypt and decrypt one byte at a time */
+      for (byteNum = 0; byteNum < tv[i].headerlen; ++byteNum)
+        {
+          err = gcry_cipher_authenticate(hde, tv[i].header + byteNum, 1);
+          if (err)
+            {
+              fail ("aes-eax, gcry_cipher_authenticate (%d) (byte-buf) failed: "
+                    "%s\n", i, gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+          err = gcry_cipher_authenticate(hdd, tv[i].header + byteNum, 1);
+          if (err)
+            {
+              fail ("aes-eax, de gcry_cipher_authenticate (%d) (byte-buf) "
+	            "failed: %s\n", i, gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+        }
+
+      for (byteNum = 0; byteNum < tv[i].inlen; ++byteNum)
+        {
+          err = gcry_cipher_encrypt (hde, out+byteNum, 1,
+                                     (tv[i].plaintext) + byteNum,
+                                     1);
+          if (err)
+            {
+              fail ("aes-eax, gcry_cipher_encrypt (%d) (byte-buf) failed: %s\n",
+                    i,  gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+        }
+
+      if (memcmp (tv[i].out, out, tv[i].inlen))
+        fail ("aes-eax, encrypt mismatch entry %d, (byte-buf)\n", i);
+
+      /* Test output to larger than 16-byte buffer. */
+      taglen2 = tv[i].taglen ? tv[i].taglen : 16 + 1;
+
+      err = gcry_cipher_gettag (hde, tag, taglen2);
+      if (err)
+        {
+          if (tv[i].should_fail)
+            goto next_tv;
+
+          fail ("aes-eax, gcry_cipher_gettag(%d, %lu) (byte-buf) failed: %s\n",
+                i, (unsigned long) taglen2, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      taglen2 = tv[i].taglen ? tv[i].taglen : 16;
+
+      if ((memcmp (tv[i].tag, tag, taglen2) != 0) ^ tv[i].should_fail)
+        fail ("aes-eax, encrypt tag mismatch entry %d, (byte-buf)\n", i);
+
+      for (byteNum = 0; byteNum < tv[i].inlen; ++byteNum)
+        {
+          err = gcry_cipher_decrypt (hdd, out+byteNum, 1, NULL, 0);
+          if (err)
+            {
+              fail ("aes-eax, gcry_cipher_decrypt (%d) (byte-buf) failed: %s\n",
+                    i, gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+        }
+
+      if (memcmp (tv[i].plaintext, out, tv[i].inlen))
+        fail ("aes-eax, decrypt mismatch entry %d\n", i);
+
+      err = gcry_cipher_checktag (hdd, tv[i].tag, taglen2);
+      if (err)
+        {
+          if (tv[i].should_fail)
+            goto next_tv;
+
+          fail ("aes-eax, gcry_cipher_checktag(%d) (byte-buf) failed: %s\n",
+                i, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      err = gcry_cipher_checktag (hdd, tag, 17);
+      if (!err)
+        {
+          fail ("aes-eax, gcry_cipher_checktag(%d) did not fail for invalid "
+	        " tag length of '%d'\n", i, 17);
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      if (tv[i].should_fail)
+        {
+          fail ("aes-eax, negative test succeeded %d\n", i);
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+    next_tv:
+      gcry_cipher_close (hde);
+      gcry_cipher_close (hdd);
+    }
+  if (verbose)
+    fprintf (stderr, "  Completed EAX checks.\n");
+}
+
+
+static void
+check_eax_cipher (void)
+{
+  /* Large buffers, no splitting. */
+  _check_eax_cipher(0xffffffff);
+  /* Split input to one byte buffers. */
+  _check_eax_cipher(1);
+  /* Split input to 7 byte buffers. */
+  _check_eax_cipher(7);
+  /* Split input to 16 byte buffers. */
+  _check_eax_cipher(16);
+}
+
+
 static void
 _check_poly1305_cipher (unsigned int step)
 {
-  struct tv
+  static const struct tv
   {
     int algo;
     const char *key;
@@ -5813,6 +6346,7 @@ get_algo_mode_blklen (int algo, int mode)
     case GCRY_CIPHER_MODE_CTR:
     case GCRY_CIPHER_MODE_CCM:
     case GCRY_CIPHER_MODE_GCM:
+    case GCRY_CIPHER_MODE_EAX:
     case GCRY_CIPHER_MODE_POLY1305:
       return 1;
     }
@@ -5894,7 +6428,7 @@ check_one_cipher_core (int algo, int mode, int flags,
   if ((mode == GCRY_CIPHER_MODE_CBC && (flags & GCRY_CIPHER_CBC_CTS)) ||
       mode == GCRY_CIPHER_MODE_XTS)
     {
-      /* Input cannot be split in to multiple operations with CTS . */
+      /* Input cannot be split in to multiple operations with CTS. */
       blklen = nplain;
     }
 
@@ -6281,6 +6815,7 @@ check_ciphers (void)
       check_one_cipher (algos[i], GCRY_CIPHER_MODE_CBC, 0);
       check_one_cipher (algos[i], GCRY_CIPHER_MODE_CBC, GCRY_CIPHER_CBC_CTS);
       check_one_cipher (algos[i], GCRY_CIPHER_MODE_CTR, 0);
+      check_one_cipher (algos[i], GCRY_CIPHER_MODE_EAX, 0);
       if (gcry_cipher_get_algo_blklen (algos[i]) == GCRY_CCM_BLOCK_LEN)
         check_one_cipher (algos[i], GCRY_CIPHER_MODE_CCM, 0);
       if (gcry_cipher_get_algo_blklen (algos[i]) == GCRY_GCM_BLOCK_LEN)
@@ -6333,6 +6868,7 @@ check_cipher_modes(void)
   check_poly1305_cipher ();
   check_ocb_cipher ();
   check_xts_cipher ();
+  check_eax_cipher ();
   check_gost28147_cipher ();
   check_stream_cipher ();
   check_stream_cipher_large_block ();
diff --git a/tests/bench-slope.c b/tests/bench-slope.c
index 75e6e43d3..e34104f7b 100644
--- a/tests/bench-slope.c
+++ b/tests/bench-slope.c
@@ -1231,6 +1231,53 @@ static struct bench_ops ocb_authenticate_ops = {
   &bench_ocb_authenticate_do_bench
 };
 
+static void
+bench_eax_encrypt_do_bench (struct bench_obj *obj, void *buf,
+			    size_t buflen)
+{
+  char nonce[16] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
+                     0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88,
+                     0x00, 0x00, 0x01, 0x00 };
+  bench_aead_encrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
+}
+
+static void
+bench_eax_decrypt_do_bench (struct bench_obj *obj, void *buf,
+			    size_t buflen)
+{
+  char nonce[16] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
+                     0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88,
+                     0x00, 0x00, 0x01, 0x00 };
+  bench_aead_decrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
+}
+
+static void
+bench_eax_authenticate_do_bench (struct bench_obj *obj, void *buf,
+				 size_t buflen)
+{
+  char nonce[16] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
+                     0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88,
+                     0x00, 0x00, 0x01, 0x00 };
+  bench_aead_authenticate_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
+}
+
+static struct bench_ops eax_encrypt_ops = {
+  &bench_encrypt_init,
+  &bench_encrypt_free,
+  &bench_eax_encrypt_do_bench
+};
+
+static struct bench_ops eax_decrypt_ops = {
+  &bench_encrypt_init,
+  &bench_encrypt_free,
+  &bench_eax_decrypt_do_bench
+};
+
+static struct bench_ops eax_authenticate_ops = {
+  &bench_encrypt_init,
+  &bench_encrypt_free,
+  &bench_eax_authenticate_do_bench
+};
 
 static void
 bench_poly1305_encrypt_do_bench (struct bench_obj *obj, void *buf,
@@ -1291,6 +1338,9 @@ static struct bench_cipher_mode cipher_modes[] = {
   {GCRY_CIPHER_MODE_CCM, "CCM enc", &ccm_encrypt_ops},
   {GCRY_CIPHER_MODE_CCM, "CCM dec", &ccm_decrypt_ops},
   {GCRY_CIPHER_MODE_CCM, "CCM auth", &ccm_authenticate_ops},
+  {GCRY_CIPHER_MODE_EAX, "EAX enc",  &eax_encrypt_ops},
+  {GCRY_CIPHER_MODE_EAX, "EAX dec",  &eax_decrypt_ops},
+  {GCRY_CIPHER_MODE_EAX, "EAX auth", &eax_authenticate_ops},
   {GCRY_CIPHER_MODE_GCM, "GCM enc", &gcm_encrypt_ops},
   {GCRY_CIPHER_MODE_GCM, "GCM dec", &gcm_decrypt_ops},
   {GCRY_CIPHER_MODE_GCM, "GCM auth", &gcm_authenticate_ops},
diff --git a/tests/benchmark.c b/tests/benchmark.c
index 44a8711d9..59ea32c66 100644
--- a/tests/benchmark.c
+++ b/tests/benchmark.c
@@ -779,6 +779,8 @@ cipher_bench ( const char *algoname )
       NULL, GCRY_GCM_BLOCK_LEN, GCRY_GCM_BLOCK_LEN },
     { GCRY_CIPHER_MODE_OCB, "      OCB", 1,
       NULL, 16, 16, 15 },
+    { GCRY_CIPHER_MODE_EAX, "      EAX", 0,
+      NULL, 0, 8, 8 },
     { GCRY_CIPHER_MODE_STREAM, "", 0 },
     {0}
   };




More information about the Gcrypt-devel mailing list