Completed the RNG implementaion switching.
authorWerner Koch <wk@gnupg.org>
Fri, 22 Aug 2008 19:17:05 +0000 (19:17 +0000)
committerWerner Koch <wk@gnupg.org>
Fri, 22 Aug 2008 19:17:05 +0000 (19:17 +0000)
Added framework for running RNG tests.
Added an experimental option --enable-hmac-binary-check to configure.
 --This line, and those below, will be ignored--

M    src/fips.c
M    src/cipher-proto.h
M    src/global.c
M    src/hmac256.c
M    src/ChangeLog
M    src/hmac256.h
M    src/Makefile.am
M    tests/Makefile.am
M    configure.ac
M    doc/gcrypt.texi
M    random/random-fips.c
M    random/random.c
M    random/rand-internal.h
M    random/random.h
M    random/ChangeLog
M    ChangeLog
M    README

17 files changed:
ChangeLog
README
configure.ac
doc/gcrypt.texi
random/ChangeLog
random/rand-internal.h
random/random-fips.c
random/random.c
random/random.h
src/ChangeLog
src/Makefile.am
src/cipher-proto.h
src/fips.c
src/global.c
src/hmac256.c
src/hmac256.h
tests/Makefile.am

index 24ae36c..f78a85c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2008-08-22  Werner Koch  <wk@g10code.com>
+
+       * configure.ac: Add option --enable-hmac-binary-check.
+       (DL_LIBS): Check whether -ldl is required.
+
 2008-08-19  Werner Koch  <wk@g10code.com>
 
        Release 1.4.2rc1.
diff --git a/README b/README
index 714bc42..ff5bebb 100644 (file)
--- a/README
+++ b/README
                       auto - Compile linux, egd and unix in and 
                              automagically select at runtime.
   
+     --enable-hmac-binary-check
+                     Include support to check the binary at runtime
+                     against a HMAC checksum.  This works only in FIPS
+                     mode and on systems providing the dladdr function.
+
      --disable-padlock-support
                      Disable support for the PadLock engine of VIA
                      processors.  The default is to use PadLock if
                      available.  Try this if you get problems with
                      assembler code.
 
+       
 
 
     License
index e332ce2..82823f1 100644 (file)
@@ -449,6 +449,19 @@ AC_ARG_WITH(capabilities,
             [use_capabilities="$withval"],[use_capabilities=no])
 AC_MSG_RESULT($use_capabilities)
 
+# Implementation of the --enable-hmac-binary-check.
+AC_MSG_CHECKING([whether a HMAC binary check is requested])
+AC_ARG_ENABLE(hmac-binary-check,
+              AC_HELP_STRING([--enable-hmac-binary-check],
+                             [Enable library integrity check]),
+              [use_hmac_binary_check=$enableval],
+              [use_hmac_binary_check=no])
+AC_MSG_RESULT($use_hmac_binary_check)
+if test "$use_hmac_binary_check" = yes ; then
+    AC_DEFINE(ENABLE_HMAC_BINARY_CHECK,1,
+              [Define to support an HMAC based integrity check])
+fi
+
 
 # Implementation of the --disable-padlock-support switch.
 AC_MSG_CHECKING([whether padlock support is requested])
@@ -692,7 +705,25 @@ AC_CHECK_FUNCS(fcntl ftruncate)
 
 GNUPG_CHECK_MLOCK
 
+
+#
+# Check wether it is necessary to link against libdl.
+#
+DL_LIBS=""
+if test "$use_hmac_binary_check" = yes ; then
+  _gcry_save_libs="$LIBS"
+  LIBS=""
+  AC_SEARCH_LIBS(dlopen, c dl,,,)
+  DL_LIBS=$LIBS
+  LIBS="$_gcry_save_libs"
+  LIBGCRYPT_CONFIG_LIBS="${LIBGCRYPT_CONFIG_LIBS} ${DL_LIBS}"
+fi
+AC_SUBST(DL_LIBS)
+
+
+#
 # Check whether we can use Linux capabilities as requested.
+#
 if test "$use_capabilities" = "yes" ; then
 use_capabilities=no
 AC_CHECK_HEADERS(sys/capability.h)
index 7d2adee..b13464c 100644 (file)
@@ -4607,7 +4607,9 @@ If Libgcrypt is used FIPS mode these restrictions are effective:
 
 @itemize
 
