summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2013-09-07 10:06:46 +0200
committerWerner Koch <wk@gnupg.org>2013-09-10 22:09:15 +0200
commitf3bca0c77c4979504f95fdbc618f7458e61e3e45 (patch)
tree9001cc1a8709dbe0f80ab3747ab062ac15e67246
parent0a28b2d2c9181a536fc894e24626714832619923 (diff)
downloadlibgcrypt-f3bca0c77c4979504f95fdbc618f7458e61e3e45.tar.gz
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 <wk@gnupg.org>
-rw-r--r--NEWS6
-rw-r--r--cipher/md.c78
-rw-r--r--cipher/sha1.c14
-rw-r--r--doc/gcrypt.texi63
-rw-r--r--src/cipher.h2
-rw-r--r--src/gcrypt.h.in14
-rw-r--r--src/libgcrypt.def1
-rw-r--r--src/libgcrypt.vers1
-rw-r--r--src/visibility.c12
-rw-r--r--src/visibility.h3
-rw-r--r--tests/basic.c78
-rw-r--r--tests/hmac.c58
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
@@ -2522,6 +2522,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)
{
static struct algos
@@ -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;
}