From f3bca0c77c4979504f95fdbc618f7458e61e3e45 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Sat, 7 Sep 2013 10:06:46 +0200 Subject: md: Add function gcry_md_hash_buffers. * src/gcrypt.h.in (gcry_buffer_t): new. (gcry_md_hash_buffers): New. * src/visibility.c, src/visibility.h: Add wrapper for new function. * src/libgcrypt.def, src/libgcrypt.vers: Export new function. * cipher/md.c (gcry_md_hash_buffers): New. * cipher/sha1.c (_gcry_sha1_hash_buffers): New. * tests/basic.c (check_one_md_multi): New. (check_digests): Run that test. * tests/hmac.c (check_hmac_multi): New. (main): Run that test. Signed-off-by: Werner Koch --- NEWS | 6 +++++ cipher/md.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ cipher/sha1.c | 14 ++++++++++ doc/gcrypt.texi | 63 ++++++++++++++++++++++++++++++++++++++++--- src/cipher.h | 2 ++ src/gcrypt.h.in | 14 ++++++++++ src/libgcrypt.def | 1 + src/libgcrypt.vers | 1 + src/visibility.c | 12 +++++++++ src/visibility.h | 3 +++ tests/basic.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++---- tests/hmac.c | 58 ++++++++++++++++++++++++++++++++++++++++ 12 files changed, 321 insertions(+), 9 deletions(-) diff --git a/NEWS b/NEWS index 5a39a389..e5ea856d 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,10 @@ Noteworthy changes in version 1.6.0 (unreleased) * Support Deterministic DSA as per RFC-6969. + * Added a scatter gather hash convenience function. + + * Added several MPI helper functions. + * Interface changes relative to the 1.5.0 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gcry_ac_* REMOVED. @@ -40,6 +44,8 @@ Noteworthy changes in version 1.6.0 (unreleased) gcry_md_list REMOVED. gcry_md_start_debug REMOVED (macro). gcry_md_stop_debug REMOVED (macro). + gcry_md_hash_buffers NEW. + gcry_buffer_t NEW. GCRYCTL_SET_ENFORCED_FIPS_FLAG NEW. GCRYCTL_SET_PREFERRED_RNG_TYPE NEW. GCRYCTL_GET_CURRENT_RNG_TYPE NEW. diff --git a/cipher/md.c b/cipher/md.c index 46567a19..44dc6dc4 100644 --- a/cipher/md.c +++ b/cipher/md.c @@ -1,6 +1,7 @@ /* md.c - message digest dispatcher * Copyright (C) 1998, 1999, 2002, 2003, 2006, * 2008 Free Software Foundation, Inc. + * Copyright (C) 2013 g10 Code GmbH * * This file is part of Libgcrypt. * @@ -1035,6 +1036,83 @@ gcry_md_hash_buffer (int algo, void *digest, } } + +/* 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) +{ + 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; + + if (algo == GCRY_MD_SHA1 && !hmac) + _gcry_sha1_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; + + 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 (); + } + } + + rc = md_open (&h, algo, 0, hmac); + if (rc) + return rc; + + if (hmac) + { + rc = gcry_err_code + (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), md_digest_length (algo)); + md_close (h); + } + + return 0; +} + + static int md_get_algo (gcry_md_hd_t a) { diff --git a/cipher/sha1.c b/cipher/sha1.c index 4b784acf..c29f4880 100644 --- a/cipher/sha1.c +++ b/cipher/sha1.c @@ -373,6 +373,20 @@ _gcry_sha1_hash_buffer (void *outbuf, const void *buffer, size_t length) } +/* Variant of the above shortcut function using a multiple buffers. */ +void +_gcry_sha1_hash_buffers (void *outbuf, const gcry_buffer_t *iov, int iovcnt) +{ + SHA1_CONTEXT hd; + + sha1_init (&hd); + for (;iovcnt > 0; iov++, iovcnt--) + sha1_write (&hd, (const char*)iov[0].data + iov[0].off, iov[0].len); + sha1_final (&hd); + memcpy (outbuf, hd.buf, 20); +} + + /* Self-test section. diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi index 09501f06..afef7f76 100644 --- a/doc/gcrypt.texi +++ b/doc/gcrypt.texi @@ -3115,8 +3115,36 @@ The function does return @code{NULL} if the requested algorithm has not been enabled. @end deftypefun -Because it is often necessary to get the message digest of one block of -memory, a fast convenience function is available for this task: +Because it is often necessary to get the message digest of blocks of +memory, two fast convenience function are available for this task: + +@deftypefun gpg_err_code_t gcry_md_hash_buffers ( @ + @w{int @var{algo}}, @w{unsigned int @var{flags}}, @ + @w{void *@var{digest}}, @ + @w{const gcry_buffer_t *@var{iov}}, @w{int @var{iovcnt}} ) + +@code{gcry_md_hash_buffers} is a shortcut function to calculate a +message digest from several buffers. This function does not require a +context and immediately returns the message digest of of the data +described by @var{iov} and @var{iovcnt}. @var{digest} must be +allocated by the caller, large enough to hold the message digest +yielded by the the specified algorithm @var{algo}. This required size +may be obtained by using the function @code{gcry_md_get_algo_dlen}. + +@var{iov} is an array of buffer descriptions with @var{iovcnt} items. +The caller should zero out the structures in this array and for each +array item set the fields @code{.data} to the address of the data to +be hashed, @code{.len} to number of bytes to be hashed. If @var{.off} +is also set, the data is taken starting at @var{.off} bytes from the +begin of the buffer. The field @code{.size} is not used. + +The only supported flag value for @var{flags} is +@var{GCRY_MD_FLAG_HMAC} which turns this function into a HMAC +function; the first item in @var{iov} is then used as the key. + +On success the function returns 0 and stores the resulting hash or MAC +at @var{digest}. +@end deftypefun @deftypefun void gcry_md_hash_buffer (int @var{algo}, void *@var{digest}, const void *@var{buffer}, size_t @var{length}); @@ -3128,8 +3156,8 @@ enough to hold the message digest yielded by the the specified algorithm @var{algo}. This required size may be obtained by using the function @code{gcry_md_get_algo_dlen}. -Note that this function will abort the process if an unavailable -algorithm is used. +Note that in contrast to @code{gcry_md_hash_buffers} this function +will abort the process if an unavailable algorithm is used. @end deftypefun @c *********************************** @@ -4359,8 +4387,10 @@ wrong. @menu * Memory allocation:: Functions related with memory allocation. * Context management:: Functions related with context management. +* Buffer description:: A data type to describe buffers. @end menu + @node Memory allocation @section Memory allocation @@ -4418,6 +4448,31 @@ Release the context object @var{ctx} and all associated resources. A @code{NULL} passed as @var{ctx} is ignored. @end deftypefun +@node Buffer description +@section Buffer description + +To help hashing non-contiguous areas of memory a general purpose data +type is defined: + +@deftp {Data type} {gcry_buffer_t} +This type is a structure to describe a buffer. The user should make +sure that this structure is initialized to zero. The available fields +of this structure are: + +@table @code + @item .size + This is either 0 for no information available or indicates the + allocated length of the buffer. + @item .off + This is the offset into the buffer. + @item .len + This is the valid length of the buffer starting at @code{.off}. + @item .data + This is the address of the buffer. + @end table +@end deftp + + @c ********************************************************** @c ********************* Tools **************************** diff --git a/src/cipher.h b/src/cipher.h index ea8ba2af..7791083b 100644 --- a/src/cipher.h +++ b/src/cipher.h @@ -79,6 +79,8 @@ void _gcry_rmd160_hash_buffer (void *outbuf, /*-- sha1.c --*/ void _gcry_sha1_hash_buffer (void *outbuf, const void *buffer, size_t length); +void _gcry_sha1_hash_buffers (void *outbuf, + const gcry_buffer_t *iov, int iovcnt); /*-- rijndael.c --*/ void _gcry_aes_cfb_enc (void *context, unsigned char *iv, diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in index 5d30ced1..71ea0730 100644 --- a/src/gcrypt.h.in +++ b/src/gcrypt.h.in @@ -232,6 +232,16 @@ typedef struct gcry_mpi *GCRY_MPI _GCRY_GCC_ATTR_DEPRECATED; typedef struct gcry_mpi *GcryMPI _GCRY_GCC_ATTR_DEPRECATED; #endif +/* A structure used for scatter gather hashing. */ +typedef struct +{ + size_t size; /* The allocated size of the buffer or 0. */ + size_t off; /* Offset into the buffer. */ + size_t len; /* The used length of the buffer. */ + void *data; /* The buffer. */ +} gcry_buffer_t; + + /* Check that the library fulfills the version requirement. */ @@ -1151,6 +1161,10 @@ unsigned char *gcry_md_read (gcry_md_hd_t hd, int algo); void gcry_md_hash_buffer (int algo, void *digest, const void *buffer, size_t length); +/* Convenience function to hash multiple buffers. */ +gpg_error_t gcry_md_hash_buffers (int algo, unsigned int flags, void *digest, + const gcry_buffer_t *iov, int iovcnt); + /* Retrieve the algorithm used with HD. This does not work reliable if more than one algorithm is enabled in HD. */ int gcry_md_get_algo (gcry_md_hd_t hd); diff --git a/src/libgcrypt.def b/src/libgcrypt.def index 9c691ec2..9fa42458 100644 --- a/src/libgcrypt.def +++ b/src/libgcrypt.def @@ -245,6 +245,7 @@ EXPORTS gcry_mpi_ec_curve_point @218 + gcry_md_hash_buffers @219 ;; end of file with public symbols for Windows. diff --git a/src/libgcrypt.vers b/src/libgcrypt.vers index beb691ee..904ab412 100644 --- a/src/libgcrypt.vers +++ b/src/libgcrypt.vers @@ -39,6 +39,7 @@ GCRYPT_1.6 { gcry_md_algo_info; gcry_md_algo_name; gcry_md_close; gcry_md_copy; gcry_md_ctl; gcry_md_enable; gcry_md_get; gcry_md_get_algo; gcry_md_get_algo_dlen; gcry_md_hash_buffer; + gcry_md_hash_buffers; gcry_md_info; gcry_md_is_enabled; gcry_md_is_secure; gcry_md_map_name; gcry_md_open; gcry_md_read; gcry_md_reset; gcry_md_setkey; diff --git a/src/visibility.c b/src/visibility.c index 404da20b..48725ffe 100644 --- a/src/visibility.c +++ b/src/visibility.c @@ -997,6 +997,18 @@ gcry_md_hash_buffer (int algo, void *digest, _gcry_md_hash_buffer (algo, digest, buffer, length); } +gpg_error_t +gcry_md_hash_buffers (int algo, unsigned int flags, void *digest, + const gcry_buffer_t *iov, int iovcnt) +{ + if (!fips_is_operational ()) + { + (void)fips_not_operational (); + fips_signal_error ("called in non-operational state"); + } + return _gcry_md_hash_buffers (algo, flags, digest, iov, iovcnt); +} + int gcry_md_get_algo (gcry_md_hd_t hd) { diff --git a/src/visibility.h b/src/visibility.h index b4da48e8..5e3556c0 100644 --- a/src/visibility.h +++ b/src/visibility.h @@ -63,6 +63,7 @@ #define gcry_md_get_algo _gcry_md_get_algo #define gcry_md_get_algo_dlen _gcry_md_get_algo_dlen #define gcry_md_hash_buffer _gcry_md_hash_buffer +#define gcry_md_hash_buffers _gcry_md_hash_buffers #define gcry_md_info _gcry_md_info #define gcry_md_is_enabled _gcry_md_is_enabled #define gcry_md_is_secure _gcry_md_is_secure @@ -279,6 +280,7 @@ gcry_err_code_t gcry_md_get (gcry_md_hd_t hd, int algo, #undef gcry_md_get_algo #undef gcry_md_get_algo_dlen #undef gcry_md_hash_buffer +#undef gcry_md_hash_buffers #undef gcry_md_info #undef gcry_md_is_enabled #undef gcry_md_is_secure @@ -455,6 +457,7 @@ MARK_VISIBLE (gcry_md_get) MARK_VISIBLE (gcry_md_get_algo) MARK_VISIBLE (gcry_md_get_algo_dlen) MARK_VISIBLE (gcry_md_hash_buffer) +MARK_VISIBLE (gcry_md_hash_buffers) MARK_VISIBLE (gcry_md_info) MARK_VISIBLE (gcry_md_is_enabled) MARK_VISIBLE (gcry_md_is_secure) diff --git a/tests/basic.c b/tests/basic.c index 4fbca43b..26c85caf 100644 --- a/tests/basic.c +++ b/tests/basic.c @@ -2521,6 +2521,72 @@ check_one_md (int algo, const char *data, int len, const char *expect) } +static void +check_one_md_multi (int algo, const char *data, int len, const char *expect) +{ + gpg_error_t err; + gcry_buffer_t iov[3]; + int iovcnt; + char digest[64]; + int mdlen; + int i; + + mdlen = gcry_md_get_algo_dlen (algo); + if (mdlen < 1 || mdlen > 64) + { + fail ("check_one_md_multi: algo %d, gcry_md_get_algo_dlen failed: %d\n", + algo, mdlen); + return; + } + + if (*data == '!' && !data[1]) + return; /* We can't do that here. */ + + memset (iov, 0, sizeof iov); + + iov[0].data = (void*)data; + if (len) + { + iov[0].len = 1; + len--; + data++; + } + iovcnt = 1; + if (len >= 4) + { + iov[iovcnt].data = (void*)data; + iov[iovcnt].len = 4; + iovcnt++; + data += 4; + len -= 4; + } + iov[iovcnt].data = (void*)data; + iov[iovcnt].len = len; + iovcnt++; + assert (iovcnt <= DIM (iov)); + + err = gcry_md_hash_buffers (algo, 0, digest, iov, iovcnt); + if (err) + { + fail ("check_one_md_multi: algo %d, gcry_hash_buffers failed: %s\n", + algo, gpg_strerror (err)); + return; + } + if (memcmp (digest, expect, mdlen)) + { + printf ("computed: "); + for (i = 0; i < mdlen; i++) + printf ("%02x ", digest[i] & 0xFF); + printf ("\nexpected: "); + for (i = 0; i < mdlen; i++) + printf ("%02x ", expect[i] & 0xFF); + printf ("\n"); + + fail ("check_one_md_multi: algo %d, digest mismatch\n", algo); + } +} + + static void check_digests (void) { @@ -2742,11 +2808,11 @@ check_digests (void) "\x08\xEB\xA2\x66\x29\x12\x9D\x8F\xB7\xCB\x57\x21\x1B\x92\x81\xA6" "\x55\x17\xCC\x87\x9D\x7B\x96\x21\x42\xC6\x5F\x5A\x7A\xF0\x14\x67" }, { GCRY_MD_WHIRLPOOL, - "!", - "\x0C\x99\x00\x5B\xEB\x57\xEF\xF5\x0A\x7C\xF0\x05\x56\x0D\xDF\x5D" - "\x29\x05\x7F\xD8\x6B\x20\xBF\xD6\x2D\xEC\xA0\xF1\xCC\xEA\x4A\xF5" - "\x1F\xC1\x54\x90\xED\xDC\x47\xAF\x32\xBB\x2B\x66\xC3\x4F\xF9\xAD" - "\x8C\x60\x08\xAD\x67\x7F\x77\x12\x69\x53\xB2\x26\xE4\xED\x8B\x01" }, + "!", + "\x0C\x99\x00\x5B\xEB\x57\xEF\xF5\x0A\x7C\xF0\x05\x56\x0D\xDF\x5D" + "\x29\x05\x7F\xD8\x6B\x20\xBF\xD6\x2D\xEC\xA0\xF1\xCC\xEA\x4A\xF5" + "\x1F\xC1\x54\x90\xED\xDC\x47\xAF\x32\xBB\x2B\x66\xC3\x4F\xF9\xAD" + "\x8C\x60\x08\xAD\x67\x7F\x77\x12\x69\x53\xB2\x26\xE4\xED\x8B\x01" }, { 0 }, }; int i; @@ -2773,6 +2839,8 @@ check_digests (void) check_one_md (algos[i].md, algos[i].data, strlen (algos[i].data), algos[i].expect); + check_one_md_multi (algos[i].md, algos[i].data, strlen (algos[i].data), + algos[i].expect); } if (verbose) diff --git a/tests/hmac.c b/tests/hmac.c index 5d695ea0..f4dc9450 100644 --- a/tests/hmac.c +++ b/tests/hmac.c @@ -148,6 +148,63 @@ check_hmac (void) } + +static void +check_hmac_multi (void) +{ + gpg_error_t err; + unsigned char key[128]; + const char msg[] = "Sample #1"; + const char mac[] = ("\x4f\x4c\xa3\xd5\xd6\x8b\xa7\xcc\x0a\x12" + "\x08\xc9\xc6\x1e\x9c\x5d\xa0\x40\x3c\x0a"); + gcry_buffer_t iov[4]; + char digest[64]; + int i; + int algo; + int maclen; + + if (verbose) + fprintf (stderr, "checking HMAC using multiple buffers\n"); + for (i=0; i < 64; i++) + key[i] = i; + + memset (iov, 0, sizeof iov); + iov[0].data = key; + iov[0].len = 64; + iov[1].data = (void*)msg; + iov[1].off = 0; + iov[1].len = 3; + iov[2].data = (void*)msg; + iov[2].off = 3; + iov[2].len = 1; + iov[3].data = (void*)msg; + iov[3].off = 4; + iov[3].len = 5; + + algo = GCRY_MD_SHA1; + maclen = gcry_md_get_algo_dlen (algo); + err = gcry_md_hash_buffers (algo, GCRY_MD_FLAG_HMAC, digest, iov, 4); + if (err) + { + fail ("gcry_md_hash_buffers failed: %s\n", algo, gpg_strerror (err)); + return; + } + + if (memcmp (digest, mac, maclen)) + { + printf ("computed: "); + for (i = 0; i < maclen; i++) + printf ("%02x ", digest[i] & 0xFF); + printf ("\nexpected: "); + for (i = 0; i < maclen; i++) + printf ("%02x ", mac[i] & 0xFF); + printf ("\n"); + + fail ("gcry_md_hash_buffers, algo %d, MAC does not match\n", algo); + } +} + + int main (int argc, char **argv) { @@ -166,6 +223,7 @@ main (int argc, char **argv) if (debug) gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0); check_hmac (); + check_hmac_multi (); return error_count ? 1 : 0; } -- cgit v1.2.1