diff options
Diffstat (limited to 'comm/third_party/libgcrypt/cipher/md.c')
-rw-r--r-- | comm/third_party/libgcrypt/cipher/md.c | 1639 |
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); +} |