-@item  TBD
+@item 
+It may only be used on systesm with a /dev/random device.  Swicthing
+into FIPS mode on other systems will fail at runtime.
 
 
 
index 9f87453..73bfc6f 100644 (file)
@@ -1,3 +1,11 @@
+2008-08-22  Werner Koch  <wk@g10code.com>
+
+       * random.c (_gcry_update_random_seed_file): Move operational check
+       to _gcry_vcontrol.
+       (_gcry_fast_random_poll): Ditto.
+       (_gcry_random_selftest): New.
+       * random-fips.c (_gcry_rngfips_selftest): New.
+
 2008-08-21  Werner Koch  <wk@g10code.com>
 
        * random-fips.c: Finish implementation.
index 7a2edf5..e3c0192 100644 (file)
@@ -20,6 +20,7 @@
 #ifndef G10_RAND_INTERNAL_H
 #define G10_RAND_INTERNAL_H
 
+#include "../src/cipher-proto.h"
 
 /* Constants used to define the origin of random added to the pool.
    The code is sensitive to the order of the values.  */
@@ -76,6 +77,8 @@ void _gcry_rngfips_randomize (void *buffer, size_t length,
                                 enum gcry_random_level level);
 void _gcry_rngfips_create_nonce (void *buffer, size_t length);
 
+gcry_error_t _gcry_rngfips_selftest (selftest_report_func_t report);
+
 
 
 
index 077679d..cbecefc 100644 (file)
@@ -602,6 +602,7 @@ get_random (void *buffer, size_t length, rng_context_t rng_ctx)
 }
 
 
+\f
 /* Initialize this random subsystem.  If FULL is false, this function
    merely calls the basic initialization of the module and does not do
    anything more.  Doing this is not really required but when running
@@ -639,9 +640,11 @@ _gcry_rngfips_initialize (int full)
 }
 
 
+/* Print some statistics about the RNG.  */
 void
 _gcry_rngfips_dump_stats (void)
 {
+  /* Not yet implemented.  */
 }
 
 
@@ -656,11 +659,11 @@ _gcry_rngfips_is_faked (void)
 
 /* Add BUFLEN bytes from BUF to the internal random pool.  QUALITY
    should be in the range of 0..100 to indicate the goodness of the
-   entropy added, or -1 for goodness not known.  */
+   entropy added, or -1 for goodness not known. */
 gcry_error_t
 _gcry_rngfips_add_bytes (const void *buf, size_t buflen, int quality)
 {
-  return 0;
+  return 0;  /* Not implemented. */
 }   
 
     
@@ -691,3 +694,18 @@ _gcry_rngfips_create_nonce (void *buffer, size_t length)
   get_random (buffer, length, nonce_context);
 }
 
