diff --git a/cipher/md.c b/cipher/md.c index efb7376a..99de81c3 100644 --- a/cipher/md.c +++ b/cipher/md.c @@ -1,1639 +1,1636 @@ /* 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 . */ #include #include #include #include #include #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 ()) + if (!err && !spec->flags.fips && 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); }