[PATCH 1/2] [v2] Add new MAC API, initially with HMAC

Jussi Kivilinna jussi.kivilinna at iki.fi
Mon Nov 18 17:32:38 CET 2013


Does these look ok for pushing?

--

* cipher/Makefile.am: Add 'mac.c', 'mac-internal.h' and 'mac-hmac.c'.
* cipher/bufhelp.h (buf_eq_const): New.
* cipher/cipher-ccm.c (_gcry_cipher_ccm_tag): Use 'buf_eq_const' for
constant-time compare.
* cipher/mac-hmac.c: New.
* cipher/mac-internal.h: New.
* cipher/mac.c: New.
* src/gcrypt-int.h [GPG_ERROR_VERSION_NUMBER < 1.13]
(GPG_ERR_MAC_ALGO): New.
* src/gcrypt.h.in (gcry_mac_handle, gcry_mac_hd_t, gcry_mac_algos)
(gcry_mac_flags, gcry_mac_open, gcry_mac_close, gcry_mac_ctl)
(gcry_mac_algo_info, gcry_mac_setkey, gcry_mac_setiv, gcry_mac_write)
(gcry_mac_read, gcry_mac_verify, gcry_mac_get_algo_maclen)
(gcry_mac_get_algo_keylen, gcry_mac_algo_name, gcry_mac_map_name)
(gcry_mac_reset, gcry_mac_test_algo): New.
* src/libgcrypt.def (gcry_mac_open, gcry_mac_close, gcry_mac_ctl)
(gcry_mac_algo_info, gcry_mac_setkey, gcry_mac_setiv, gcry_mac_write)
(gcry_mac_read, gcry_mac_verify, gcry_mac_get_algo_maclen)
(gcry_mac_get_algo_keylen, gcry_mac_algo_name, gcry_mac_map_name): New.
* src/libgcrypt.vers (gcry_mac_open, gcry_mac_close, gcry_mac_ctl)
(gcry_mac_algo_info, gcry_mac_setkey, gcry_mac_setiv, gcry_mac_write)
(gcry_mac_read, gcry_mac_verify, gcry_mac_get_algo_maclen)
(gcry_mac_get_algo_keylen, gcry_mac_algo_name, gcry_mac_map_name): New.
* src/visibility.c (gcry_mac_open, gcry_mac_close, gcry_mac_ctl)
(gcry_mac_algo_info, gcry_mac_setkey, gcry_mac_setiv, gcry_mac_write)
(gcry_mac_read, gcry_mac_verify, gcry_mac_get_algo_maclen)
(gcry_mac_get_algo_keylen, gcry_mac_algo_name, gcry_mac_map_name): New.
* src/visibility.h (gcry_mac_open, gcry_mac_close, gcry_mac_ctl)
(gcry_mac_algo_info, gcry_mac_setkey, gcry_mac_setiv, gcry_mac_write)
(gcry_mac_read, gcry_mac_verify, gcry_mac_get_algo_maclen)
(gcry_mac_get_algo_keylen, gcry_mac_algo_name, gcry_mac_map_name): New.
* tests/basic.c (check_one_mac, check_mac): New.
(main): Call 'check_mac'.
* tests/bench-slope.c (bench_print_header, bench_print_footer): Allow
variable algorithm name width.
(_cipher_bench, hash_bench): Update to above change.
(bench_hash_do_bench): Add 'gcry_md_reset'.
(bench_mac_mode, bench_mac_init, bench_mac_free, bench_mac_do_bench)
(mac_ops, mac_modes, mac_bench_one, _mac_bench, mac_bench): New.
(main): Add 'mac' benchmark options.
* tests/benchmark.c (mac_repetitions, mac_bench): New.
(main): Add 'mac' benchmark options.
--

Add MAC API, with HMAC algorithms. Internally uses HMAC functionality of the
MD module.

[v2]:
 - Add documentation for MAC API.
 - Change length argument for gcry_mac_read from size_t to size_t* for
   returning number of written bytes.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>
---
 cipher/Makefile.am    |    2 
 cipher/bufhelp.h      |   17 ++
 cipher/cipher-ccm.c   |   13 -
 cipher/mac-hmac.c     |  278 ++++++++++++++++++++++++++++++
 cipher/mac-internal.h |  140 +++++++++++++++
 cipher/mac.c          |  449 +++++++++++++++++++++++++++++++++++++++++++++++++
 doc/gcrypt.texi       |  254 +++++++++++++++++++++++++++-
 src/gcrypt-int.h      |    4 
 src/gcrypt.h.in       |   97 +++++++++++
 src/libgcrypt.def     |   14 ++
 src/libgcrypt.vers    |    5 +
 src/visibility.c      |  106 ++++++++++++
 src/visibility.h      |   42 +++++
 tests/basic.c         |  396 +++++++++++++++++++++++++++++++++++++++++++
 tests/bench-slope.c   |  182 +++++++++++++++++++-
 tests/benchmark.c     |  146 ++++++++++++++++
 16 files changed, 2122 insertions(+), 23 deletions(-)
 create mode 100644 cipher/mac-hmac.c
 create mode 100644 cipher/mac-internal.h
 create mode 100644 cipher/mac.c

diff --git a/cipher/Makefile.am b/cipher/Makefile.am
index 95d484e..1d54b07 100644
--- a/cipher/Makefile.am
+++ b/cipher/Makefile.am
@@ -44,6 +44,8 @@ cipher-ccm.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 \
 kdf.c kdf-internal.h \
 hmac-tests.c \
 bithelp.h  \
diff --git a/cipher/bufhelp.h b/cipher/bufhelp.h
index dc39b46..45a7209 100644
--- a/cipher/bufhelp.h
+++ b/cipher/bufhelp.h
@@ -220,6 +220,23 @@ buf_xor_n_copy(void *_dst_xor, void *_srcdst_cpy, const void *_src, size_t len)
 }
 
 
+/* Constant-time compare of two buffers.  Returns 1 if buffers are equal,
+   and 0 if buffers differ.  */
+static inline int
+buf_eq_const(const void *_a, const void *_b, size_t len)
+{
+  const byte *a = _a;
+  const byte *b = _b;
+  size_t diff, i;
+
+  /* Constant-time compare. */
+  for (i = 0, diff = 0; i < len; i++)
+    diff -= !!(a[i] - b[i]);
+
+  return !diff;
+}
+
+
 #ifndef BUFHELP_FAST_UNALIGNED_ACCESS
 
 /* Functions for loading and storing unaligned u32 values of different
diff --git a/cipher/cipher-ccm.c b/cipher/cipher-ccm.c
index 2a1fc74..1eba7f0 100644
--- a/cipher/cipher-ccm.c
+++ b/cipher/cipher-ccm.c
@@ -289,13 +289,8 @@ _gcry_cipher_ccm_tag (gcry_cipher_hd_t c, unsigned char *outbuf,
     }
   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;
+      return buf_eq_const(outbuf, c->u_iv.iv, outbuflen) ?
+             GPG_ERR_NO_ERROR : GPG_ERR_CHECKSUM;
     }
 }
 
@@ -304,7 +299,7 @@ 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);
+  return _gcry_cipher_ccm_tag (c, outtag, taglen, 0);
 }
 
 
@@ -312,7 +307,7 @@ 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);
+  return _gcry_cipher_ccm_tag (c, (unsigned char *)intag, taglen, 1);
 }
 
 
diff --git a/cipher/mac-hmac.c b/cipher/mac-hmac.c
new file mode 100644
index 0000000..b4cd380
--- /dev/null
+++ b/cipher/mac-hmac.c
@@ -0,0 +1,278 @@
+/* mac-hmac.c  -  HMAC 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 "./mac-internal.h"
+#include "bufhelp.h"
+
+
+static int map_mac_algo_to_md (int mac_algo)
+{
+  switch (mac_algo)
+    {
+    default:
+      return GCRY_MD_NONE;
+    case GCRY_MAC_HMAC_MD4:
+      return GCRY_MD_MD4;
+    case GCRY_MAC_HMAC_MD5:
+      return GCRY_MD_MD5;
+    case GCRY_MAC_HMAC_SHA1:
+      return GCRY_MD_SHA1;
+    case GCRY_MAC_HMAC_SHA224:
+      return GCRY_MD_SHA224;
+    case GCRY_MAC_HMAC_SHA256:
+      return GCRY_MD_SHA256;
+    case GCRY_MAC_HMAC_SHA384:
+      return GCRY_MD_SHA384;
+    case GCRY_MAC_HMAC_SHA512:
+      return GCRY_MD_SHA512;
+    case GCRY_MAC_HMAC_RMD160:
+      return GCRY_MD_RMD160;
+    case GCRY_MAC_HMAC_TIGER1:
+      return GCRY_MD_TIGER1;
+    case GCRY_MAC_HMAC_WHIRLPOOL:
+      return GCRY_MD_WHIRLPOOL;
+    case GCRY_MAC_HMAC_GOSTR3411_94:
+      return GCRY_MD_GOSTR3411_94;
+    case GCRY_MAC_HMAC_STRIBOG256:
+      return GCRY_MD_STRIBOG256;
+    case GCRY_MAC_HMAC_STRIBOG512:
+      return GCRY_MD_STRIBOG512;
+    }
+}
+
+
+static gcry_err_code_t hmac_open (gcry_mac_hd_t h)
+{
+  gcry_err_code_t err;
+  gcry_md_hd_t hd;
+  int secure = (h->magic == CTX_MAGIC_SECURE);
+  unsigned int flags;
+  int md_algo;
+
+  md_algo = map_mac_algo_to_md(h->spec->algo);
+
+  flags = GCRY_MD_FLAG_HMAC;
+  flags |= (secure ? GCRY_MD_FLAG_SECURE : 0);
+
+  err = gcry_md_open (&hd, md_algo, flags);
+  if (err)
+    return err;
+
+  h->u.hmac.md_algo = md_algo;
+  h->u.hmac.md_ctx = hd;
+  return 0;
+}
+
+
+static void hmac_close (gcry_mac_hd_t h)
+{
+  gcry_md_close (h->u.hmac.md_ctx);
+  h->u.hmac.md_ctx = NULL;
+}
+
+
+static gcry_err_code_t
+hmac_setkey (gcry_mac_hd_t h, const unsigned char *key, size_t keylen)
+{
+  return gcry_md_setkey (h->u.hmac.md_ctx, key, keylen);
+}
+
+
+static gcry_err_code_t
+hmac_reset (gcry_mac_hd_t h)
+{
+  gcry_md_reset (h->u.hmac.md_ctx);
+  return 0;
+}
+
+
+static gcry_err_code_t
+hmac_write (gcry_mac_hd_t h, const unsigned char *buf, size_t buflen)
+{
+  gcry_md_write (h->u.hmac.md_ctx, buf, buflen);
+  return 0;
+}
+
+
+static gcry_err_code_t
+hmac_read (gcry_mac_hd_t h, unsigned char *outbuf, size_t *outlen)
+{
+  unsigned int dlen;
+  const unsigned char *digest;
+
+  dlen = gcry_md_get_algo_dlen (h->u.hmac.md_algo);
+  digest = gcry_md_read (h->u.hmac.md_ctx, h->u.hmac.md_algo);
+
+  if (*outlen <= dlen)
+    buf_cpy (outbuf, digest, *outlen);
+  else
+    {
+      buf_cpy (outbuf, digest, dlen);
+      *outlen = dlen;
+    }
+
+  return 0;
+}
+
+
+static gcry_err_code_t
+hmac_verify (gcry_mac_hd_t h, const unsigned char *buf, size_t buflen)
+{
+  unsigned int dlen;
+  const unsigned char *digest;
+
+  dlen = gcry_md_get_algo_dlen (h->u.hmac.md_algo);
+  digest = gcry_md_read (h->u.hmac.md_ctx, h->u.hmac.md_algo);
+
+  if (buflen > dlen)
+    return GPG_ERR_INV_LENGTH;
+
+  return buf_eq_const (buf, digest, buflen) ? 0 : GPG_ERR_CHECKSUM;
+}
+
+
+static unsigned int hmac_get_maclen(int algo)
+{
+  return gcry_md_get_algo_dlen (map_mac_algo_to_md (algo));
+}
+
+
+static unsigned int hmac_get_keylen(int algo)
+{
+  /* Return blocksize for default key length. */
+  switch (algo)
+    {
+    case GCRY_MAC_HMAC_SHA384:
+    case GCRY_MAC_HMAC_SHA512:
+      return 128;
+    case GCRY_MAC_HMAC_GOSTR3411_94:
+      return 32;
+    default:
+      return 64;
+    }
+}
+
+
+static const gcry_mac_spec_ops_t hmac_ops =
+  {
+    hmac_open,
+    hmac_close,
+    hmac_setkey,
+    NULL,
+    hmac_reset,
+    hmac_write,
+    hmac_read,
+    hmac_verify,
+    hmac_get_maclen,
+    hmac_get_keylen
+  };
+
+
+#if USE_SHA1
+gcry_mac_spec_t _gcry_mac_type_spec_hmac_sha1 =
+  {
+    GCRY_MAC_HMAC_SHA1, {0, 1}, "HMAC_SHA1",
+    &hmac_ops
+  };
+#endif
+#if USE_SHA256
+gcry_mac_spec_t _gcry_mac_type_spec_hmac_sha256 =
+  {
+    GCRY_MAC_HMAC_SHA256, {0, 1}, "HMAC_SHA256",
+    &hmac_ops
+  };
+gcry_mac_spec_t _gcry_mac_type_spec_hmac_sha224 =
+  {
+    GCRY_MAC_HMAC_SHA224, {0, 1}, "HMAC_SHA224",
+    &hmac_ops
+  };
+#endif
+#if USE_SHA512
+gcry_mac_spec_t _gcry_mac_type_spec_hmac_sha512 =
+  {
+    GCRY_MAC_HMAC_SHA512, {0, 1}, "HMAC_SHA512",
+    &hmac_ops
+  };
+gcry_mac_spec_t _gcry_mac_type_spec_hmac_sha384 =
+  {
+    GCRY_MAC_HMAC_SHA384, {0, 1}, "HMAC_SHA384",
+    &hmac_ops
+  };
+#endif
+#ifdef USE_GOST_R_3411_94
+gcry_mac_spec_t _gcry_mac_type_spec_hmac_gost3411_94 =
+  {
+    GCRY_MAC_HMAC_GOSTR3411_94, {0, 0}, "HMAC_GOSTR3411_94",
+    &hmac_ops
+  };
+#endif
+#ifdef USE_GOST_R_3411_12
+gcry_mac_spec_t _gcry_mac_type_spec_hmac_stribog256 =
+  {
+    GCRY_MAC_HMAC_STRIBOG256, {0, 0}, "HMAC_STRIBOG256",
+    &hmac_ops
+  };
+gcry_mac_spec_t _gcry_mac_type_spec_hmac_stribog512 =
+  {
+    GCRY_MAC_HMAC_STRIBOG512, {0, 0}, "HMAC_STRIBOG512",
+    &hmac_ops
+  };
+#endif
+#if USE_WHIRLPOOL
+gcry_mac_spec_t _gcry_mac_type_spec_hmac_whirlpool =
+  {
+    GCRY_MAC_HMAC_WHIRLPOOL, {0, 0}, "HMAC_WHIRLPOOL",
+    &hmac_ops
+  };
+#endif
+#if USE_RMD160
+gcry_mac_spec_t _gcry_mac_type_spec_hmac_rmd160 =
+  {
+    GCRY_MAC_HMAC_RMD160, {0, 0}, "HMAC_RIPEMD160",
+    &hmac_ops
+  };
+#endif
+#if USE_TIGER
+gcry_mac_spec_t _gcry_mac_type_spec_hmac_tiger1 =
+  {
+    GCRY_MAC_HMAC_TIGER1, {0, 0}, "HMAC_TIGER",
+    &hmac_ops
+  };
+#endif
+#if USE_MD5
+gcry_mac_spec_t _gcry_mac_type_spec_hmac_md5 =
+  {
+    GCRY_MAC_HMAC_MD5, {0, 0}, "HMAC_MD5",
+    &hmac_ops
+  };
+#endif
+#if USE_MD4
+gcry_mac_spec_t _gcry_mac_type_spec_hmac_md4 =
+  {
+    GCRY_MAC_HMAC_MD4, {0, 0}, "HMAC_MD4",
+    &hmac_ops
+  };
+#endif
diff --git a/cipher/mac-internal.h b/cipher/mac-internal.h
new file mode 100644
index 0000000..9ebc035
--- /dev/null
+++ b/cipher/mac-internal.h
@@ -0,0 +1,140 @@
+/* mac-internal.h  -  Internal defs for mac.c
+ * 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/>.
+ */
+
+/* The data object used to hold a handle to an encryption object.  */
+struct gcry_mac_handle;
+typedef struct gcry_mac_handle *gcry_mac_hd_t;
+
+

