summaryrefslogtreecommitdiff
path: root/cipher
diff options
context:
space:
mode:
authorJussi Kivilinna <jussi.kivilinna@iki.fi>2013-11-15 12:28:07 +0200
committerJussi Kivilinna <jussi.kivilinna@iki.fi>2013-11-16 12:52:17 +0200
commitfcd6da37d55f248d3558ee0ff385b41b866e7ded (patch)
treeb942fea4e29d43427b4e5c221d8c40455e6b8cfe /cipher
parentb95a557a43aeed68ea5e5ce02aca42ee97bfdb3b (diff)
downloadlibgcrypt-fcd6da37d55f248d3558ee0ff385b41b866e7ded.tar.gz
Add new MAC API, initially with HMAC
* 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. * doc/gcrypt.texi: Add documentation for MAC API. * 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. [v3]: - HMAC algorithm ids start from 101. - Fix coding style for new files. Signed-off-by: Jussi Kivilinna <jussi.kivilinna@iki.fi>
Diffstat (limited to 'cipher')
-rw-r--r--cipher/Makefile.am2
-rw-r--r--cipher/bufhelp.h17
-rw-r--r--cipher/cipher-ccm.c13
-rw-r--r--cipher/mac-hmac.c272
-rw-r--r--cipher/mac-internal.h140
-rw-r--r--cipher/mac.c447
6 files changed, 882 insertions, 9 deletions
diff --git a/cipher/Makefile.am b/cipher/Makefile.am
index 95d484e6..1d54b078 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 dc39b46d..45a72094 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 2a1fc74a..1eba7f0f 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 00000000..46b21b15
--- /dev/null
+++ b/cipher/mac-hmac.c
@@ -0,0 +1,272 @@
+/* mac-hmac.c - HMAC glue for MAC API
+ * Copyright © 2013 Jussi Kivilinna <jussi.kivilinna@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 00000000..9ebc0357
--- /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@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 00000000..9708b3a6
--- /dev/null
+++ b/cipher/mac.c
@@ -0,0 +1,447 @@
+/* mac.c - message authentication code dispatcher
+ * Copyright © 2013 Jussi Kivilinna <jussi.kivilinna@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->close)
+ 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);
+}