[PATCH 3/3] Add CMAC (Cipher-based MAC) to MAC API

Jussi Kivilinna jussi.kivilinna at iki.fi
Sat Nov 16 13:15:56 CET 2013


* cipher/Makefile.am: Add 'cipher-cmac.c' and 'mac-cmac.c'.
* cipher/cipher-cmac.c: New.
* cipher/cipher-internal.h (gcry_cipher_handle): Add 'u_keys'.
(gcry_cipher_handle.u_mode): Add 'cmac'.
* cipher/cipher.c (gcry_cipher_open): Rename to...
(_gcry_cipher_open_internal): ...this and add CMAC.
(gcry_cipher_open): New wrapper that disallows use of internal
modes (CMAC) from outside.
(cipher_setkey, cipher_encrypt, cipher_decrypt)
(_gcry_cipher_authenticate, _gcry_cipher_gettag)
(_gcry_cipher_checktag): Add handling for CMAC mode.
(cipher_reset): Do not reset 'marks.key'.
* cipher/mac-cmac.c: New.
* cipher/mac-internal.h: Add CMAC support and algorithms.
* cipher/mac.c: Add CMAC algorithms.
* src/cipher.h (gcry_cipher_internal_modes): New.
(_gcry_cipher_open_internal, _gcry_cipher_cmac_authenticate)
(_gcry_cipher_cmac_get_tag, _gcry_cipher_cmac_check_tag)
(_gcry_cipher_cmac_set_subkeys): New prototypes.
* src/gcrypt.h.in (gcry_mac_algos): Add CMAC algorithms.
* tests/basic.c (check_mac): Add CMAC test vectors.
--

Patch adds CMAC (Cipher-based MAC) as defined in RFC 4493 and NIST
Special Publication 800-38B.

Internally CMAC is added to cipher module, but is available to outside
only through MAC API.

TODO:
 - Documentation.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>
---
 cipher/Makefile.am       |    4 -
 cipher/cipher-cmac.c     |  237 ++++++++++++++++++++++++++++++++++++++++++++++
 cipher/cipher-internal.h |   13 ++-
 cipher/cipher.c          |   55 +++++++++++
 cipher/mac-cmac.c        |  230 +++++++++++++++++++++++++++++++++++++++++++++
 cipher/mac-internal.h    |   41 ++++++++
 cipher/mac.c             |   33 ++++++
 src/cipher.h             |   24 +++++
 src/gcrypt.h.in          |   14 +++
 tests/basic.c            |  141 +++++++++++++++++++++++++++
 10 files changed, 788 insertions(+), 4 deletions(-)
 create mode 100644 cipher/cipher-cmac.c
 create mode 100644 cipher/mac-cmac.c

diff --git a/cipher/Makefile.am b/cipher/Makefile.am
index 1d54b07..2fd9822 100644
--- a/cipher/Makefile.am
+++ b/cipher/Makefile.am
@@ -40,12 +40,12 @@ 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-ccm.c cipher-cmac.c \
 cipher-selftest.c cipher-selftest.h \
 pubkey.c pubkey-internal.h pubkey-util.c \
 md.c \
 mac.c mac-internal.h \
-mac-hmac.c \
+mac-hmac.c mac-cmac.c \
 kdf.c kdf-internal.h \
 hmac-tests.c \
 bithelp.h  \