+/*
+ *
+ * Message authentication code related definitions.
+ *
+ */
+
+
+/* Magic values for the context structure.  */
+#define CTX_MAGIC_NORMAL 0x59d9b8af
+#define CTX_MAGIC_SECURE 0x12c27cd0
+
+
+/* MAC module functions. */
+typedef gcry_err_code_t (*gcry_mac_open_func_t)(gcry_mac_hd_t h);
+typedef void (*gcry_mac_close_func_t)(gcry_mac_hd_t h);
+typedef gcry_err_code_t (*gcry_mac_setkey_func_t)(gcry_mac_hd_t h,
+						  const unsigned char *key,
+						  size_t keylen);
+typedef gcry_err_code_t (*gcry_mac_setiv_func_t)(gcry_mac_hd_t h,
+						 const unsigned char *iv,
+						 size_t ivlen);
+typedef gcry_err_code_t (*gcry_mac_reset_func_t)(gcry_mac_hd_t h);
+typedef gcry_err_code_t (*gcry_mac_write_func_t)(gcry_mac_hd_t h,
+						 const unsigned char *inbuf,
+						 size_t inlen);
+typedef gcry_err_code_t (*gcry_mac_read_func_t)(gcry_mac_hd_t h,
+						unsigned char *outbuf,
+						size_t *outlen);
+typedef gcry_err_code_t (*gcry_mac_verify_func_t)(gcry_mac_hd_t h,
+						  const unsigned char *inbuf,
+						  size_t inlen);
+typedef unsigned int (*gcry_mac_get_maclen_func_t)(int algo);
+typedef unsigned int (*gcry_mac_get_keylen_func_t)(int algo);
+
+
+typedef struct gcry_mac_spec_ops
+{
+  gcry_mac_open_func_t open;
+  gcry_mac_close_func_t close;
+  gcry_mac_setkey_func_t setkey;
+  gcry_mac_setiv_func_t setiv;
+  gcry_mac_reset_func_t reset;
+  gcry_mac_write_func_t write;
+  gcry_mac_read_func_t read;
+  gcry_mac_verify_func_t verify;
+  gcry_mac_get_maclen_func_t get_maclen;
+  gcry_mac_get_keylen_func_t get_keylen;
+} gcry_mac_spec_ops_t;
+
+
+/* Module specification structure for message authentication codes.  */
+typedef struct gcry_mac_spec
+{
+  int algo;
+  struct {
+    unsigned int disabled:1;
+    unsigned int fips:1;
+  } flags;
+  const char *name;
+  const gcry_mac_spec_ops_t *ops;
+} gcry_mac_spec_t;
+
+
+
+/* The handle structure.  */
+struct gcry_mac_handle
+{
+  int magic;
+  int algo;
+  const gcry_mac_spec_t *spec;
+  gcry_ctx_t gcry_ctx;
+  union {
+    struct {
+      gcry_md_hd_t md_ctx;
+      int md_algo;
+    } hmac;
+  } u;
+};
+
+
+/*
+ * The HMAC algorithm specifications (mac-hmac.c).
+ */
+#if USE_SHA1
+extern gcry_mac_spec_t _gcry_mac_type_spec_hmac_sha1;
+#endif
+#if USE_SHA256
+extern gcry_mac_spec_t _gcry_mac_type_spec_hmac_sha256;
+extern gcry_mac_spec_t _gcry_mac_type_spec_hmac_sha224;
+#endif
+#if USE_SHA512
+extern gcry_mac_spec_t _gcry_mac_type_spec_hmac_sha512;
+extern gcry_mac_spec_t _gcry_mac_type_spec_hmac_sha384;
+#endif
+#ifdef USE_GOST_R_3411_94
+extern gcry_mac_spec_t _gcry_mac_type_spec_hmac_gost3411_94;
+#endif
+#ifdef USE_GOST_R_3411_12
+extern gcry_mac_spec_t _gcry_mac_type_spec_hmac_stribog256;
+extern gcry_mac_spec_t _gcry_mac_type_spec_hmac_stribog512;
+#endif
+#if USE_WHIRLPOOL
+extern gcry_mac_spec_t _gcry_mac_type_spec_hmac_whirlpool;
+#endif
+#if USE_RMD160
+extern gcry_mac_spec_t _gcry_mac_type_spec_hmac_rmd160;
+#endif
+#if USE_TIGER
+extern gcry_mac_spec_t _gcry_mac_type_spec_hmac_tiger1;
+#endif
+#if USE_MD5
+extern gcry_mac_spec_t _gcry_mac_type_spec_hmac_md5;
+#endif
+#if USE_MD4
+extern gcry_mac_spec_t _gcry_mac_type_spec_hmac_md4;
+#endif
diff --git a/cipher/mac.c b/cipher/mac.c
new file mode 100644
index 0000000..54757bb
--- /dev/null
+++ b/cipher/mac.c
@@ -0,0 +1,449 @@
+/* mac.c  -  message authentication code dispatcher
+ * 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 "mac-internal.h"
+
+
+/* This is the list of the digest implementations included in
+   libgcrypt.  */
+static gcry_mac_spec_t *mac_list[] =
+  {
+#if USE_SHA1
+     &_gcry_mac_type_spec_hmac_sha1,
+#endif
+#if USE_SHA256
+     &_gcry_mac_type_spec_hmac_sha256,
+     &_gcry_mac_type_spec_hmac_sha224,
+#endif
+#if USE_SHA512
+     &_gcry_mac_type_spec_hmac_sha512,
+     &_gcry_mac_type_spec_hmac_sha384,
+#endif
+#ifdef USE_GOST_R_3411_94
+     &_gcry_mac_type_spec_hmac_gost3411_94,
+#endif
+#ifdef USE_GOST_R_3411_12
+     &_gcry_mac_type_spec_hmac_stribog256,
+     &_gcry_mac_type_spec_hmac_stribog512,
+#endif
+#if USE_WHIRLPOOL
+     &_gcry_mac_type_spec_hmac_whirlpool,
+#endif
+#if USE_RMD160
+     &_gcry_mac_type_spec_hmac_rmd160,
+#endif
+#if USE_TIGER
+     &_gcry_mac_type_spec_hmac_tiger1,
+#endif
+#if USE_MD5
+     &_gcry_mac_type_spec_hmac_md5,
+#endif
+#if USE_MD4
+     &_gcry_mac_type_spec_hmac_md4,
+#endif
+     NULL,
+  };
+
+
+

