[PATCH 2/2] [v3] Add Counter with CBC-MAC mode (CCM)

Jussi Kivilinna jussi.kivilinna at iki.fi
Mon Oct 21 12:34:11 CEST 2013


* cipher/Makefile.am: Add 'cipher-ccm.c'.
* cipher/cipher-ccm.c: New.
* cipher/cipher-internal.h (gcry_cipher_handle): Add 'u_mode'.
(_gcry_cipher_ccm_encrypt, _gcry_cipher_ccm_decrypt)
(_gcry_cipher_ccm_set_nonce, _gcry_cipher_ccm_authenticate)
(_gcry_cipher_ccm_get_tag, _gcry_cipher_ccm_check_tag)
(_gcry_cipher_ccm_set_params): New prototypes.
* cipher/cipher.c (gcry_cipher_open, cipher_encrypt, cipher_decrypt)
(_gcry_cipher_setiv, _gcry_cipher_authenticate, _gcry_cipher_gettag)
(_gcry_cipher_checktag, gry_cipher_ctl): Add handling for CCM mode.
* doc/gcrypt.texi: Add documentation for GCRY_CIPHER_MODE_CCM.
* src/gcrypt.h.in (gcry_cipher_modes): Add 'GCRY_CIPHER_MODE_CCM' and
'GCRYCTL_SET_CCM_PARAMS'.
(GCRY_CCM_BLOCK_LEN): New.
* tests/basic.c (check_ccm_cipher): New.
(check_cipher_modes): Call 'check_ccm_cipher'.
* tests/benchmark.c (ccm_aead_init): New.
(cipher_bench): Add handling for AEAD modes and add CCM benchmarking.
--

Patch adds CCM (Counter with CBC-MAC) mode as defined in RFC 3610 and NIST
Special Publication 800-38C.

Example for encrypting message (split in two buffers; buf1, buf2) and
authenticating additional non-encrypted data (split in two buffers; aadbuf1,
aadbuf2) with authentication tag length of eigth bytes:

  size_t params[3];
  taglen = 8;

  gcry_cipher_setkey(h, key, len(key));

  gcry_cipher_setiv(h, nonce, len(nonce));

  params[0] = len(buf1) + len(buf2);       /* 0: enclen */
  params[1] = len(aadbuf1) + len(aadbuf2); /* 1: aadlen */
  params[2] = taglen;                      /* 2: authtaglen */
  gcry_cipher_ctl(h, GCRYCTL_SET_CCM_PARAMS, params, sizeof(size_t) * 3);

  gcry_cipher_authenticate(h, aadbuf1, len(aadbuf1));
  gcry_cipher_authenticate(h, aadbuf2, len(aadbuf2));

  gcry_cipher_encrypt(h, buf1, len(buf1), buf1, len(buf1));
  gcry_cipher_encrypt(h, buf2, len(buf2), buf2, len(buf2));

  gcry_cipher_gettag(h, tag, taglen);

Example for decrypting above message and checking authentication tag:

  size_t params[3];
  taglen = 8;

  gcry_cipher_setkey(h, key, len(key));

  gcry_cipher_setiv(h, nonce, len(nonce));

  params[0] = len(buf1) + len(buf2);       /* 0: enclen */
  params[1] = len(aadbuf1) + len(aadbuf2); /* 1: aadlen */
  params[2] = taglen;                      /* 2: authtaglen */
  gcry_cipher_ctl(h, GCRYCTL_SET_CCM_PARAMS, params, sizeof(size_t) * 3);

  gcry_cipher_authenticate(h, aadbuf1, len(aadbuf1));
  gcry_cipher_authenticate(h, aadbuf2, len(aadbuf2));

  gcry_cipher_decrypt(h, buf1, len(buf1), buf1, len(buf1));
  gcry_cipher_decrypt(h, buf2, len(buf2), buf2, len(buf2));

  err = gcry_cipher_checktag(h, tag, taglen);
  if (gpg_err_code (err) == GPG_ERR_CHECKSUM)
    { /* Authentication failed. */ }
  else if (err == 0)
    { /* Authentication ok. */ }

Example for encrypting message without additional authenticated data:

  size_t params[3];
  taglen = 10;

  gcry_cipher_setkey(h, key, len(key));

  gcry_cipher_setiv(h, nonce, len(nonce));

  params[0] = len(buf1); /* 0: enclen */
  params[1] = 0;         /* 1: aadlen */
  params[2] = taglen;    /* 2: authtaglen */
  gcry_cipher_ctl(h, GCRYCTL_SET_CCM_PARAMS, params, sizeof(size_t) * 3);

  gcry_cipher_encrypt(h, buf1, len(buf1), buf1, len(buf1));

  gcry_cipher_gettag(h, tag, taglen);

To reset CCM state for cipher handle, one can either set new nonce or use
'gcry_cipher_reset'.

This implementation reuses existing CTR mode code for encryption/decryption
and is there for able to process multiple buffers that are not multiple of
blocksize. AAD data maybe also be passed into gcry_cipher_authenticate
in non-blocksize chunks.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>
---
 cipher/Makefile.am       |    1 
 cipher/cipher-ccm.c      |  371 ++++++++++++++++++++++
 cipher/cipher-internal.h |   48 +++
 cipher/cipher.c          |  107 ++++++
 doc/gcrypt.texi          |   16 +
 src/gcrypt.h.in          |    8 
 tests/basic.c            |  771 ++++++++++++++++++++++++++++++++++++++++++++++
 tests/benchmark.c        |   80 +++++
 8 files changed, 1377 insertions(+), 25 deletions(-)
 create mode 100644 cipher/cipher-ccm.c

diff --git a/cipher/Makefile.am b/cipher/Makefile.am
index a2b2c8a..b0efd89 100644
--- a/cipher/Makefile.am
+++ b/cipher/Makefile.am
@@ -40,6 +40,7 @@ libcipher_la_LIBADD = $(GCRYPT_MODULES)
 libcipher_la_SOURCES = \
 cipher.c cipher-internal.h \
 cipher-cbc.c cipher-cfb.c cipher-ofb.c cipher-ctr.c cipher-aeswrap.c \
+cipher-ccm.c \
 cipher-selftest.c cipher-selftest.h \
 pubkey.c pubkey-internal.h pubkey-util.c \
 md.c \