diff --git a/cipher/cipher-cmac.c b/cipher/cipher-cmac.c
new file mode 100644
index 0000000..6bab1b3
--- /dev/null
+++ b/cipher/cipher-cmac.c
@@ -0,0 +1,237 @@
+/* cmac.c - CMAC, Cipher-based MAC.
+ * 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 "g10lib.h"
+#include "cipher.h"
+#include "cipher-internal.h"
+#include "bufhelp.h"
+
+
+#define set_burn(burn, nburn) do { \
+  unsigned int __nburn = (nburn); \
+  (burn) = (burn) > __nburn ? (burn) : __nburn; } while (0)
+
+
+static void
+cmac_write (gcry_cipher_hd_t c, const byte * inbuf, size_t inlen)
+{
+  gcry_cipher_encrypt_t enc_fn = c->spec->encrypt;
+  const unsigned int blocksize = c->spec->blocksize;
+  byte outbuf[MAX_BLOCKSIZE];
+  unsigned int burn = 0;
+  unsigned int nblocks;
+
+  if (!inlen || !inbuf)
+    return;
+
+  /* Last block is needed for cmac_final.  */
+  if (c->unused + inlen <= blocksize)
+    {
+      for (; inlen && c->unused < blocksize; inlen--)
+	c->lastiv[c->unused++] = *inbuf++;
+      return;
+    }
+
+  if (c->unused)
+    {
+      for (; inlen && c->unused < blocksize; inlen--)
+	c->lastiv[c->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));
+
+      c->unused = 0;
+    }
+
+  if (c->bulk.cbc_enc && inlen > blocksize)
+    {
+      nblocks = inlen / blocksize;
+      nblocks -= (nblocks * blocksize == inlen);
+
+      c->bulk.cbc_enc (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks, 1);
+      inbuf += nblocks * blocksize;
+      inlen -= nblocks * blocksize;
+
+      wipememory (outbuf, sizeof (outbuf));
+    }
+  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));
+	inlen -= blocksize;
+	inbuf += blocksize;
+      }
+
+  /* Make sure that last block is passed to cmac_final.  */
+  if (inlen == 0)
+    BUG ();
+
+  for (; inlen && c->unused < blocksize; inlen--)
+    c->lastiv[c->unused++] = *inbuf++;
+
+  if (burn)
+    _gcry_burn_stack (burn + 4 * sizeof (void *));
+}
+
+
+static void cmac_generate_subkeys (gcry_cipher_hd_t c)
+{
+  const unsigned int blocksize = c->spec->blocksize;
+  byte rb, carry, t, bi;
+  unsigned int burn;
+  int i, j;
+  union
+  {
+    size_t _aligned;
+    byte buf[MAX_BLOCKSIZE];
+  } u;
+
+  if (MAX_BLOCKSIZE < blocksize)
+    BUG();
+
+  /* encrypt zero block */
+  memset (u.buf, 0, blocksize);
+  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 */ ;
+
+  for (j = 0; j < 2; j++)
+    {
+      /* Generate subkeys K1 and K2 */
+      carry = 0;
+      for (i = blocksize - 1; i >= 0; i--)
+	{
+	  bi = u.buf[i];
+	  t = carry | (bi << 1);
+	  carry = bi >> 7;
+	  u.buf[i] = t & 0xff;
+	  c->u_keys.cmac.subkeys[j][i] = u.buf[i];
+	}
+      u.buf[blocksize - 1] ^= carry ? rb : 0;
+      c->u_keys.cmac.subkeys[j][blocksize - 1] = u.buf[blocksize - 1];
+    }
+
+  wipememory (&u, sizeof (u));
+  if (burn)
+    _gcry_burn_stack (burn + 4 * sizeof (void *));
+}
+
+
+static void
+cmac_final (gcry_cipher_hd_t c)
+{
+  const unsigned int blocksize = c->spec->blocksize;
+  unsigned int count = c->unused;
+  unsigned int burn;
+  byte *subkey;
+
+  if (count == blocksize)
+    subkey = c->u_keys.cmac.subkeys[0]; /* K1 */
+  else
+    {
+      subkey = c->u_keys.cmac.subkeys[1]; /* K2 */
+      c->lastiv[count++] = 0x80;
+      while (count < blocksize)
+	c->lastiv[count++] = 0;
+    }
+
+  buf_xor (c->lastiv, c->lastiv, 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);
+  if (burn)
+    _gcry_burn_stack (burn + 4 * sizeof (void *));
+
+  c->unused = 0;
+}
+
+
+static gcry_err_code_t
+cmac_tag (gcry_cipher_hd_t c, unsigned char *tag, size_t taglen, int check)
+{
+  if (!tag || taglen == 0 || taglen > c->spec->blocksize)
+    return GPG_ERR_INV_ARG;
+
+  if (!c->u_mode.cmac.tag)
+    {
+      cmac_final (c);
+      c->u_mode.cmac.tag = 1;
+    }
+
+  if (!check)
+    {
+      memcpy (tag, c->u_iv.iv, taglen);
+      return GPG_ERR_NO_ERROR;
+    }
+  else
+    {
+      return buf_eq_const(tag, c->u_iv.iv, taglen) ?
+             GPG_ERR_NO_ERROR : GPG_ERR_CHECKSUM;
+    }
+}
+
+
+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;
+}
+
+
+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);
+}
+
+
+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);
+}
+
+gcry_err_code_t
+_gcry_cipher_cmac_set_subkeys (gcry_cipher_hd_t c)
+{
+  cmac_generate_subkeys (c);
+
+  return GPG_ERR_NO_ERROR;
+}
diff --git a/cipher/cipher-internal.h b/cipher/cipher-internal.h
index db06823..70351b1 100644
--- a/cipher/cipher-internal.h
+++ b/cipher/cipher-internal.h
@@ -136,8 +136,20 @@ struct gcry_cipher_handle
                                  processed.  */
       unsigned int tag:1; /* Set to 1 if tag has been finalized.  */
     } ccm;
+    /* Mode specific storage for CMAC mode. */
+    struct {
+      unsigned int tag:1; /* Set to 1 if tag has been finalized.  */
+    } cmac;
   } u_mode;
 
+  /* Mode specific storage for subkeys. _Not_ cleared by gcry_cipher_reset. */
+  union {
+    /* Mode specific storage for CMAC mode. */
+    struct {
+      unsigned char subkeys[2][MAX_BLOCKSIZE];
+    } cmac;
+  } u_keys;
+
   /* 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
@@ -217,5 +229,4 @@ gcry_err_code_t _gcry_cipher_ccm_check_tag
                  const unsigned char *intag, size_t taglen);
 
 
-
 #endif /*G10_CIPHER_INTERNAL_H*/