+
+/* Return the spec structure for the MAC algorithm ALGO.  For an
+   unknown algorithm NULL is returned.  */
+static gcry_mac_spec_t *
+spec_from_algo (int algo)
+{
+  gcry_mac_spec_t *spec;
+  int idx;
+
+  for (idx = 0; (spec = mac_list[idx]); idx++)
+    if (algo == spec->algo)
+      return spec;
+  return NULL;
+}
+
+
+/* Lookup a mac's spec by its name.  */
+static gcry_mac_spec_t *
+spec_from_name (const char *name)
+{
+  gcry_mac_spec_t *spec;
+  int idx;
+
+  for (idx=0; (spec = mac_list[idx]); idx++)
+      if (!stricmp (name, spec->name))
+        return spec;
+
+  return NULL;
+}
+
+
+/****************
+ * Map a string to the mac algo
+ */
+int
+gcry_mac_map_name (const char *string)
+{
+  gcry_mac_spec_t *spec;
+
+  if (!string)
+    return 0;
+
+  /* Not found, search a matching mac name.  */
+  spec = spec_from_name (string);
+  if (spec)
+    return spec->algo;
+
+  return 0;
+}
+
+
+/****************
+ * This function simply returns the name of the algorithm or some constant
+ * string when there is no algo.  It will never return NULL.
+ * Use the macro gcry_mac_test_algo() to check whether the algorithm
+ * is valid.
+ */
+const char *
+gcry_mac_algo_name (int algorithm)
+{
+  gcry_mac_spec_t *spec;
+
+  spec = spec_from_algo (algorithm);
+  return spec ? spec->name : "?";
+}
+
+
+static gcry_err_code_t
+check_mac_algo (int algorithm)
+{
+  gcry_mac_spec_t *spec;
+
+  spec = spec_from_algo (algorithm);
+  if (spec && !spec->flags.disabled)
+    return 0;
+
+  return GPG_ERR_MAC_ALGO;
+}
+
+
+/****************
+ * Open a message digest handle for use with algorithm ALGO.
+ */
+static gcry_err_code_t
+mac_open (gcry_mac_hd_t *hd, int algo, int secure, gcry_ctx_t ctx)
+{
+  gcry_mac_spec_t *spec;
+  gcry_err_code_t err;
+  gcry_mac_hd_t h;
+
+  spec = spec_from_algo (algo);
+  if (!spec)
+    return GPG_ERR_MAC_ALGO;
+  else if (spec->flags.disabled)
+    return GPG_ERR_MAC_ALGO;
+  else if (!spec->ops)
+    return GPG_ERR_MAC_ALGO;
+  else if (!spec->ops->open || !spec->ops->write || !spec->ops->setkey ||
+           !spec->ops->read || !spec->ops->verify || !spec->ops->reset)
+    return GPG_ERR_MAC_ALGO;
+
+  if (secure)
+    h = gcry_calloc_secure (1, sizeof(*h));
+  else
+    h = gcry_calloc (1, sizeof(*h));
+
+  if (!h)
+    return gpg_err_code_from_syserror ();
+
+  h->magic = secure ? CTX_MAGIC_SECURE : CTX_MAGIC_NORMAL;
+  h->spec = spec;
+  h->algo = algo;
+  h->gcry_ctx = ctx;
+
+  err = h->spec->ops->open (h);
+  if (err)
+    gcry_free (h);
+  else
+    *hd = h;
+
+  return err;
+}
+
+
+static gcry_error_t
+mac_reset (gcry_mac_hd_t hd)
+{
+  if (hd->spec->ops->reset)
+    return hd->spec->ops->reset (hd);
+
+  return 0;
+}
+
+
+static void
+mac_close (gcry_mac_hd_t hd)
+{
+  if (hd->spec->ops->reset)
+    hd->spec->ops->close(hd);
+
+  wipememory (hd, sizeof(*hd));
+
+  gcry_free (hd);
+}
+
+
+static gcry_error_t
+mac_setkey (gcry_mac_hd_t hd, const void *key, size_t keylen)
+{
+  if (!hd->spec->ops->setkey)
+    return GPG_ERR_INV_ARG;
+  if (keylen > 0 && !key)
+    return GPG_ERR_INV_ARG;
+
+  return hd->spec->ops->setkey(hd, key, keylen);
+}
+
+
+static gcry_error_t
+mac_setiv (gcry_mac_hd_t hd, const void *iv, size_t ivlen)
+{
+  if (!hd->spec->ops->setiv)
+    return GPG_ERR_INV_ARG;
+  if (ivlen > 0 && !iv)
+    return GPG_ERR_INV_ARG;
+
+  return hd->spec->ops->setiv(hd, iv, ivlen);
+}
+
+
+static gcry_error_t
+mac_write (gcry_mac_hd_t hd, const void *inbuf, size_t inlen)
+{
+  if (!hd->spec->ops->write)
+    return GPG_ERR_INV_ARG;
+  if (inlen > 0 && !inbuf)
+    return GPG_ERR_INV_ARG;
+
+  return hd->spec->ops->write(hd, inbuf, inlen);
+}
+
+
+static gcry_error_t
+mac_read (gcry_mac_hd_t hd, void *outbuf, size_t *outlen)
+{
+  if (!outbuf || !outlen || *outlen == 0 || !hd->spec->ops->read)
+    return GPG_ERR_INV_ARG;
+
+  return hd->spec->ops->read(hd, outbuf, outlen);
+}
+
+
+static gcry_error_t
+mac_verify (gcry_mac_hd_t hd, const void *buf, size_t buflen)
+{
+  if (!buf || buflen == 0 || !hd->spec->ops->verify)
+    return GPG_ERR_INV_ARG;
+
+  return hd->spec->ops->verify(hd, buf, buflen);
+}
+
+
+/* Create a MAC object for algorithm ALGO.  FLAGS may be
+   given as an bitwise OR of the gcry_mac_flags values.
+   H is guaranteed to be a valid handle or NULL on error.  */
+gcry_error_t
+gcry_mac_open (gcry_mac_hd_t *h, int algo, unsigned int flags, gcry_ctx_t ctx)
+{
+  gcry_err_code_t err;
+  gcry_mac_hd_t hd = NULL;
+
+  if ((flags & ~GCRY_MAC_FLAG_SECURE))
+    err = GPG_ERR_INV_ARG;
+  else
+    err = mac_open(&hd, algo, !!(flags & GCRY_MAC_FLAG_SECURE), ctx);
+
+  *h = err ? NULL : hd;
+  return gpg_error (err);
+}
+
+
+void
+gcry_mac_close (gcry_mac_hd_t hd)
+{
+  mac_close(hd);
+}
+
+
+gcry_error_t
+gcry_mac_setkey (gcry_mac_hd_t hd, const void *key, size_t keylen)
+{
+  gcry_error_t err;
+
+  err = mac_setkey(hd, key, keylen);
+
+  return gpg_error (err);
+}
+
+
+gcry_error_t
+gcry_mac_setiv (gcry_mac_hd_t hd, const void *iv, size_t ivlen)
+{
+  gcry_error_t err;
+
+  err = mac_setiv(hd, iv, ivlen);
+
+  return gpg_error (err);
+}
+
+
+gcry_error_t
+gcry_mac_write (gcry_mac_hd_t hd, const void *inbuf, size_t inlen)
+{
+  gcry_err_code_t err;
+
+  err = mac_write(hd, inbuf, inlen);
+
+  return gpg_error (err);
+}
+
+
+gcry_error_t
+gcry_mac_read (gcry_mac_hd_t hd, void *outbuf, size_t *outlen)
+{
+  gcry_error_t err;
+
+  err = mac_read(hd, outbuf, outlen);
+
+  return gpg_error (err);
+}
+
+
+gcry_error_t
+gcry_mac_verify (gcry_mac_hd_t hd, const void *buf, size_t buflen)
+{
+  gcry_err_code_t err;
+
+  err = mac_verify(hd, buf, buflen);
+
+  return gpg_error (err);
+}
+
+
+unsigned int
+gcry_mac_get_algo_maclen (int algo)
+{
+  gcry_mac_spec_t *spec;
+
+  spec = spec_from_algo (algo);
+  if (!spec || !spec->ops || !spec->ops->get_maclen)
+    return 0;
+
+  return spec->ops->get_maclen (algo);
+}
+
+
+unsigned int
+gcry_mac_get_algo_keylen (int algo)
+{
+  gcry_mac_spec_t *spec;
+
+  spec = spec_from_algo (algo);
+  if (!spec || !spec->ops || !spec->ops->get_keylen)
+    return 0;
+
+  return spec->ops->get_keylen (algo);
+}
+
+
+gcry_error_t
+gcry_mac_ctl (gcry_mac_hd_t hd, int cmd, void *buffer, size_t buflen)
+{
+  gcry_err_code_t rc = GPG_ERR_NO_ERROR;
+
+  /* Currently not used.  */
+  (void)hd;
+  (void)buffer;
+  (void)buflen;
+
+  switch (cmd)
+    {
+    case GCRYCTL_RESET:
+      rc = mac_reset (hd);
+      break;
+    default:
+      rc = GPG_ERR_INV_OP;
+    }
+  return gcry_error (rc);
+}
+
+
+/* Return information about the given MAC algorithm ALGO.
+
+    GCRYCTL_TEST_ALGO:
+  	Returns 0 if the specified algorithm ALGO is available for use.
+  	BUFFER and NBYTES must be zero.
+
+   Note: Because this function is in most cases used to return an
+   integer value, we can make it easier for the caller to just look at
+   the return value.  The caller will in all cases consult the value
+   and thereby detecting whether a error occurred or not (i.e. while
+   checking the block size)
+ */
+gcry_error_t
+gcry_mac_algo_info (int algo, int what, void *buffer, size_t *nbytes)
+{
+  gcry_err_code_t err = GPG_ERR_NO_ERROR;
+  unsigned int ui;
+
+  switch (what)
+    {
+    case GCRYCTL_GET_KEYLEN:
+      if (buffer || (!nbytes))
+	err = GPG_ERR_INV_ARG;
+      else
+	{
+	  ui = gcry_mac_get_algo_keylen (algo);
+	  if (ui > 0)
+	    *nbytes = (size_t) ui;
+	  else
+	    /* The only reason for an error is an invalid algo.  */
+	    err = GPG_ERR_MAC_ALGO;
+	}
+      break;
+    case GCRYCTL_TEST_ALGO:
+      if (buffer || nbytes)
+	err = GPG_ERR_INV_ARG;
+      else
+	err = check_mac_algo (algo);
+      break;
+
+    default:
+      err = GPG_ERR_INV_OP;
+    }
+
+  return gcry_error (err);
+}
+
diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi
index 1619f0d..0baafde 100644
--- a/doc/gcrypt.texi
+++ b/doc/gcrypt.texi
@@ -86,13 +86,14 @@ section entitled ``GNU General Public License''.
 * Handler Functions::            Working with handler functions.
 * Symmetric cryptography::       How to use symmetric cryptography.
 * Public Key cryptography::      How to use public key cryptography.
-* Hashing::                      How to use hash and MAC algorithms.
+* Hashing::                      How to use hash algorithms.
+* Message Authentication Codes:: How to use MAC algorithms.
 * Key Derivation::               How to derive keys from strings
 * Random Numbers::               How to work with random numbers.
 * S-expressions::                How to manage S-expressions.
 * MPI library::                  How to work with multi-precision-integers.
 * Prime numbers::                How to use the Prime number related functions.