+
+/* Run the self-tests.  */
+gcry_error_t
+_gcry_rngfips_selftest (selftest_report_func_t report)
+{
+  gcry_err_code_t ec = 0;
+  char buffer[8];
+
+  /* Do a simple test using the public interface.  */
+  gcry_randomize (buffer, sizeof buffer, GCRY_STRONG_RANDOM);
+
+
+  return gpg_error (ec);
+}
+
index ff7c280..3e71195 100644 (file)
@@ -75,7 +75,7 @@ _gcry_random_progress (const char *what, int printchar, int current, int total)
 void
 _gcry_random_initialize (int full)
 {
-  if ( fips_mode() )
+  if (fips_mode ())
     _gcry_rngfips_initialize (full);
   else
     _gcry_rngcsprng_initialize (full);
@@ -85,7 +85,7 @@ _gcry_random_initialize (int full)
 void
 _gcry_random_dump_stats (void)
 {
-  if ( fips_mode() )
+  if (fips_mode ())
     _gcry_rngfips_dump_stats ();
   else
     _gcry_rngcsprng_dump_stats ();
@@ -98,9 +98,10 @@ _gcry_random_dump_stats (void)
 void
 _gcry_secure_random_alloc (void)
 {
-  if ( fips_mode() )
-    return;  /* Not used; the fips rng is allows in secure mode.  */
-  _gcry_rngcsprng_secure_alloc ();
+  if (fips_mode ())
+    ;  /* Not used; the fips rng is allows in secure mode.  */
+  else
+    _gcry_rngcsprng_secure_alloc ();
 }
 
 
@@ -109,18 +110,20 @@ _gcry_secure_random_alloc (void)
 void
 _gcry_enable_quick_random_gen (void)
 {
-  if ( fips_mode() )
-    return;  /* Not used.  */
-  _gcry_rngcsprng_enable_quick_gen ();
+  if (fips_mode ())
+    ;  /* Not used.  */
+  else
+    _gcry_rngcsprng_enable_quick_gen ();
 }
 
 
 void
 _gcry_set_random_daemon_socket (const char *socketname)
 {
-  if ( fips_mode() )
-    return;  /* Not used.  */
-  _gcry_rngcsprng_set_daemon_socket (socketname);
+  if (fips_mode ())
+    ;  /* Not used.  */
+  else
+    _gcry_rngcsprng_set_daemon_socket (socketname);
 }
 
 /* With ONOFF set to 1, enable the use of the daemon.  With ONOFF set
@@ -131,7 +134,8 @@ _gcry_use_random_daemon (int onoff)
 {
   if (fips_mode ())
     return 0; /* Never enabled in fips mode.  */
-  return _gcry_rngcsprng_use_daemon (onoff);
+  else
+    return _gcry_rngcsprng_use_daemon (onoff);
 }
 
 
@@ -141,8 +145,9 @@ int
 _gcry_random_is_faked (void)
 {
   if (fips_mode ())
-    return 0; /* Never faked in fips mode.  */
-  return _gcry_rngcsprng_is_faked ();
+    return _gcry_rngfips_is_faked ();
+  else
+    return _gcry_rngcsprng_is_faked ();
 }
 
 
@@ -154,7 +159,8 @@ gcry_random_add_bytes (const void *buf, size_t buflen, int quality)
 {
   if (fips_mode ())
     return 0; /* No need for this in fips mode.  */
-  return _gcry_rngcsprng_add_bytes (buf, buflen, quality);
+  else
+    return _gcry_rngcsprng_add_bytes (buf, buflen, quality);
 }   
 
   
@@ -217,7 +223,10 @@ gcry_randomize (void *buffer, size_t length, enum gcry_random_level level)
 void
 _gcry_set_random_seed_file (const char *name)
 {
-  _gcry_rngcsprng_set_seed_file (name);
+  if (fips_mode ())
+    ; /* No need for this in fips mode.  */
+  else
+    _gcry_rngcsprng_set_seed_file (name);
 }
 
 
@@ -226,10 +235,10 @@ _gcry_set_random_seed_file (const char *name)
 void
 _gcry_update_random_seed_file (void)
 {
-  if (!fips_is_operational ())  /* FIXME:  This does no look correct.  */
-    return;
-
-  _gcry_rngcsprng_update_seed_file ();
+  if (fips_mode ())
+    ; /* No need for this in fips mode.  */
+  else
+    _gcry_rngcsprng_update_seed_file ();
 }
 
 
@@ -244,10 +253,10 @@ _gcry_update_random_seed_file (void)
 void
 _gcry_fast_random_poll (void)
 {
-  if (!fips_is_operational ())
-    return;
-
-  _gcry_rngcsprng_fast_poll ();
+  if (fips_mode ())
+    ; /* No need for this in fips mode.  */
+  else
+    _gcry_rngcsprng_fast_poll ();
 }
 
 
@@ -256,6 +265,21 @@ _gcry_fast_random_poll (void)
 void
 gcry_create_nonce (void *buffer, size_t length)
 {
-  _gcry_rngcsprng_create_nonce (buffer, length);
+  if (fips_mode ())
+    _gcry_rngfips_create_nonce (buffer, length);
+  else
+    _gcry_rngcsprng_create_nonce (buffer, length);
+}
+
+
+/* Run the self-tests for the RNG.  This is currently only implemented
+   for the FIPS generator.  */
+gpg_error_t
+_gcry_random_selftest (selftest_report_func_t report)
+{
+  if (fips_mode ())
+    return _gcry_rngfips_selftest (report);
+  else
+    return gpg_error (GPG_ERR_NOT_SUPPORTED);
 }
 
index 1db189e..eda44d3 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "types.h"
 
+/*-- random.c --*/
 void _gcry_register_random_progress (void (*cb)(void *,const char*,int,int,int),
                                      void *cb_data );
 
@@ -38,6 +39,7 @@ void _gcry_update_random_seed_file (void);
 byte *_gcry_get_random_bits( size_t nbits, int level, int secure );
 void _gcry_fast_random_poll( void );
 
+
 /*-- rndegd.c --*/
 gpg_error_t _gcry_rndegd_set_socket_name (const char *name);
 
index b811b6e..7f54e74 100644 (file)
@@ -1,3 +1,14 @@
+2008-08-22  Werner Koch  <wk@g10code.com>
+
+       * hmac256.c (_gcry_hmac256_file): New.
+       (main): New option --binary.
+       * fips.c (check_binary_integrity): New.
+       (_gcry_fips_run_selftests): Run it.
+
+       * global.c (_gcry_vcontrol) <GCRYCTL_UPDATE_RANDOM_SEED_FILE>:
+       Check for fips operational state.
+       (_gcry_vcontrol) <GCRYCTL_FAST_POLL>: Ditt.
+
 2008-08-21  Werner Koch  <wk@g10code.com>
 
        * misc.c (_gcry_log_printhex): New.
index d9a5845..bb88520 100644 (file)
@@ -121,3 +121,12 @@ endif USE_RANDOM_DAEMON
 install-data-local: install-def-file
 
 uninstall-local: uninstall-def-file
+
+# FIXME: We ned to figure out how to gte the actual name (parsing
+# libgcrypt.la?) and how to create the hmac file already at link time
+# so that it can be used without first installing.
+#install-exec-hook:
+#      ./hmac256 --binary "What am I, a doctor or a moonshuttle conductor?" \
+#           $(DESTDIR)$(libdir)/libgcrypt.so.11.5.0 \
+#          >$(DESTDIR)$(libdir)/libgcrypt.so.11.5.0.hmac
+
index 4b826c1..ae5492b 100644 (file)
@@ -79,5 +79,6 @@ gcry_error_t _gcry_md_selftest (int algo, selftest_report_func_t report);
 gcry_error_t _gcry_pk_selftest (int algo, selftest_report_func_t report);
 gcry_error_t _gcry_hmac_selftest (int algo, selftest_report_func_t report);
 
+gcry_error_t _gcry_random_selftest (selftest_report_func_t report);
 
 #endif /*G10_CIPHER_PROTO_H*/
index ed4b9fd..8f36797 100644 (file)
 #include <stdlib.h>
 #include <errno.h>
 #include <unistd.h>
-
-/* #include <dlfcn.h>  /\* FIXME:  GNU only *\/ */
+#ifdef ENABLE_HMAC_BINARY_CHECK
+# include <dlfcn.h> 
+#endif
 
 #include "g10lib.h"
 #include "ath.h"
 #include "cipher-proto.h"
+#include "hmac256.h"
 
 /* The states of the finite state machine used in fips mode.  */
 enum module_states 
@@ -417,17 +419,79 @@ run_pubkey_selftests (void)
 }
 
 
