/* md.c - message digest dispatcher * Copyright (C) 1998, 1999, 2002, 2003, 2006, * 2008 Free Software Foundation, Inc. * * 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" #include "ath.h" #include "rmd.h" /* A dummy extraspec so that we do not need to tests the extraspec field from the module specification against NULL and instead directly test the respective fields of extraspecs. */ static md_extra_spec_t dummy_extra_spec; /* This is the list of the digest implementations included in libgcrypt. */ static struct digest_table_entry { gcry_md_spec_t *digest; md_extra_spec_t *extraspec; unsigned int algorithm; int fips_allowed; } digest_table[] = { #if USE_CRC /* We allow the CRC algorithms even in FIPS mode because they are actually no cryptographic primitives. */ { &_gcry_digest_spec_crc32, &dummy_extra_spec, GCRY_MD_CRC32, 1 }, { &_gcry_digest_spec_crc32_rfc1510, &dummy_extra_spec, GCRY_MD_CRC32_RFC1510, 1 }, { &_gcry_digest_spec_crc24_rfc2440, &dummy_extra_spec, GCRY_MD_CRC24_RFC2440, 1 }, #endif #if USE_MD4 { &_gcry_digest_spec_md4, &dummy_extra_spec, GCRY_MD_MD4 }, #endif #if USE_MD5 { &_gcry_digest_spec_md5, &dummy_extra_spec, GCRY_MD_MD5, 1 }, #endif #if USE_RMD160 { &_gcry_digest_spec_rmd160, &dummy_extra_spec, GCRY_MD_RMD160 }, #endif #if USE_SHA1 { &_gcry_digest_spec_sha1, &_gcry_digest_extraspec_sha1, GCRY_MD_SHA1, 1 }, #endif #if USE_SHA256 { &_gcry_digest_spec_sha256, &_gcry_digest_extraspec_sha256, GCRY_MD_SHA256, 1 }, { &_gcry_digest_spec_sha224, &_gcry_digest_extraspec_sha224, GCRY_MD_SHA224, 1 }, #endif #if USE_SHA512 { &_gcry_digest_spec_sha512, &_gcry_digest_extraspec_sha512, GCRY_MD_SHA512, 1 }, { &_gcry_digest_spec_sha384, &_gcry_digest_extraspec_sha384, GCRY_MD_SHA384, 1 }, #endif #if USE_TIGER { &_gcry_digest_spec_tiger, &dummy_extra_spec, GCRY_MD_TIGER }, { &_gcry_digest_spec_tiger1, &dummy_extra_spec, GCRY_MD_TIGER1 }, { &_gcry_digest_spec_tiger2, &dummy_extra_spec, GCRY_MD_TIGER2 }, #endif #if USE_WHIRLPOOL { &_gcry_digest_spec_whirlpool, &dummy_extra_spec, GCRY_MD_WHIRLPOOL }, #endif { NULL }, }; /* List of registered digests. */ static gcry_module_t digests_registered; /* This is the lock protecting DIGESTS_REGISTERED. */ static ath_mutex_t digests_registered_lock = ATH_MUTEX_INITIALIZER; /* Flag to check wether the default ciphers have already been registered. */ static int default_digests_registered; typedef struct gcry_md_list { gcry_md_spec_t *digest; gcry_module_t module; struct gcry_md_list *next; size_t actual_struct_size; /* Allocated size of this structure. */ PROPERLY_ALIGNED_TYPE context; } 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. */ int secure; FILE *debug; int finalized; GcryDigestEntry *list; byte *macpads; int macpads_Bsize; /* Blocksize as used for the HMAC pads. */ }; #define CTX_MAGIC_NORMAL 0x11071961 #define CTX_MAGIC_SECURE 0x16917011 /* Convenient macro for registering the default digests. */ #define REGISTER_DEFAULT_DIGESTS \ do \ { \ ath_mutex_lock (&digests_registered_lock); \ if (! default_digests_registered) \ { \ md_register_default (); \ default_digests_registered = 1; \ } \ ath_mutex_unlock (&digests_registered_lock); \ } \ while (0) static const char * digest_algo_to_string( int algo ); static gcry_err_code_t check_digest_algo (int algo); static gcry_err_code_t md_open (gcry_md_hd_t *h, int algo, int secure, int hmac); static gcry_err_code_t md_enable (gcry_md_hd_t hd, int algo); static gcry_err_code_t md_copy (gcry_md_hd_t a, gcry_md_hd_t *b); 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 void md_final(gcry_md_hd_t a); 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 const byte *md_asn_oid( int algo, size_t *asnlen, size_t *mdlen ); static void md_start_debug ( gcry_md_hd_t a, const char *suffix ); static void md_stop_debug ( gcry_md_hd_t a ); /* Internal function. Register all the ciphers included in CIPHER_TABLE. Returns zero on success or an error code. */ static void md_register_default (void) { gcry_err_code_t err = 0; int i; for (i = 0; !err && digest_table[i].digest; i++) { if ( fips_mode ()) { if (!digest_table[i].fips_allowed) continue; if (digest_table[i].algorithm == GCRY_MD_MD5 && _gcry_enforced_fips_mode () ) continue; /* Do not register in enforced fips mode. */ } err = _gcry_module_add (&digests_registered, digest_table[i].algorithm, (void *) digest_table[i].digest, (void *) digest_table[i].extraspec, NULL); } if (err) BUG (); } /* Internal callback function. */ static int gcry_md_lookup_func_name (void *spec, void *data) { gcry_md_spec_t *digest = (gcry_md_spec_t *) spec; char *name = (char *) data; return (!_stricmp (digest->name, name)); } /* Internal callback function. Used via _gcry_module_lookup. */ static int gcry_md_lookup_func_oid (void *spec, void *data) { gcry_md_spec_t *digest = (gcry_md_spec_t *) spec; char *oid = (char *) data; gcry_md_oid_spec_t *oid_specs = digest->oids; int ret = 0, i; if (oid_specs) { for (i = 0; oid_specs[i].oidstring && (! ret); i++) if (!_stricmp (oid, oid_specs[i].oidstring)) ret = 1; } return ret; } /* Internal function. Lookup a digest entry by it's name. */ static gcry_module_t gcry_md_lookup_name (const char *name) { gcry_module_t digest; digest = _gcry_module_lookup (digests_registered, (void *) name, gcry_md_lookup_func_name); return digest; } /* Internal function. Lookup a cipher entry by it's oid. */ static gcry_module_t gcry_md_lookup_oid (const char *oid) { gcry_module_t digest; digest = _gcry_module_lookup (digests_registered, (void *) oid, gcry_md_lookup_func_oid); return digest; } /* Register a new digest module whose specification can be found in DIGEST. On success, a new algorithm ID is stored in ALGORITHM_ID and a pointer representhing this module is stored in MODULE. */ gcry_error_t _gcry_md_register (gcry_md_spec_t *digest, md_extra_spec_t *extraspec, unsigned int *algorithm_id, gcry_module_t *module) { gcry_err_code_t err = 0; gcry_module_t mod; /* We do not support module loading in fips mode. */ if (fips_mode ()) return gpg_error (GPG_ERR_NOT_SUPPORTED); ath_mutex_lock (&digests_registered_lock); err = _gcry_module_add (&digests_registered, 0, (void *) digest, (void *)(extraspec? extraspec : &dummy_extra_spec), &mod); ath_mutex_unlock (&digests_registered_lock); if (! err) { *module = mod; *algorithm_id = mod->mod_id; } return gcry_error (err); } /* Unregister the digest identified by ID, which must have been registered with gcry_digest_register. */ void gcry_md_unregister (gcry_module_t module) { ath_mutex_lock (&digests_registered_lock); _gcry_module_release (module); ath_mutex_unlock (&digests_registered_lock); } static int search_oid (const char *oid, int *algorithm, gcry_md_oid_spec_t *oid_spec) { gcry_module_t module; int ret = 0; if (oid && ((! strncmp (oid, "oid.", 4)) || (! strncmp (oid, "OID.", 4)))) oid += 4; module = gcry_md_lookup_oid (oid); if (module) { gcry_md_spec_t *digest = module->spec; int i; for (i = 0; digest->oids[i].oidstring && !ret; i++) if (!_stricmp (oid, digest->oids[i].oidstring)) { if (algorithm) *algorithm = module->mod_id; if (oid_spec) *oid_spec = digest->oids[i]; ret = 1; } _gcry_module_release (module); } return ret; } /**************** * Map a string to the digest algo */ int gcry_md_map_name (const char *string) { gcry_module_t digest; int ret, algorithm = 0; if (! string) return 0; REGISTER_DEFAULT_DIGESTS; /* 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 */ ath_mutex_lock (&digests_registered_lock); ret = search_oid (string, &algorithm, NULL); if (! ret) { /* Not found, search a matching digest name. */ digest = gcry_md_lookup_name (string); if (digest) { algorithm = digest->mod_id; _gcry_module_release (digest); } } ath_mutex_unlock (&digests_registered_lock); return algorithm; } /**************** * Map a digest algo to a string */ static const char * digest_algo_to_string (int algorithm) { const char *name = NULL; gcry_module_t digest; REGISTER_DEFAULT_DIGESTS; ath_mutex_lock (&digests_registered_lock); digest = _gcry_module_lookup_id (digests_registered, algorithm); if (digest) { name = ((gcry_md_spec_t *) digest->spec)->name; _gcry_module_release (digest); } ath_mutex_unlock (&digests_registered_lock); return name; } /**************** * 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) { const char *s = digest_algo_to_string (algorithm); return s ? s : "?"; } static gcry_err_code_t check_digest_algo (int algorithm) { gcry_err_code_t rc = 0; gcry_module_t digest; REGISTER_DEFAULT_DIGESTS; ath_mutex_lock (&digests_registered_lock); digest = _gcry_module_lookup_id (digests_registered, algorithm); if (digest) _gcry_module_release (digest); else rc = GPG_ERR_DIGEST_ALGO; ath_mutex_unlock (&digests_registered_lock); return rc; } /**************** * 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, int secure, int hmac) { gcry_err_code_t err = GPG_ERR_NO_ERROR; 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 = gcry_malloc_secure (n + sizeof (struct gcry_md_context)); else hd = gcry_malloc (n + sizeof (struct gcry_md_context)); if (! hd) err = gpg_err_code_from_errno (errno); if (! err) { hd->ctx = ctx = (struct gcry_md_context *) ((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->secure = secure; if (hmac) { switch (algo) { case GCRY_MD_SHA384: case GCRY_MD_SHA512: ctx->macpads_Bsize = 128; break; default: ctx->macpads_Bsize = 64; break; } ctx->macpads = gcry_malloc_secure (2*(ctx->macpads_Bsize)); if (!ctx->macpads) { err = gpg_err_code_from_errno (errno); md_close (hd); } } } 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_error_t gcry_md_open (gcry_md_hd_t *h, int algo, unsigned int flags) { gcry_err_code_t err = GPG_ERR_NO_ERROR; gcry_md_hd_t hd; if ((flags & ~(GCRY_MD_FLAG_SECURE | GCRY_MD_FLAG_HMAC))) err = GPG_ERR_INV_ARG; else { err = md_open (&hd, algo, (flags & GCRY_MD_FLAG_SECURE), (flags & GCRY_MD_FLAG_HMAC)); } *h = err? NULL : hd; return gcry_error (err); } 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 *digest = NULL; GcryDigestEntry *entry; gcry_module_t module; gcry_err_code_t err = 0; for (entry = h->list; entry; entry = entry->next) if (entry->module->mod_id == algorithm) return err; /* already enabled */ REGISTER_DEFAULT_DIGESTS; ath_mutex_lock (&digests_registered_lock); module = _gcry_module_lookup_id (digests_registered, algorithm); ath_mutex_unlock (&digests_registered_lock); if (! module) { log_debug ("md_enable: algorithm %d not available\n", algorithm); err = GPG_ERR_DIGEST_ALGO; } else digest = (gcry_md_spec_t *) module->spec; 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) { size_t size = (sizeof (*entry) + digest->contextsize - sizeof (entry->context)); /* And allocate a new list entry. */ if (h->secure) entry = gcry_malloc_secure (size); else entry = gcry_malloc (size); if (! entry) err = gpg_err_code_from_errno (errno); else { entry->digest = digest; entry->module = module; entry->next = h->list; entry->actual_struct_size = size; h->list = entry; /* And init this instance. */ entry->digest->init (&entry->context.c); } } if (err) { if (module) { ath_mutex_lock (&digests_registered_lock); _gcry_module_release (module); ath_mutex_unlock (&digests_registered_lock); } } return err; } gcry_error_t gcry_md_enable (gcry_md_hd_t hd, int algorithm) { return gcry_error (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 = GPG_ERR_NO_ERROR; 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->secure) bhd = gcry_malloc_secure (n + sizeof (struct gcry_md_context)); else bhd = gcry_malloc (n + sizeof (struct gcry_md_context)); if (! bhd) err = gpg_err_code_from_errno (errno); if (! err) { bhd->ctx = b = (struct gcry_md_context *) ((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; if (a->macpads) { b->macpads = gcry_malloc_secure (2*(a->macpads_Bsize)); if (! b->macpads) { err = gpg_err_code_from_errno (errno); md_close (bhd); } else memcpy (b->macpads, a->macpads, (2*(a->macpads_Bsize))); } } /* Copy the complete list of algorithms. The copied list is reversed, but that doesn't matter. */ if (!err) { for (ar = a->list; ar; ar = ar->next) { if (a->secure) br = gcry_malloc_secure (sizeof *br + ar->digest->contextsize - sizeof(ar->context)); else br = gcry_malloc (sizeof *br + ar->digest->contextsize - sizeof (ar->context)); if (!br) { err = gpg_err_code_from_errno (errno); md_close (bhd); break; } memcpy (br, ar, (sizeof (*br) + ar->digest->contextsize - sizeof (ar->context))); br->next = b->list; b->list = br; /* Add a reference to the module. */ ath_mutex_lock (&digests_registered_lock); _gcry_module_use (br->module); ath_mutex_unlock (&digests_registered_lock); } } if (a->debug && !err) md_start_debug (bhd, "unknown"); if (!err) *b_hd = bhd; return err; } gcry_error_t gcry_md_copy (gcry_md_hd_t *handle, gcry_md_hd_t hd) { gcry_err_code_t err; err = md_copy (hd, handle); if (err) *handle = NULL; return gcry_error (err); } /* * 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->finalized = 0; for (r = a->ctx->list; r; r = r->next) { memset (r->context.c, 0, r->digest->contextsize); (*r->digest->init) (&r->context.c); } if (a->ctx->macpads) md_write (a, a->ctx->macpads, a->ctx->macpads_Bsize); /* inner pad */ } 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; ath_mutex_lock (&digests_registered_lock); _gcry_module_release (r->module); ath_mutex_unlock (&digests_registered_lock); wipememory (r, r->actual_struct_size); gcry_free (r); } if (a->ctx->macpads) { wipememory (a->ctx->macpads, 2*(a->ctx->macpads_Bsize)); gcry_free(a->ctx->macpads); } wipememory (a, a->ctx->actual_handle_size); gcry_free(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->digest->write) (&r->context.c, a->buf, a->bufpos); (*r->digest->write) (&r->context.c, inbuf, inlen); } a->bufpos = 0; } 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->finalized) return; if (a->bufpos) md_write (a, NULL, 0); for (r = a->ctx->list; r; r = r->next) (*r->digest->final) (&r->context.c); a->ctx->finalized = 1; if (a->ctx->macpads) { /* Finish the hmac. */ int algo = md_get_algo (a); byte *p = md_read (a, algo); size_t dlen = md_digest_length (algo); gcry_md_hd_t om; gcry_err_code_t err = md_open (&om, algo, a->ctx->secure, 0); if (err) _gcry_fatal_error (err, NULL); md_write (om, (a->ctx->macpads)+(a->ctx->macpads_Bsize), a->ctx->macpads_Bsize); md_write (om, p, dlen); md_final (om); /* Replace our digest with the mac (they have the same size). */ memcpy (p, md_read (om, algo), dlen); md_close (om); } } static gcry_err_code_t prepare_macpads (gcry_md_hd_t hd, const unsigned char *key, size_t keylen) { int i; int algo = md_get_algo (hd); unsigned char *helpkey = NULL; unsigned char *ipad, *opad; if (!algo) return GPG_ERR_DIGEST_ALGO; /* Might happen if no algo is enabled. */ if ( keylen > hd->ctx->macpads_Bsize ) { helpkey = gcry_malloc_secure (md_digest_length (algo)); if (!helpkey) return gpg_err_code_from_errno (errno); gcry_md_hash_buffer (algo, helpkey, key, keylen); key = helpkey; keylen = md_digest_length (algo); gcry_assert ( keylen <= hd->ctx->macpads_Bsize ); } memset ( hd->ctx->macpads, 0, 2*(hd->ctx->macpads_Bsize) ); ipad = hd->ctx->macpads; opad = (hd->ctx->macpads)+(hd->ctx->macpads_Bsize); memcpy ( ipad, key, keylen ); memcpy ( opad, key, keylen ); for (i=0; i < hd->ctx->macpads_Bsize; i++ ) { ipad[i] ^= 0x36; opad[i] ^= 0x5c; } gcry_free (helpkey); return GPG_ERR_NO_ERROR; } gcry_error_t gcry_md_ctl (gcry_md_hd_t hd, int cmd, void *buffer, size_t buflen) { gcry_err_code_t rc = 0; switch (cmd) { case GCRYCTL_FINALIZE: md_final (hd); break; case GCRYCTL_SET_KEY: rc = gcry_err_code (gcry_md_setkey (hd, buffer, buflen)); 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 gcry_error (rc); } gcry_error_t gcry_md_setkey (gcry_md_hd_t hd, const void *key, size_t keylen) { gcry_err_code_t rc = GPG_ERR_NO_ERROR; if (!hd->ctx->macpads) rc = GPG_ERR_CONFLICT; else { rc = prepare_macpads (hd, key, keylen); if (! rc) gcry_md_reset (hd); } return gcry_error (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"); return r->digest->read( &r->context.c ); } } else { for (r = a->ctx->list; r; r = r->next) if (r->module->mod_id == algo) return r->digest->read (&r->context.c); } BUG(); 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); } /* * 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) { if (algo == GCRY_MD_SHA1) _gcry_sha1_hash_buffer (digest, buffer, length); else if (algo == GCRY_MD_RMD160 && !fips_mode () ) _gcry_rmd160_hash_buffer (digest, buffer, length); 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; 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 (); } } err = md_open (&h, algo, 0, 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); } } 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->module->mod_id : 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_module_t digest; int mdlen = 0; REGISTER_DEFAULT_DIGESTS; ath_mutex_lock (&digests_registered_lock); digest = _gcry_module_lookup_id (digests_registered, algorithm); if (digest) { mdlen = ((gcry_md_spec_t *) digest->spec)->mdlen; _gcry_module_release (digest); } ath_mutex_unlock (&digests_registered_lock); return mdlen; } /**************** * 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) { const byte *asnoid = NULL; gcry_module_t digest; REGISTER_DEFAULT_DIGESTS; ath_mutex_lock (&digests_registered_lock); digest = _gcry_module_lookup_id (digests_registered, algorithm); if (digest) { if (asnlen) *asnlen = ((gcry_md_spec_t *) digest->spec)->asnlen; if (mdlen) *mdlen = ((gcry_md_spec_t *) digest->spec)->mdlen; asnoid = ((gcry_md_spec_t *) digest->spec)->asnoid; _gcry_module_release (digest); } else log_bug ("no ASN.1 OID for md algo %d\n", algorithm); ath_mutex_unlock (&digests_registered_lock); 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. * * 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 occured or not (i.e. while checking * the block size) */ gcry_error_t gcry_md_algo_info (int algo, int what, void *buffer, size_t *nbytes) { gcry_err_code_t err = GPG_ERR_NO_ERROR; switch (what) { case GCRYCTL_TEST_ALGO: if (buffer || nbytes) err = GPG_ERR_INV_ARG; else err = 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. */ err = check_digest_algo (algo); if (!err) { 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) err = GPG_ERR_TOO_SHORT; else err = GPG_ERR_INV_ARG; } } break; default: err = GPG_ERR_INV_OP; } return gcry_error (err); } 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; } #ifdef HAVE_U64_TYPEDEF { /* a kludge to pull in the __muldi3 for Solaris */ volatile u32 a = (u32)(ulong)md; volatile u64 b = 42; volatile u64 c; c = a * b; } #endif } /* * 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_error_t gcry_md_info (gcry_md_hd_t h, int cmd, void *buffer, size_t *nbytes) { gcry_err_code_t err = GPG_ERR_NO_ERROR; switch (cmd) { case GCRYCTL_IS_SECURE: *nbytes = h->ctx->secure; break; case GCRYCTL_IS_ALGO_ENABLED: { GcryDigestEntry *r; int algo; if ( !buffer || (nbytes && (*nbytes != sizeof (int)))) err = GPG_ERR_INV_ARG; else { algo = *(int*)buffer; *nbytes = 0; for(r=h->ctx->list; r; r = r->next ) { if (r->module->mod_id == algo) { *nbytes = 1; break; } } } break; } default: err = GPG_ERR_INV_OP; } return gcry_error (err); } /* Explicitly initialize this module. */ gcry_err_code_t _gcry_md_init (void) { gcry_err_code_t err = GPG_ERR_NO_ERROR; REGISTER_DEFAULT_DIGESTS; return err; } 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; } /* Get a list consisting of the IDs of the loaded message digest modules. If LIST is zero, write the number of loaded message digest modules to LIST_LENGTH and return. If LIST is non-zero, the first *LIST_LENGTH algorithm IDs are stored in LIST, which must be of according size. In case there are less message digest modules than *LIST_LENGTH, *LIST_LENGTH is updated to the correct number. */ gcry_error_t gcry_md_list (int *list, int *list_length) { gcry_err_code_t err = GPG_ERR_NO_ERROR; ath_mutex_lock (&digests_registered_lock); err = _gcry_module_list (digests_registered, list, list_length); ath_mutex_unlock (&digests_registered_lock); return err; } /* 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_module_t module = NULL; cipher_extra_spec_t *extraspec = NULL; gcry_err_code_t ec = 0; REGISTER_DEFAULT_DIGESTS; ath_mutex_lock (&digests_registered_lock); module = _gcry_module_lookup_id (digests_registered, algo); if (module && !(module->flags & FLAG_MODULE_DISABLED)) extraspec = module->extraspec; ath_mutex_unlock (&digests_registered_lock); if (extraspec && extraspec->selftest) ec = extraspec->selftest (algo, extended, report); else { ec = GPG_ERR_DIGEST_ALGO; if (report) report ("digest", algo, "module", module && !(module->flags & FLAG_MODULE_DISABLED)? "no selftest available" : module? "algorithm disabled" : "algorithm not found"); } if (module) { ath_mutex_lock (&digests_registered_lock); _gcry_module_release (module); ath_mutex_unlock (&digests_registered_lock); } return gpg_error (ec); }