-* Utilities::                   Utility functions.
+* Utilities::                    Utility functions.
 * Tools::                        Utility tools
 * Architecture::                 How Libgcrypt works internally.
 
@@ -2967,7 +2968,7 @@ function will be extended to cover more algorithms.
 
 Libgcrypt provides an easy and consistent to use interface for hashing.
 Hashing is buffered and several hash algorithms can be updated at once.
-It is possible to compute a MAC using the same routines.  The
+It is possible to compute a HMAC using the same routines.  The
 programming model follows an open/process/close paradigm and is in that
 similar to other building blocks provided by Libgcrypt.
 
@@ -3395,6 +3396,253 @@ because @code{gcry_md_close} implicitly stops debugging.
 @end deftypefun
 
 
+
+ at c **********************************************************
+ at c *******************  MAC Functions  **********************
+ at c **********************************************************
+ at node Message Authentication Codes
+ at chapter Message Authentication Codes
+
+Libgcrypt provides an easy and consistent to use interface for generating
+Message Authentication Codes (MAC). MAC generation is buffered and interface
+similar to the one used with hash algorithms. The programming model follows
+an open/process/close paradigm and is in that similar to other building blocks
+provided by Libgcrypt.
+
+ at menu
+* Available MAC algorithms::   List of MAC algorithms supported by the library.
+* Working with MAC algorithms::  List of functions related to MAC algorithms.
+ at end menu
+
+ at node Available MAC algorithms
+ at section Available MAC algorithms
+
+ at c begin table of MAC algorithms
+ at cindex HMAC-SHA-1
+ at cindex HMAC-SHA-224, HMAC-SHA-256, HMAC-SHA-384, HMAC-SHA-512
+ at cindex HMAC-RIPE-MD-160
+ at cindex HMAC-MD2, HMAC-MD4, HMAC-MD5
+ at cindex HMAC-TIGER1
+ at cindex HMAC-Whirlpool
+ at cindex HMAC-Stribog-256, HMAC-Stribog-512
+ at cindex HMAC-GOSTR-3411-94
+ at table @code
+ at item GCRY_MAC_NONE
+This is not a real algorithm but used by some functions as an error
+return value.  This constant is guaranteed to have the value @code{0}.
+
+ at item GCRY_MAC_HMAC_SHA256
+This is keyed-hash message authentication code (HMAC) message authentication
+algorithm based on the SHA-256 hash algorithm.
+
+ at item GCRY_MAC_HMAC_SHA224
+This is HMAC message authentication algorithm based on the SHA-224 hash
+algorithm.
+
+ at item GCRY_MAC_HMAC_SHA512
+This is HMAC message authentication algorithm based on the SHA-512 hash
+algorithm.
+
+ at item GCRY_MAC_HMAC_SHA384
+This is HMAC message authentication algorithm based on the SHA-384 hash
+algorithm.
+
+ at item GCRY_MAC_HMAC_SHA1
+This is HMAC message authentication algorithm based on the SHA-1 hash
+algorithm.
+
+ at item GCRY_MAC_HMAC_MD5
+This is HMAC message authentication algorithm based on the MD5 hash
+algorithm.
+
+ at item GCRY_MAC_HMAC_MD4
+This is HMAC message authentication algorithm based on the MD4 hash
+algorithm.
+
+ at item GCRY_MAC_HMAC_RMD160
+This is HMAC message authentication algorithm based on the RIPE-MD-160 hash
+algorithm.
+
+ at item GCRY_MAC_HMAC_WHIRLPOOL
+This is HMAC message authentication algorithm based on the WHIRLPOOL hash
+algorithm.
+
+ at item GCRY_MAC_HMAC_GOSTR3411_94
+This is HMAC message authentication algorithm based on the GOST R 34.11-94 hash
+algorithm.
+
+ at item GCRY_MAC_HMAC_STRIBOG256
+This is HMAC message authentication algorithm based on the 256-bit hash
+algorithm described in GOST R 34.11-2012.
+
+ at item GCRY_MAC_HMAC_STRIBOG512
+This is HMAC message authentication algorithm based on the 512-bit hash
+algorithm described in GOST R 34.11-2012.
+
+ at end table
+ at c end table of MAC algorithms
+
+ at node Working with MAC algorithms
+ at section Working with MAC algorithms
+
+To use most of these function it is necessary to create a context;
+this is done using:
+
+ at deftypefun gcry_error_t gcry_mac_open (gcry_mac_hd_t *@var{hd}, int @var{algo}, unsigned int @var{flags}, gcry_ctx_t @var{ctx})
+
+Create a MAC object for algorithm @var{algo}. @var{flags} may be given as an
+bitwise OR of constants described below. @var{hd} is guaranteed to either
+receive a valid handle or NULL. @var{ctx} is context object to associate MAC
+object with. @var{ctx} maybe set to NULL.
+
+For a list of supported algorithms, see @xref{Available MAC algorithms}.
+
+The flags allowed for @var{mode} are:
+
+ at c begin table of MAC flags
+ at table @code
+ at item GCRY_MAC_FLAG_SECURE
+Allocate all buffers and the resulting MAC in "secure memory".  Use this if the
+MAC data is highly confidential.
+
+ at end table
+ at c begin table of MAC flags
+
+ at end deftypefun
+ at c end function gcry_mac_open
+
+
+In order to use a handle for performing MAC algorithm operations, a
+`key' has to be set first:
+
+ at deftypefun gcry_error_t gcry_mac_setkey (gcry_mac_hd_t @var{h}, const void *@var{key}, size_t @var{keylen})
+
+Set the MAC key to the value of @var{key} of length @var{keylen} bytes. With
+HMAC algorithms, there is no restriction on the length of the key.
+ at end deftypefun
+
+
+ at c Some MAC algorithms need initialization vector to be set, which can be
+ at c performed with function:
+ at c 
+ at c @deftypefun gcry_error_t gcry_mac_setiv (gcry_mac_hd_t @var{h}, const void *@var{iv}, size_t @var{ivlen})
+ at c 
+ at c Set the IV to the value of @var{iv} of length @var{ivlen} bytes.
+ at c @end deftypefun
+
+
+After you are done with the MAC calculation, you should release the resources
+by using:
+
+ at deftypefun void gcry_mac_close (gcry_mac_hd_t @var{h})
+
+Release all resources of MAC context @var{h}.  @var{h} should not be
+used after a call to this function.  A @code{NULL} passed as @var{h} is
+ignored.  The function also clears all sensitive information associated
+with this handle.
+ at end deftypefun
+
+
+Often you have to do several MAC operations using the same algorithm.
+To avoid the overhead of creating and releasing context, a reset function
+is provided:
+
+ at deftypefun gcry_error_t gcry_mac_reset (gcry_mac_hd_t @var{h})
+
+Reset the current context to its initial state. This is effectively identical
+to a close followed by an open and setting same key.
+
+Note that gcry_mac_reset is implemented as a macro.
+ at end deftypefun
+
+
+Now that we have prepared everything to calculate MAC, it is time to
+see how it is actually done.
+
+ at deftypefun gcry_error_t gcry_mac_write (gcry_mac_hd_t @var{h}, const void *@var{buffer}, size_t @var{length})
+
+Pass @var{length} bytes of the data in @var{buffer} to the MAC object
+with handle @var{h} to update the MAC values.
+ at end deftypefun
+
+
+The way to read out the calculated MAC is by using the function:
+
+ at deftypefun gcry_error_t gcry_mac_read (gcry_mac_hd_t @var{h}, void *@var{buffer}, size_t *@var{length})
+
+ at code{gcry_mac_read} returns the MAC after finalizing the calculation.
+Function copies the resulting MAC value to @var{buffer} of the length
+ at var{length}. If @var{length} is larger than length of resulting MAC value,
+then length of MAC is returned through @var{length}.
+ at end deftypefun
+
+
+To compare existing MAC value with recalculated MAC, one is to use the function:
+
+ at deftypefun gcry_error_t gcry_mac_verify (gcry_mac_hd_t @var{h}, void *@var{buffer}, size_t @var{length})
+
+ at code{gcry_mac_verify} finalizes MAC calculation and compares result with
+ at var{length} bytes of data in @var{buffer}. Error code @code{GPG_ERR_CHECKSUM}
+is returned if the MAC value in the buffer @var{buffer} does not match
+the MAC calculated in object @var{h}.
+ at end deftypefun
+
+
+ at c ***********************************
+ at c ***** MAC info functions **********
+ at c ***********************************
+
+MAC algorithms are identified by internal algorithm numbers (see
+ at code{gcry_mac_open} for a list).  However, in most applications they are
+used by names, so two functions are available to map between string
+representations and MAC algorithm identifiers.
+
+ at deftypefun {const char *} gcry_mac_algo_name (int @var{algo})
+
+Map the MAC algorithm id @var{algo} to a string representation of the
+algorithm name.  For unknown algorithms this function returns the
+string @code{"?"}.  This function should not be used to test for the
+availability of an algorithm.
+ at end deftypefun
+
+ at deftypefun int gcry_mac_map_name (const char *@var{name})
+
+Map the algorithm with @var{name} to a MAC algorithm identifier.
+Returns 0 if the algorithm name is not known. This function should not
+be used to test for the availability of an algorithm.
+ at end deftypefun
+
+
+To test whether an algorithm is actually available for use, the
+following macro should be used:
+
+ at deftypefun gcry_error_t gcry_mac_test_algo (int @var{algo})
+
+The macro returns 0 if the MAC algorithm @var{algo} is available for use.
+ at end deftypefun
+
+
+If the length of a message digest is not known, it can be retrieved
+using the following function:
+
+ at deftypefun {unsigned int} gcry_mac_get_algo_maclen (int @var{algo})
+
+Retrieve the length in bytes of the MAC yielded by algorithm @var{algo}.
+This is often used prior to @code{gcry_mac_read} to allocate sufficient memory
+for the MAC value. On error @code{0} is returned.
+ at end deftypefun
+
+
+ at deftypefun unsigned int gcry_mac_get_algo_keylen (@var{algo})
+
+This function returns length of the key for MAC algorithm @var{algo}.  If
+the algorithm supports multiple key lengths, the default supported key
+length is returned.  On error @code{0} is returned.  The key length is
+returned as number of octets.
+ at end deftypefun
+
+
+
 @c *******************************************************
 @c *******************  KDF  *****************************
 @c *******************************************************
diff --git a/src/gcrypt-int.h b/src/gcrypt-int.h
index 295328b..974fef9 100644
--- a/src/gcrypt-int.h
+++ b/src/gcrypt-int.h
@@ -37,4 +37,8 @@
 # define GPG_ERR_BROKEN_SECKEY      196
 #endif
 
+#if GPG_ERROR_VERSION_NUMBER < 0x010d00  /* 1.13 */
+# define GPG_ERR_MAC_ALGO           197
+#endif
+
 #endif /*GCRY_GCRYPT_INT_H*/
diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in
index fffc15c..b352f56 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -1279,6 +1279,103 @@ void gcry_md_debug (gcry_md_hd_t hd, const char *suffix);
 #define gcry_md_get_asnoid(a,b,n) \
             gcry_md_algo_info((a), GCRYCTL_GET_ASNOID, (b), (n))
 
+

+
+/**********************************************
+ *                                            *
+ *   Message Authentication Code Functions    *
+ *                                            *
+ **********************************************/
+
+/* The data object used to hold a handle to an encryption object.  */
+struct gcry_mac_handle;
+typedef struct gcry_mac_handle *gcry_mac_hd_t;
+
+/* Algorithm IDs for the hash functions we know about. Not all of them
+   are implemented. */
+enum gcry_mac_algos
+  {
+    GCRY_MAC_NONE               = 0,
+
+    GCRY_MAC_HMAC_SHA256        = 1,
+    GCRY_MAC_HMAC_SHA224        = 2,
+    GCRY_MAC_HMAC_SHA512        = 3,
+    GCRY_MAC_HMAC_SHA384        = 4,
+    GCRY_MAC_HMAC_SHA1          = 5,
+    GCRY_MAC_HMAC_MD5           = 6,
+    GCRY_MAC_HMAC_MD4           = 7,
+    GCRY_MAC_HMAC_RMD160        = 8,
+    GCRY_MAC_HMAC_TIGER1        = 9, /* The fixed TIGER variant */
+    GCRY_MAC_HMAC_WHIRLPOOL     = 10,
+    GCRY_MAC_HMAC_GOSTR3411_94  = 11,
+    GCRY_MAC_HMAC_STRIBOG256    = 12,
+    GCRY_MAC_HMAC_STRIBOG512    = 13
+  };
+
+/* Flags used with the open function.  */
+enum gcry_mac_flags
+  {
+    GCRY_MAC_FLAG_SECURE = 1,  /* Allocate all buffers in "secure" memory.  */
+  };
+
+/* Create a MAC handle for algorithm ALGO.  FLAGS may be given as an bitwise OR
+   of the gcry_mac_flags values.  CTX maybe NULL or gcry_ctx_t object to be
+   associated with HANDLE.  */
+gcry_error_t gcry_mac_open (gcry_mac_hd_t *handle, int algo,
+                            unsigned int flags, gcry_ctx_t ctx);
+
+/* Close the MAC handle H and release all resource. */
+void gcry_mac_close (gcry_mac_hd_t h);
+
+/* Perform various operations on the MAC object H. */
+gcry_error_t gcry_mac_ctl (gcry_mac_hd_t h, int cmd, void *buffer,
+                           size_t buflen);
+
+/* Retrieve various information about the MAC algorithm ALGO. */
+gcry_error_t gcry_mac_algo_info (int algo, int what, void *buffer,
+                                 size_t *nbytes);
+
+/* Set KEY of length KEYLEN bytes for the MAC handle HD.  */
+gcry_error_t gcry_mac_setkey (gcry_mac_hd_t hd, const void *key,
+                              size_t keylen);
+
+/* Set initialization vector IV of length IVLEN for the MAC handle HD. */
+gcry_error_t gcry_mac_setiv (gcry_mac_hd_t hd, const void *iv,
+                             size_t ivlen);
+
+/* Pass LENGTH bytes of data in BUFFER to the MAC object HD so that
+   it can update the MAC values.  */
+gcry_error_t gcry_mac_write (gcry_mac_hd_t hd, const void *buffer,
+                             size_t length);
+
+/* Read out the final authentication code from the MAC object HD to BUFFER. */
+gcry_error_t gcry_mac_read (gcry_mac_hd_t hd, void *buffer, size_t *buflen);
+
+/* Verify the final authentication code from the MAC object HD with BUFFER. */
+gcry_error_t gcry_mac_verify (gcry_mac_hd_t hd, const void *buffer,
+                              size_t buflen);
+
+/* Retrieve the length in bytes of the MAC yielded by algorithm ALGO. */
+unsigned int gcry_mac_get_algo_maclen (int algo);
+
+/* Retrieve the default key length in bytes used with algorithm A. */
+unsigned int gcry_mac_get_algo_keylen (int algo);
+
+/* Map the MAC algorithm whose ID is contained in ALGORITHM to a
+   string representation of the algorithm name.  For unknown algorithm
+   IDs this function returns "?".  */
+const char *gcry_mac_algo_name (int algorithm) _GCRY_GCC_ATTR_PURE;
+
+/* Map the algorithm name NAME to an MAC algorithm ID.  Return 0 if
+   the algorithm name is not known. */
+int gcry_mac_map_name (const char *name) _GCRY_GCC_ATTR_PURE;
+
+/* Reset the handle to the state after open/setkey.  */
+#define gcry_mac_reset(h)  gcry_mac_ctl ((h), GCRYCTL_RESET, NULL, 0)
+
+/* Return 0 if the algorithm A is available for use. */
+#define gcry_mac_test_algo(a) \
+            gcry_mac_algo_info( (a), GCRYCTL_TEST_ALGO, NULL, NULL )
 
 

 /******************************
diff --git a/src/libgcrypt.def b/src/libgcrypt.def
index e2aba2b..a90efce 100644
--- a/src/libgcrypt.def
+++ b/src/libgcrypt.def
@@ -261,5 +261,19 @@ EXPORTS
 
       gcry_mpi_set_opaque_copy  @229
 
+      gcry_mac_algo_info        @230
+      gcry_mac_algo_name        @231
+      gcry_mac_map_name         @232
+      gcry_mac_get_algo_maclen  @233
+      gcry_mac_get_algo_keylen  @234
+      gcry_mac_open             @235
+      gcry_mac_close            @236
+      gcry_mac_setkey           @237
+      gcry_mac_setiv            @238
+      gcry_mac_write            @239
+      gcry_mac_read             @240
+      gcry_mac_verify           @241
+      gcry_mac_ctl              @242
+
 
 ;; end of file with public symbols for Windows.
diff --git a/src/libgcrypt.vers b/src/libgcrypt.vers
index c3a09b8..5118c81 100644
--- a/src/libgcrypt.vers
+++ b/src/libgcrypt.vers
@@ -53,6 +53,11 @@ GCRYPT_1.6 {
     gcry_cipher_setkey; gcry_cipher_setiv; gcry_cipher_setctr;
     gcry_cipher_authenticate; gcry_cipher_gettag; gcry_cipher_checktag;
 
+    gcry_mac_algo_info; gcry_mac_algo_name; gcry_mac_map_name;
+    gcry_mac_get_algo_maclen; gcry_mac_get_algo_keylen;
+    gcry_mac_open; gcry_mac_close; gcry_mac_setkey; gcry_mac_setiv;
+    gcry_mac_write; gcry_mac_read; gcry_mac_verify; gcry_mac_ctl;
+
     gcry_pk_algo_info; gcry_pk_algo_name; gcry_pk_ctl;
     gcry_pk_decrypt; gcry_pk_encrypt; gcry_pk_genkey;
     gcry_pk_get_keygrip; gcry_pk_get_nbits;
diff --git a/src/visibility.c b/src/visibility.c
index 852bdf9..3235746 100644
--- a/src/visibility.c
+++ b/src/visibility.c
@@ -829,6 +829,112 @@ gcry_cipher_get_algo_blklen (int algo)
 }
 
 gcry_error_t
+gcry_mac_algo_info (int algo, int what, void *buffer, size_t *nbytes)
+{
+  if (!fips_is_operational ())
+    return gpg_error (fips_not_operational ());
+
+  return _gcry_mac_algo_info (algo, what, buffer, nbytes);
+}
+
+const char *
+gcry_mac_algo_name (int algorithm)
+{
+  return _gcry_mac_algo_name (algorithm);
+}
+
+int
+gcry_mac_map_name (const char *string)
+{
+  return _gcry_mac_map_name (string);
+}
+
+unsigned int
+gcry_mac_get_algo_maclen (int algo)
+{
+  return _gcry_mac_get_algo_maclen (algo);
+}
+
+unsigned int
+gcry_mac_get_algo_keylen (int algo)
+{
+  return _gcry_mac_get_algo_keylen (algo);
+}
+
+gcry_error_t
+gcry_mac_open (gcry_mac_hd_t *handle, int algo, unsigned int flags,
+	       gcry_ctx_t ctx)
+{
+  if (!fips_is_operational ())
+    {
+      *handle = NULL;
+      return gpg_error (fips_not_operational ());
+    }
+
+  return _gcry_mac_open (handle, algo, flags, ctx);
+}
+
+void
+gcry_mac_close (gcry_mac_hd_t hd)
+{
+  _gcry_mac_close (hd);
+}
+
+gcry_error_t
+gcry_mac_setkey (gcry_mac_hd_t hd, const void *key, size_t keylen)
+{
+  if (!fips_is_operational ())
+    return gpg_error (fips_not_operational ());
+
+  return _gcry_mac_setkey (hd, key, keylen);
+}
+
+gcry_error_t
+gcry_mac_setiv (gcry_mac_hd_t hd, const void *iv, size_t ivlen)
+{
+  if (!fips_is_operational ())
+    return gpg_error (fips_not_operational ());
+
+  return _gcry_mac_setiv (hd, iv, ivlen);
+}
+
+gcry_error_t
+gcry_mac_write (gcry_mac_hd_t hd, const void *buf, size_t buflen)
+{
+  if (!fips_is_operational ())
+    return gpg_error (fips_not_operational ());
+
+  return _gcry_mac_write (hd, buf, buflen);
+}
+
+gcry_error_t
+gcry_mac_read (gcry_mac_hd_t hd, void *outbuf, size_t *outlen)
+{
+  if (!fips_is_operational ())
+    return gpg_error (fips_not_operational ());
+
+  return _gcry_mac_read (hd, outbuf, outlen);
+}
+
+gcry_error_t
+gcry_mac_verify (gcry_mac_hd_t hd, const void *buf, size_t buflen)
+{
+  if (!fips_is_operational ())
+    return gpg_error (fips_not_operational ());
+
+  return _gcry_mac_verify (hd, buf, buflen);
+}
+
+gcry_error_t
+gcry_mac_ctl (gcry_mac_hd_t h, int cmd, void *buffer, size_t buflen)
+{
+  if (!fips_is_operational ())
+    return gpg_error (fips_not_operational ());
+
+  return _gcry_mac_ctl (h, cmd, buffer, buflen);
+}
+
+gcry_error_t
 gcry_pk_encrypt (gcry_sexp_t *result, gcry_sexp_t data, gcry_sexp_t pkey)
 {
   if (!fips_is_operational ())
diff --git a/src/visibility.h b/src/visibility.h
index 438db5b..318ff44 100644
--- a/src/visibility.h
+++ b/src/visibility.h
@@ -94,6 +94,20 @@
 #define gcry_cipher_mode_from_oid   _gcry_cipher_mode_from_oid
 #define gcry_cipher_open            _gcry_cipher_open
 
+#define gcry_mac_algo_info          _gcry_mac_algo_info
+#define gcry_mac_algo_name          _gcry_mac_algo_name
+#define gcry_mac_map_name           _gcry_mac_map_name
+#define gcry_mac_get_algo_maclen    _gcry_mac_get_algo_maclen
+#define gcry_mac_get_algo_keylen    _gcry_mac_get_algo_keylen
+#define gcry_mac_open               _gcry_mac_open
+#define gcry_mac_close              _gcry_mac_close
+#define gcry_mac_setkey             _gcry_mac_setkey
+#define gcry_mac_setiv              _gcry_mac_setiv
+#define gcry_mac_write              _gcry_mac_write
+#define gcry_mac_read               _gcry_mac_read
+#define gcry_mac_verify             _gcry_mac_verify
+#define gcry_mac_ctl                _gcry_mac_ctl
+
 #define gcry_pk_algo_info           _gcry_pk_algo_info
 #define gcry_pk_algo_name           _gcry_pk_algo_name
 #define gcry_pk_ctl                 _gcry_pk_ctl
@@ -313,6 +327,20 @@ gcry_err_code_t gcry_md_get (gcry_md_hd_t hd, int algo,
 #undef gcry_cipher_mode_from_oid
 #undef gcry_cipher_open
 
+#undef gcry_mac_algo_info
+#undef gcry_mac_algo_name
+#undef gcry_mac_map_name
+#undef gcry_mac_get_algo_maclen
+#undef gcry_mac_get_algo_keylen
+#undef gcry_mac_open
+#undef gcry_mac_close
+#undef gcry_mac_setkey
+#undef gcry_mac_setiv
+#undef gcry_mac_write
+#undef gcry_mac_read
+#undef gcry_mac_verify
+#undef gcry_mac_ctl
+
 #undef gcry_pk_algo_info
 #undef gcry_pk_algo_name
 #undef gcry_pk_ctl
@@ -493,6 +521,20 @@ MARK_VISIBLE (gcry_cipher_map_name)
 MARK_VISIBLE (gcry_cipher_mode_from_oid)
 MARK_VISIBLE (gcry_cipher_open)
 
+MARK_VISIBLE (gcry_mac_algo_info)
+MARK_VISIBLE (gcry_mac_algo_name)
+MARK_VISIBLE (gcry_mac_map_name)
+MARK_VISIBLE (gcry_mac_get_algo_maclen)
+MARK_VISIBLE (gcry_mac_get_algo_keylen)
+MARK_VISIBLE (gcry_mac_open)
+MARK_VISIBLE (gcry_mac_close)
+MARK_VISIBLE (gcry_mac_setkey)
+MARK_VISIBLE (gcry_mac_setiv)
+MARK_VISIBLE (gcry_mac_write)
+MARK_VISIBLE (gcry_mac_read)
+MARK_VISIBLE (gcry_mac_verify)
+MARK_VISIBLE (gcry_mac_ctl)
+
 MARK_VISIBLE (gcry_pk_algo_info)
 MARK_VISIBLE (gcry_pk_algo_name)
 MARK_VISIBLE (gcry_pk_ctl)
diff --git a/tests/basic.c b/tests/basic.c
index 99d63ae..e7569d8 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -4038,7 +4038,400 @@ check_hmac (void)
 
   if (verbose)
     fprintf (stderr, "Completed hashed MAC checks.\n");
- }
+}
+
+
+static void
+check_one_mac (int algo, const char *data, int datalen,
+	       const char *key, int keylen, const char *expect)
+{
+  gcry_mac_hd_t hd;
+  unsigned char *p;
+  unsigned int maclen;
+  size_t macoutlen;
+  int i;
+  gcry_error_t err = 0;
+
+  err = gcry_mac_open (&hd, algo, 0, NULL);
+  if (err)
+    {
+      fail ("algo %d, gcry_mac_open failed: %s\n", algo, gpg_strerror (err));
+      return;
+    }
+
+  maclen = gcry_mac_get_algo_maclen (algo);
+  if (maclen < 1 || maclen > 500)
+    {
+      fail ("algo %d, gcry_mac_get_algo_maclen failed: %d\n", algo, maclen);
+      return;
+    }
+
+  p = malloc(maclen);
+  if (!p)
+    {
+      fail ("algo %d, could not malloc %d bytes\n", algo, maclen);
+      return;
+    }
+
+  err = gcry_mac_setkey (hd, key, keylen);
+  if (err)
+    fail("algo %d, mac gcry_mac_setkey failed: %s\n", algo, gpg_strerror (err));
+  if (err)
+    goto out;
+
+  err = gcry_mac_write (hd, data, datalen);
+  if (err)
+    fail("algo %d, mac gcry_mac_write failed: %s\n", algo, gpg_strerror (err));
+  if (err)
+    goto out;
+
+  err = gcry_mac_verify (hd, expect, maclen);
+  if (err)
+    fail("algo %d, mac gcry_mac_verify failed: %s\n", algo, gpg_strerror (err));
+  if (err)
+    goto out;
+
+  macoutlen = maclen;
+  err = gcry_mac_read (hd, p, &macoutlen);
+  if (err)
+    fail("algo %d, mac gcry_mac_read failed: %s\n", algo, gpg_strerror (err));
+  if (err)
+    goto out;
+
+  if (memcmp (p, expect, maclen))
+    {
+      printf ("computed: ");
+      for (i = 0; i < maclen; i++)
+	printf ("%02x ", p[i] & 0xFF);
+      printf ("\nexpected: ");
+      for (i = 0; i < maclen; i++)
+	printf ("%02x ", expect[i] & 0xFF);
+      printf ("\n");
+
+      fail ("algo %d, digest mismatch\n", algo);
+    }
+  if (err)
+    goto out;
+
+out:
+  free (p);
+  gcry_mac_close (hd);
+}
+
+static void
+check_mac (void)
+{
+  static const struct algos
+  {
+    int algo;
+    const char *data;
+    const char *key;
+    const char *expect;
+  } algos[] =
+    {
+      { GCRY_MAC_HMAC_MD5, "what do ya want for nothing?", "Jefe",
+        "\x75\x0c\x78\x3e\x6a\xb0\xb5\x03\xea\xa8\x6e\x31\x0a\x5d\xb7\x38" },
+      { GCRY_MAC_HMAC_MD5,
+        "Hi There",
+        "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+        "\x92\x94\x72\x7a\x36\x38\xbb\x1c\x13\xf4\x8e\xf8\x15\x8b\xfc\x9d" },
+      { GCRY_MAC_HMAC_MD5,
+        "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+        "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+        "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+        "\xdd\xdd\xdd\xdd\xdd",
+        "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA",
+        "\x56\xbe\x34\x52\x1d\x14\x4c\x88\xdb\xb8\xc7\x33\xf0\xe8\xb3\xf6" },
+      { GCRY_MAC_HMAC_MD5,
+        "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+        "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+        "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+        "\xcd\xcd\xcd\xcd\xcd",
+        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+        "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19",
+        "\x69\x7e\xaf\x0a\xca\x3a\x3a\xea\x3a\x75\x16\x47\x46\xff\xaa\x79" },
+      { GCRY_MAC_HMAC_MD5, "Test With Truncation",
+        "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c",
+        "\x56\x46\x1e\xf2\x34\x2e\xdc\x00\xf9\xba\xb9\x95\x69\x0e\xfd\x4c" },
+      { GCRY_MAC_HMAC_MD5, "Test Using Larger Than Block-Size Key - Hash Key First",
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa",
+        "\x6b\x1a\xb7\xfe\x4b\xd7\xbf\x8f\x0b\x62\xe6\xce\x61\xb9\xd0\xcd" },
+      { GCRY_MAC_HMAC_MD5,
+        "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa",
+        "\x6f\x63\x0f\xad\x67\xcd\xa0\xee\x1f\xb1\xf5\x62\xdb\x3a\xa5\x3e", },
+      { GCRY_MAC_HMAC_SHA256, "what do ya want for nothing?", "Jefe",
+        "\x5b\xdc\xc1\x46\xbf\x60\x75\x4e\x6a\x04\x24\x26\x08\x95\x75\xc7\x5a"
+        "\x00\x3f\x08\x9d\x27\x39\x83\x9d\xec\x58\xb9\x64\xec\x38\x43" },
+      { GCRY_MAC_HMAC_SHA256,
+        "Hi There",
+        "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+        "\x0b\x0b\x0b",
+        "\xb0\x34\x4c\x61\xd8\xdb\x38\x53\x5c\xa8\xaf\xce\xaf\x0b\xf1\x2b\x88"
+        "\x1d\xc2\x00\xc9\x83\x3d\xa7\x26\xe9\x37\x6c\x2e\x32\xcf\xf7" },
+      { GCRY_MAC_HMAC_SHA256,
+        "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+        "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+        "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+        "\xdd\xdd\xdd\xdd\xdd",
+        "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+        "\xAA\xAA\xAA\xAA",
+        "\x77\x3e\xa9\x1e\x36\x80\x0e\x46\x85\x4d\xb8\xeb\xd0\x91\x81\xa7"
+        "\x29\x59\x09\x8b\x3e\xf8\xc1\x22\xd9\x63\x55\x14\xce\xd5\x65\xfe" },
+      { GCRY_MAC_HMAC_SHA256,
+        "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+        "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+        "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+        "\xcd\xcd\xcd\xcd\xcd",
+        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+        "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19",
+        "\x82\x55\x8a\x38\x9a\x44\x3c\x0e\xa4\xcc\x81\x98\x99\xf2\x08"
+        "\x3a\x85\xf0\xfa\xa3\xe5\x78\xf8\x07\x7a\x2e\x3f\xf4\x67\x29\x66\x5b" },
+      { GCRY_MAC_HMAC_SHA256,
+        "Test Using Larger Than Block-Size Key - Hash Key First",
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa",
+        "\x60\xe4\x31\x59\x1e\xe0\xb6\x7f\x0d\x8a\x26\xaa\xcb\xf5\xb7\x7f"
+        "\x8e\x0b\xc6\x21\x37\x28\xc5\x14\x05\x46\x04\x0f\x0e\xe3\x7f\x54" },
+      { GCRY_MAC_HMAC_SHA256,
+        "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.",
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa",
+        "\x9b\x09\xff\xa7\x1b\x94\x2f\xcb\x27\x63\x5f\xbc\xd5\xb0\xe9\x44"
+        "\xbf\xdc\x63\x64\x4f\x07\x13\x93\x8a\x7f\x51\x53\x5c\x3a\x35\xe2" },
+      { GCRY_MAC_HMAC_SHA224, "what do ya want for nothing?", "Jefe",
+        "\xa3\x0e\x01\x09\x8b\xc6\xdb\xbf\x45\x69\x0f\x3a\x7e\x9e\x6d\x0f"
+        "\x8b\xbe\xa2\xa3\x9e\x61\x48\x00\x8f\xd0\x5e\x44" },
+      { GCRY_MAC_HMAC_SHA224,
+        "Hi There",
+        "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+        "\x0b\x0b\x0b",
+        "\x89\x6f\xb1\x12\x8a\xbb\xdf\x19\x68\x32\x10\x7c\xd4\x9d\xf3\x3f\x47"
+        "\xb4\xb1\x16\x99\x12\xba\x4f\x53\x68\x4b\x22" },
+      { GCRY_MAC_HMAC_SHA224,
+        "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+        "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+        "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+        "\xdd\xdd\xdd\xdd\xdd",
+        "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+        "\xAA\xAA\xAA\xAA",
+        "\x7f\xb3\xcb\x35\x88\xc6\xc1\xf6\xff\xa9\x69\x4d\x7d\x6a\xd2\x64"
+        "\x93\x65\xb0\xc1\xf6\x5d\x69\xd1\xec\x83\x33\xea" },
+      { GCRY_MAC_HMAC_SHA224,
+        "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+        "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+        "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+        "\xcd\xcd\xcd\xcd\xcd",
+        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+        "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19",
+        "\x6c\x11\x50\x68\x74\x01\x3c\xac\x6a\x2a\xbc\x1b\xb3\x82\x62"
+        "\x7c\xec\x6a\x90\xd8\x6e\xfc\x01\x2d\xe7\xaf\xec\x5a" },
+      { GCRY_MAC_HMAC_SHA224,
+        "Test Using Larger Than Block-Size Key - Hash Key First",
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa",
+        "\x95\xe9\xa0\xdb\x96\x20\x95\xad\xae\xbe\x9b\x2d\x6f\x0d\xbc\xe2"
+        "\xd4\x99\xf1\x12\xf2\xd2\xb7\x27\x3f\xa6\x87\x0e" },
+      { GCRY_MAC_HMAC_SHA224,
+        "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.",
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa",
+        "\x3a\x85\x41\x66\xac\x5d\x9f\x02\x3f\x54\xd5\x17\xd0\xb3\x9d\xbd"
+        "\x94\x67\x70\xdb\x9c\x2b\x95\xc9\xf6\xf5\x65\xd1" },
+      { GCRY_MAC_HMAC_SHA384, "what do ya want for nothing?", "Jefe",
+        "\xaf\x45\xd2\xe3\x76\x48\x40\x31\x61\x7f\x78\xd2\xb5\x8a\x6b\x1b"
+        "\x9c\x7e\xf4\x64\xf5\xa0\x1b\x47\xe4\x2e\xc3\x73\x63\x22\x44\x5e"
+        "\x8e\x22\x40\xca\x5e\x69\xe2\xc7\x8b\x32\x39\xec\xfa\xb2\x16\x49" },
+      { GCRY_MAC_HMAC_SHA384,
+        "Hi There",
+        "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+        "\x0b\x0b\x0b",
+        "\xaf\xd0\x39\x44\xd8\x48\x95\x62\x6b\x08\x25\xf4\xab\x46\x90\x7f\x15"
+        "\xf9\xda\xdb\xe4\x10\x1e\xc6\x82\xaa\x03\x4c\x7c\xeb\xc5\x9c\xfa\xea"
+        "\x9e\xa9\x07\x6e\xde\x7f\x4a\xf1\x52\xe8\xb2\xfa\x9c\xb6" },
+      { GCRY_MAC_HMAC_SHA384,
+        "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+        "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+        "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+        "\xdd\xdd\xdd\xdd\xdd",
+        "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+        "\xAA\xAA\xAA\xAA",
+        "\x88\x06\x26\x08\xd3\xe6\xad\x8a\x0a\xa2\xac\xe0\x14\xc8\xa8\x6f"
+        "\x0a\xa6\x35\xd9\x47\xac\x9f\xeb\xe8\x3e\xf4\xe5\x59\x66\x14\x4b"
+        "\x2a\x5a\xb3\x9d\xc1\x38\x14\xb9\x4e\x3a\xb6\xe1\x01\xa3\x4f\x27" },
+      { GCRY_MAC_HMAC_SHA384,
+        "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+        "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+        "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+        "\xcd\xcd\xcd\xcd\xcd",
+        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+        "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19",
+        "\x3e\x8a\x69\xb7\x78\x3c\x25\x85\x19\x33\xab\x62\x90\xaf\x6c\xa7"
+        "\x7a\x99\x81\x48\x08\x50\x00\x9c\xc5\x57\x7c\x6e\x1f\x57\x3b\x4e"
+        "\x68\x01\xdd\x23\xc4\xa7\xd6\x79\xcc\xf8\xa3\x86\xc6\x74\xcf\xfb" },
+      { GCRY_MAC_HMAC_SHA384,
+        "Test Using Larger Than Block-Size Key - Hash Key First",
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa",
+        "\x4e\xce\x08\x44\x85\x81\x3e\x90\x88\xd2\xc6\x3a\x04\x1b\xc5\xb4"
+        "\x4f\x9e\xf1\x01\x2a\x2b\x58\x8f\x3c\xd1\x1f\x05\x03\x3a\xc4\xc6"
+        "\x0c\x2e\xf6\xab\x40\x30\xfe\x82\x96\x24\x8d\xf1\x63\xf4\x49\x52" },
+      { GCRY_MAC_HMAC_SHA384,
+        "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.",
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa",
+        "\x66\x17\x17\x8e\x94\x1f\x02\x0d\x35\x1e\x2f\x25\x4e\x8f\xd3\x2c"
+        "\x60\x24\x20\xfe\xb0\xb8\xfb\x9a\xdc\xce\xbb\x82\x46\x1e\x99\xc5"
+        "\xa6\x78\xcc\x31\xe7\x99\x17\x6d\x38\x60\xe6\x11\x0c\x46\x52\x3e" },
+      { GCRY_MAC_HMAC_SHA512, "what do ya want for nothing?", "Jefe",
+        "\x16\x4b\x7a\x7b\xfc\xf8\x19\xe2\xe3\x95\xfb\xe7\x3b\x56\xe0\xa3"
+        "\x87\xbd\x64\x22\x2e\x83\x1f\xd6\x10\x27\x0c\xd7\xea\x25\x05\x54"
+        "\x97\x58\xbf\x75\xc0\x5a\x99\x4a\x6d\x03\x4f\x65\xf8\xf0\xe6\xfd"
+        "\xca\xea\xb1\xa3\x4d\x4a\x6b\x4b\x63\x6e\x07\x0a\x38\xbc\xe7\x37" },
+      { GCRY_MAC_HMAC_SHA512,
+        "Hi There",
+        "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+        "\x0b\x0b\x0b",
+        "\x87\xaa\x7c\xde\xa5\xef\x61\x9d\x4f\xf0\xb4\x24\x1a\x1d\x6c\xb0"
+        "\x23\x79\xf4\xe2\xce\x4e\xc2\x78\x7a\xd0\xb3\x05\x45\xe1\x7c\xde"
+        "\xda\xa8\x33\xb7\xd6\xb8\xa7\x02\x03\x8b\x27\x4e\xae\xa3\xf4\xe4"
+        "\xbe\x9d\x91\x4e\xeb\x61\xf1\x70\x2e\x69\x6c\x20\x3a\x12\x68\x54" },
+      { GCRY_MAC_HMAC_SHA512,
+        "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+        "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+        "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+        "\xdd\xdd\xdd\xdd\xdd",
+        "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+        "\xAA\xAA\xAA\xAA",
+        "\xfa\x73\xb0\x08\x9d\x56\xa2\x84\xef\xb0\xf0\x75\x6c\x89\x0b\xe9"
+        "\xb1\xb5\xdb\xdd\x8e\xe8\x1a\x36\x55\xf8\x3e\x33\xb2\x27\x9d\x39"
+        "\xbf\x3e\x84\x82\x79\xa7\x22\xc8\x06\xb4\x85\xa4\x7e\x67\xc8\x07"
+        "\xb9\x46\xa3\x37\xbe\xe8\x94\x26\x74\x27\x88\x59\xe1\x32\x92\xfb"  },
+      { GCRY_MAC_HMAC_SHA512,
+        "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+        "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+        "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+        "\xcd\xcd\xcd\xcd\xcd",
+        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+        "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19",
+        "\xb0\xba\x46\x56\x37\x45\x8c\x69\x90\xe5\xa8\xc5\xf6\x1d\x4a\xf7"
+        "\xe5\x76\xd9\x7f\xf9\x4b\x87\x2d\xe7\x6f\x80\x50\x36\x1e\xe3\xdb"
+        "\xa9\x1c\xa5\xc1\x1a\xa2\x5e\xb4\xd6\x79\x27\x5c\xc5\x78\x80\x63"
+        "\xa5\xf1\x97\x41\x12\x0c\x4f\x2d\xe2\xad\xeb\xeb\x10\xa2\x98\xdd" },
+      { GCRY_MAC_HMAC_SHA512,
+        "Test Using Larger Than Block-Size Key - Hash Key First",
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa",
+        "\x80\xb2\x42\x63\xc7\xc1\xa3\xeb\xb7\x14\x93\xc1\xdd\x7b\xe8\xb4"
+        "\x9b\x46\xd1\xf4\x1b\x4a\xee\xc1\x12\x1b\x01\x37\x83\xf8\xf3\x52"
+        "\x6b\x56\xd0\x37\xe0\x5f\x25\x98\xbd\x0f\xd2\x21\x5d\x6a\x1e\x52"
+        "\x95\xe6\x4f\x73\xf6\x3f\x0a\xec\x8b\x91\x5a\x98\x5d\x78\x65\x98" },
+      { GCRY_MAC_HMAC_SHA512,
+        "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.",
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+        "\xaa\xaa\xaa",
+        "\xe3\x7b\x6a\x77\x5d\xc8\x7d\xba\xa4\xdf\xa9\xf9\x6e\x5e\x3f\xfd"
+        "\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" },
+      { 0 },
+    };
+  int i;
+
+  if (verbose)
+    fprintf (stderr, "Starting MAC checks.\n");
+
+  for (i = 0; algos[i].algo; i++)
+    {
+      if ((gcry_mac_test_algo (algos[i].algo)
+	   || algos[i].algo == GCRY_MAC_HMAC_MD5) && in_fips_mode)
+        {
+          if (verbose)
+            fprintf (stderr, "  algorithm %d not available in fips mode\n",
+		     algos[i].algo);
+          continue;
+        }
+      if (verbose)
+	fprintf (stderr,
+                 "  checking %s [%i] for %zi byte key and %zi byte data\n",
+		 gcry_mac_algo_name (algos[i].algo),
+		 algos[i].algo,
+		 strlen(algos[i].key), strlen(algos[i].data));
+
+      check_one_mac (algos[i].algo, algos[i].data, strlen (algos[i].data),
+		     algos[i].key, strlen(algos[i].key),
+		     algos[i].expect);
+    }
+
+  if (verbose)
+    fprintf (stderr, "Completed MAC checks.\n");
+}
 
 /* Check that the signature SIG matches the hash HASH. PKEY is the
    public key used for the verification. BADHASH is a hash value which
@@ -4996,6 +5389,7 @@ main (int argc, char **argv)
       check_bulk_cipher_modes ();
       check_digests ();
       check_hmac ();
+      check_mac ();
       check_pubkey ();
     }
 
diff --git a/tests/bench-slope.c b/tests/bench-slope.c
index 79b868c..f9d3c46 100644
--- a/tests/bench-slope.c
+++ b/tests/bench-slope.c
@@ -493,17 +493,17 @@ bench_print_result (double nsecs_per_byte)
 }
 
 static void
-bench_print_header (const char *algo_name)
+bench_print_header (int algo_width, const char *algo_name)
 {
-  printf (" %-14s | ", algo_name);
+  printf (" %-*s | ", algo_width, algo_name);
   printf ("%14s %15s %13s\n", "nanosecs/byte", "mebibytes/sec",
 	  "cycles/byte");
 }
 
 static void
-bench_print_footer (void)
+bench_print_footer (int algo_width)
 {
-  printf (" %-14s =\n", "");
+  printf (" %-*s =\n", algo_width, "");
 }
 
 
@@ -854,12 +854,12 @@ _cipher_bench (int algo)
 
   algoname = gcry_cipher_algo_name (algo);
 
-  bench_print_header (algoname);
+  bench_print_header (14, algoname);
 
   for (i = 0; cipher_modes[i].mode; i++)
     cipher_bench_one (algo, &cipher_modes[i]);
 
-  bench_print_footer ();
+  bench_print_footer (14);
 }
 
 
@@ -937,6 +937,7 @@ bench_hash_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
 {
   gcry_md_hd_t hd = obj->priv;
 
+  gcry_md_reset (hd);
   gcry_md_write (hd, buf, buflen);
   gcry_md_final (hd);
 }
@@ -993,7 +994,7 @@ hash_bench (char **argv, int argc)
 
   printf ("Hash:\n");
 
-  bench_print_header ("");
+  bench_print_header (14, "");
 
   if (argv && argc)
     {
@@ -1011,7 +1012,161 @@ hash_bench (char **argv, int argc)
 	  _hash_bench (i);
     }
 
-  bench_print_footer ();
+  bench_print_footer (14);
+}
+
+
+/************************************************************ MAC benchmarks. */
+
+struct bench_mac_mode
+{
+  const char *name;
+  struct bench_ops *ops;
+
+  int algo;
+};
+
+
+static int
+bench_mac_init (struct bench_obj *obj)
+{
+  struct bench_mac_mode *mode = obj->priv;
+  gcry_mac_hd_t hd;
+  int err;
+  unsigned int keylen;
+  void *key;
+
+  obj->min_bufsize = BUF_START_SIZE;
+  obj->max_bufsize = BUF_END_SIZE;
+  obj->step_size = BUF_STEP_SIZE;
+  obj->num_measure_repetitions = NUM_MEASUREMENT_REPETITIONS;
+
+  keylen = gcry_mac_get_algo_keylen (mode->algo);
+  if (keylen == 0)
+    keylen = 32;
+  key = malloc (keylen);
+  if (!key)
+    {
+      fprintf (stderr, PGM ": couldn't allocate %d bytes\n", keylen);
+      exit (1);
+    }
+  memset(key, 42, keylen);
+
+  err = gcry_mac_open (&hd, mode->algo, 0, NULL);
+  if (err)
+    {
+      fprintf (stderr, PGM ": error opening mac `%s'\n",
+	       gcry_mac_algo_name (mode->algo));
+      free (key);
+      exit (1);
+    }
+
+  err = gcry_mac_setkey (hd, key, keylen);
+  free (key);
+  if (err)
+    {
+      fprintf (stderr, PGM ": error setting key for mac `%s'\n",
+	       gcry_mac_algo_name (mode->algo));
+      exit (1);
+    }
+
+  obj->priv = hd;
+
+  return 0;
+}
+
+static void
+bench_mac_free (struct bench_obj *obj)
+{
+  gcry_mac_hd_t hd = obj->priv;
+
+  gcry_mac_close (hd);
+}
+
+static void
+bench_mac_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
+{
+  gcry_mac_hd_t hd = obj->priv;
+  size_t bs;
+  char b;
+
+  gcry_mac_reset (hd);
+  gcry_mac_write (hd, buf, buflen);
+  bs = sizeof(b);
+  gcry_mac_read (hd, &b, &bs);
+}
+
+static struct bench_ops mac_ops = {
+  &bench_mac_init,
+  &bench_mac_free,
+  &bench_mac_do_bench
+};
+
+
+static struct bench_mac_mode mac_modes[] = {
+  {"", &mac_ops},
+  {0},
+};
+
+
+static void
+mac_bench_one (int algo, struct bench_mac_mode *pmode)
+{
+  struct bench_mac_mode mode = *pmode;
+  struct bench_obj obj = { 0 };
+  double result;
+
+  mode.algo = algo;
+
+  if (mode.name[0] == '\0')
+    printf (" %-18s | ", gcry_mac_algo_name (algo));
+  else
+    printf (" %18s | ", mode.name);
+  fflush (stdout);
+
+  obj.ops = mode.ops;
+  obj.priv = &mode;
+
+  result = do_slope_benchmark (&obj);
+
+  bench_print_result (result);
+}
+
+static void
+_mac_bench (int algo)
+{
+  int i;
+
+  for (i = 0; mac_modes[i].name; i++)
+    mac_bench_one (algo, &mac_modes[i]);
+}
+
+void
+mac_bench (char **argv, int argc)
+{
+  int i, algo;
+
+  printf ("MAC:\n");
+
+  bench_print_header (18, "");
+
+  if (argv && argc)
+    {
+      for (i = 0; i < argc; i++)
+	{
+	  algo = gcry_mac_map_name (argv[i]);
+	  if (algo)
+	    _mac_bench (algo);
+	}
+    }
+  else
+    {
+      for (i = 1; i < 400; i++)
+	if (!gcry_mac_test_algo (i))
+	  _mac_bench (i);
+    }
+
+  bench_print_footer (18);
 }
 
 