diff --git a/cipher/cipher-ccm.c b/cipher/cipher-ccm.c
new file mode 100644
index 0000000..e51fa32
--- /dev/null
+++ b/cipher/cipher-ccm.c
@@ -0,0 +1,371 @@
+/* cipher-ccm.c - CTR mode with CBC-MAC mode implementation
+ * Copyright © 2013 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 "ath.h"
+#include "bufhelp.h"
+#include "./cipher-internal.h"
+
+
+#define set_burn(burn, nburn) do { \
+  unsigned int __nburn = (nburn); \
+  (burn) = (burn) > __nburn ? (burn) : __nburn; } while (0)
+
+
+static unsigned int
+do_cbc_mac (gcry_cipher_hd_t c, const unsigned char *inbuf, size_t inlen,
+            int do_padding)
+{
+  const unsigned int blocksize = 16;
+  unsigned char tmp[blocksize];
+  unsigned int burn = 0;
+  unsigned int unused = c->u_mode.ccm.mac_unused;
+  size_t nblocks;
+
+  if (inlen == 0 && (unused == 0 || !do_padding))
+    return 0;
+
+  do
+    {
+      if (inlen + unused < blocksize || unused > 0)
+        {
+          for (; inlen && unused < blocksize; inlen--)
+            c->u_mode.ccm.macbuf[unused++] = *inbuf++;
+        }
+      if (!inlen)
+        {
+          if (!do_padding)
+            break;
+
+          while (unused < blocksize)
+            c->u_mode.ccm.macbuf[unused++] = 0;
+        }
+
+      if (unused > 0)
+        {
+          /* Process one block from macbuf.  */
+          buf_xor(c->u_iv.iv, c->u_iv.iv, c->u_mode.ccm.macbuf, blocksize);
+          set_burn (burn, c->spec->encrypt ( &c->context.c, c->u_iv.iv,
+                                             c->u_iv.iv ));
+
+          unused = 0;
+        }
+
+      if (c->bulk.cbc_enc)
+        {
+          nblocks = inlen / blocksize;
+          c->bulk.cbc_enc (&c->context.c, c->u_iv.iv, tmp, inbuf, nblocks, 1);
+          inbuf += nblocks * blocksize;
+          inlen -= nblocks * blocksize;
+
+          wipememory (tmp, sizeof(tmp));
+        }
+      else
+        {
+          while (inlen >= blocksize)
+            {
+              buf_xor(c->u_iv.iv, c->u_iv.iv, inbuf, blocksize);
+
+              set_burn (burn, c->spec->encrypt ( &c->context.c, c->u_iv.iv,
+                                                 c->u_iv.iv ));
+
+              inlen -= blocksize;
+              inbuf += blocksize;
+            }
+        }
+    }
+  while (inlen > 0);
+
+  c->u_mode.ccm.mac_unused = unused;
+
+  if (burn)
+    burn += 4 * sizeof(void *);
+
+  return burn;
+}
+
+
+gcry_err_code_t
+_gcry_cipher_ccm_set_nonce (gcry_cipher_hd_t c, const unsigned char *nonce,
+                            size_t noncelen)
+{
+  size_t L = 15 - noncelen;
+  size_t L_;
+
+  L_ = L - 1;
+
+  if (!nonce)
+    return GPG_ERR_INV_ARG;
+  /* Length field must be 2, 3, ..., or 8. */
+  if (L < 2 || L > 8)
+    return GPG_ERR_INV_LENGTH;
+
+  /* Reset state */
+  memset (&c->u_mode, 0, sizeof(c->u_mode));
+  memset (&c->marks, 0, sizeof(c->marks));
+  memset (&c->u_iv, 0, sizeof(c->u_iv));
+  memset (&c->u_ctr, 0, sizeof(c->u_ctr));
+  memset (c->lastiv, 0, sizeof(c->lastiv));
+  c->unused = 0;
+
+  /* Setup CTR */
+  c->u_ctr.ctr[0] = L_;
+  memcpy (&c->u_ctr.ctr[1], nonce, noncelen);
+  memset (&c->u_ctr.ctr[1 + noncelen], 0, L);
+
+  /* Setup IV */
+  c->u_iv.iv[0] = L_;
+  memcpy (&c->u_iv.iv[1], nonce, noncelen);
+  /* Add (8 * M_ + 64 * flags) to iv[0] and set iv[noncelen + 1 ... 15] later
+     in set_aad.  */
+  memset (&c->u_iv.iv[1 + noncelen], 0, L);
+
+  c->u_mode.ccm.nonce = 1;
+
+  return GPG_ERR_NO_ERROR;
+}
+
+
+gcry_err_code_t
+_gcry_cipher_ccm_set_params (gcry_cipher_hd_t c, size_t encryptlen,
+                             size_t aadlen, size_t taglen)
+{
+  unsigned int burn = 0;
+  unsigned char b0[16];
+  size_t noncelen = 15 - (c->u_iv.iv[0] + 1);
+  size_t M = taglen;
+  size_t M_;
+  int i;
+
+  M_ = (M - 2) / 2;
+
+  /* Authentication field must be 4, 6, 8, 10, 12, 14 or 16. */
+  if ((M_ * 2 + 2) != M || M < 4 || M > 16)
+    return GPG_ERR_INV_LENGTH;
+  if (!c->u_mode.ccm.nonce || c->u_mode.ccm.tag)
+    return GPG_ERR_INV_STATE;
+  if (c->u_mode.ccm.params)
+    return GPG_ERR_INV_STATE;
+
+  c->u_mode.ccm.authlen = taglen;
+  c->u_mode.ccm.encryptlen = encryptlen;
+  c->u_mode.ccm.aadlen = aadlen;
+
+  /* Complete IV setup.  */
+  c->u_iv.iv[0] += (aadlen > 0) * 64 + M_ * 8;
+  for (i = 16 - 1; i >= 1 + noncelen; i--)
+    {
+      c->u_iv.iv[i] = encryptlen & 0xff;
+      encryptlen >>= 8;
+    }
+
+  memcpy (b0, c->u_iv.iv, 16);
+  memset (c->u_iv.iv, 0, 16);
+
+  set_burn (burn, do_cbc_mac (c, b0, 16, 0));
+
+  if (aadlen == 0)
+    {
+      /* Do nothing.  */
+    }
+  else if (aadlen > 0 && aadlen <= (unsigned int)0xfeff)
+    {
+      b0[0] = (aadlen >> 8) & 0xff;
+      b0[1] = aadlen & 0xff;
+      set_burn (burn, do_cbc_mac (c, b0, 2, 0));
+    }
+  else if (aadlen > 0xfeff && aadlen <= (unsigned int)0xffffffff)
+    {
+      b0[0] = 0xff;
+      b0[1] = 0xfe;
+      buf_put_be32(&b0[2], aadlen);
+      set_burn (burn, do_cbc_mac (c, b0, 6, 0));
+    }
+#ifdef HAVE_U64_TYPEDEF
+  else if (aadlen > (unsigned int)0xffffffff)
+    {
+      b0[0] = 0xff;
+      b0[1] = 0xff;
+      buf_put_be64(&b0[2], aadlen);
+      set_burn (burn, do_cbc_mac (c, b0, 10, 0));
+    }
+#endif
+
+  /* Generate S_0 and increase counter.  */
+  set_burn (burn, c->spec->encrypt ( &c->context.c, c->u_mode.ccm.s0,
+                                     c->u_ctr.ctr ));
+  c->u_ctr.ctr[15]++;
+
+  if (burn)
+    _gcry_burn_stack (burn + sizeof(void *) * 5);
+
+  c->u_mode.ccm.params = 1;
+
+  return GPG_ERR_NO_ERROR;
+}
+
+
+gcry_err_code_t
+_gcry_cipher_ccm_authenticate (gcry_cipher_hd_t c, const unsigned char *abuf,
+                               size_t abuflen)
+{
+  unsigned int burn;
+
+  if (abuflen > 0 && !abuf)
+    return GPG_ERR_INV_ARG;
+  if (!c->u_mode.ccm.nonce || !c->u_mode.ccm.params || c->u_mode.ccm.tag)
+    return GPG_ERR_INV_STATE;
+  if (abuflen > c->u_mode.ccm.aadlen)
+    return GPG_ERR_INV_LENGTH;
+
+  c->u_mode.ccm.aadlen -= abuflen;
+  burn = do_cbc_mac (c, abuf, abuflen, c->u_mode.ccm.aadlen == 0);
+
+  if (burn)
+    _gcry_burn_stack (burn + sizeof(void *) * 5);
+
+  return GPG_ERR_NO_ERROR;
+}
+
+
+gcry_err_code_t
+_gcry_cipher_ccm_tag (gcry_cipher_hd_t c, unsigned char *outbuf,
+		      size_t outbuflen, int check)
+{
+  unsigned int burn;
+
+  if (!outbuf || outbuflen == 0)
+    return GPG_ERR_INV_ARG;
+  /* Tag length must be same as initial authlen.  */
+  if (c->u_mode.ccm.authlen != outbuflen)
+    return GPG_ERR_INV_LENGTH;
+  if (!c->u_mode.ccm.nonce || !c->u_mode.ccm.params || c->u_mode.ccm.aadlen > 0)
+    return GPG_ERR_INV_STATE;
+  /* Initial encrypt length must match with length of actual data processed.  */
+  if (c->u_mode.ccm.encryptlen > 0)
+    return GPG_ERR_UNFINISHED;
+
+  if (!c->u_mode.ccm.tag)
+    {
+      burn = do_cbc_mac (c, NULL, 0, 1); /* Perform final padding.  */
+
+      /* Add S_0 */
+      buf_xor (c->u_iv.iv, c->u_iv.iv, c->u_mode.ccm.s0, 16);
+
+      wipememory (c->u_ctr.ctr, 16);
+      wipememory (c->u_mode.ccm.s0, 16);
+      wipememory (c->u_mode.ccm.macbuf, 16);
+
+      if (burn)
+        _gcry_burn_stack (burn + sizeof(void *) * 5);
+    }
+
+  if (!check)
+    {
+      memcpy (outbuf, c->u_iv.iv, outbuflen);
+      return GPG_ERR_NO_ERROR;
+    }
+  else
+    {
+      int diff, i;
+
+      /* Constant-time compare. */
+      for (i = 0, diff = 0; i < outbuflen; i++)
+        diff -= !!(outbuf[i] - c->u_iv.iv[i]);
+
+      return !diff ? GPG_ERR_NO_ERROR : GPG_ERR_CHECKSUM;
+    }
+}
+
+
+gcry_err_code_t
+_gcry_cipher_ccm_get_tag (gcry_cipher_hd_t c, unsigned char *outtag,
+			  size_t taglen)
+{
+	return _gcry_cipher_ccm_tag (c, outtag, taglen, 0);
+}
+
+
+gcry_err_code_t
+_gcry_cipher_ccm_check_tag (gcry_cipher_hd_t c, const unsigned char *intag,
+			    size_t taglen)
+{
+	return _gcry_cipher_ccm_tag (c, (unsigned char *)intag, taglen, 1);
+}
+
+
+gcry_err_code_t
+_gcry_cipher_ccm_encrypt (gcry_cipher_hd_t c, unsigned char *outbuf,
+                          unsigned int outbuflen, const unsigned char *inbuf,
+                          unsigned int inbuflen)
+{
+  unsigned int burn;
+
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+  if (!c->u_mode.ccm.nonce || c->u_mode.ccm.tag || !c->u_mode.ccm.params ||
+      c->u_mode.ccm.aadlen > 0)
+    return GPG_ERR_INV_STATE;
+  if (inbuflen > c->u_mode.ccm.encryptlen)
+    return GPG_ERR_INV_LENGTH;
+
+  c->u_mode.ccm.encryptlen -= inbuflen;
+  burn = do_cbc_mac (c, inbuf, inbuflen, 0);
+  if (burn)
+    _gcry_burn_stack (burn + sizeof(void *) * 5);
+
+  return _gcry_cipher_ctr_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+}
+
+
+gcry_err_code_t
+_gcry_cipher_ccm_decrypt (gcry_cipher_hd_t c, unsigned char *outbuf,
+                          unsigned int outbuflen, const unsigned char *inbuf,
+                          unsigned int inbuflen)
+{
+  gcry_err_code_t err;
+  unsigned int burn;
+
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+  if (!c->u_mode.ccm.nonce || c->u_mode.ccm.tag || !c->u_mode.ccm.params ||
+      c->u_mode.ccm.aadlen > 0)
+    return GPG_ERR_INV_STATE;
+  if (inbuflen > c->u_mode.ccm.encryptlen)
+    return GPG_ERR_INV_LENGTH;
+
+  err = _gcry_cipher_ctr_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+  if (err)
+    return err;
+
+  c->u_mode.ccm.encryptlen -= inbuflen;
+  burn = do_cbc_mac (c, outbuf, inbuflen, 0);
+  if (burn)
+    _gcry_burn_stack (burn + sizeof(void *) * 5);
+
+  return err;
+}
+
diff --git a/cipher/cipher-internal.h b/cipher/cipher-internal.h
index b60ef38..fbaef4f 100644
--- a/cipher/cipher-internal.h
+++ b/cipher/cipher-internal.h
@@ -100,7 +100,8 @@ struct gcry_cipher_handle
 
   /* The initialization vector.  For best performance we make sure
      that it is properly aligned.  In particular some implementations
-     of bulk operations expect an 16 byte aligned IV.  */
+     of bulk operations expect an 16 byte aligned IV.  IV is also used
+     to store CBC-MAC in CCM mode; counter IV is stored in U_CTR.  */
   union {
     cipher_context_alignment_t iv_align;
     unsigned char iv[MAX_BLOCKSIZE];
@@ -117,6 +118,26 @@ struct gcry_cipher_handle
   unsigned char lastiv[MAX_BLOCKSIZE];
   int unused;  /* Number of unused bytes in LASTIV. */
 
+  union {
+    /* Mode specific storage for CCM mode. */
+    struct {
+      size_t encryptlen;
+      size_t aadlen;
+      unsigned int authlen;
+
+      /* Space to save partial input lengths for MAC. */
+      unsigned char macbuf[GCRY_CCM_BLOCK_LEN];
+      int mac_unused;  /* Number of unprocessed bytes in MACBUF. */
+
+      unsigned char s0[GCRY_CCM_BLOCK_LEN];
+
+      unsigned int nonce:1;/* Set to 1 if nonce has been set.  */
+      unsigned int params:1; /* Set to 1 if CCM parameters has been
+                                processed.  */
+      unsigned int tag:1; /* Set to 1 if tag has been finalized.  */
+    } ccm;
+  } u_mode;
+
   /* What follows are two contexts of the cipher in use.  The first
      one needs to be aligned well enough for the cipher operation
      whereas the second one is a copy created by cipher_setkey and
@@ -175,5 +196,30 @@ gcry_err_code_t _gcry_cipher_aeswrap_decrypt
                    const byte *inbuf, unsigned int inbuflen);
 
 
+/*-- cipher-ccm.c --*/
+gcry_err_code_t _gcry_cipher_ccm_encrypt
+/*           */ (gcry_cipher_hd_t c,
+                 unsigned char *outbuf, unsigned int outbuflen,
+                 const unsigned char *inbuf, unsigned int inbuflen);
+gcry_err_code_t _gcry_cipher_ccm_decrypt
+/*           */ (gcry_cipher_hd_t c,
+                 unsigned char *outbuf, unsigned int outbuflen,
+                 const unsigned char *inbuf, unsigned int inbuflen);
+gcry_err_code_t _gcry_cipher_ccm_set_nonce
+/*           */ (gcry_cipher_hd_t c, const unsigned char *nonce,
+                 size_t noncelen);
+gcry_err_code_t _gcry_cipher_ccm_authenticate
+/*           */ (gcry_cipher_hd_t c, const unsigned char *abuf, size_t abuflen);
+gcry_err_code_t _gcry_cipher_ccm_set_params
+/*           */ (gcry_cipher_hd_t c, size_t encryptedlen, size_t aadlen,
+                 size_t taglen);
+gcry_err_code_t _gcry_cipher_ccm_get_tag
+/*           */ (gcry_cipher_hd_t c,
+                 unsigned char *outtag, size_t taglen);
+gcry_err_code_t _gcry_cipher_ccm_check_tag
+/*           */ (gcry_cipher_hd_t c,
+                 const unsigned char *intag, size_t taglen);
+
+
 
 #endif /*G10_CIPHER_INTERNAL_H*/
diff --git a/cipher/cipher.c b/cipher/cipher.c
index 36c79db..db7f505 100644
--- a/cipher/cipher.c
+++ b/cipher/cipher.c
@@ -375,6 +375,13 @@ gcry_cipher_open (gcry_cipher_hd_t *handle,
   if (! err)
     switch (mode)
       {
+      case GCRY_CIPHER_MODE_CCM:
+	if (spec->blocksize != GCRY_CCM_BLOCK_LEN)
+	  err = GPG_ERR_INV_CIPHER_MODE;
+	if (!spec->encrypt || !spec->decrypt)
+	  err = GPG_ERR_INV_CIPHER_MODE;
+	break;
+
       case GCRY_CIPHER_MODE_ECB:
       case GCRY_CIPHER_MODE_CBC:
       case GCRY_CIPHER_MODE_CFB:
@@ -613,6 +620,8 @@ cipher_reset (gcry_cipher_hd_t c)
   memset (c->u_iv.iv, 0, c->spec->blocksize);
   memset (c->lastiv, 0, c->spec->blocksize);
   memset (c->u_ctr.ctr, 0, c->spec->blocksize);
+  memset (&c->u_mode, 0, sizeof c->u_mode);
+  c->unused = 0;
 }
 
 
@@ -718,6 +727,10 @@ cipher_encrypt (gcry_cipher_hd_t c, byte *outbuf, unsigned int outbuflen,
                                          inbuf, inbuflen);
       break;
 
+    case GCRY_CIPHER_MODE_CCM:
+      rc = _gcry_cipher_ccm_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+      break;
+
     case GCRY_CIPHER_MODE_STREAM:
       c->spec->stencrypt (&c->context.c,
                           outbuf, (byte*)/*arggg*/inbuf, inbuflen);
@@ -811,6 +824,10 @@ cipher_decrypt (gcry_cipher_hd_t c, byte *outbuf, unsigned int outbuflen,
                                          inbuf, inbuflen);
       break;
 
+    case GCRY_CIPHER_MODE_CCM:
+      rc = _gcry_cipher_ccm_decrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+      break;
+
     case GCRY_CIPHER_MODE_STREAM:
       c->spec->stdecrypt (&c->context.c,
                           outbuf, (byte*)/*arggg*/inbuf, inbuflen);
@@ -885,8 +902,19 @@ _gcry_cipher_setkey (gcry_cipher_hd_t hd, const void *key, size_t keylen)
 gcry_error_t
 _gcry_cipher_setiv (gcry_cipher_hd_t hd, const void *iv, size_t ivlen)
 {
-  cipher_setiv (hd, iv, ivlen);
-  return 0;
+  gcry_err_code_t rc = GPG_ERR_NO_ERROR;
+
+  switch (hd->mode)
+    {
+      case GCRY_CIPHER_MODE_CCM:
+        rc = _gcry_cipher_ccm_set_nonce (hd, iv, ivlen);
+        break;
+
+      default:
+        cipher_setiv (hd, iv, ivlen);
+        break;
+    }
+  return gpg_error (rc);
 }
 
 /* Set counter for CTR mode.  (CTR,CTRLEN) must denote a buffer of
@@ -914,34 +942,61 @@ gcry_error_t
 _gcry_cipher_authenticate (gcry_cipher_hd_t hd, const void *abuf,
                            size_t abuflen)
 {
-  log_fatal ("gcry_cipher_authenticate: invalid mode %d\n", hd->mode);
+  gcry_err_code_t rc;
+
+  switch (hd->mode)
+    {
+    case GCRY_CIPHER_MODE_CCM:
+      rc = _gcry_cipher_ccm_authenticate (hd, abuf, abuflen);
+      break;
 
-  (void)abuf;
-  (void)abuflen;
+    default:
+      log_fatal ("gcry_cipher_authenticate: invalid mode %d\n", hd->mode);
+      rc = GPG_ERR_INV_CIPHER_MODE;
+      break;
+    }
 
-  return gpg_error (GPG_ERR_INV_CIPHER_MODE);
+  return gpg_error (rc);
 }
 
 gcry_error_t
 _gcry_cipher_gettag (gcry_cipher_hd_t hd, void *outtag, size_t taglen)
 {
-  log_fatal ("gcry_cipher_gettag: invalid mode %d\n", hd->mode);
+  gcry_err_code_t rc;
+
+  switch (hd->mode)
+    {
+    case GCRY_CIPHER_MODE_CCM:
+      rc = _gcry_cipher_ccm_get_tag (hd, outtag, taglen);
+      break;
 
-  (void)outtag;
-  (void)taglen;
+    default:
+      log_fatal ("gcry_cipher_gettag: invalid mode %d\n", hd->mode);
+      rc = GPG_ERR_INV_CIPHER_MODE;
+      break;
+    }
 
-  return gpg_error (GPG_ERR_INV_CIPHER_MODE);
+  return gpg_error (rc);
 }
 
 gcry_error_t
 _gcry_cipher_checktag (gcry_cipher_hd_t hd, const void *intag, size_t taglen)
 {
-  log_fatal ("gcry_cipher_checktag: invalid mode %d\n", hd->mode);
+  gcry_err_code_t rc;
+
+  switch (hd->mode)
+    {
+    case GCRY_CIPHER_MODE_CCM:
+      rc = _gcry_cipher_ccm_check_tag (hd, intag, taglen);
+      break;
 
-  (void)intag;
-  (void)taglen;
+    default:
+      log_fatal ("gcry_cipher_checktag: invalid mode %d\n", hd->mode);
+      rc = GPG_ERR_INV_CIPHER_MODE;
+      break;
+    }
 
-  return gpg_error (GPG_ERR_INV_CIPHER_MODE);
+  return gpg_error (rc);
 }
 
 
@@ -980,6 +1035,30 @@ gcry_cipher_ctl( gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
 	h->flags &= ~GCRY_CIPHER_CBC_MAC;
       break;
 
+    case GCRYCTL_SET_CCM_PARAMS:
+      {
+        size_t params[3];
+        size_t encryptedlen;
+        size_t aadlen;
+        size_t authtaglen;
+
+        if (h->mode != GCRY_CIPHER_MODE_CCM)
+          return gcry_error (GPG_ERR_INV_CIPHER_MODE);
+
+        if (!buffer || buflen != 3 * sizeof(size_t))
+          return gcry_error (GPG_ERR_INV_ARG);
+
+        /* This command is used to pass additional length parameters needed
+           by CCM mode to initialize CBC-MAC.  */
+        memcpy (params, buffer, sizeof(params));
+        encryptedlen = params[0];
+        aadlen = params[1];
+        authtaglen = params[2];
+
+        rc = _gcry_cipher_ccm_set_params (h, encryptedlen, aadlen, authtaglen);
+      }
+      break;
+
     case GCRYCTL_DISABLE_ALGO:
       /* This command expects NULL for H and BUFFER to point to an
          integer with the algo number.  */
diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi
index 0049fa0..91fe399 100644
--- a/doc/gcrypt.texi
+++ b/doc/gcrypt.texi
@@ -1635,6 +1635,12 @@ may be specified 64 bit (8 byte) shorter than then input buffer.  As
 per specs the input length must be at least 128 bits and the length
 must be a multiple of 64 bits.
 
+ at item  GCRY_CIPHER_MODE_CCM
+ at cindex CCM, Counter with CBC-MAC mode
+Counter with CBC-MAC mode is an Authenticated Encryption with
+Associated Data (AEAD) block cipher mode, which is specified in
+'NIST Special Publication 800-38C' and RFC 3610.
+
 @end table
 
 @node Working with cipher handles
@@ -1661,11 +1667,13 @@ The cipher mode to use must be specified via @var{mode}.  See
 @xref{Available cipher modes}, for a list of supported cipher modes
 and the according constants.  Note that some modes are incompatible
 with some algorithms - in particular, stream mode
-(@code{GCRY_CIPHER_MODE_STREAM}) only works with stream ciphers. Any
-block cipher mode (@code{GCRY_CIPHER_MODE_ECB},
+(@code{GCRY_CIPHER_MODE_STREAM}) only works with stream ciphers. The
+block cipher modes (@code{GCRY_CIPHER_MODE_ECB},
 @code{GCRY_CIPHER_MODE_CBC}, @code{GCRY_CIPHER_MODE_CFB},
- at code{GCRY_CIPHER_MODE_OFB} or @code{GCRY_CIPHER_MODE_CTR}) will work
-with any block cipher algorithm.
+ at code{GCRY_CIPHER_MODE_OFB} and @code{GCRY_CIPHER_MODE_CTR}) will work
+with any block cipher algorithm. The @code{GCRY_CIPHER_MODE_CCM} 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 f0ae927..63bfd24 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -325,7 +325,8 @@ enum gcry_ctl_cmds
     GCRYCTL_SET_PREFERRED_RNG_TYPE = 65,
     GCRYCTL_GET_CURRENT_RNG_TYPE = 66,
     GCRYCTL_DISABLE_LOCKED_SECMEM = 67,
-    GCRYCTL_DISABLE_PRIV_DROP = 68
+    GCRYCTL_DISABLE_PRIV_DROP = 68,
+    GCRYCTL_SET_CCM_PARAMS = 69
   };
 
 /* Perform various operations defined by CMD. */
