md: Add function gcry_md_hash_buffers.
authorWerner Koch <wk@gnupg.org>
Sat, 7 Sep 2013 08:06:46 +0000 (10:06 +0200)
committerWerner Koch <wk@gnupg.org>
Tue, 10 Sep 2013 20:09:15 +0000 (22:09 +0200)
* 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>
12 files changed:
NEWS
cipher/md.c
cipher/sha1.c
doc/gcrypt.texi
src/cipher.h
src/gcrypt.h.in
src/libgcrypt.def
src/libgcrypt.vers
src/visibility.c
src/visibility.h
tests/basic.c
tests/hmac.c

diff --git a/NEWS b/NEWS
index 5a39a38..e5ea856 100644 (file)
--- 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.
index 46567a1..44dc6dc 100644 (file)
@@ -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)
 {
index 4b784ac..c29f488 100644 (file)
@@ -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);
+}
+
+
 \f
 /*
      Self-test section.
index 09501f0..afef7f7 100644 (file)
@@ -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  ****************************
index ea8ba2a..7791083 100644 (file)
@@ -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,
index 5d30ced..71ea073 100644 (file)
@@ -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;
+
+
 \f
 
 /* 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);
index 9c691ec..9fa4245 100644 (file)
@@ -245,6 +245,7 @@ EXPORTS
 
       gcry_mpi_ec_curve_point   @218
 
+      gcry_md_hash_buffers      @219
 
 
 ;; end of file with public symbols for Windows.
index beb691e..904ab41 100644 (file)
@@ -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;
index 404da20..48725ff 100644 (file)
@@ -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)
 {
index b4da48e..5e3556c 100644 (file)
@@ -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)
index 4fbca43..26c85ca 100644 (file)
@@ -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)
index 5d695ea..f4dc945 100644 (file)
@@ -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;
 }