@@ -1021,7 +1176,7 @@ void
 print_help (void)
 {
   static const char *help_lines[] = {
-    "usage: bench-slope [options] [hash|cipher [algonames]]",
+    "usage: bench-slope [options] [hash|mac|cipher [algonames]]",
     "",
     " options:",
     "     --cpu-mhz <mhz>           Set CPU speed for calculating cycles per bytes",
@@ -1147,6 +1302,7 @@ main (int argc, char **argv)
     {
       warm_up_cpu ();
       hash_bench (NULL, 0);
+      mac_bench (NULL, 0);
       cipher_bench (NULL, 0);
     }
   else if (!strcmp (*argv, "hash"))
@@ -1157,6 +1313,14 @@ main (int argc, char **argv)
       warm_up_cpu ();
       hash_bench ((argc == 0) ? NULL : argv, argc);
     }
+  else if (!strcmp (*argv, "mac"))
+    {
+      argc--;
+      argv++;
+
+      warm_up_cpu ();
+      mac_bench ((argc == 0) ? NULL : argv, argc);
+    }
   else if (!strcmp (*argv, "cipher"))
     {
       argc--;
diff --git a/tests/benchmark.c b/tests/benchmark.c
index 93874fa..c4accd3 100644
--- a/tests/benchmark.c
+++ b/tests/benchmark.c
@@ -47,6 +47,9 @@ static int cipher_repetitions;
 /* Number of hash repetitions.  */
 static int hash_repetitions;
 
+/* Number of hash repetitions.  */
+static int mac_repetitions;
+
 /* Alignment of the buffers.  */
 static int buffer_alignment;
 
@@ -436,6 +439,126 @@ md_bench ( const char *algoname )
 }
 
 
+
+static void
+mac_bench ( const char *algoname )
+{
+  int algo;
+  gcry_mac_hd_t hd;
+  int step, pos, j, i, repcount;
+  char buf_base[1000+15];
+  size_t bufsize = 1000;
+  char *buf;
+  char mac[3][512];
+  char key[512];
+  unsigned int maclen, keylen;
+  size_t macoutlen;
+  gcry_error_t err = GPG_ERR_NO_ERROR;
+
+  if (!algoname)
+    {
+      for (i=1; i < 400; i++)
+        if (in_fips_mode && i == GCRY_MAC_HMAC_MD5)
+          ; /* Don't use MD5 in fips mode.  */
+        else if ( !gcry_mac_test_algo (i) )
+          mac_bench (gcry_mac_algo_name (i));
+      return;
+    }
+
+  buf = buf_base + ((16 - ((size_t)buf_base & 0x0f)) % buffer_alignment);
+
+  algo = gcry_mac_map_name (algoname);
+  if (!algo)
+    {
+      fprintf (stderr, PGM ": invalid hash algorithm `%s'\n", algoname);
+      exit (1);
+    }
+
+  maclen = gcry_mac_get_algo_maclen (algo);
+  if (maclen > sizeof(mac))
+    maclen = sizeof(mac);
+
+  keylen = gcry_mac_get_algo_keylen (algo);
+  if (keylen == 0)
+    keylen = 32;
+  if (keylen > sizeof(key))
+    keylen = sizeof(key);
+  for (i=0; i < keylen; i++)
+    key[i] = (keylen - i) ^ 0x54;
+
+  err = gcry_mac_open (&hd, algo, 0, NULL);
+  if (err)
+    {
+      fprintf (stderr, PGM ": error opening mac algorithm `%s': %s\n", algoname,
+               gpg_strerror (err));
+      exit (1);
+    }
+
+  err = gcry_mac_setkey (hd, key, keylen);
+  if (err)
+    {
+      fprintf (stderr, PGM ": error setting key for mac algorithm `%s': %s\n",
+               algoname, gpg_strerror (err));
+      exit (1);
+    }
+
+  for (i=0; i < bufsize; i++)
+    buf[i] = i;
+
+  printf ("%-20s", gcry_mac_algo_name (algo));
+
+  start_timer ();
+  for (repcount=0; repcount < mac_repetitions; repcount++)
+    for (i=0; i < 1000; i++)
+      gcry_mac_write (hd, buf, bufsize);
+  macoutlen = maclen;
+  gcry_mac_read (hd, mac[0], &macoutlen);
+  stop_timer ();
+  printf (" %s", elapsed_time ());
+  fflush (stdout);
+
+  gcry_mac_reset (hd);
+  start_timer ();
+  for (repcount=0; repcount < mac_repetitions; repcount++)
+    for (i=0; i < 1000; i++)
+      for (step=bufsize/10, pos=0, j=0; j < 10; j++, pos+=step)
+        gcry_mac_write (hd, &buf[pos], step);
+  macoutlen = maclen;
+  gcry_mac_read (hd, mac[1], &macoutlen);
+  stop_timer ();
+  printf (" %s", elapsed_time ());
+  fflush (stdout);
+
+  gcry_mac_reset (hd);
+  start_timer ();
+  for (repcount=0; repcount < mac_repetitions; repcount++)
+    for (i=0; i < 1000; i++)
+      for (step=bufsize/100, pos=0, j=0; j < 100; j++, pos+=step)
+        gcry_mac_write (hd, &buf[pos], step);
+  macoutlen = maclen;
+  gcry_mac_read (hd, mac[2], &macoutlen);
+  stop_timer ();
+  printf (" %s", elapsed_time ());
+  fflush (stdout);
+
+  gcry_mac_close (hd);
+
+  for (i=1; i < 3; i++)
+    {
+      if (memcmp(mac[i-1], mac[i], maclen))
+        {
+          fprintf (stderr, PGM ": mac mismatch with algorithm `%s'\n",
+                   algoname);
+          exit(1);
+        }
+    }
+
+  putchar ('\n');
+  fflush (stdout);
+}
+
+
+
 static void ccm_aead_init(gcry_cipher_hd_t hd, size_t buflen, int authlen)
 {
   const int _L = 4;
@@ -1186,7 +1309,7 @@ main( int argc, char **argv )
       else if (!strcmp (*argv, "--help"))
         {
           fputs ("usage: benchmark "
-                 "[md|cipher|random|mpi|rsa|dsa|ecc [algonames]]\n",
+                 "[md|mac|cipher|random|mpi|rsa|dsa|ecc [algonames]]\n",
                  stdout);
           exit (0);
         }
@@ -1256,6 +1379,15 @@ main( int argc, char **argv )
               argc--; argv++;
             }
         }