@@ -884,7 +885,8 @@ enum gcry_cipher_modes
     GCRY_CIPHER_MODE_STREAM = 4,  /* Used with stream ciphers. */
     GCRY_CIPHER_MODE_OFB    = 5,  /* Outer feedback. */
     GCRY_CIPHER_MODE_CTR    = 6,  /* Counter. */
-    GCRY_CIPHER_MODE_AESWRAP= 7   /* AES-WRAP algorithm.  */
+    GCRY_CIPHER_MODE_AESWRAP= 7,  /* AES-WRAP algorithm.  */
+    GCRY_CIPHER_MODE_CCM    = 8   /* Counter with CBC-MAC.  */
   };
 
 /* Flags used with the open function. */
@@ -896,6 +898,8 @@ enum gcry_cipher_flags
     GCRY_CIPHER_CBC_MAC     = 8   /* Enable CBC message auth. code (MAC). */
   };
 
+/* CCM works only with blocks of 128 bits.  */
+#define GCRY_CCM_BLOCK_LEN  (128 / 8)
 
 /* Create a handle for algorithm ALGO to be used in MODE.  FLAGS may
    be given as an bitwise OR of the gcry_cipher_flags values. */
diff --git a/tests/basic.c b/tests/basic.c
index 1d6e637..f7771e1 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -1139,6 +1139,776 @@ check_ofb_cipher (void)
 
 
 static void