-/* Run self-tests for the random number generator.  Return 0 on
+/* Run self-tests for the random number generator.  Returns 0 on
    success. */
 static int
 run_random_selftests (void)
 {
-  char buffer[8];
+  gpg_error_t err;
 
-  /* FIXME: For now we just try to get a few bytes.  */
-  gcry_randomize (buffer, sizeof buffer, GCRY_STRONG_RANDOM);
+  err = _gcry_random_selftest (reporter);
+  reporter ("random", 0, NULL, err? gpg_strerror (err):NULL);
+  
+  return !!err;
+}
 
+/* Run an integrity check on the binary.  Returns 0 on success.  */
+static int
+check_binary_integrity (void)
+{
+#ifdef ENABLE_HMAC_BINARY_CHECK
+  gpg_error_t err;
+  Dl_info info;
+  unsigned char digest[32];
+  int dlen;
+  char *fname = NULL;
+  const char key[] = "What am I, a doctor or a moonshuttle conductor?";
+  
+  if (!dladdr ("gcry_check_version", &info))
+    err = gpg_error_from_syserror ();
+  else
+    {
+      dlen = _gcry_hmac256_file (digest, sizeof digest, info.dli_fname,
+                                 key, strlen (key));
+      if (dlen < 0)
+        err = gpg_error_from_syserror ();
+      else if (dlen != 32)
+        err = gpg_error (GPG_ERR_INTERNAL);
+      else
+        {
+          FILE *fp;
+  
+          fname = gcry_malloc (strlen (info.dli_fname) + 5 + 1 );
+          if (!fname)
+            err = gpg_error_from_syserror ();
+          else
+            {
+              strcpy (stpcpy (fname, info.dli_fname), ".hmac");
+              fp = fopen (fname, "rb");
+              if (!fp)
+                err = gpg_error_from_syserror ();
+              else
+                {
+                  char buffer[33];
+                  int n;
+
+                  /* We expect a file of exactly 32 bytes.  Consider
+                     the self-test failed if this is not the case or
+                     if it does not match the just computed HMAC.  */
+                  if ((n=fread (buffer, 1, 33, fp)) != 32
+                      || memcmp (digest, buffer, 32) )
+                    err = gpg_error (GPG_ERR_SELFTEST_FAILED);
+                  else
+                    err = 0;
+
+                  fclose (fp);
+                }
+            }
+        }
+    }
+  reporter ("binary", 0, fname, err? gpg_strerror (err):NULL);
+  gcry_free (fname);
+  return !!err;
+#else
   return 0;
+#endif
 }
 
 