diff --git a/cipher/cipher.c b/cipher/cipher.c
index 80aa7a7..1e54a9e 100644
--- a/cipher/cipher.c
+++ b/cipher/cipher.c
@@ -344,6 +344,24 @@ gcry_error_t
 gcry_cipher_open (gcry_cipher_hd_t *handle,
 		  int algo, int mode, unsigned int flags)
 {
+  gcry_err_code_t err;
+  gcry_cipher_hd_t h = NULL;
+
+  if (mode >= GCRY_CIPHER_MODE_INTERNAL)
+    err = GPG_ERR_INV_CIPHER_MODE;
+  else
+    err = _gcry_cipher_open_internal (&h, algo, mode, flags);
+
+  *handle = err ? NULL : h;
+
+  return gcry_error (err);
+}
+
+
+gcry_err_code_t
+_gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
+			    int algo, int mode, unsigned int flags)
+{
   int secure = (flags & GCRY_CIPHER_SECURE);
   gcry_cipher_spec_t *spec;
   gcry_cipher_hd_t h = NULL;
@@ -388,6 +406,7 @@ gcry_cipher_open (gcry_cipher_hd_t *handle,
       case GCRY_CIPHER_MODE_OFB:
       case GCRY_CIPHER_MODE_CTR:
       case GCRY_CIPHER_MODE_AESWRAP:
+      case GCRY_CIPHER_MODE_CMAC:
 	if (!spec->encrypt || !spec->decrypt)
 	  err = GPG_ERR_INV_CIPHER_MODE;
 	break;
@@ -567,10 +586,20 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, size_t keylen)
               (void *) &c->context.c,
               c->spec->contextsize);
       c->marks.key = 1;
+
+      switch (c->mode)
+        {
+        case GCRY_CIPHER_MODE_CMAC:
+          _gcry_cipher_cmac_set_subkeys (c);
+          break;
+        default:
+          break;
+        };
     }
   else
     c->marks.key = 0;
 
+
   return gcry_error (ret);
 }
 
@@ -613,6 +642,10 @@ cipher_setiv (gcry_cipher_hd_t c, const byte *iv, size_t ivlen)
 static void
 cipher_reset (gcry_cipher_hd_t c)
 {
+  unsigned int marks_key;
+
+  marks_key = c->marks.key;
+
   memcpy (&c->context.c,
 	  (char *) &c->context.c + c->spec->contextsize,
 	  c->spec->contextsize);
@@ -622,6 +655,8 @@ cipher_reset (gcry_cipher_hd_t c)
   memset (c->u_ctr.ctr, 0, c->spec->blocksize);
   memset (&c->u_mode, 0, sizeof c->u_mode);
   c->unused = 0;
+
+  c->marks.key = marks_key;
 }
 
 