+check_ccm_cipher (void)
+{
+  static const struct tv
+  {
+    int algo;
+    int keylen;
+    const char *key;
+    int noncelen;
+    const char *nonce;
+    int aadlen;
+    const char *aad;
+    int plainlen;
+    const char *plaintext;
+    int cipherlen;
+    const char *ciphertext;
+  } tv[] =
+    {
+      /* RFC 3610 */
+      { GCRY_CIPHER_AES, /* Packet Vector #1 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x03\x02\x01\x00\xA0\xA1\xA2\xA3\xA4\xA5",
+          8, "\x00\x01\x02\x03\x04\x05\x06\x07",
+          23,
+          "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E",
+          31,
+          "\x58\x8C\x97\x9A\x61\xC6\x63\xD2\xF0\x66\xD0\xC2\xC0\xF9\x89\x80\x6D\x5F\x6B\x61\xDA\xC3\x84\x17\xE8\xD1\x2C\xFD\xF9\x26\xE0"},
+      { GCRY_CIPHER_AES, /* Packet Vector #2 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x04\x03\x02\x01\xA0\xA1\xA2\xA3\xA4\xA5",
+          8, "\x00\x01\x02\x03\x04\x05\x06\x07",
+          24,
+          "\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,
+          "\x72\xC9\x1A\x36\xE1\x35\xF8\xCF\x29\x1C\xA8\x94\x08\x5C\x87\xE3\xCC\x15\xC4\x39\xC9\xE4\x3A\x3B\xA0\x91\xD5\x6E\x10\x40\x09\x16"},
+      { GCRY_CIPHER_AES, /* Packet Vector #3 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x05\x04\x03\x02\xA0\xA1\xA2\xA3\xA4\xA5",
+          8, "\x00\x01\x02\x03\x04\x05\x06\x07",
+          25,
+          "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20",
+          33,
+          "\x51\xB1\xE5\xF4\x4A\x19\x7D\x1D\xA4\x6B\x0F\x8E\x2D\x28\x2A\xE8\x71\xE8\x38\xBB\x64\xDA\x85\x96\x57\x4A\xDA\xA7\x6F\xBD\x9F\xB0\xC5"},
+      { GCRY_CIPHER_AES, /* Packet Vector #4 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x06\x05\x04\x03\xA0\xA1\xA2\xA3\xA4\xA5",
+          12, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B",
+          19,
+          "\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E",
+          27,
+          "\xA2\x8C\x68\x65\x93\x9A\x9A\x79\xFA\xAA\x5C\x4C\x2A\x9D\x4A\x91\xCD\xAC\x8C\x96\xC8\x61\xB9\xC9\xE6\x1E\xF1"},
+      { GCRY_CIPHER_AES, /* Packet Vector #5 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x07\x06\x05\x04\xA0\xA1\xA2\xA3\xA4\xA5",
+          12, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B",
+          20,
+          "\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
+          28,
+          "\xDC\xF1\xFB\x7B\x5D\x9E\x23\xFB\x9D\x4E\x13\x12\x53\x65\x8A\xD8\x6E\xBD\xCA\x3E\x51\xE8\x3F\x07\x7D\x9C\x2D\x93"},
+      { GCRY_CIPHER_AES, /* Packet Vector #6 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x08\x07\x06\x05\xA0\xA1\xA2\xA3\xA4\xA5",
+          12, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B",
+          21,
+          "\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20",
+          29,
+          "\x6F\xC1\xB0\x11\xF0\x06\x56\x8B\x51\x71\xA4\x2D\x95\x3D\x46\x9B\x25\x70\xA4\xBD\x87\x40\x5A\x04\x43\xAC\x91\xCB\x94"},
+      { GCRY_CIPHER_AES, /* Packet Vector #7 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x09\x08\x07\x06\xA0\xA1\xA2\xA3\xA4\xA5",
+          8, "\x00\x01\x02\x03\x04\x05\x06\x07",
+          23,
+          "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E",
+          33,
+          "\x01\x35\xD1\xB2\xC9\x5F\x41\xD5\xD1\xD4\xFE\xC1\x85\xD1\x66\xB8\x09\x4E\x99\x9D\xFE\xD9\x6C\x04\x8C\x56\x60\x2C\x97\xAC\xBB\x74\x90"},
+      { GCRY_CIPHER_AES, /* Packet Vector #8 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x0A\x09\x08\x07\xA0\xA1\xA2\xA3\xA4\xA5",
+          8, "\x00\x01\x02\x03\x04\x05\x06\x07",
+          24,
+          "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
+          34,
+          "\x7B\x75\x39\x9A\xC0\x83\x1D\xD2\xF0\xBB\xD7\x58\x79\xA2\xFD\x8F\x6C\xAE\x6B\x6C\xD9\xB7\xDB\x24\xC1\x7B\x44\x33\xF4\x34\x96\x3F\x34\xB4"},
+      { GCRY_CIPHER_AES, /* Packet Vector #9 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x0B\x0A\x09\x08\xA0\xA1\xA2\xA3\xA4\xA5",
+          8, "\x00\x01\x02\x03\x04\x05\x06\x07",
+          25,
+          "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20",
+          35,
+          "\x82\x53\x1A\x60\xCC\x24\x94\x5A\x4B\x82\x79\x18\x1A\xB5\xC8\x4D\xF2\x1C\xE7\xF9\xB7\x3F\x42\xE1\x97\xEA\x9C\x07\xE5\x6B\x5E\xB1\x7E\x5F\x4E"},
+      { GCRY_CIPHER_AES, /* Packet Vector #10 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x0C\x0B\x0A\x09\xA0\xA1\xA2\xA3\xA4\xA5",
+          12, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B",
+          19,
+          "\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E",
+          29,
+          "\x07\x34\x25\x94\x15\x77\x85\x15\x2B\x07\x40\x98\x33\x0A\xBB\x14\x1B\x94\x7B\x56\x6A\xA9\x40\x6B\x4D\x99\x99\x88\xDD"},
+      { GCRY_CIPHER_AES, /* Packet Vector #11 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x0D\x0C\x0B\x0A\xA0\xA1\xA2\xA3\xA4\xA5",
+          12, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B",
+          20,
+          "\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
+          30,
+          "\x67\x6B\xB2\x03\x80\xB0\xE3\x01\xE8\xAB\x79\x59\x0A\x39\x6D\xA7\x8B\x83\x49\x34\xF5\x3A\xA2\xE9\x10\x7A\x8B\x6C\x02\x2C"},
+      { GCRY_CIPHER_AES, /* Packet Vector #12 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x0E\x0D\x0C\x0B\xA0\xA1\xA2\xA3\xA4\xA5",
+          12, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B",
+          21,
+          "\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20",
+          31,
+          "\xC0\xFF\xA0\xD6\xF0\x5B\xDB\x67\xF2\x4D\x43\xA4\x33\x8D\x2A\xA4\xBE\xD7\xB2\x0E\x43\xCD\x1A\xA3\x16\x62\xE7\xAD\x65\xD6\xDB"},
+      { GCRY_CIPHER_AES, /* Packet Vector #13 */
+          16, "\xD7\x82\x8D\x13\xB2\xB0\xBD\xC3\x25\xA7\x62\x36\xDF\x93\xCC\x6B",
+          13, "\x00\x41\x2B\x4E\xA9\xCD\xBE\x3C\x96\x96\x76\x6C\xFA",
+          8, "\x0B\xE1\xA8\x8B\xAC\xE0\x18\xB1",
+          23,
+          "\x08\xE8\xCF\x97\xD8\x20\xEA\x25\x84\x60\xE9\x6A\xD9\xCF\x52\x89\x05\x4D\x89\x5C\xEA\xC4\x7C",
+          31,
+          "\x4C\xB9\x7F\x86\xA2\xA4\x68\x9A\x87\x79\x47\xAB\x80\x91\xEF\x53\x86\xA6\xFF\xBD\xD0\x80\xF8\xE7\x8C\xF7\xCB\x0C\xDD\xD7\xB3"},
+      { GCRY_CIPHER_AES, /* Packet Vector #14 */
+          16, "\xD7\x82\x8D\x13\xB2\xB0\xBD\xC3\x25\xA7\x62\x36\xDF\x93\xCC\x6B",
+          13, "\x00\x33\x56\x8E\xF7\xB2\x63\x3C\x96\x96\x76\x6C\xFA",
+          8, "\x63\x01\x8F\x76\xDC\x8A\x1B\xCB",
+          24,
+          "\x90\x20\xEA\x6F\x91\xBD\xD8\x5A\xFA\x00\x39\xBA\x4B\xAF\xF9\xBF\xB7\x9C\x70\x28\x94\x9C\xD0\xEC",
+          32,
+          "\x4C\xCB\x1E\x7C\xA9\x81\xBE\xFA\xA0\x72\x6C\x55\xD3\x78\x06\x12\x98\xC8\x5C\x92\x81\x4A\xBC\x33\xC5\x2E\xE8\x1D\x7D\x77\xC0\x8A"},
+      { GCRY_CIPHER_AES, /* Packet Vector #15 */
+          16, "\xD7\x82\x8D\x13\xB2\xB0\xBD\xC3\x25\xA7\x62\x36\xDF\x93\xCC\x6B",
+          13, "\x00\x10\x3F\xE4\x13\x36\x71\x3C\x96\x96\x76\x6C\xFA",
+          8, "\xAA\x6C\xFA\x36\xCA\xE8\x6B\x40",
+          25,
+          "\xB9\x16\xE0\xEA\xCC\x1C\x00\xD7\xDC\xEC\x68\xEC\x0B\x3B\xBB\x1A\x02\xDE\x8A\x2D\x1A\xA3\x46\x13\x2E",
+          33,
+          "\xB1\xD2\x3A\x22\x20\xDD\xC0\xAC\x90\x0D\x9A\xA0\x3C\x61\xFC\xF4\xA5\x59\xA4\x41\x77\x67\x08\x97\x08\xA7\x76\x79\x6E\xDB\x72\x35\x06"},
+      { GCRY_CIPHER_AES, /* Packet Vector #16 */
+          16, "\xD7\x82\x8D\x13\xB2\xB0\xBD\xC3\x25\xA7\x62\x36\xDF\x93\xCC\x6B",
+          13, "\x00\x76\x4C\x63\xB8\x05\x8E\x3C\x96\x96\x76\x6C\xFA",
+          12, "\xD0\xD0\x73\x5C\x53\x1E\x1B\xEC\xF0\x49\xC2\x44",
+          19,
+          "\x12\xDA\xAC\x56\x30\xEF\xA5\x39\x6F\x77\x0C\xE1\xA6\x6B\x21\xF7\xB2\x10\x1C",
+          27,
+          "\x14\xD2\x53\xC3\x96\x7B\x70\x60\x9B\x7C\xBB\x7C\x49\x91\x60\x28\x32\x45\x26\x9A\x6F\x49\x97\x5B\xCA\xDE\xAF"},
+      { GCRY_CIPHER_AES, /* Packet Vector #17 */
+          16, "\xD7\x82\x8D\x13\xB2\xB0\xBD\xC3\x25\xA7\x62\x36\xDF\x93\xCC\x6B",
+          13, "\x00\xF8\xB6\x78\x09\x4E\x3B\x3C\x96\x96\x76\x6C\xFA",
+          12, "\x77\xB6\x0F\x01\x1C\x03\xE1\x52\x58\x99\xBC\xAE",
+          20,
+          "\xE8\x8B\x6A\x46\xC7\x8D\x63\xE5\x2E\xB8\xC5\x46\xEF\xB5\xDE\x6F\x75\xE9\xCC\x0D",
+          28,
+          "\x55\x45\xFF\x1A\x08\x5E\xE2\xEF\xBF\x52\xB2\xE0\x4B\xEE\x1E\x23\x36\xC7\x3E\x3F\x76\x2C\x0C\x77\x44\xFE\x7E\x3C"},
+      { GCRY_CIPHER_AES, /* Packet Vector #18 */
+          16, "\xD7\x82\x8D\x13\xB2\xB0\xBD\xC3\x25\xA7\x62\x36\xDF\x93\xCC\x6B",
+          13, "\x00\xD5\x60\x91\x2D\x3F\x70\x3C\x96\x96\x76\x6C\xFA",
+          12, "\xCD\x90\x44\xD2\xB7\x1F\xDB\x81\x20\xEA\x60\xC0",
+          21,
+          "\x64\x35\xAC\xBA\xFB\x11\xA8\x2E\x2F\x07\x1D\x7C\xA4\xA5\xEB\xD9\x3A\x80\x3B\xA8\x7F",
+          29,
+          "\x00\x97\x69\xEC\xAB\xDF\x48\x62\x55\x94\xC5\x92\x51\xE6\x03\x57\x22\x67\x5E\x04\xC8\x47\x09\x9E\x5A\xE0\x70\x45\x51"},
+      { GCRY_CIPHER_AES, /* Packet Vector #19 */
+          16, "\xD7\x82\x8D\x13\xB2\xB0\xBD\xC3\x25\xA7\x62\x36\xDF\x93\xCC\x6B",
+          13, "\x00\x42\xFF\xF8\xF1\x95\x1C\x3C\x96\x96\x76\x6C\xFA",
+          8, "\xD8\x5B\xC7\xE6\x9F\x94\x4F\xB8",
+          23,
+          "\x8A\x19\xB9\x50\xBC\xF7\x1A\x01\x8E\x5E\x67\x01\xC9\x17\x87\x65\x98\x09\xD6\x7D\xBE\xDD\x18",
+          33,
+          "\xBC\x21\x8D\xAA\x94\x74\x27\xB6\xDB\x38\x6A\x99\xAC\x1A\xEF\x23\xAD\xE0\xB5\x29\x39\xCB\x6A\x63\x7C\xF9\xBE\xC2\x40\x88\x97\xC6\xBA"},
+      { GCRY_CIPHER_AES, /* Packet Vector #20 */
+          16, "\xD7\x82\x8D\x13\xB2\xB0\xBD\xC3\x25\xA7\x62\x36\xDF\x93\xCC\x6B",
+          13, "\x00\x92\x0F\x40\xE5\x6C\xDC\x3C\x96\x96\x76\x6C\xFA",
+          8, "\x74\xA0\xEB\xC9\x06\x9F\x5B\x37",
+          24,
+          "\x17\x61\x43\x3C\x37\xC5\xA3\x5F\xC1\xF3\x9F\x40\x63\x02\xEB\x90\x7C\x61\x63\xBE\x38\xC9\x84\x37",
+          34,
+          "\x58\x10\xE6\xFD\x25\x87\x40\x22\xE8\x03\x61\xA4\x78\xE3\xE9\xCF\x48\x4A\xB0\x4F\x44\x7E\xFF\xF6\xF0\xA4\x77\xCC\x2F\xC9\xBF\x54\x89\x44"},
+      { GCRY_CIPHER_AES, /* Packet Vector #21 */
+          16, "\xD7\x82\x8D\x13\xB2\xB0\xBD\xC3\x25\xA7\x62\x36\xDF\x93\xCC\x6B",
+          13, "\x00\x27\xCA\x0C\x71\x20\xBC\x3C\x96\x96\x76\x6C\xFA",
+          8, "\x44\xA3\xAA\x3A\xAE\x64\x75\xCA",
+          25,
+          "\xA4\x34\xA8\xE5\x85\x00\xC6\xE4\x15\x30\x53\x88\x62\xD6\x86\xEA\x9E\x81\x30\x1B\x5A\xE4\x22\x6B\xFA",
+          35,
+          "\xF2\xBE\xED\x7B\xC5\x09\x8E\x83\xFE\xB5\xB3\x16\x08\xF8\xE2\x9C\x38\x81\x9A\x89\xC8\xE7\x76\xF1\x54\x4D\x41\x51\xA4\xED\x3A\x8B\x87\xB9\xCE"},
+      { GCRY_CIPHER_AES, /* Packet Vector #22 */
+          16, "\xD7\x82\x8D\x13\xB2\xB0\xBD\xC3\x25\xA7\x62\x36\xDF\x93\xCC\x6B",
+          13, "\x00\x5B\x8C\xCB\xCD\x9A\xF8\x3C\x96\x96\x76\x6C\xFA",
+          12, "\xEC\x46\xBB\x63\xB0\x25\x20\xC3\x3C\x49\xFD\x70",
+          19,
+          "\xB9\x6B\x49\xE2\x1D\x62\x17\x41\x63\x28\x75\xDB\x7F\x6C\x92\x43\xD2\xD7\xC2",
+          29,
+          "\x31\xD7\x50\xA0\x9D\xA3\xED\x7F\xDD\xD4\x9A\x20\x32\xAA\xBF\x17\xEC\x8E\xBF\x7D\x22\xC8\x08\x8C\x66\x6B\xE5\xC1\x97"},
+      { GCRY_CIPHER_AES, /* Packet Vector #23 */
+          16, "\xD7\x82\x8D\x13\xB2\xB0\xBD\xC3\x25\xA7\x62\x36\xDF\x93\xCC\x6B",
+          13, "\x00\x3E\xBE\x94\x04\x4B\x9A\x3C\x96\x96\x76\x6C\xFA",
+          12, "\x47\xA6\x5A\xC7\x8B\x3D\x59\x42\x27\xE8\x5E\x71",
+          20,
+          "\xE2\xFC\xFB\xB8\x80\x44\x2C\x73\x1B\xF9\x51\x67\xC8\xFF\xD7\x89\x5E\x33\x70\x76",
+          30,
+          "\xE8\x82\xF1\xDB\xD3\x8C\xE3\xED\xA7\xC2\x3F\x04\xDD\x65\x07\x1E\xB4\x13\x42\xAC\xDF\x7E\x00\xDC\xCE\xC7\xAE\x52\x98\x7D"},
+      { GCRY_CIPHER_AES, /* Packet Vector #24 */
+          16, "\xD7\x82\x8D\x13\xB2\xB0\xBD\xC3\x25\xA7\x62\x36\xDF\x93\xCC\x6B",
+          13, "\x00\x8D\x49\x3B\x30\xAE\x8B\x3C\x96\x96\x76\x6C\xFA",
+          12, "\x6E\x37\xA6\xEF\x54\x6D\x95\x5D\x34\xAB\x60\x59",
+          21,
+          "\xAB\xF2\x1C\x0B\x02\xFE\xB8\x8F\x85\x6D\xF4\xA3\x73\x81\xBC\xE3\xCC\x12\x85\x17\xD4",
+          31,
+          "\xF3\x29\x05\xB8\x8A\x64\x1B\x04\xB9\xC9\xFF\xB5\x8C\xC3\x90\x90\x0F\x3D\xA1\x2A\xB1\x6D\xCE\x9E\x82\xEF\xA1\x6D\xA6\x20\x59"},
+      /* RFC 5528 */
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #1 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x03\x02\x01\x00\xA0\xA1\xA2\xA3\xA4\xA5",
+          8, "\x00\x01\x02\x03\x04\x05\x06\x07",
+          23,
+          "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E",
+          31,
+          "\xBA\x73\x71\x85\xE7\x19\x31\x04\x92\xF3\x8A\x5F\x12\x51\xDA\x55\xFA\xFB\xC9\x49\x84\x8A\x0D\xFC\xAE\xCE\x74\x6B\x3D\xB9\xAD"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #2 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x04\x03\x02\x01\xA0\xA1\xA2\xA3\xA4\xA5",
+          8, "\x00\x01\x02\x03\x04\x05\x06\x07",
+          24,
+          "\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,
+          "\x5D\x25\x64\xBF\x8E\xAF\xE1\xD9\x95\x26\xEC\x01\x6D\x1B\xF0\x42\x4C\xFB\xD2\xCD\x62\x84\x8F\x33\x60\xB2\x29\x5D\xF2\x42\x83\xE8"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #3 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x05\x04\x03\x02\xA0\xA1\xA2\xA3\xA4\xA5",
+          8, "\x00\x01\x02\x03\x04\x05\x06\x07",
+          25,
+          "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20",
+          33,
+          "\x81\xF6\x63\xD6\xC7\x78\x78\x17\xF9\x20\x36\x08\xB9\x82\xAD\x15\xDC\x2B\xBD\x87\xD7\x56\xF7\x92\x04\xF5\x51\xD6\x68\x2F\x23\xAA\x46"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #4 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x06\x05\x04\x03\xA0\xA1\xA2\xA3\xA4\xA5",
+          12, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B",
+          19,
+          "\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E",
+          27,
+          "\xCA\xEF\x1E\x82\x72\x11\xB0\x8F\x7B\xD9\x0F\x08\xC7\x72\x88\xC0\x70\xA4\xA0\x8B\x3A\x93\x3A\x63\xE4\x97\xA0"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #5 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x07\x06\x05\x04\xA0\xA1\xA2\xA3\xA4\xA5",
+          12, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B",
+          20,
+          "\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
+          28,
+          "\x2A\xD3\xBA\xD9\x4F\xC5\x2E\x92\xBE\x43\x8E\x82\x7C\x10\x23\xB9\x6A\x8A\x77\x25\x8F\xA1\x7B\xA7\xF3\x31\xDB\x09"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #6 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x08\x07\x06\x05\xA0\xA1\xA2\xA3\xA4\xA5",
+          12, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B",
+          21,
+          "\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20",
+          29,
+          "\xFE\xA5\x48\x0B\xA5\x3F\xA8\xD3\xC3\x44\x22\xAA\xCE\x4D\xE6\x7F\xFA\x3B\xB7\x3B\xAB\xAB\x36\xA1\xEE\x4F\xE0\xFE\x28"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #7 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x09\x08\x07\x06\xA0\xA1\xA2\xA3\xA4\xA5",
+          8, "\x00\x01\x02\x03\x04\x05\x06\x07",
+          23,
+          "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E",
+          33,
+          "\x54\x53\x20\x26\xE5\x4C\x11\x9A\x8D\x36\xD9\xEC\x6E\x1E\xD9\x74\x16\xC8\x70\x8C\x4B\x5C\x2C\xAC\xAF\xA3\xBC\xCF\x7A\x4E\xBF\x95\x73"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #8 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x0A\x09\x08\x07\xA0\xA1\xA2\xA3\xA4\xA5",
+          8, "\x00\x01\x02\x03\x04\x05\x06\x07",
+          24,
+          "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
+          34,
+          "\x8A\xD1\x9B\x00\x1A\x87\xD1\x48\xF4\xD9\x2B\xEF\x34\x52\x5C\xCC\xE3\xA6\x3C\x65\x12\xA6\xF5\x75\x73\x88\xE4\x91\x3E\xF1\x47\x01\xF4\x41"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #9 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x0B\x0A\x09\x08\xA0\xA1\xA2\xA3\xA4\xA5",
+          8, "\x00\x01\x02\x03\x04\x05\x06\x07",
+          25,
+          "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20",
+          35,
+          "\x5D\xB0\x8D\x62\x40\x7E\x6E\x31\xD6\x0F\x9C\xA2\xC6\x04\x74\x21\x9A\xC0\xBE\x50\xC0\xD4\xA5\x77\x87\x94\xD6\xE2\x30\xCD\x25\xC9\xFE\xBF\x87"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #10 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x0C\x0B\x0A\x09\xA0\xA1\xA2\xA3\xA4\xA5",
+          12, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B",
+          19,
+          "\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E",
+          29,
+          "\xDB\x11\x8C\xCE\xC1\xB8\x76\x1C\x87\x7C\xD8\x96\x3A\x67\xD6\xF3\xBB\xBC\x5C\xD0\x92\x99\xEB\x11\xF3\x12\xF2\x32\x37"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #11 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x0D\x0C\x0B\x0A\xA0\xA1\xA2\xA3\xA4\xA5",
+          12, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B",
+          20,
+          "\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
+          30,
+          "\x7C\xC8\x3D\x8D\xC4\x91\x03\x52\x5B\x48\x3D\xC5\xCA\x7E\xA9\xAB\x81\x2B\x70\x56\x07\x9D\xAF\xFA\xDA\x16\xCC\xCF\x2C\x4E"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #12 */
+          16, "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
+          13, "\x00\x00\x00\x0E\x0D\x0C\x0B\xA0\xA1\xA2\xA3\xA4\xA5",
+          12, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B",
+          21,
+          "\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20",
+          31,
+          "\x2C\xD3\x5B\x88\x20\xD2\x3E\x7A\xA3\x51\xB0\xE9\x2F\xC7\x93\x67\x23\x8B\x2C\xC7\x48\xCB\xB9\x4C\x29\x47\x79\x3D\x64\xAF\x75"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #13 */
+          16, "\xD7\x5C\x27\x78\x07\x8C\xA9\x3D\x97\x1F\x96\xFD\xE7\x20\xF4\xCD",
+          13, "\x00\xA9\x70\x11\x0E\x19\x27\xB1\x60\xB6\xA3\x1C\x1C",
+          8, "\x6B\x7F\x46\x45\x07\xFA\xE4\x96",
+          23,
+          "\xC6\xB5\xF3\xE6\xCA\x23\x11\xAE\xF7\x47\x2B\x20\x3E\x73\x5E\xA5\x61\xAD\xB1\x7D\x56\xC5\xA3",
+          31,
+          "\xA4\x35\xD7\x27\x34\x8D\xDD\x22\x90\x7F\x7E\xB8\xF5\xFD\xBB\x4D\x93\x9D\xA6\x52\x4D\xB4\xF6\x45\x58\xC0\x2D\x25\xB1\x27\xEE"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #14 */
+          16, "\xD7\x5C\x27\x78\x07\x8C\xA9\x3D\x97\x1F\x96\xFD\xE7\x20\xF4\xCD",
+          13, "\x00\x83\xCD\x8C\xE0\xCB\x42\xB1\x60\xB6\xA3\x1C\x1C",
+          8, "\x98\x66\x05\xB4\x3D\xF1\x5D\xE7",
+          24,
+          "\x01\xF6\xCE\x67\x64\xC5\x74\x48\x3B\xB0\x2E\x6B\xBF\x1E\x0A\xBD\x26\xA2\x25\x72\xB4\xD8\x0E\xE7",
+          32,
+          "\x8A\xE0\x52\x50\x8F\xBE\xCA\x93\x2E\x34\x6F\x05\xE0\xDC\x0D\xFB\xCF\x93\x9E\xAF\xFA\x3E\x58\x7C\x86\x7D\x6E\x1C\x48\x70\x38\x06"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #15 */
+          16, "\xD7\x5C\x27\x78\x07\x8C\xA9\x3D\x97\x1F\x96\xFD\xE7\x20\xF4\xCD",
+          13, "\x00\x5F\x54\x95\x0B\x18\xF2\xB1\x60\xB6\xA3\x1C\x1C",
+          8, "\x48\xF2\xE7\xE1\xA7\x67\x1A\x51",
+          25,
+          "\xCD\xF1\xD8\x40\x6F\xC2\xE9\x01\x49\x53\x89\x70\x05\xFB\xFB\x8B\xA5\x72\x76\xF9\x24\x04\x60\x8E\x08",
+          33,
+          "\x08\xB6\x7E\xE2\x1C\x8B\xF2\x6E\x47\x3E\x40\x85\x99\xE9\xC0\x83\x6D\x6A\xF0\xBB\x18\xDF\x55\x46\x6C\xA8\x08\x78\xA7\x90\x47\x6D\xE5"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #16 */
+          16, "\xD7\x5C\x27\x78\x07\x8C\xA9\x3D\x97\x1F\x96\xFD\xE7\x20\xF4\xCD",
+          13, "\x00\xEC\x60\x08\x63\x31\x9A\xB1\x60\xB6\xA3\x1C\x1C",
+          12, "\xDE\x97\xDF\x3B\x8C\xBD\x6D\x8E\x50\x30\xDA\x4C",
+          19,
+          "\xB0\x05\xDC\xFA\x0B\x59\x18\x14\x26\xA9\x61\x68\x5A\x99\x3D\x8C\x43\x18\x5B",
+          27,
+          "\x63\xB7\x8B\x49\x67\xB1\x9E\xDB\xB7\x33\xCD\x11\x14\xF6\x4E\xB2\x26\x08\x93\x68\xC3\x54\x82\x8D\x95\x0C\xC5"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #17 */
+          16, "\xD7\x5C\x27\x78\x07\x8C\xA9\x3D\x97\x1F\x96\xFD\xE7\x20\xF4\xCD",
+          13, "\x00\x60\xCF\xF1\xA3\x1E\xA1\xB1\x60\xB6\xA3\x1C\x1C",
+          12, "\xA5\xEE\x93\xE4\x57\xDF\x05\x46\x6E\x78\x2D\xCF",
+          20,
+          "\x2E\x20\x21\x12\x98\x10\x5F\x12\x9D\x5E\xD9\x5B\x93\xF7\x2D\x30\xB2\xFA\xCC\xD7",
+          28,
+          "\x0B\xC6\xBB\xE2\xA8\xB9\x09\xF4\x62\x9E\xE6\xDC\x14\x8D\xA4\x44\x10\xE1\x8A\xF4\x31\x47\x38\x32\x76\xF6\x6A\x9F"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #18 */
+          16, "\xD7\x5C\x27\x78\x07\x8C\xA9\x3D\x97\x1F\x96\xFD\xE7\x20\xF4\xCD",
+          13, "\x00\x0F\x85\xCD\x99\x5C\x97\xB1\x60\xB6\xA3\x1C\x1C",
+          12, "\x24\xAA\x1B\xF9\xA5\xCD\x87\x61\x82\xA2\x50\x74",
+          21,
+          "\x26\x45\x94\x1E\x75\x63\x2D\x34\x91\xAF\x0F\xC0\xC9\x87\x6C\x3B\xE4\xAA\x74\x68\xC9",
+          29,
+          "\x22\x2A\xD6\x32\xFA\x31\xD6\xAF\x97\x0C\x34\x5F\x7E\x77\xCA\x3B\xD0\xDC\x25\xB3\x40\xA1\xA3\xD3\x1F\x8D\x4B\x44\xB7"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #19 */
+          16, "\xD7\x5C\x27\x78\x07\x8C\xA9\x3D\x97\x1F\x96\xFD\xE7\x20\xF4\xCD",
+          13, "\x00\xC2\x9B\x2C\xAA\xC4\xCD\xB1\x60\xB6\xA3\x1C\x1C",
+          8, "\x69\x19\x46\xB9\xCA\x07\xBE\x87",
+          23,
+          "\x07\x01\x35\xA6\x43\x7C\x9D\xB1\x20\xCD\x61\xD8\xF6\xC3\x9C\x3E\xA1\x25\xFD\x95\xA0\xD2\x3D",
+          33,
+          "\x05\xB8\xE1\xB9\xC4\x9C\xFD\x56\xCF\x13\x0A\xA6\x25\x1D\xC2\xEC\xC0\x6C\xCC\x50\x8F\xE6\x97\xA0\x06\x6D\x57\xC8\x4B\xEC\x18\x27\x68"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #20 */
+          16, "\xD7\x5C\x27\x78\x07\x8C\xA9\x3D\x97\x1F\x96\xFD\xE7\x20\xF4\xCD",
+          13, "\x00\x2C\x6B\x75\x95\xEE\x62\xB1\x60\xB6\xA3\x1C\x1C",
+          8, "\xD0\xC5\x4E\xCB\x84\x62\x7D\xC4",
+          24,
+          "\xC8\xC0\x88\x0E\x6C\x63\x6E\x20\x09\x3D\xD6\x59\x42\x17\xD2\xE1\x88\x77\xDB\x26\x4E\x71\xA5\xCC",
+          34,
+          "\x54\xCE\xB9\x68\xDE\xE2\x36\x11\x57\x5E\xC0\x03\xDF\xAA\x1C\xD4\x88\x49\xBD\xF5\xAE\x2E\xDB\x6B\x7F\xA7\x75\xB1\x50\xED\x43\x83\xC5\xA9"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #21 */
+          16, "\xD7\x5C\x27\x78\x07\x8C\xA9\x3D\x97\x1F\x96\xFD\xE7\x20\xF4\xCD",
+          13, "\x00\xC5\x3C\xD4\xC2\xAA\x24\xB1\x60\xB6\xA3\x1C\x1C",
+          8, "\xE2\x85\xE0\xE4\x80\x8C\xDA\x3D",
+          25,
+          "\xF7\x5D\xAA\x07\x10\xC4\xE6\x42\x97\x79\x4D\xC2\xB7\xD2\xA2\x07\x57\xB1\xAA\x4E\x44\x80\x02\xFF\xAB",
+          35,
+          "\xB1\x40\x45\x46\xBF\x66\x72\x10\xCA\x28\xE3\x09\xB3\x9B\xD6\xCA\x7E\x9F\xC8\x28\x5F\xE6\x98\xD4\x3C\xD2\x0A\x02\xE0\xBD\xCA\xED\x20\x10\xD3"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #22 */
+          16, "\xD7\x5C\x27\x78\x07\x8C\xA9\x3D\x97\x1F\x96\xFD\xE7\x20\xF4\xCD",
+          13, "\x00\xBE\xE9\x26\x7F\xBA\xDC\xB1\x60\xB6\xA3\x1C\x1C",
+          12, "\x6C\xAE\xF9\x94\x11\x41\x57\x0D\x7C\x81\x34\x05",
+          19,
+          "\xC2\x38\x82\x2F\xAC\x5F\x98\xFF\x92\x94\x05\xB0\xAD\x12\x7A\x4E\x41\x85\x4E",
+          29,
+          "\x94\xC8\x95\x9C\x11\x56\x9A\x29\x78\x31\xA7\x21\x00\x58\x57\xAB\x61\xB8\x7A\x2D\xEA\x09\x36\xB6\xEB\x5F\x62\x5F\x5D"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #23 */
+          16, "\xD7\x5C\x27\x78\x07\x8C\xA9\x3D\x97\x1F\x96\xFD\xE7\x20\xF4\xCD",
+          13, "\x00\xDF\xA8\xB1\x24\x50\x07\xB1\x60\xB6\xA3\x1C\x1C",
+          12, "\x36\xA5\x2C\xF1\x6B\x19\xA2\x03\x7A\xB7\x01\x1E",
+          20,
+          "\x4D\xBF\x3E\x77\x4A\xD2\x45\xE5\xD5\x89\x1F\x9D\x1C\x32\xA0\xAE\x02\x2C\x85\xD7",
+          30,
+          "\x58\x69\xE3\xAA\xD2\x44\x7C\x74\xE0\xFC\x05\xF9\xA4\xEA\x74\x57\x7F\x4D\xE8\xCA\x89\x24\x76\x42\x96\xAD\x04\x11\x9C\xE7"},
+      { GCRY_CIPHER_CAMELLIA128, /* Packet Vector #24 */
+          16, "\xD7\x5C\x27\x78\x07\x8C\xA9\x3D\x97\x1F\x96\xFD\xE7\x20\xF4\xCD",
+          13, "\x00\x3B\x8F\xD8\xD3\xA9\x37\xB1\x60\xB6\xA3\x1C\x1C",
+          12, "\xA4\xD4\x99\xF7\x84\x19\x72\x8C\x19\x17\x8B\x0C",
+          21,
+          "\x9D\xC9\xED\xAE\x2F\xF5\xDF\x86\x36\xE8\xC6\xDE\x0E\xED\x55\xF7\x86\x7E\x33\x33\x7D",
+          31,
+          "\x4B\x19\x81\x56\x39\x3B\x0F\x77\x96\x08\x6A\xAF\xB4\x54\xF8\xC3\xF0\x34\xCC\xA9\x66\x94\x5F\x1F\xCE\xA7\xE1\x1B\xEE\x6A\x2F"}
+    };
+  static const int cut[] = { 0, 1, 8, 10, 16, 19, -1 };
+  gcry_cipher_hd_t hde, hdd;
+  unsigned char out[MAX_DATA_LEN];
+  size_t ctl_params[3];
+  int split, aadsplit;
+  size_t j, i, keylen, blklen, authlen;
+  gcry_error_t err = 0;
+
+  if (verbose)
+    fprintf (stderr, "  Starting CCM checks.\n");
+
+  for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++)
+    {
+      if (verbose)
+        fprintf (stderr, "    checking CCM mode for %s [%i]\n",
+                 gcry_cipher_algo_name (tv[i].algo),
+                 tv[i].algo);
+
+      for (j = 0; j < sizeof (cut) / sizeof (cut[0]); j++)
+        {
+          split = cut[j] < 0 ? tv[i].plainlen : cut[j];
+          if (tv[i].plainlen < split)
+            continue;
+
+          err = gcry_cipher_open (&hde, tv[i].algo, GCRY_CIPHER_MODE_CCM, 0);
+          if (!err)
+            err = gcry_cipher_open (&hdd, tv[i].algo, GCRY_CIPHER_MODE_CCM, 0);
+          if (err)
+            {
+              fail ("cipher-ccm, gcry_cipher_open failed: %s\n",
+                    gpg_strerror (err));
+              return;
+            }
+
+          keylen = gcry_cipher_get_algo_keylen(tv[i].algo);
+          if (!keylen)
+            {
+              fail ("cipher-ccm, 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 ("cipher-ccm, gcry_cipher_setkey failed: %s\n",
+                    gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+
+          blklen = gcry_cipher_get_algo_blklen(tv[i].algo);
+          if (!blklen)
+            {
+              fail ("cipher-ccm, gcry_cipher_get_algo_blklen failed\n");
+              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 ("cipher-ccm, gcry_cipher_setiv failed: %s\n",
+                    gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+
+          authlen = tv[i].cipherlen - tv[i].plainlen;
+          ctl_params[0] = tv[i].plainlen; /* encryptedlen */
+          ctl_params[1] = tv[i].aadlen; /* aadlen */
+          ctl_params[2] = authlen; /* authtaglen */
+          err = gcry_cipher_ctl (hde, GCRYCTL_SET_CCM_PARAMS, ctl_params,
+                                 sizeof(ctl_params));
+          if (!err)
+            err = gcry_cipher_ctl (hdd, GCRYCTL_SET_CCM_PARAMS, ctl_params,
+                                   sizeof(ctl_params));
+          if (err)
+            {
+              fail ("cipher-ccm, gcry_cipher_ctl GCRYCTL_SET_CCM_PARAMS failed:"
+                    "%s\n", gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+
+          aadsplit = split > tv[i].aadlen ? 0 : split;
+
+          err = gcry_cipher_authenticate (hde, tv[i].aad,
+                                          tv[i].aadlen - aadsplit);
+          if (!err)
+            err = gcry_cipher_authenticate (hde,
+                                            &tv[i].aad[tv[i].aadlen - aadsplit],
+                                            aadsplit);
+          if (!err)
+            err = gcry_cipher_authenticate (hdd, tv[i].aad,
+                                            tv[i].aadlen - aadsplit);
+          if (!err)
+            err = gcry_cipher_authenticate (hdd,
+                                            &tv[i].aad[tv[i].aadlen - aadsplit],
+                                            aadsplit);
+          if (err)
+            {
+              fail ("cipher-ccm, gcry_cipher_authenticate failed: %s\n",
+                   gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+
+          err = gcry_cipher_encrypt (hde, out, MAX_DATA_LEN, tv[i].plaintext,
+                                     tv[i].plainlen - split);
+          if (!err)
+            err = gcry_cipher_encrypt (hde, &out[tv[i].plainlen - split],
+                                       MAX_DATA_LEN - (tv[i].plainlen - split),
+                                       &tv[i].plaintext[tv[i].plainlen - split],
+                                       split);
+          if (err)
+            {
+              fail ("cipher-ccm, gcry_cipher_encrypt (%d:%d) failed: %s\n",
+                    i, j, gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+
+          err = gcry_cipher_gettag (hde, &out[tv[i].plainlen], authlen);
+          if (err)
+            {
+              fail ("cipher-ccm, gcry_cipher_gettag (%d:%d) failed: %s\n",
+                    i, j, gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+
+          if (memcmp (tv[i].ciphertext, out, tv[i].cipherlen))
+            fail ("cipher-ccm, encrypt mismatch entry %d:%d\n", i, j);
+
+          err = gcry_cipher_decrypt (hdd, out, tv[i].plainlen - split, NULL, 0);
+          if (!err)
+            err = gcry_cipher_decrypt (hdd, &out[tv[i].plainlen - split], split,
+                                       NULL, 0);
+          if (err)
+            {
+              fail ("cipher-ccm, gcry_cipher_decrypt (%d:%d) failed: %s\n",
+                    i, j, gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+
+          if (memcmp (tv[i].plaintext, out, tv[i].plainlen))
+            fail ("cipher-ccm, decrypt mismatch entry %d:%d\n", i, j);
+
+          err = gcry_cipher_checktag (hdd, &out[tv[i].plainlen], authlen);
+          if (err)
+            {
+              fail ("cipher-ccm, gcry_cipher_checktag (%d:%d) failed: %s\n",
+                    i, j, gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+        }
+    }
+
+  /* Large buffer tests.  */
+
+  /* Test encoding of aadlen > 0xfeff.  */
+  {
+    static const char key[]={0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,
+                             0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f};
+    static const char iv[]={0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19};
+    static const char tag[]={0x9C,0x76,0xE7,0x33,0xD5,0x15,0xB3,0x6C,
+                             0xBA,0x76,0x95,0xF7,0xFB,0x91};
+    char buf[1024];
+    size_t enclen = 0x20000;
+    size_t aadlen = 0x20000;
+    size_t taglen = sizeof(tag);
+
+    err = gcry_cipher_open (&hde, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CCM, 0);
+    if (err)
+      {
+        fail ("cipher-ccm-large, gcry_cipher_open failed: %s\n",
+              gpg_strerror (err));
+        return;
+      }
+
+    err = gcry_cipher_setkey (hde, key, sizeof (key));
+    if (err)
+      {
+         fail ("cipher-ccm-large, gcry_cipher_setkey failed: %s\n",
+               gpg_strerror (err));
+         gcry_cipher_close (hde);
+         return;
+      }
+
+    err = gcry_cipher_setiv (hde, iv, sizeof (iv));
+    if (err)
+      {
+        fail ("cipher-ccm-large, gcry_cipher_setiv failed: %s\n",
+              gpg_strerror (err));
+        gcry_cipher_close (hde);
+        return;
+      }
+
+    ctl_params[0] = enclen; /* encryptedlen */
+    ctl_params[1] = aadlen; /* aadlen */
+    ctl_params[2] = taglen; /* authtaglen */
+    err = gcry_cipher_ctl (hde, GCRYCTL_SET_CCM_PARAMS, ctl_params,
+                           sizeof(ctl_params));
+    if (err)
+      {
+        fail ("cipher-ccm-large, gcry_cipher_ctl GCRYCTL_SET_CCM_PARAMS failed:"
+              "%s\n", gpg_strerror (err));
+        gcry_cipher_close (hde);
+        return;
+      }
+
+    memset (buf, 0xaa, sizeof(buf));
+
+    for (i = 0; i < aadlen; i += sizeof(buf))
+      {
+        err = gcry_cipher_authenticate (hde, buf, sizeof (buf));
+        if (err)
+          {
+            fail ("cipher-ccm-large, gcry_cipher_authenticate failed: %s\n",
+                 gpg_strerror (err));
+            gcry_cipher_close (hde);
+            return;
+          }
+      }
+
+    for (i = 0; i < enclen; i += sizeof(buf))
+      {
+        memset (buf, 0xee, sizeof(buf));
+        err = gcry_cipher_encrypt (hde, buf, sizeof (buf), NULL, 0);
+        if (err)
+          {
+            fail ("cipher-ccm-large, gcry_cipher_encrypt failed: %s\n",
+                 gpg_strerror (err));
+            gcry_cipher_close (hde);
+            return;
+          }
+      }
+
+    err = gcry_cipher_gettag (hde, buf, taglen);
+    if (err)
+      {
+        fail ("cipher-ccm-large, gcry_cipher_gettag failed: %s\n",
+              gpg_strerror (err));
+        gcry_cipher_close (hde);
+        return;
+      }
+
+    if (memcmp (buf, tag, taglen) != 0)
+      fail ("cipher-ccm-large, encrypt mismatch entry\n");
+  }
+
+#if 0
+  /* Test encoding of aadlen > 0xffffffff.  */
+  {
+    static const char key[]={0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,
+                             0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f};
+    static const char iv[]={0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19};
+    static const char tag[]={0x01,0xB2,0xC3,0x4A,0xA6,0x6A,0x07,0x6D,
+                             0xBC,0xBD,0xEA,0x17,0xD3,0x73,0xD7,0xD4};
+    char buf[1024];
+    size_t enclen = (size_t)0xffffffff + 1 + 1024;
+    size_t aadlen = (size_t)0xffffffff + 1 + 1024;
+    size_t taglen = sizeof(tag);
+
+    err = gcry_cipher_open (&hde, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CCM, 0);
+    if (err)
+      {
+        fail ("cipher-ccm-huge, gcry_cipher_open failed: %s\n",
+              gpg_strerror (err));
+        return;
+      }
+
+    err = gcry_cipher_setkey (hde, key, sizeof (key));
+    if (err)
+      {
+         fail ("cipher-ccm-huge, gcry_cipher_setkey failed: %s\n",
+               gpg_strerror (err));
+         gcry_cipher_close (hde);
+         return;
+      }
+
+    err = gcry_cipher_setiv (hde, iv, sizeof (iv));
+    if (err)
+      {
+        fail ("cipher-ccm-huge, gcry_cipher_setiv failed: %s\n",
+              gpg_strerror (err));
+        gcry_cipher_close (hde);
+        return;
+      }
+
+    ctl_params[0] = enclen; /* encryptedlen */
+    ctl_params[1] = aadlen; /* aadlen */
+    ctl_params[2] = taglen; /* authtaglen */
+    err = gcry_cipher_ctl (hde, GCRYCTL_SET_CCM_PARAMS, ctl_params,
+                           sizeof(ctl_params));
+    if (err)
+      {
+        fail ("cipher-ccm-huge, gcry_cipher_ctl GCRYCTL_SET_CCM_PARAMS failed:"
+              "%s\n", gpg_strerror (err));
+        gcry_cipher_close (hde);
+        return;
+      }
+
+    memset (buf, 0xaa, sizeof(buf));
+
+    for (i = 0; i < aadlen; i += sizeof(buf))
+      {
+        err = gcry_cipher_authenticate (hde, buf, sizeof (buf));
+        if (err)
+          {
+            fail ("cipher-ccm-huge, gcry_cipher_authenticate failed: %s\n",
+                 gpg_strerror (err));
+            gcry_cipher_close (hde);
+            return;
+          }
+      }
+
+    for (i = 0; i < enclen; i += sizeof(buf))
+      {
+        memset (buf, 0xee, sizeof(buf));
+        err = gcry_cipher_encrypt (hde, buf, sizeof (buf), NULL, 0);
+        if (err)
+          {
+            fail ("cipher-ccm-huge, gcry_cipher_encrypt failed: %s\n",
+                 gpg_strerror (err));
+            gcry_cipher_close (hde);
+            return;
+          }
+      }
+
+    err = gcry_cipher_gettag (hde, buf, taglen);
+    if (err)
+      {
+        fail ("cipher-ccm-huge, gcry_cipher_gettag failed: %s\n",
+              gpg_strerror (err));
+        gcry_cipher_close (hde);
+        return;
+      }
+
+    if (memcmp (buf, tag, taglen) != 0)
+      fail ("cipher-ccm-huge, encrypt mismatch entry\n");
+  }
+#endif
+
+  if (verbose)
+    fprintf (stderr, "  Completed CCM checks.\n");
+}
+
+
+static void
 check_stream_cipher (void)
 {
   struct tv
@@ -2455,6 +3225,7 @@ check_cipher_modes(void)
   check_ctr_cipher ();
   check_cfb_cipher ();
   check_ofb_cipher ();
+  check_ccm_cipher ();
   check_stream_cipher ();
   check_stream_cipher_large_block ();
 
diff --git a/tests/benchmark.c b/tests/benchmark.c
index ecda0d3..a9b31f7 100644
--- a/tests/benchmark.c
+++ b/tests/benchmark.c
@@ -435,6 +435,40 @@ md_bench ( const char *algoname )
   fflush (stdout);
 }
 
+
+static void ccm_aead_init(gcry_cipher_hd_t hd, size_t buflen, int authlen)
+{
+  const int _L = 4;
+  const int noncelen = 15 - _L;
+  char nonce[noncelen];
+  size_t params[3];
+  gcry_error_t err = GPG_ERR_NO_ERROR;
+
+  memset (nonce, 0x33, noncelen);
+
+  err = gcry_cipher_setiv (hd, nonce, noncelen);
+  if (err)
+    {
+      fprintf (stderr, "gcry_cipher_setiv failed: %s\n",
+               gpg_strerror (err));
+      gcry_cipher_close (hd);
+      exit (1);
+    }
+
+  params[0] = buflen; /* encryptedlen */
+  params[1] = 0; /* aadlen */
+  params[2] = authlen; /* authtaglen */
+  err = gcry_cipher_ctl (hd, GCRYCTL_SET_CCM_PARAMS, params, sizeof(params));
+  if (err)
+    {
+      fprintf (stderr, "gcry_cipher_setiv failed: %s\n",
+               gpg_strerror (err));
+      gcry_cipher_close (hd);
+      exit (1);
+    }
+}
+
+
 static void
 cipher_bench ( const char *algoname )
 {
@@ -448,12 +482,21 @@ cipher_bench ( const char *algoname )
   char *raw_outbuf, *raw_buf;
   size_t allocated_buflen, buflen;
   int repetitions;
-  static struct { int mode; const char *name; int blocked; } modes[] = {
+  static const struct {
+    int mode;
+    const char *name;
+    int blocked;
+    void (* const aead_init)(gcry_cipher_hd_t hd, size_t buflen, int authlen);
+    int req_blocksize;
+    int authlen;
+  } modes[] = {
     { GCRY_CIPHER_MODE_ECB, "   ECB/Stream", 1 },
     { GCRY_CIPHER_MODE_CBC, "      CBC", 1 },
     { GCRY_CIPHER_MODE_CFB, "      CFB", 0 },
     { GCRY_CIPHER_MODE_OFB, "      OFB", 0 },
     { GCRY_CIPHER_MODE_CTR, "      CTR", 0 },
+    { GCRY_CIPHER_MODE_CCM, "      CCM", 0,
+      ccm_aead_init, GCRY_CCM_BLOCK_LEN, 8 },
     { GCRY_CIPHER_MODE_STREAM, "", 0 },
     {0}
   };
@@ -542,9 +585,16 @@ cipher_bench ( const char *algoname )
   for (modeidx=0; modes[modeidx].mode; modeidx++)
     {
       if ((blklen > 1 && modes[modeidx].mode == GCRY_CIPHER_MODE_STREAM)
-          | (blklen == 1 && modes[modeidx].mode != GCRY_CIPHER_MODE_STREAM))
+          || (blklen == 1 && modes[modeidx].mode != GCRY_CIPHER_MODE_STREAM))
         continue;
 
+      if (modes[modeidx].req_blocksize > 0
+          && blklen != modes[modeidx].req_blocksize)
+        {
+          printf (" %7s %7s", "-", "-" );
+          continue;
+        }
+
       for (i=0; i < sizeof buf; i++)
         buf[i] = i;
 
@@ -585,7 +635,18 @@ cipher_bench ( const char *algoname )
                   exit (1);
                 }
             }
-          err = gcry_cipher_encrypt ( hd, outbuf, buflen, buf, buflen);
+          if (modes[modeidx].aead_init)
+            {
+              (*modes[modeidx].aead_init) (hd, buflen, modes[modeidx].authlen);
+              err = gcry_cipher_encrypt (hd, outbuf, buflen, buf, buflen);
+              if (err)
+                break;
+              err = gcry_cipher_gettag (hd, outbuf, modes[modeidx].authlen);
+            }
+          else
+            {
+              err = gcry_cipher_encrypt (hd, outbuf, buflen, buf, buflen);
+            }
         }
       stop_timer ();
 
@@ -632,7 +693,18 @@ cipher_bench ( const char *algoname )
                   exit (1);
                 }
             }
-          err = gcry_cipher_decrypt ( hd, outbuf, buflen,  buf, buflen);
+          if (modes[modeidx].aead_init)
+            {
+              (*modes[modeidx].aead_init) (hd, buflen, modes[modeidx].authlen);
+              err = gcry_cipher_decrypt (hd, outbuf, buflen, buf, buflen);
+              if (err)
+                break;
+              err = gcry_cipher_checktag (hd, outbuf, modes[modeidx].authlen);
+              if (gpg_err_code (err) == GPG_ERR_CHECKSUM)
+                err = gpg_error (GPG_ERR_NO_ERROR);
+            }
+          else
+            err = gcry_cipher_decrypt (hd, outbuf, buflen, buf, buflen);
         }
       stop_timer ();
       printf (" %s", elapsed_time ());




More information about the Gcrypt-devel mailing list