@@ -439,15 +503,6 @@ _gcry_fips_run_selftests (void)
   
   fips_new_state (STATE_SELFTEST);
 
-/*   { */
-/*     Dl_info info; */
-
-/*     if (dladdr ("gcry_check_version", &info)) */
-/*       log_info ("DL_info:  fname=`%s'\n", */
-/*                 info.dli_fname); */
-/*   } */
-
-
   if (run_cipher_selftests ())
     goto leave;
 
@@ -463,6 +518,11 @@ _gcry_fips_run_selftests (void)
   if (run_random_selftests ())
     goto leave;
 
+  /* Now check the integrity of the binary.  We do this this after
+     having checked the HMAC code.  */
+  if (check_binary_integrity ())
+    goto leave;
+
   /* All selftests passed.  */
   result = STATE_OPERATIONAL;
 
index ed4e480..2b7f0bc 100644 (file)
@@ -349,7 +349,8 @@ _gcry_vcontrol (enum gcry_ctl_cmds cmd, va_list arg_ptr)
       break;
 
     case GCRYCTL_UPDATE_RANDOM_SEED_FILE:
-      _gcry_update_random_seed_file ();
+      if ( fips_is_operational () )
+        _gcry_update_random_seed_file ();
       break;
 
     case GCRYCTL_SET_VERBOSITY:
@@ -405,7 +406,9 @@ _gcry_vcontrol (enum gcry_ctl_cmds cmd, va_list arg_ptr)
       /* We need to do make sure that the random pool is really
          initialized so that the poll function is not a NOP. */
       _gcry_random_initialize (1);
-      _gcry_fast_random_poll (); 
+
+      if ( fips_is_operational () )
+        _gcry_fast_random_poll (); 
       break;
 
     case GCRYCTL_SET_RNDEGD_SOCKET:
index 9cfffa2..8a318af 100644 (file)
@@ -52,6 +52,9 @@
 #include <string.h>
 #include <assert.h>
 #include <errno.h>
+#if defined(__WIN32) && defined(STANDALONE)
+# include <fcntl.h> /* We need setmode().  */
+#endif
 
 #include "hmac256.h"
 