@@ -717,6 +752,10 @@ cipher_encrypt (gcry_cipher_hd_t c, byte *outbuf, size_t outbuflen,
       rc = _gcry_cipher_ccm_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
       break;
 
+    case GCRY_CIPHER_MODE_CMAC:
+      rc = GPG_ERR_INV_CIPHER_MODE;
+      break;
+
     case GCRY_CIPHER_MODE_STREAM:
       c->spec->stencrypt (&c->context.c,
                           outbuf, (byte*)/*arggg*/inbuf, inbuflen);
@@ -817,6 +856,10 @@ cipher_decrypt (gcry_cipher_hd_t c, byte *outbuf, size_t outbuflen,
       rc = _gcry_cipher_ccm_decrypt (c, outbuf, outbuflen, inbuf, inbuflen);
       break;
 
+    case GCRY_CIPHER_MODE_CMAC:
+      rc = GPG_ERR_INV_CIPHER_MODE;
+      break;
+
     case GCRY_CIPHER_MODE_STREAM:
       c->spec->stdecrypt (&c->context.c,
                           outbuf, (byte*)/*arggg*/inbuf, inbuflen);
@@ -942,6 +985,10 @@ _gcry_cipher_authenticate (gcry_cipher_hd_t hd, const void *abuf,
       rc = _gcry_cipher_ccm_authenticate (hd, abuf, abuflen);
       break;
 
+    case GCRY_CIPHER_MODE_CMAC:
+      rc = _gcry_cipher_cmac_authenticate (hd, abuf, abuflen);
+      break;
+
     default:
       log_error ("gcry_cipher_authenticate: invalid mode %d\n", hd->mode);
       rc = GPG_ERR_INV_CIPHER_MODE;
@@ -962,6 +1009,10 @@ _gcry_cipher_gettag (gcry_cipher_hd_t hd, void *outtag, size_t taglen)
       rc = _gcry_cipher_ccm_get_tag (hd, outtag, taglen);
       break;
 
+    case GCRY_CIPHER_MODE_CMAC:
+      rc = _gcry_cipher_cmac_get_tag (hd, outtag, taglen);
+      break;
+
     default:
       log_error ("gcry_cipher_gettag: invalid mode %d\n", hd->mode);
       rc = GPG_ERR_INV_CIPHER_MODE;
@@ -982,6 +1033,10 @@ _gcry_cipher_checktag (gcry_cipher_hd_t hd, const void *intag, size_t taglen)
       rc = _gcry_cipher_ccm_check_tag (hd, intag, taglen);
       break;
 
+    case GCRY_CIPHER_MODE_CMAC:
+      rc = _gcry_cipher_cmac_check_tag (hd, intag, taglen);
+      break;
+
     default:
       log_error ("gcry_cipher_checktag: invalid mode %d\n", hd->mode);
       rc = GPG_ERR_INV_CIPHER_MODE;
diff --git a/cipher/mac-cmac.c b/cipher/mac-cmac.c
new file mode 100644
index 0000000..b35b6cd
--- /dev/null
+++ b/cipher/mac-cmac.c
@@ -0,0 +1,230 @@
+/* mac-cmac.c  -  CMAC glue for MAC API
+ * 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 "./mac-internal.h"
+
+
+static int map_mac_algo_to_cipher (int mac_algo)
+{
+  switch (mac_algo)
+    {
+    default:
+      return GCRY_CIPHER_NONE;
+    case GCRY_MAC_CMAC_AES:
+      return GCRY_CIPHER_AES;
+    case GCRY_MAC_CMAC_3DES:
+      return GCRY_CIPHER_3DES;
+    case GCRY_MAC_CMAC_CAMELLIA:
+      return GCRY_CIPHER_CAMELLIA128;
+    case GCRY_MAC_CMAC_IDEA:
+      return GCRY_CIPHER_IDEA;
+    case GCRY_MAC_CMAC_CAST5:
+      return GCRY_CIPHER_CAST5;
+    case GCRY_MAC_CMAC_BLOWFISH:
+      return GCRY_CIPHER_BLOWFISH;
+    case GCRY_MAC_CMAC_TWOFISH:
+      return GCRY_CIPHER_TWOFISH;
+    case GCRY_MAC_CMAC_SERPENT:
+      return GCRY_CIPHER_SERPENT128;
+    case GCRY_MAC_CMAC_SEED:
+      return GCRY_CIPHER_SEED;
+    case GCRY_MAC_CMAC_RFC2268:
+      return GCRY_CIPHER_RFC2268_128;
+    case GCRY_MAC_CMAC_GOST28147:
+      return GCRY_CIPHER_GOST28147;
+    }
+}
+
+
+static gcry_err_code_t cmac_open (gcry_mac_hd_t h)
+{
+  gcry_err_code_t err;
+  gcry_cipher_hd_t hd;
+  int secure = (h->magic == CTX_MAGIC_SECURE);
+  int cipher_algo;
+  unsigned int flags;
+
+  cipher_algo = map_mac_algo_to_cipher(h->spec->algo);
+  flags = (secure ? GCRY_CIPHER_SECURE : 0);
+
+  err = _gcry_cipher_open_internal (&hd, cipher_algo, GCRY_CIPHER_MODE_CMAC,
+				    flags);
+  if (err)
+    return err;
+
+  h->u.cmac.cipher_algo = cipher_algo;
+  h->u.cmac.ctx = hd;
+  return 0;
+}
+
+
+static void cmac_close (gcry_mac_hd_t h)
+{
+  _gcry_cipher_close (h->u.cmac.ctx);
+  h->u.cmac.ctx = NULL;
+}
+
+
+static gcry_err_code_t
+cmac_setkey (gcry_mac_hd_t h, const unsigned char *key, size_t keylen)
+{
+  return _gcry_cipher_setkey (h->u.cmac.ctx, key, keylen);
+}
+
+
+static gcry_err_code_t
+cmac_reset (gcry_mac_hd_t h)
+{
+  return gcry_cipher_reset (h->u.cmac.ctx);
+}
+
+
+static gcry_err_code_t
+cmac_write (gcry_mac_hd_t h, const unsigned char *buf, size_t buflen)
+{
+  return _gcry_cipher_cmac_authenticate (h->u.cmac.ctx, buf, buflen);
+}
+
+
+static gcry_err_code_t
+cmac_read (gcry_mac_hd_t h, unsigned char *outbuf, size_t outlen)
+{
+  return _gcry_cipher_cmac_get_tag (h->u.cmac.ctx, outbuf, outlen);
+}
+
+
+static gcry_err_code_t
+cmac_verify (gcry_mac_hd_t h, const unsigned char *buf, size_t buflen)
+{
+  return _gcry_cipher_cmac_check_tag (h->u.cmac.ctx, buf, buflen);
+}
+
+
+static unsigned int cmac_get_maclen (int algo)
+{
+  return _gcry_cipher_get_algo_blklen (map_mac_algo_to_cipher (algo));
+}
+
+
+static unsigned int cmac_get_keylen (int algo)
+{
+  return _gcry_cipher_get_algo_keylen (map_mac_algo_to_cipher (algo));
+}
+
+
+static gcry_mac_spec_ops_t cmac_ops =
+  {
+    cmac_open,
+    cmac_close,
+    cmac_setkey,
+    NULL,
+    cmac_reset,
+    cmac_write,
+    cmac_read,
+    cmac_verify,
+    cmac_get_maclen,
+    cmac_get_keylen
+  };
+
+
+#if USE_BLOWFISH
+gcry_mac_spec_t _gcry_mac_type_spec_cmac_blowfish =
+  {
+    GCRY_MAC_CMAC_BLOWFISH, {0, 0}, "CMAC_BLOWFISH",
+    &cmac_ops
+  };
+#endif
+#if USE_DES
+gcry_mac_spec_t _gcry_mac_type_spec_cmac_tripledes =
+  {
+    GCRY_MAC_CMAC_3DES, {0, 1}, "CMAC_3DES",
+    &cmac_ops
+  };
+#endif
+#if USE_CAST5
+gcry_mac_spec_t _gcry_mac_type_spec_cmac_cast5 =
+  {
+    GCRY_MAC_CMAC_CAST5, {0, 0}, "CMAC_CAST5",
+    &cmac_ops
+  };
+#endif
+#if USE_AES
+gcry_mac_spec_t _gcry_mac_type_spec_cmac_aes =
+  {
+    GCRY_MAC_CMAC_AES, {0, 1}, "CMAC_AES",
+    &cmac_ops
+  };
+#endif
+#if USE_TWOFISH
+gcry_mac_spec_t _gcry_mac_type_spec_cmac_twofish =
+  {
+    GCRY_MAC_CMAC_TWOFISH, {0, 0}, "CMAC_TWOFISH",
+    &cmac_ops
+  };
+#endif
+#if USE_SERPENT
+gcry_mac_spec_t _gcry_mac_type_spec_cmac_serpent =
+  {
+    GCRY_MAC_CMAC_SERPENT, {0, 0}, "CMAC_SERPENT",
+    &cmac_ops
+  };
+#endif
+#if USE_RFC2268
+gcry_mac_spec_t _gcry_mac_type_spec_cmac_rfc2268 =
+  {
+    GCRY_MAC_CMAC_RFC2268, {0, 0}, "CMAC_RFC2268",
+    &cmac_ops
+  };
+#endif
+#if USE_SEED
+gcry_mac_spec_t _gcry_mac_type_spec_cmac_seed =
+  {
+    GCRY_MAC_CMAC_SEED, {0, 0}, "CMAC_SEED",
+    &cmac_ops
+  };
+#endif
+#if USE_CAMELLIA
+gcry_mac_spec_t _gcry_mac_type_spec_cmac_camellia =
+  {
+    GCRY_MAC_CMAC_CAMELLIA, {0, 0}, "CMAC_CAMELLIA",
+    &cmac_ops
+  };
+#endif
+#ifdef USE_IDEA
+gcry_mac_spec_t _gcry_mac_type_spec_cmac_idea =
+  {
+    GCRY_MAC_CMAC_IDEA, {0, 0}, "CMAC_IDEA",
+    &cmac_ops
+  };
+#endif
+#if USE_GOST28147
+gcry_mac_spec_t _gcry_mac_type_spec_cmac_gost28147 =
+  {
+    GCRY_MAC_CMAC_GOST28147, {0, 0}, "CMAC_GOST28147",
+    &cmac_ops
+  };
+#endif
diff --git a/cipher/mac-internal.h b/cipher/mac-internal.h
index 3da40f7..aeffb19 100644
--- a/cipher/mac-internal.h
+++ b/cipher/mac-internal.h
@@ -98,6 +98,10 @@ struct gcry_mac_handle
       gcry_md_hd_t md_ctx;
       int md_algo;
     } hmac;
+    struct {
+      gcry_cipher_hd_t ctx;
+      int cipher_algo;
+    } cmac;
   } u;
 };
 
@@ -138,3 +142,40 @@ extern gcry_mac_spec_t _gcry_mac_type_spec_hmac_md5;
 #if USE_MD4
 extern gcry_mac_spec_t _gcry_mac_type_spec_hmac_md4;
 #endif
+
+/*
+ * The CMAC algorithm specifications (mac-cmac.c).
+ */
+#if USE_BLOWFISH
+extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_blowfish;
+#endif
+#if USE_DES
+extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_tripledes;
+#endif
+#if USE_CAST5
+extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_cast5;
+#endif
+#if USE_AES
+extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_aes;
+#endif
+#if USE_TWOFISH
+extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_twofish;
+#endif
+#if USE_SERPENT
+extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_serpent;
+#endif
+#if USE_RFC2268
+extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_rfc2268;
+#endif
+#if USE_SEED
+extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_seed;
+#endif
+#if USE_CAMELLIA
+extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_camellia;
+#endif
+#ifdef USE_IDEA
+extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_idea;
+#endif
+#if USE_GOST28147
+extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_gost28147;
+#endif
diff --git a/cipher/mac.c b/cipher/mac.c
index 2a4bce8..dca4af3 100644
--- a/cipher/mac.c
+++ b/cipher/mac.c
@@ -64,6 +64,39 @@ static gcry_mac_spec_t *mac_list[] =
 #if USE_MD4
      &_gcry_mac_type_spec_hmac_md4,
 #endif
+#if USE_BLOWFISH
+     &_gcry_mac_type_spec_cmac_blowfish,
+#endif
+#if USE_DES
+     &_gcry_mac_type_spec_cmac_tripledes,
+#endif
+#if USE_CAST5
+     &_gcry_mac_type_spec_cmac_cast5,
+#endif
+#if USE_AES
+     &_gcry_mac_type_spec_cmac_aes,
+#endif
+#if USE_TWOFISH
+     &_gcry_mac_type_spec_cmac_twofish,
+#endif
+#if USE_SERPENT
+     &_gcry_mac_type_spec_cmac_serpent,
+#endif
+#if USE_RFC2268
+     &_gcry_mac_type_spec_cmac_rfc2268,
+#endif
+#if USE_SEED
+     &_gcry_mac_type_spec_cmac_seed,
+#endif
+#if USE_CAMELLIA
+     &_gcry_mac_type_spec_cmac_camellia,
+#endif
+#ifdef USE_IDEA
+     &_gcry_mac_type_spec_cmac_idea,
+#endif
+#if USE_GOST28147
+     &_gcry_mac_type_spec_cmac_gost28147,
+#endif
      NULL,
   };
 
diff --git a/src/cipher.h b/src/cipher.h
index 38d2a97..10bfe0c 100644
--- a/src/cipher.h
+++ b/src/cipher.h
@@ -84,6 +84,30 @@ struct pk_encoding_ctx
 
 #include "cipher-proto.h"
 
+/* The internal encryption modes. */
+enum gcry_cipher_internal_modes
+  {
+    GCRY_CIPHER_MODE_INTERNAL = 0x10000,
+    GCRY_CIPHER_MODE_CMAC     = 0x10000 + 1   /* Cipher-based MAC. */
+  };
+
+
+/*-- cipher.c --*/
+gcry_err_code_t _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
+					    int algo, int mode,
+					    unsigned int flags);
+
+/*-- cipher-cmac.c --*/
+gcry_err_code_t _gcry_cipher_cmac_authenticate
+/*           */ (gcry_cipher_hd_t c, const unsigned char *abuf, size_t abuflen);
+gcry_err_code_t _gcry_cipher_cmac_get_tag
+/*           */ (gcry_cipher_hd_t c,
+                 unsigned char *outtag, size_t taglen);
+gcry_err_code_t _gcry_cipher_cmac_check_tag
+/*           */ (gcry_cipher_hd_t c,
+                 const unsigned char *intag, size_t taglen);
+gcry_err_code_t _gcry_cipher_cmac_set_subkeys
+/*           */ (gcry_cipher_hd_t c);
 
 /*-- rmd160.c --*/
 void _gcry_rmd160_hash_buffer (void *outbuf,
diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in
index b0bece1..dd2318c 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -1309,7 +1309,19 @@ enum gcry_mac_algos
     GCRY_MAC_HMAC_WHIRLPOOL     = 10,
     GCRY_MAC_HMAC_GOSTR3411_94  = 11,
     GCRY_MAC_HMAC_STRIBOG256    = 12,
-    GCRY_MAC_HMAC_STRIBOG512    = 13
+    GCRY_MAC_HMAC_STRIBOG512    = 13,
+
+    GCRY_MAC_CMAC_AES           = 100,
+    GCRY_MAC_CMAC_3DES          = 101,
+    GCRY_MAC_CMAC_CAMELLIA      = 102,
+    GCRY_MAC_CMAC_CAST5         = 103,
+    GCRY_MAC_CMAC_BLOWFISH      = 104,
+    GCRY_MAC_CMAC_TWOFISH       = 105,
+    GCRY_MAC_CMAC_SERPENT       = 106,
+    GCRY_MAC_CMAC_SEED          = 107,
+    GCRY_MAC_CMAC_RFC2268       = 108,
+    GCRY_MAC_CMAC_IDEA          = 109,
+    GCRY_MAC_CMAC_GOST28147     = 110
   };
 
 /* Flags used with the open function.  */
diff --git a/tests/basic.c b/tests/basic.c
index 6ca1803..d04a78e 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -4398,6 +4398,147 @@ check_mac (void)
         "\xde\xbd\x71\xf8\x86\x72\x89\x86\x5d\xf5\xa3\x2d\x20\xcd\xc9\x44"
         "\xb6\x02\x2c\xac\x3c\x49\x82\xb1\x0d\x5e\xeb\x55\xc3\xe4\xde\x15"
         "\x13\x46\x76\xfb\x6d\xe0\x44\x60\x65\xc9\x74\x40\xfa\x8c\x6a\x58" },