+      else if (!strcmp (*argv, "--mac-repetitions"))
+        {
+          argc--; argv++;
+          if (argc)
+            {
+              mac_repetitions = atoi(*argv);
+              argc--; argv++;
+            }
+        }
       else if (!strcmp (*argv, "--pk-count"))
         {
           argc--; argv++;
@@ -1330,12 +1462,16 @@ main( int argc, char **argv )
     cipher_repetitions = 1;
   if (hash_repetitions < 1)
     hash_repetitions = 1;
+  if (mac_repetitions < 1)
+    mac_repetitions = 1;
 
   if ( !argc )
     {
       gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
       md_bench (NULL);
       putchar ('\n');
+      mac_bench (NULL);
+      putchar ('\n');
       cipher_bench (NULL);
       putchar ('\n');
       rsa_bench (pk_count, 1, no_blinding);
@@ -1367,6 +1503,14 @@ main( int argc, char **argv )
         for (argc--, argv++; argc; argc--, argv++)
           md_bench ( *argv );
     }
+  else if ( !strcmp (*argv, "mac"))
+    {
+      if (argc == 1)
+        mac_bench (NULL);
+      else
+        for (argc--, argv++; argc; argc--, argv++)
+          mac_bench ( *argv );
+    }
   else if ( !strcmp (*argv, "cipher"))
     {
       if (argc == 1)




More information about the Gcrypt-devel mailing list