@@ -361,6 +364,7 @@ _gcry_hmac256_release (hmac256_context_t ctx)
 {
   if (ctx)
     {
+      /* Note: We need to take care not to modify errno.  */
       if (ctx->use_hmac)
         my_wipememory (ctx->opad, 64);
       free (ctx);
@@ -441,6 +445,78 @@ _gcry_hmac256_finalize (hmac256_context_t hd, size_t *r_dlen)
 }
 
 
+/* Convenience function to compute the HMAC-SHA256 of one file.  The
+   user needs to provide a buffer RESULT of at least 32 bytes, he
+   needs to put the size of the buffer into RESULTSIZE and the
+   FILENAME.  KEY and KEYLEN are as described for _gcry_hmac256_new.
+   On success the function returns the valid length of the result
+   buffer (which will be 32) or -1 on error.  On error ERRNO is set
+   appropriate.  */ 
+int
+_gcry_hmac256_file (void *result, size_t resultsize, const char *filename,
+                    const void *key, size_t keylen)
+{
+  FILE *fp;
+  hmac256_context_t hd;
+  size_t buffer_size, nread, digestlen;
+  char *buffer;
+  const unsigned char *digest;
+  
+  fp = fopen (filename, "rb");
+  if (!fp)
+    return -1;
+
+  hd = _gcry_hmac256_new (key, keylen);
+  if (!hd)
+    {
+      fclose (fp);
+      return -1;
+    }
+
+  buffer_size = 32768;
+  buffer = malloc (buffer_size);
+  if (!buffer)
+    {
+      fclose (fp);
+      _gcry_hmac256_release (hd);
+      return -1;
+    }
+
+  while ( (nread = fread (buffer, 1, buffer_size, fp)))
+    _gcry_hmac256_update (hd, buffer, nread);
+
+  free (buffer);
+
+  if (ferror (fp))
+    {
+      fclose (fp);
+      _gcry_hmac256_release (hd);
+      return -1;
+    }
+
+  fclose (fp);
+
+  digest = _gcry_hmac256_finalize (hd, &digestlen);
+  if (!digest)
+    {
+      _gcry_hmac256_release (hd);
+      return -1;
+    }
+  
+  if (digestlen > resultsize)
+    {
+      _gcry_hmac256_release (hd);
+      errno = EINVAL;
+      return -1;
+    }
+  memcpy (result, digest, digestlen);
+  _gcry_hmac256_release (hd);
+
+  return digestlen;
+}
+
+
+
 #ifdef STANDALONE
 static int
 selftest (void)
@@ -574,8 +650,12 @@ main (int argc, char **argv)
   char buffer[4096];
   size_t n, dlen, idx;
   int use_stdin = 0;
+  int use_binary = 0;
 
   assert (sizeof (u32) == 4);
+#ifdef __WIN32
+  setmode (fileno (stdin), O_BINARY);
+#endif
 
   if (argc)
     {
@@ -609,14 +689,23 @@ main (int argc, char **argv)
                  stdout);
           exit (0);
         }
+      else if (!strcmp (*argv, "--binary"))
+        {
+          argc--; argv++;
+          use_binary = 1;
+        }
     }          
 
   if (argc < 1)
     {
-      fprintf (stderr, "usage: %s key [filename]\n", pgm);
+      fprintf (stderr, "usage: %s [--binary] key [filename]\n", pgm);
       exit (1);
     }
 
+#ifdef __WIN32
+  if (use_binary)
+    setmode (fileno (stdout), O_BINARY);
+#endif
 
   key = *argv;
   argc--, argv++;
@@ -664,15 +753,27 @@ main (int argc, char **argv)
                    pgm, strerror (errno));
           exit (1);
         }
-      for (idx=0; idx < dlen; idx++)
-        printf ("%02x", digest[idx]);
-      _gcry_hmac256_release (hd);
-      if (use_stdin)
+      if (use_binary)
         {
-          putchar ('\n');
-          break;
+          if (fwrite (digest, dlen, 1, stdout) != 1)
+            {
+              fprintf (stderr, "%s: error writing output: %s\n", 
+                       pgm, strerror (errno));
+              exit (1);
+            }
+        }
+      else
+        {
+          for (idx=0; idx < dlen; idx++)
+            printf ("%02x", digest[idx]);
+          _gcry_hmac256_release (hd);
+          if (use_stdin)
+            {
+              putchar ('\n');
+              break;
+            }
+          printf ("  %s\n", fname);
         }
-      printf ("  %s\n", fname);
     }
 
   return 0;
index 2f4d1ea..df28e72 100644 (file)
@@ -29,5 +29,8 @@ void _gcry_hmac256_update (hmac256_context_t hd, const void *buf, size_t len);
 const void *_gcry_hmac256_finalize (hmac256_context_t hd, size_t *r_dlen);
 void _gcry_hmac256_release (hmac256_context_t hd);
 
+int _gcry_hmac256_file (void *result, size_t resultsize, const char *filename,
+                        const void *key, size_t keylen);
+
 
 #endif /*HMAC256_H*/
index 2af4dbd..4f6ab42 100644 (file)
@@ -36,7 +36,7 @@ TESTS += benchmark
 AM_CPPFLAGS = -I../src -I$(top_srcdir)/src
 AM_CFLAGS = $(GPG_ERROR_CFLAGS)
 
-LDADD = ../src/libgcrypt.la
+LDADD = ../src/libgcrypt.la $(DL_LIBS)
 
 EXTRA_PROGRAMS = testapi
 noinst_PROGRAMS = $(TESTS)