summaryrefslogtreecommitdiffstats
path: root/comm/third_party/libgcrypt/cipher/md.c
diff options
context:
space:
mode:
Diffstat (limited to 'comm/third_party/libgcrypt/cipher/md.c')
-rw-r--r--comm/third_party/libgcrypt/cipher/md.c1639
1 files changed, 1639 insertions, 0 deletions
diff --git a/comm/third_party/libgcrypt/cipher/md.c b/comm/third_party/libgcrypt/cipher/md.c
new file mode 100644
index 0000000000..efb7376a1a
--- /dev/null
+++ b/comm/third_party/libgcrypt/cipher/md.c
@@ -0,0 +1,1639 @@
+/* md.c - message digest dispatcher
+ * Copyright (C) 1998, 1999, 2002, 2003, 2006,
+ * 2008 Free Software Foundation, Inc.
+ * Copyright (C) 2013, 2014 g10 Code GmbH
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser general Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "g10lib.h"
+#include "cipher.h"
+
+
+/* This is the list of the digest implementations included in
+ libgcrypt. */
+static gcry_md_spec_t * const digest_list[] =
+ {
+#if USE_CRC
+ &_gcry_digest_spec_crc32,
+ &_gcry_digest_spec_crc32_rfc1510,
+ &_gcry_digest_spec_crc24_rfc2440,
+#endif
+#if USE_SHA1
+ &_gcry_digest_spec_sha1,
+#endif
+#if USE_SHA256
+ &_gcry_digest_spec_sha256,
+ &_gcry_digest_spec_sha224,
+#endif
+#if USE_SHA512
+ &_gcry_digest_spec_sha512,
+ &_gcry_digest_spec_sha384,
+ &_gcry_digest_spec_sha512_256,
+ &_gcry_digest_spec_sha512_224,
+#endif
+#if USE_SHA3
+ &_gcry_digest_spec_sha3_224,
+ &_gcry_digest_spec_sha3_256,
+ &_gcry_digest_spec_sha3_384,
+ &_gcry_digest_spec_sha3_512,
+ &_gcry_digest_spec_shake128,
+ &_gcry_digest_spec_shake256,
+#endif
+#if USE_GOST_R_3411_94
+ &_gcry_digest_spec_gost3411_94,
+ &_gcry_digest_spec_gost3411_cp,
+#endif
+#if USE_GOST_R_3411_12
+ &_gcry_digest_spec_stribog_256,
+ &_gcry_digest_spec_stribog_512,
+#endif
+#if USE_WHIRLPOOL
+ &_gcry_digest_spec_whirlpool,
+#endif
+#if USE_RMD160
+ &_gcry_digest_spec_rmd160,
+#endif
+#if USE_TIGER
+ &_gcry_digest_spec_tiger,
+ &_gcry_digest_spec_tiger1,
+ &_gcry_digest_spec_tiger2,
+#endif
+#if USE_MD5
+ &_gcry_digest_spec_md5,
+#endif
+#if USE_MD4
+ &_gcry_digest_spec_md4,
+#endif
+#if USE_MD2
+ &_gcry_digest_spec_md2,
+#endif
+#if USE_BLAKE2
+ &_gcry_digest_spec_blake2b_512,
+ &_gcry_digest_spec_blake2b_384,
+ &_gcry_digest_spec_blake2b_256,
+ &_gcry_digest_spec_blake2b_160,
+ &_gcry_digest_spec_blake2s_256,
+ &_gcry_digest_spec_blake2s_224,
+ &_gcry_digest_spec_blake2s_160,
+ &_gcry_digest_spec_blake2s_128,
+#endif
+#if USE_SM3
+ &_gcry_digest_spec_sm3,
+#endif
+ NULL
+ };
+
+/* Digest implementations starting with index 0 (enum gcry_md_algos) */
+static gcry_md_spec_t * const digest_list_algo0[] =
+ {
+ NULL, /* GCRY_MD_NONE */
+#if USE_MD5
+ &_gcry_digest_spec_md5,
+#else
+ NULL,
+#endif
+#if USE_SHA1
+ &_gcry_digest_spec_sha1,
+#else
+ NULL,
+#endif
+#if USE_RMD160
+ &_gcry_digest_spec_rmd160,
+#else
+ NULL,
+#endif
+ NULL, /* Unused index 4 */
+#if USE_MD2
+ &_gcry_digest_spec_md2,
+#else
+ NULL,
+#endif
+#if USE_TIGER
+ &_gcry_digest_spec_tiger,
+#else
+ NULL,
+#endif
+ NULL, /* GCRY_MD_HAVAL */
+#if USE_SHA256
+ &_gcry_digest_spec_sha256,
+#else
+ NULL,
+#endif
+#if USE_SHA512
+ &_gcry_digest_spec_sha384,
+ &_gcry_digest_spec_sha512,
+#else
+ NULL,
+ NULL,
+#endif
+#if USE_SHA256
+ &_gcry_digest_spec_sha224
+#else
+ NULL
+#endif
+ };
+
+/* Digest implementations starting with index 301 (enum gcry_md_algos) */
+static gcry_md_spec_t * const digest_list_algo301[] =
+ {
+#if USE_MD4
+ &_gcry_digest_spec_md4,
+#else
+ NULL,
+#endif
+#if USE_CRC
+ &_gcry_digest_spec_crc32,
+ &_gcry_digest_spec_crc32_rfc1510,
+ &_gcry_digest_spec_crc24_rfc2440,
+#else
+ NULL,
+ NULL,
+ NULL,
+#endif
+#if USE_WHIRLPOOL
+ &_gcry_digest_spec_whirlpool,
+#else
+ NULL,
+#endif
+#if USE_TIGER
+ &_gcry_digest_spec_tiger1,
+ &_gcry_digest_spec_tiger2,
+#else
+ NULL,
+ NULL,
+#endif
+#if USE_GOST_R_3411_94
+ &_gcry_digest_spec_gost3411_94,
+#else
+ NULL,
+#endif
+#if USE_GOST_R_3411_12
+ &_gcry_digest_spec_stribog_256,
+ &_gcry_digest_spec_stribog_512,
+#else
+ NULL,
+ NULL,
+#endif
+#if USE_GOST_R_3411_94
+ &_gcry_digest_spec_gost3411_cp,
+#else
+ NULL,
+#endif
+#if USE_SHA3
+ &_gcry_digest_spec_sha3_224,
+ &_gcry_digest_spec_sha3_256,
+ &_gcry_digest_spec_sha3_384,
+ &_gcry_digest_spec_sha3_512,
+ &_gcry_digest_spec_shake128,
+ &_gcry_digest_spec_shake256,
+#else
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+#endif
+#if USE_BLAKE2
+ &_gcry_digest_spec_blake2b_512,
+ &_gcry_digest_spec_blake2b_384,
+ &_gcry_digest_spec_blake2b_256,
+ &_gcry_digest_spec_blake2b_160,
+ &_gcry_digest_spec_blake2s_256,
+ &_gcry_digest_spec_blake2s_224,
+ &_gcry_digest_spec_blake2s_160,
+ &_gcry_digest_spec_blake2s_128,
+#else
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+#endif
+#if USE_SM3
+ &_gcry_digest_spec_sm3,
+#else
+ NULL,
+#endif
+#if USE_SHA512
+ &_gcry_digest_spec_sha512_256,
+ &_gcry_digest_spec_sha512_224,
+#else
+ NULL,
+ NULL,
+#endif
+ };
+
+
+typedef struct gcry_md_list
+{
+ gcry_md_spec_t *spec;
+ struct gcry_md_list *next;
+ size_t actual_struct_size; /* Allocated size of this structure. */
+ PROPERLY_ALIGNED_TYPE context[1];
+} GcryDigestEntry;
+
+/* This structure is put right after the gcry_md_hd_t buffer, so that
+ * only one memory block is needed. */
+struct gcry_md_context
+{
+ int magic;
+ size_t actual_handle_size; /* Allocated size of this handle. */
+ FILE *debug;
+ struct {
+ unsigned int secure:1;
+ unsigned int finalized:1;
+ unsigned int bugemu1:1;
+ unsigned int hmac:1;
+ } flags;
+ GcryDigestEntry *list;
+};
+
+
+#define CTX_MAGIC_NORMAL 0x11071961
+#define CTX_MAGIC_SECURE 0x16917011
+
+static gcry_err_code_t md_enable (gcry_md_hd_t hd, int algo);
+static void md_close (gcry_md_hd_t a);
+static void md_write (gcry_md_hd_t a, const void *inbuf, size_t inlen);
+static byte *md_read( gcry_md_hd_t a, int algo );
+static int md_get_algo( gcry_md_hd_t a );
+static int md_digest_length( int algo );
+static void md_start_debug ( gcry_md_hd_t a, const char *suffix );
+static void md_stop_debug ( gcry_md_hd_t a );
+
+
+
+static int
+map_algo (int algo)
+{
+ return algo;
+}
+
+
+/* Return the spec structure for the hash algorithm ALGO. For an
+ unknown algorithm NULL is returned. */
+static gcry_md_spec_t *
+spec_from_algo (int algo)
+{
+ gcry_md_spec_t *spec = NULL;
+
+ algo = map_algo (algo);
+
+ if (algo >= 0 && algo < DIM(digest_list_algo0))
+ spec = digest_list_algo0[algo];
+ else if (algo >= 301 && algo < 301 + DIM(digest_list_algo301))
+ spec = digest_list_algo301[algo - 301];
+
+ if (spec)
+ gcry_assert (spec->algo == algo);
+
+ return spec;
+}
+
+
+/* Lookup a hash's spec by its name. */
+static gcry_md_spec_t *
+spec_from_name (const char *name)
+{
+ gcry_md_spec_t *spec;
+ int idx;
+
+ for (idx=0; (spec = digest_list[idx]); idx++)
+ {
+ if (!stricmp (name, spec->name))
+ return spec;
+ }
+
+ return NULL;
+}
+
+
+/* Lookup a hash's spec by its OID. */
+static gcry_md_spec_t *
+spec_from_oid (const char *oid)
+{
+ gcry_md_spec_t *spec;
+ gcry_md_oid_spec_t *oid_specs;
+ int idx, j;
+
+ for (idx=0; (spec = digest_list[idx]); idx++)
+ {
+ oid_specs = spec->oids;
+ if (oid_specs)
+ {
+ for (j = 0; oid_specs[j].oidstring; j++)
+ if (!stricmp (oid, oid_specs[j].oidstring))
+ return spec;
+ }
+ }
+
+ return NULL;
+}
+
+
+static gcry_md_spec_t *
+search_oid (const char *oid, gcry_md_oid_spec_t *oid_spec)
+{
+ gcry_md_spec_t *spec;
+ int i;
+
+ if (!oid)
+ return NULL;
+
+ if (!strncmp (oid, "oid.", 4) || !strncmp (oid, "OID.", 4))
+ oid += 4;
+
+ spec = spec_from_oid (oid);
+ if (spec && spec->oids)
+ {
+ for (i = 0; spec->oids[i].oidstring; i++)
+ if (!stricmp (oid, spec->oids[i].oidstring))
+ {
+ if (oid_spec)
+ *oid_spec = spec->oids[i];
+ return spec;
+ }
+ }
+
+ return NULL;
+}
+
+
+/****************
+ * Map a string to the digest algo
+ */
+int
+_gcry_md_map_name (const char *string)
+{
+ gcry_md_spec_t *spec;
+
+ if (!string)
+ return 0;
+
+ /* If the string starts with a digit (optionally prefixed with
+ either "OID." or "oid."), we first look into our table of ASN.1
+ object identifiers to figure out the algorithm */
+ spec = search_oid (string, NULL);
+ if (spec)
+ return spec->algo;
+
+ /* Not found, search a matching digest 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_md_test_algo() to check whether the algorithm
+ * is valid.
+ */
+const char *
+_gcry_md_algo_name (int algorithm)
+{
+ gcry_md_spec_t *spec;
+
+ spec = spec_from_algo (algorithm);
+ return spec ? spec->name : "?";
+}
+
+
+static gcry_err_code_t
+check_digest_algo (int algorithm)
+{
+ gcry_md_spec_t *spec;
+
+ spec = spec_from_algo (algorithm);
+ if (spec && !spec->flags.disabled)
+ return 0;
+
+ return GPG_ERR_DIGEST_ALGO;
+
+}
+
+
+/****************
+ * Open a message digest handle for use with algorithm ALGO.
+ * More algorithms may be added by md_enable(). The initial algorithm
+ * may be 0.
+ */
+static gcry_err_code_t
+md_open (gcry_md_hd_t *h, int algo, unsigned int flags)
+{
+ gcry_err_code_t err = 0;
+ int secure = !!(flags & GCRY_MD_FLAG_SECURE);
+ int hmac = !!(flags & GCRY_MD_FLAG_HMAC);
+ int bufsize = secure ? 512 : 1024;
+ struct gcry_md_context *ctx;
+ gcry_md_hd_t hd;
+ size_t n;
+
+ /* Allocate a memory area to hold the caller visible buffer with it's
+ * control information and the data required by this module. Set the
+ * context pointer at the beginning to this area.
+ * We have to use this strange scheme because we want to hide the
+ * internal data but have a variable sized buffer.
+ *
+ * +---+------+---........------+-------------+
+ * !ctx! bctl ! buffer ! private !
+ * +---+------+---........------+-------------+
+ * ! ^
+ * !---------------------------!
+ *
+ * We have to make sure that private is well aligned.
+ */
+ n = sizeof (struct gcry_md_handle) + bufsize;
+ n = ((n + sizeof (PROPERLY_ALIGNED_TYPE) - 1)
+ / sizeof (PROPERLY_ALIGNED_TYPE)) * sizeof (PROPERLY_ALIGNED_TYPE);
+
+ /* Allocate and set the Context pointer to the private data */
+ if (secure)
+ hd = xtrymalloc_secure (n + sizeof (struct gcry_md_context));
+ else
+ hd = xtrymalloc (n + sizeof (struct gcry_md_context));
+
+ if (! hd)
+ err = gpg_err_code_from_errno (errno);
+
+ if (! err)
+ {
+ hd->ctx = ctx = (void *) ((char *) hd + n);
+ /* Setup the globally visible data (bctl in the diagram).*/
+ hd->bufsize = n - sizeof (struct gcry_md_handle) + 1;
+ hd->bufpos = 0;
+
+ /* Initialize the private data. */
+ memset (hd->ctx, 0, sizeof *hd->ctx);
+ ctx->magic = secure ? CTX_MAGIC_SECURE : CTX_MAGIC_NORMAL;
+ ctx->actual_handle_size = n + sizeof (struct gcry_md_context);
+ ctx->flags.secure = secure;
+ ctx->flags.hmac = hmac;
+ ctx->flags.bugemu1 = !!(flags & GCRY_MD_FLAG_BUGEMU1);
+ }
+
+ if (! err)
+ {
+ /* Hmmm, should we really do that? - yes [-wk] */
+ _gcry_fast_random_poll ();
+
+ if (algo)
+ {
+ err = md_enable (hd, algo);
+ if (err)
+ md_close (hd);
+ }
+ }
+
+ if (! err)
+ *h = hd;
+
+ return err;
+}
+
+/* Create a message digest object for algorithm ALGO. FLAGS may be
+ given as an bitwise OR of the gcry_md_flags values. ALGO may be
+ given as 0 if the algorithms to be used are later set using
+ gcry_md_enable. H is guaranteed to be a valid handle or NULL on
+ error. */
+gcry_err_code_t
+_gcry_md_open (gcry_md_hd_t *h, int algo, unsigned int flags)
+{
+ gcry_err_code_t rc;
+ gcry_md_hd_t hd;
+
+ if ((flags & ~(GCRY_MD_FLAG_SECURE
+ | GCRY_MD_FLAG_HMAC
+ | GCRY_MD_FLAG_BUGEMU1)))
+ rc = GPG_ERR_INV_ARG;
+ else
+ rc = md_open (&hd, algo, flags);
+
+ *h = rc? NULL : hd;
+ return rc;
+}
+
+
+
+static gcry_err_code_t
+md_enable (gcry_md_hd_t hd, int algorithm)
+{
+ struct gcry_md_context *h = hd->ctx;
+ gcry_md_spec_t *spec;
+ GcryDigestEntry *entry;
+ gcry_err_code_t err = 0;
+
+ for (entry = h->list; entry; entry = entry->next)
+ if (entry->spec->algo == algorithm)
+ return 0; /* Already enabled */
+
+ spec = spec_from_algo (algorithm);
+ if (!spec)
+ {
+ log_debug ("md_enable: algorithm %d not available\n", algorithm);
+ err = GPG_ERR_DIGEST_ALGO;
+ }
+
+
+ if (!err && algorithm == GCRY_MD_MD5 && fips_mode ())
+ {
+ _gcry_inactivate_fips_mode ("MD5 used");
+ if (_gcry_enforced_fips_mode () )
+ {
+ /* We should never get to here because we do not register
+ MD5 in enforced fips mode. But better throw an error. */
+ err = GPG_ERR_DIGEST_ALGO;
+ }
+ }
+
+ if (!err && h->flags.hmac && spec->read == NULL)
+ {
+ /* Expandable output function cannot act as part of HMAC. */
+ err = GPG_ERR_DIGEST_ALGO;
+ }
+
+ if (!err)
+ {
+ size_t size = (sizeof (*entry)
+ + spec->contextsize * (h->flags.hmac? 3 : 1)
+ - sizeof (entry->context));
+
+ /* And allocate a new list entry. */
+ if (h->flags.secure)
+ entry = xtrymalloc_secure (size);
+ else
+ entry = xtrymalloc (size);
+
+ if (! entry)
+ err = gpg_err_code_from_errno (errno);
+ else
+ {
+ entry->spec = spec;
+ entry->next = h->list;
+ entry->actual_struct_size = size;
+ h->list = entry;
+
+ /* And init this instance. */
+ entry->spec->init (entry->context,
+ h->flags.bugemu1? GCRY_MD_FLAG_BUGEMU1:0);
+ }
+ }
+
+ return err;
+}
+
+
+gcry_err_code_t
+_gcry_md_enable (gcry_md_hd_t hd, int algorithm)
+{
+ return md_enable (hd, algorithm);
+}
+
+
+static gcry_err_code_t
+md_copy (gcry_md_hd_t ahd, gcry_md_hd_t *b_hd)
+{
+ gcry_err_code_t err = 0;
+ struct gcry_md_context *a = ahd->ctx;
+ struct gcry_md_context *b;
+ GcryDigestEntry *ar, *br;
+ gcry_md_hd_t bhd;
+ size_t n;
+
+ if (ahd->bufpos)
+ md_write (ahd, NULL, 0);
+
+ n = (char *) ahd->ctx - (char *) ahd;
+ if (a->flags.secure)
+ bhd = xtrymalloc_secure (n + sizeof (struct gcry_md_context));
+ else
+ bhd = xtrymalloc (n + sizeof (struct gcry_md_context));
+
+ if (!bhd)
+ {
+ err = gpg_err_code_from_syserror ();
+ goto leave;
+ }
+
+ bhd->ctx = b = (void *) ((char *) bhd + n);
+ /* No need to copy the buffer due to the write above. */
+ gcry_assert (ahd->bufsize == (n - sizeof (struct gcry_md_handle) + 1));
+ bhd->bufsize = ahd->bufsize;
+ bhd->bufpos = 0;
+ gcry_assert (! ahd->bufpos);
+ memcpy (b, a, sizeof *a);
+ b->list = NULL;
+ b->debug = NULL;
+
+ /* Copy the complete list of algorithms. The copied list is
+ reversed, but that doesn't matter. */
+ for (ar = a->list; ar; ar = ar->next)
+ {
+ if (a->flags.secure)
+ br = xtrymalloc_secure (ar->actual_struct_size);
+ else
+ br = xtrymalloc (ar->actual_struct_size);
+ if (!br)
+ {
+ err = gpg_err_code_from_syserror ();
+ md_close (bhd);
+ goto leave;
+ }
+
+ memcpy (br, ar, ar->actual_struct_size);
+ br->next = b->list;
+ b->list = br;
+ }
+
+ if (a->debug)
+ md_start_debug (bhd, "unknown");
+
+ *b_hd = bhd;
+
+ leave:
+ return err;
+}
+
+
+gcry_err_code_t
+_gcry_md_copy (gcry_md_hd_t *handle, gcry_md_hd_t hd)
+{
+ gcry_err_code_t rc;
+
+ rc = md_copy (hd, handle);
+ if (rc)
+ *handle = NULL;
+ return rc;
+}
+
+
+/*
+ * Reset all contexts and discard any buffered stuff. This may be used
+ * instead of a md_close(); md_open().
+ */
+void
+_gcry_md_reset (gcry_md_hd_t a)
+{
+ GcryDigestEntry *r;
+
+ /* Note: We allow this even in fips non operational mode. */
+
+ a->bufpos = a->ctx->flags.finalized = 0;
+
+ if (a->ctx->flags.hmac)
+ for (r = a->ctx->list; r; r = r->next)
+ {
+ memcpy (r->context, (char *)r->context + r->spec->contextsize,
+ r->spec->contextsize);
+ }
+ else
+ for (r = a->ctx->list; r; r = r->next)
+ {
+ memset (r->context, 0, r->spec->contextsize);
+ (*r->spec->init) (r->context,
+ a->ctx->flags.bugemu1? GCRY_MD_FLAG_BUGEMU1:0);
+ }
+}
+
+
+static void
+md_close (gcry_md_hd_t a)
+{
+ GcryDigestEntry *r, *r2;
+
+ if (! a)
+ return;
+ if (a->ctx->debug)
+ md_stop_debug (a);
+ for (r = a->ctx->list; r; r = r2)
+ {
+ r2 = r->next;
+ wipememory (r, r->actual_struct_size);
+ xfree (r);
+ }
+
+ wipememory (a, a->ctx->actual_handle_size);
+ xfree(a);
+}
+
+
+void
+_gcry_md_close (gcry_md_hd_t hd)
+{
+ /* Note: We allow this even in fips non operational mode. */
+ md_close (hd);
+}
+
+
+static void
+md_write (gcry_md_hd_t a, const void *inbuf, size_t inlen)
+{
+ GcryDigestEntry *r;
+
+ if (a->ctx->debug)
+ {
+ if (a->bufpos && fwrite (a->buf, a->bufpos, 1, a->ctx->debug) != 1)
+ BUG();
+ if (inlen && fwrite (inbuf, inlen, 1, a->ctx->debug) != 1)
+ BUG();
+ }
+
+ for (r = a->ctx->list; r; r = r->next)
+ {
+ if (a->bufpos)
+ (*r->spec->write) (r->context, a->buf, a->bufpos);
+ (*r->spec->write) (r->context, inbuf, inlen);
+ }
+ a->bufpos = 0;
+}
+
+
+/* Note that this function may be used after finalize and read to keep
+ on writing to the transform function so to mitigate timing
+ attacks. */
+void
+_gcry_md_write (gcry_md_hd_t hd, const void *inbuf, size_t inlen)
+{
+ md_write (hd, inbuf, inlen);
+}
+
+
+static void
+md_final (gcry_md_hd_t a)
+{
+ GcryDigestEntry *r;
+
+ if (a->ctx->flags.finalized)
+ return;
+
+ if (a->bufpos)
+ md_write (a, NULL, 0);
+
+ for (r = a->ctx->list; r; r = r->next)
+ (*r->spec->final) (r->context);
+
+ a->ctx->flags.finalized = 1;
+
+ if (!a->ctx->flags.hmac)
+ return;
+
+ for (r = a->ctx->list; r; r = r->next)
+ {
+ byte *p;
+ size_t dlen = r->spec->mdlen;
+ byte *hash;
+ gcry_err_code_t err;
+
+ if (r->spec->read == NULL)
+ continue;
+
+ p = r->spec->read (r->context);
+
+ if (a->ctx->flags.secure)
+ hash = xtrymalloc_secure (dlen);
+ else
+ hash = xtrymalloc (dlen);
+ if (!hash)
+ {
+ err = gpg_err_code_from_errno (errno);
+ _gcry_fatal_error (err, NULL);
+ }
+
+ memcpy (hash, p, dlen);
+ memcpy (r->context, (char *)r->context + r->spec->contextsize * 2,
+ r->spec->contextsize);
+ (*r->spec->write) (r->context, hash, dlen);
+ (*r->spec->final) (r->context);
+ xfree (hash);
+ }
+}
+
+
+static gcry_err_code_t
+md_setkey (gcry_md_hd_t h, const unsigned char *key, size_t keylen)
+{
+ gcry_err_code_t rc = 0;
+ GcryDigestEntry *r;
+ int algo_had_setkey = 0;
+
+ if (!h->ctx->list)
+ return GPG_ERR_DIGEST_ALGO; /* Might happen if no algo is enabled. */
+
+ if (h->ctx->flags.hmac)
+ return GPG_ERR_DIGEST_ALGO; /* Tried md_setkey for HMAC md. */
+
+ for (r = h->ctx->list; r; r = r->next)
+ {
+ switch (r->spec->algo)
+ {
+#if USE_BLAKE2
+ /* TODO? add spec->init_with_key? */
+ case GCRY_MD_BLAKE2B_512:
+ case GCRY_MD_BLAKE2B_384:
+ case GCRY_MD_BLAKE2B_256:
+ case GCRY_MD_BLAKE2B_160:
+ case GCRY_MD_BLAKE2S_256:
+ case GCRY_MD_BLAKE2S_224:
+ case GCRY_MD_BLAKE2S_160:
+ case GCRY_MD_BLAKE2S_128:
+ algo_had_setkey = 1;
+ memset (r->context, 0, r->spec->contextsize);
+ rc = _gcry_blake2_init_with_key (r->context,
+ h->ctx->flags.bugemu1
+ ? GCRY_MD_FLAG_BUGEMU1:0,
+ key, keylen, r->spec->algo);
+ break;
+#endif
+ default:
+ rc = GPG_ERR_DIGEST_ALGO;
+ break;
+ }
+
+ if (rc)
+ break;
+ }
+
+ if (rc && !algo_had_setkey)
+ {
+ /* None of algorithms had setkey implementation, so contexts were not
+ * modified. Just return error. */
+ return rc;
+ }
+ else if (rc && algo_had_setkey)
+ {
+ /* Some of the contexts have been modified, but got error. Reset
+ * all contexts. */
+ _gcry_md_reset (h);
+ return rc;
+ }
+
+ /* Successful md_setkey implies reset. */
+ h->bufpos = h->ctx->flags.finalized = 0;
+
+ return 0;
+}
+
+
+static gcry_err_code_t
+prepare_macpads (gcry_md_hd_t a, const unsigned char *key, size_t keylen)
+{
+ GcryDigestEntry *r;
+
+ if (!a->ctx->list)
+ return GPG_ERR_DIGEST_ALGO; /* Might happen if no algo is enabled. */
+
+ if (!a->ctx->flags.hmac)
+ return GPG_ERR_DIGEST_ALGO; /* Tried prepare_macpads for non-HMAC md. */
+
+ for (r = a->ctx->list; r; r = r->next)
+ {
+ const unsigned char *k;
+ size_t k_len;
+ unsigned char *key_allocated = NULL;
+ int macpad_Bsize;
+ int i;
+
+ switch (r->spec->algo)
+ {
+ /* TODO: add spec->blocksize */
+ case GCRY_MD_SHA3_224:
+ macpad_Bsize = 1152 / 8;
+ break;
+ case GCRY_MD_SHA3_256:
+ macpad_Bsize = 1088 / 8;
+ break;
+ case GCRY_MD_SHA3_384:
+ macpad_Bsize = 832 / 8;
+ break;
+ case GCRY_MD_SHA3_512:
+ macpad_Bsize = 576 / 8;
+ break;
+ case GCRY_MD_SHA384:
+ case GCRY_MD_SHA512:
+ case GCRY_MD_SHA512_256:
+ case GCRY_MD_SHA512_224:
+ case GCRY_MD_BLAKE2B_512:
+ case GCRY_MD_BLAKE2B_384:
+ case GCRY_MD_BLAKE2B_256:
+ case GCRY_MD_BLAKE2B_160:
+ macpad_Bsize = 128;
+ break;
+ case GCRY_MD_GOSTR3411_94:
+ case GCRY_MD_GOSTR3411_CP:
+ macpad_Bsize = 32;
+ break;
+ default:
+ macpad_Bsize = 64;
+ break;
+ }
+
+ if ( keylen > macpad_Bsize )
+ {
+ k = key_allocated = xtrymalloc_secure (r->spec->mdlen);
+ if (!k)
+ return gpg_err_code_from_errno (errno);
+ _gcry_md_hash_buffer (r->spec->algo, key_allocated, key, keylen);
+ k_len = r->spec->mdlen;
+ gcry_assert ( k_len <= macpad_Bsize );
+ }
+ else
+ {
+ k = key;
+ k_len = keylen;
+ }
+
+ (*r->spec->init) (r->context,
+ a->ctx->flags.bugemu1? GCRY_MD_FLAG_BUGEMU1:0);
+ a->bufpos = 0;
+ for (i=0; i < k_len; i++ )
+ _gcry_md_putc (a, k[i] ^ 0x36);
+ for (; i < macpad_Bsize; i++ )
+ _gcry_md_putc (a, 0x36);
+ (*r->spec->write) (r->context, a->buf, a->bufpos);
+ memcpy ((char *)r->context + r->spec->contextsize, r->context,
+ r->spec->contextsize);
+
+ (*r->spec->init) (r->context,
+ a->ctx->flags.bugemu1? GCRY_MD_FLAG_BUGEMU1:0);
+ a->bufpos = 0;
+ for (i=0; i < k_len; i++ )
+ _gcry_md_putc (a, k[i] ^ 0x5c);
+ for (; i < macpad_Bsize; i++ )
+ _gcry_md_putc (a, 0x5c);
+ (*r->spec->write) (r->context, a->buf, a->bufpos);
+ memcpy ((char *)r->context + r->spec->contextsize*2, r->context,
+ r->spec->contextsize);
+
+ xfree (key_allocated);
+ }
+
+ a->bufpos = 0;
+ return 0;
+}
+
+
+gcry_err_code_t
+_gcry_md_ctl (gcry_md_hd_t hd, int cmd, void *buffer, size_t buflen)
+{
+ gcry_err_code_t rc = 0;
+
+ (void)buflen; /* Currently not used. */
+
+ switch (cmd)
+ {
+ case GCRYCTL_FINALIZE:
+ md_final (hd);
+ break;
+ case GCRYCTL_START_DUMP:
+ md_start_debug (hd, buffer);
+ break;
+ case GCRYCTL_STOP_DUMP:
+ md_stop_debug ( hd );
+ break;
+ default:
+ rc = GPG_ERR_INV_OP;
+ }
+ return rc;
+}
+
+
+gcry_err_code_t
+_gcry_md_setkey (gcry_md_hd_t hd, const void *key, size_t keylen)
+{
+ gcry_err_code_t rc;
+
+ if (hd->ctx->flags.hmac)
+ {
+ rc = prepare_macpads (hd, key, keylen);
+ if (!rc)
+ _gcry_md_reset (hd);
+ }
+ else
+ {
+ rc = md_setkey (hd, key, keylen);
+ }
+
+ return rc;
+}
+
+
+/* The new debug interface. If SUFFIX is a string it creates an debug
+ file for the context HD. IF suffix is NULL, the file is closed and
+ debugging is stopped. */
+void
+_gcry_md_debug (gcry_md_hd_t hd, const char *suffix)
+{
+ if (suffix)
+ md_start_debug (hd, suffix);
+ else
+ md_stop_debug (hd);
+}
+
+
+/****************
+ * If ALGO is null get the digest for the used algo (which should be
+ * only one)
+ */
+static byte *
+md_read( gcry_md_hd_t a, int algo )
+{
+ GcryDigestEntry *r = a->ctx->list;
+
+ if (! algo)
+ {
+ /* Return the first algorithm */
+ if (r)
+ {
+ if (r->next)
+ log_debug ("more than one algorithm in md_read(0)\n");
+ if (r->spec->read)
+ return r->spec->read (r->context);
+ }
+ }
+ else
+ {
+ for (r = a->ctx->list; r; r = r->next)
+ if (r->spec->algo == algo)
+ {
+ if (r->spec->read)
+ return r->spec->read (r->context);
+ break;
+ }
+ }
+
+ if (r && !r->spec->read)
+ _gcry_fatal_error (GPG_ERR_DIGEST_ALGO,
+ "requested algo has no fixed digest length");
+ else
+ _gcry_fatal_error (GPG_ERR_DIGEST_ALGO, "requested algo not in md context");
+ return NULL;
+}
+
+
+/*
+ * Read out the complete digest, this function implictly finalizes
+ * the hash.
+ */
+byte *
+_gcry_md_read (gcry_md_hd_t hd, int algo)
+{
+ /* This function is expected to always return a digest, thus we
+ can't return an error which we actually should do in
+ non-operational state. */
+ _gcry_md_ctl (hd, GCRYCTL_FINALIZE, NULL, 0);
+ return md_read (hd, algo);
+}
+
+
+/****************
+ * If ALGO is null get the digest for the used algo (which should be
+ * only one)
+ */
+static gcry_err_code_t
+md_extract(gcry_md_hd_t a, int algo, void *out, size_t outlen)
+{
+ GcryDigestEntry *r = a->ctx->list;
+
+ if (!algo)
+ {
+ /* Return the first algorithm */
+ if (r && r->spec->extract)
+ {
+ if (r->next)
+ log_debug ("more than one algorithm in md_extract(0)\n");
+ r->spec->extract (r->context, out, outlen);
+ return 0;
+ }
+ }
+ else
+ {
+ for (r = a->ctx->list; r; r = r->next)
+ if (r->spec->algo == algo && r->spec->extract)
+ {
+ r->spec->extract (r->context, out, outlen);
+ return 0;
+ }
+ }
+
+ return GPG_ERR_DIGEST_ALGO;
+}
+
+
+/*
+ * Expand the output from XOF class digest, this function implictly finalizes
+ * the hash.
+ */
+gcry_err_code_t
+_gcry_md_extract (gcry_md_hd_t hd, int algo, void *out, size_t outlen)
+{
+ _gcry_md_ctl (hd, GCRYCTL_FINALIZE, NULL, 0);
+ return md_extract (hd, algo, out, outlen);
+}
+
+
+/*
+ * Read out an intermediate digest. Not yet functional.
+ */
+gcry_err_code_t
+_gcry_md_get (gcry_md_hd_t hd, int algo, byte *buffer, int buflen)
+{
+ (void)hd;
+ (void)algo;
+ (void)buffer;
+ (void)buflen;
+
+ /*md_digest ... */
+ fips_signal_error ("unimplemented function called");
+ return GPG_ERR_INTERNAL;
+}
+
+
+/*
+ * Shortcut function to hash a buffer with a given algo. The only
+ * guaranteed supported algorithms are RIPE-MD160 and SHA-1. The
+ * supplied digest buffer must be large enough to store the resulting
+ * hash. No error is returned, the function will abort on an invalid
+ * algo. DISABLED_ALGOS are ignored here. */
+void
+_gcry_md_hash_buffer (int algo, void *digest,
+ const void *buffer, size_t length)
+{
+ gcry_md_spec_t *spec;
+
+ spec = spec_from_algo (algo);
+ if (!spec)
+ {
+ log_debug ("md_hash_buffer: algorithm %d not available\n", algo);
+ return;
+ }
+
+ if (algo == GCRY_MD_MD5 && fips_mode ())
+ {
+ _gcry_inactivate_fips_mode ("MD5 used");
+ if (_gcry_enforced_fips_mode () )
+ {
+ /* We should never get to here because we do not register
+ MD5 in enforced fips mode. */
+ _gcry_fips_noreturn ();
+ }
+ }
+
+ if (spec->hash_buffer != NULL)
+ {
+ spec->hash_buffer (digest, buffer, length);
+ }
+ else if (spec->hash_buffers != NULL)
+ {
+ gcry_buffer_t iov;
+
+ iov.size = 0;
+ iov.data = (void *)buffer;
+ iov.off = 0;
+ iov.len = length;
+
+ spec->hash_buffers (digest, &iov, 1);
+ }
+ else
+ {
+ /* For the others we do not have a fast function, so we use the
+ normal functions. */
+ gcry_md_hd_t h;
+ gpg_err_code_t err;
+
+ err = md_open (&h, algo, 0);
+ if (err)
+ log_bug ("gcry_md_open failed for algo %d: %s",
+ algo, gpg_strerror (gcry_error(err)));
+ md_write (h, (byte *) buffer, length);
+ md_final (h);
+ memcpy (digest, md_read (h, algo), md_digest_length (algo));
+ md_close (h);
+ }
+}
+
+
+/* Shortcut function to hash multiple buffers with a given algo. In
+ contrast to gcry_md_hash_buffer, this function returns an error on
+ invalid arguments or on other problems; disabled algorithms are
+ _not_ ignored but flagged as an error.
+
+ The data to sign is taken from the array IOV which has IOVCNT items.
+
+ The only supported flag in FLAGS is GCRY_MD_FLAG_HMAC which turns
+ this function into a HMAC function; the first item in IOV is then
+ used as the key.
+
+ On success 0 is returned and resulting hash or HMAC is stored at
+ DIGEST which must have been provided by the caller with an
+ appropriate length. */
+gpg_err_code_t
+_gcry_md_hash_buffers (int algo, unsigned int flags, void *digest,
+ const gcry_buffer_t *iov, int iovcnt)
+{
+ gcry_md_spec_t *spec;
+ int hmac;
+
+ if (!iov || iovcnt < 0)
+ return GPG_ERR_INV_ARG;
+ if (flags & ~(GCRY_MD_FLAG_HMAC))
+ return GPG_ERR_INV_ARG;
+
+ hmac = !!(flags & GCRY_MD_FLAG_HMAC);
+ if (hmac && iovcnt < 1)
+ return GPG_ERR_INV_ARG;
+
+ spec = spec_from_algo (algo);
+ if (!spec)
+ {
+ log_debug ("md_hash_buffers: algorithm %d not available\n", algo);
+ return GPG_ERR_DIGEST_ALGO;
+ }
+
+ if (algo == GCRY_MD_MD5 && fips_mode ())
+ {
+ _gcry_inactivate_fips_mode ("MD5 used");
+ if (_gcry_enforced_fips_mode () )
+ {
+ /* We should never get to here because we do not register
+ MD5 in enforced fips mode. */
+ _gcry_fips_noreturn ();
+ }
+ }
+
+ if (!hmac && spec->hash_buffers)
+ {
+ spec->hash_buffers (digest, iov, iovcnt);
+ }
+ else
+ {
+ /* For the others we do not have a fast function, so we use the
+ normal functions. */
+ gcry_md_hd_t h;
+ gpg_err_code_t rc;
+ int dlen;
+
+ /* Detect SHAKE128 like algorithms which we can't use because
+ * our API does not allow for a variable length digest. */
+ dlen = md_digest_length (algo);
+ if (!dlen)
+ return GPG_ERR_DIGEST_ALGO;
+
+ rc = md_open (&h, algo, (hmac? GCRY_MD_FLAG_HMAC:0));
+ if (rc)
+ return rc;
+
+ if (hmac)
+ {
+ rc = _gcry_md_setkey (h,
+ (const char*)iov[0].data + iov[0].off,
+ iov[0].len);
+ if (rc)
+ {
+ md_close (h);
+ return rc;
+ }
+ iov++; iovcnt--;
+ }
+ for (;iovcnt; iov++, iovcnt--)
+ md_write (h, (const char*)iov[0].data + iov[0].off, iov[0].len);
+ md_final (h);
+ memcpy (digest, md_read (h, algo), dlen);
+ md_close (h);
+ }
+
+ return 0;
+}
+
+
+static int
+md_get_algo (gcry_md_hd_t a)
+{
+ GcryDigestEntry *r = a->ctx->list;
+
+ if (r && r->next)
+ {
+ fips_signal_error ("possible usage error");
+ log_error ("WARNING: more than one algorithm in md_get_algo()\n");
+ }
+ return r ? r->spec->algo : 0;
+}
+
+
+int
+_gcry_md_get_algo (gcry_md_hd_t hd)
+{
+ return md_get_algo (hd);
+}
+
+
+/****************
+ * Return the length of the digest
+ */
+static int
+md_digest_length (int algorithm)
+{
+ gcry_md_spec_t *spec;
+
+ spec = spec_from_algo (algorithm);
+ return spec? spec->mdlen : 0;
+}
+
+
+/****************
+ * Return the length of the digest in bytes.
+ * This function will return 0 in case of errors.
+ */
+unsigned int
+_gcry_md_get_algo_dlen (int algorithm)
+{
+ return md_digest_length (algorithm);
+}
+
+
+/* Hmmm: add a mode to enumerate the OIDs
+ * to make g10/sig-check.c more portable */
+static const byte *
+md_asn_oid (int algorithm, size_t *asnlen, size_t *mdlen)
+{
+ gcry_md_spec_t *spec;
+ const byte *asnoid = NULL;
+
+ spec = spec_from_algo (algorithm);
+ if (spec)
+ {
+ if (asnlen)
+ *asnlen = spec->asnlen;
+ if (mdlen)
+ *mdlen = spec->mdlen;
+ asnoid = spec->asnoid;
+ }
+ else
+ log_bug ("no ASN.1 OID for md algo %d\n", algorithm);
+
+ return asnoid;
+}
+
+
+/****************
+ * Return information about the given cipher algorithm
+ * WHAT select the kind of information returned:
+ * GCRYCTL_TEST_ALGO:
+ * Returns 0 when the specified algorithm is available for use.
+ * buffer and nbytes must be zero.
+ * GCRYCTL_GET_ASNOID:
+ * Return the ASNOID of the algorithm in buffer. if buffer is NULL, only
+ * the required length is returned.
+ * GCRYCTL_SELFTEST
+ * Helper for the regression tests - shall not be used by applications.
+ *
+ * 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_err_code_t
+_gcry_md_algo_info (int algo, int what, void *buffer, size_t *nbytes)
+{
+ gcry_err_code_t rc;
+
+ switch (what)
+ {
+ case GCRYCTL_TEST_ALGO:
+ if (buffer || nbytes)
+ rc = GPG_ERR_INV_ARG;
+ else
+ rc = check_digest_algo (algo);
+ break;
+
+ case GCRYCTL_GET_ASNOID:
+ /* We need to check that the algo is available because
+ md_asn_oid would otherwise raise an assertion. */
+ rc = check_digest_algo (algo);
+ if (!rc)
+ {
+ const char unsigned *asn;
+ size_t asnlen;
+
+ asn = md_asn_oid (algo, &asnlen, NULL);
+ if (buffer && (*nbytes >= asnlen))
+ {
+ memcpy (buffer, asn, asnlen);
+ *nbytes = asnlen;
+ }
+ else if (!buffer && nbytes)
+ *nbytes = asnlen;
+ else
+ {
+ if (buffer)
+ rc = GPG_ERR_TOO_SHORT;
+ else
+ rc = GPG_ERR_INV_ARG;
+ }
+ }
+ break;
+
+ case GCRYCTL_SELFTEST:
+ /* Helper function for the regression tests. */
+ rc = gpg_err_code (_gcry_md_selftest (algo, nbytes? (int)*nbytes : 0,
+ NULL));
+ break;
+
+ default:
+ rc = GPG_ERR_INV_OP;
+ break;
+ }
+
+ return rc;
+}
+
+
+static void
+md_start_debug ( gcry_md_hd_t md, const char *suffix )
+{
+ static int idx=0;
+ char buf[50];
+
+ if (fips_mode ())
+ return;
+
+ if ( md->ctx->debug )
+ {
+ log_debug("Oops: md debug already started\n");
+ return;
+ }
+ idx++;
+ snprintf (buf, DIM(buf)-1, "dbgmd-%05d.%.10s", idx, suffix );
+ md->ctx->debug = fopen(buf, "w");
+ if ( !md->ctx->debug )
+ log_debug("md debug: can't open %s\n", buf );
+}
+
+
+static void
+md_stop_debug( gcry_md_hd_t md )
+{
+ if ( md->ctx->debug )
+ {
+ if ( md->bufpos )
+ md_write ( md, NULL, 0 );
+ fclose (md->ctx->debug);
+ md->ctx->debug = NULL;
+ }
+
+ { /* a kludge to pull in the __muldi3 for Solaris */
+ volatile u32 a = (u32)(uintptr_t)md;
+ volatile u64 b = 42;
+ volatile u64 c;
+ c = a * b;
+ (void)c;
+ }
+}
+
+
+
+/*
+ * Return information about the digest handle.
+ * GCRYCTL_IS_SECURE:
+ * Returns 1 when the handle works on secured memory
+ * otherwise 0 is returned. There is no error return.
+ * GCRYCTL_IS_ALGO_ENABLED:
+ * Returns 1 if the algo is enabled for that handle.
+ * The algo must be passed as the address of an int.
+ */
+gcry_err_code_t
+_gcry_md_info (gcry_md_hd_t h, int cmd, void *buffer, size_t *nbytes)
+{
+ gcry_err_code_t rc = 0;
+
+ switch (cmd)
+ {
+ case GCRYCTL_IS_SECURE:
+ *nbytes = h->ctx->flags.secure;
+ break;
+
+ case GCRYCTL_IS_ALGO_ENABLED:
+ {
+ GcryDigestEntry *r;
+ int algo;
+
+ if ( !buffer || !nbytes || *nbytes != sizeof (int))
+ rc = GPG_ERR_INV_ARG;
+ else
+ {
+ algo = *(int*)buffer;
+
+ *nbytes = 0;
+ for(r=h->ctx->list; r; r = r->next ) {
+ if (r->spec->algo == algo)
+ {
+ *nbytes = 1;
+ break;
+ }
+ }
+ }
+ break;
+ }
+
+ default:
+ rc = GPG_ERR_INV_OP;
+ }
+
+ return rc;
+}
+
+
+/* Explicitly initialize this module. */
+gcry_err_code_t
+_gcry_md_init (void)
+{
+ if (fips_mode())
+ {
+ /* disable algorithms that are disallowed in fips */
+ int idx;
+ gcry_md_spec_t *spec;
+
+ for (idx = 0; (spec = digest_list[idx]); idx++)
+ if (!spec->flags.fips)
+ spec->flags.disabled = 1;
+ }
+
+ return 0;
+}
+
+
+int
+_gcry_md_is_secure (gcry_md_hd_t a)
+{
+ size_t value;
+
+ if (_gcry_md_info (a, GCRYCTL_IS_SECURE, NULL, &value))
+ value = 1; /* It seems to be better to assume secure memory on
+ error. */
+ return value;
+}
+
+
+int
+_gcry_md_is_enabled (gcry_md_hd_t a, int algo)
+{
+ size_t value;
+
+ value = sizeof algo;
+ if (_gcry_md_info (a, GCRYCTL_IS_ALGO_ENABLED, &algo, &value))
+ value = 0;
+ return value;
+}
+
+
+/* Run the selftests for digest algorithm ALGO with optional reporting
+ function REPORT. */
+gpg_error_t
+_gcry_md_selftest (int algo, int extended, selftest_report_func_t report)
+{
+ gcry_err_code_t ec = 0;
+ gcry_md_spec_t *spec;
+
+ spec = spec_from_algo (algo);
+ if (spec && !spec->flags.disabled && spec->selftest)
+ ec = spec->selftest (algo, extended, report);
+ else
+ {
+ ec = (spec && spec->selftest) ? GPG_ERR_DIGEST_ALGO
+ /* */ : GPG_ERR_NOT_IMPLEMENTED;
+ if (report)
+ report ("digest", algo, "module",
+ (spec && !spec->flags.disabled)?
+ "no selftest available" :
+ spec? "algorithm disabled" : "algorithm not found");
+ }
+
+ return gpg_error (ec);
+}