+      /* CMAC AES and DES test vectors from
+         http://web.archive.org/web/20130930212819/http://csrc.nist.gov/publica\
+         tions/nistpubs/800-38B/Updated_CMAC_Examples.pdf */
+      { GCRY_MAC_CMAC_AES,
+        "",
+        "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+        "\xbb\x1d\x69\x29\xe9\x59\x37\x28\x7f\xa3\x7d\x12\x9b\x75\x67\x46" },
+      { GCRY_MAC_CMAC_AES,
+        "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a",
+        "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+        "\x07\x0a\x16\xb4\x6b\x4d\x41\x44\xf7\x9b\xdd\x9d\xd0\x4a\x28\x7c" },
+      { GCRY_MAC_CMAC_AES,
+        "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+        "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+        "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11",
+        "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+        "\xdf\xa6\x67\x47\xde\x9a\xe6\x30\x30\xca\x32\x61\x14\x97\xc8\x27" },
+      { GCRY_MAC_CMAC_AES,
+        "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+        "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+        "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+        "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+        "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+        "\x51\xf0\xbe\xbf\x7e\x3b\x9d\x92\xfc\x49\x74\x17\x79\x36\x3c\xfe" },
+      { GCRY_MAC_CMAC_AES,
+        "",
+        "\x8e\x73\xb0\xf7\xda\x0e\x64\x52\xc8\x10\xf3\x2b\x80\x90\x79\xe5"
+        "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b",
+        "\xd1\x7d\xdf\x46\xad\xaa\xcd\xe5\x31\xca\xc4\x83\xde\x7a\x93\x67" },
+      { GCRY_MAC_CMAC_AES,
+        "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a",
+        "\x8e\x73\xb0\xf7\xda\x0e\x64\x52\xc8\x10\xf3\x2b\x80\x90\x79\xe5"
+        "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b",
+        "\x9e\x99\xa7\xbf\x31\xe7\x10\x90\x06\x62\xf6\x5e\x61\x7c\x51\x84" },
+      { GCRY_MAC_CMAC_AES,
+        "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+        "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+        "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11",
+        "\x8e\x73\xb0\xf7\xda\x0e\x64\x52\xc8\x10\xf3\x2b\x80\x90\x79\xe5"
+        "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b",
+        "\x8a\x1d\xe5\xbe\x2e\xb3\x1a\xad\x08\x9a\x82\xe6\xee\x90\x8b\x0e" },
+      { GCRY_MAC_CMAC_AES,
+        "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+        "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+        "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+        "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+        "\x8e\x73\xb0\xf7\xda\x0e\x64\x52\xc8\x10\xf3\x2b\x80\x90\x79\xe5"
+        "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b",
+        "\xa1\xd5\xdf\x0e\xed\x79\x0f\x79\x4d\x77\x58\x96\x59\xf3\x9a\x11" },
+      { GCRY_MAC_CMAC_AES,
+        "",
+        "\x60\x3d\xeb\x10\x15\xca\x71\xbe\x2b\x73\xae\xf0\x85\x7d\x77\x81"
+        "\x1f\x35\x2c\x07\x3b\x61\x08\xd7\x2d\x98\x10\xa3\x09\x14\xdf\xf4",
+        "\x02\x89\x62\xf6\x1b\x7b\xf8\x9e\xfc\x6b\x55\x1f\x46\x67\xd9\x83" },
+      { GCRY_MAC_CMAC_AES,
+        "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a",
+        "\x60\x3d\xeb\x10\x15\xca\x71\xbe\x2b\x73\xae\xf0\x85\x7d\x77\x81"
+        "\x1f\x35\x2c\x07\x3b\x61\x08\xd7\x2d\x98\x10\xa3\x09\x14\xdf\xf4",
+        "\x28\xa7\x02\x3f\x45\x2e\x8f\x82\xbd\x4b\xf2\x8d\x8c\x37\xc3\x5c" },
+      { GCRY_MAC_CMAC_AES,
+        "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+        "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+        "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11",
+        "\x60\x3d\xeb\x10\x15\xca\x71\xbe\x2b\x73\xae\xf0\x85\x7d\x77\x81"
+        "\x1f\x35\x2c\x07\x3b\x61\x08\xd7\x2d\x98\x10\xa3\x09\x14\xdf\xf4",
+        "\xaa\xf3\xd8\xf1\xde\x56\x40\xc2\x32\xf5\xb1\x69\xb9\xc9\x11\xe6" },
+      { GCRY_MAC_CMAC_AES,
+        "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+        "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+        "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+        "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+        "\x60\x3d\xeb\x10\x15\xca\x71\xbe\x2b\x73\xae\xf0\x85\x7d\x77\x81"
+        "\x1f\x35\x2c\x07\x3b\x61\x08\xd7\x2d\x98\x10\xa3\x09\x14\xdf\xf4",
+        "\xe1\x99\x21\x90\x54\x9f\x6e\xd5\x69\x6a\x2c\x05\x6c\x31\x54\x10" },
+      { GCRY_MAC_CMAC_3DES,
+        "",
+        "\x8a\xa8\x3b\xf8\xcb\xda\x10\x62\x0b\xc1\xbf\x19\xfb\xb6\xcd\x58"
+        "\xbc\x31\x3d\x4a\x37\x1c\xa8\xb5",
+        "\xb7\xa6\x88\xe1\x22\xff\xaf\x95" },
+      { GCRY_MAC_CMAC_3DES,
+        "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96",
+        "\x8a\xa8\x3b\xf8\xcb\xda\x10\x62\x0b\xc1\xbf\x19\xfb\xb6\xcd\x58"
+        "\xbc\x31\x3d\x4a\x37\x1c\xa8\xb5",
+        "\x8e\x8f\x29\x31\x36\x28\x37\x97" },
+      { GCRY_MAC_CMAC_3DES,
+        "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+        "\xae\x2d\x8a\x57",
+        "\x8a\xa8\x3b\xf8\xcb\xda\x10\x62\x0b\xc1\xbf\x19\xfb\xb6\xcd\x58"
+        "\xbc\x31\x3d\x4a\x37\x1c\xa8\xb5",
+        "\x74\x3d\xdb\xe0\xce\x2d\xc2\xed" },
+      { GCRY_MAC_CMAC_3DES,
+        "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+        "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51",
+        "\x8a\xa8\x3b\xf8\xcb\xda\x10\x62\x0b\xc1\xbf\x19\xfb\xb6\xcd\x58"
+        "\xbc\x31\x3d\x4a\x37\x1c\xa8\xb5",
+        "\x33\xe6\xb1\x09\x24\x00\xea\xe5" },
+      { GCRY_MAC_CMAC_3DES,
+        "",
+        "\x4c\xf1\x51\x34\xa2\x85\x0d\xd5\x8a\x3d\x10\xba\x80\x57\x0d\x38"
+        "\x4c\xf1\x51\x34\xa2\x85\x0d\xd5",
+        "\xbd\x2e\xbf\x9a\x3b\xa0\x03\x61" },
+      { GCRY_MAC_CMAC_3DES,
+        "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96",
+        "\x4c\xf1\x51\x34\xa2\x85\x0d\xd5\x8a\x3d\x10\xba\x80\x57\x0d\x38"
+        "\x4c\xf1\x51\x34\xa2\x85\x0d\xd5",
+        "\x4f\xf2\xab\x81\x3c\x53\xce\x83" },
+      { GCRY_MAC_CMAC_3DES,
+        "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+        "\xae\x2d\x8a\x57",
+        "\x4c\xf1\x51\x34\xa2\x85\x0d\xd5\x8a\x3d\x10\xba\x80\x57\x0d\x38"
+        "\x4c\xf1\x51\x34\xa2\x85\x0d\xd5",
+        "\x62\xdd\x1b\x47\x19\x02\xbd\x4e" },
+      { GCRY_MAC_CMAC_3DES,
+        "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+        "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51",
+        "\x4c\xf1\x51\x34\xa2\x85\x0d\xd5\x8a\x3d\x10\xba\x80\x57\x0d\x38"
+        "\x4c\xf1\x51\x34\xa2\x85\x0d\xd5",
+        "\x31\xb1\xe4\x31\xda\xbc\x4e\xb8" },
+      /* CMAC Camellia test vectors from
+         http://tools.ietf.org/html/draft-kato-ipsec-camellia-cmac96and128-05 */
+      { GCRY_MAC_CMAC_CAMELLIA,
+        "",
+        "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+        "\xba\x92\x57\x82\xaa\xa1\xf5\xd9\xa0\x0f\x89\x64\x80\x94\xfc\x71" },
+      { GCRY_MAC_CMAC_CAMELLIA,
+        "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a",
+        "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+        "\x6d\x96\x28\x54\xa3\xb9\xfd\xa5\x6d\x7d\x45\xa9\x5e\xe1\x79\x93" },
+      { GCRY_MAC_CMAC_CAMELLIA,
+        "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+        "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+        "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11",
+        "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+        "\x5c\x18\xd1\x19\xcc\xd6\x76\x61\x44\xac\x18\x66\x13\x1d\x9f\x22" },
+      { GCRY_MAC_CMAC_CAMELLIA,
+        "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+        "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+        "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+        "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+        "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+        "\xc2\x69\x9a\x6e\xba\x55\xce\x9d\x93\x9a\x8a\x4e\x19\x46\x6e\xe9" },
       { 0 },
     };
   int i;




More information about the Gcrypt-devel mailing list