Use syslog to log important messages.
authorWerner Koch <wk@gnupg.org>
Mon, 15 Sep 2008 19:21:57 +0000 (19:21 +0000)
committerWerner Koch <wk@gnupg.org>
Mon, 15 Sep 2008 19:21:57 +0000 (19:21 +0000)
Add an external RNG test hook.

16 files changed:
ChangeLog
NEWS
configure.ac
random/ChangeLog
random/rand-internal.h
random/random-fips.c
random/random.c
random/random.h
src/ChangeLog
src/fips.c
src/g10lib.h
src/gcrypt.h.in
src/global.c
tests/ChangeLog
tests/Makefile.am
tests/fipsrngdrv.c [new file with mode: 0644]

index 8842917..2cbd745 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2008-09-15  Werner Koch  <wk@g10code.com>
+
+       * configure.ac: Cehck for syslog.
+
 2008-09-08  Werner Koch  <wk@g10code.com>
 
        Release 1.4.2.
diff --git a/NEWS b/NEWS
index c3037d0..2bc44e4 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,15 @@
 Noteworthy changes in version 1.4.3
 ------------------------------------------------
 
+ * Try to auto-initialize Libgcrypt to minimize the effect of
+   applications not doing that correctly.  This is not a perfect
+   solution but given that many applicationion would totally fail
+   without such a hack, we try to help at least with the most common
+   cases.  Folks, please read the manual to learn how to properly
+   initialize Libgcrypt!
+
+ * Log fatal error via syslog.
+
  * More self-tests.
 
  * Documentation cleanups.
index a4dd27c..e8faa31 100644 (file)
@@ -700,7 +700,7 @@ AC_CHECK_FUNCS(stpcpy strcasecmp)
 AC_CHECK_FUNCS(strtoul memmove stricmp atexit raise)
 # Other checks
 AC_CHECK_FUNCS(strerror rand mmap getpagesize sysconf waitpid wait4)
-AC_CHECK_FUNCS(gettimeofday getrusage gethrtime clock_gettime)
+AC_CHECK_FUNCS(gettimeofday getrusage gethrtime clock_gettime syslog)
 AC_CHECK_FUNCS(fcntl ftruncate)
 
 GNUPG_CHECK_MLOCK
index 1904268..ed101f6 100644 (file)
@@ -1,3 +1,16 @@
+2008-09-15  Werner Koch  <wk@g10code.com>
+
+       * random.c (_gcry_random_init_external_test): New.
+       (_gcry_random_run_external_test): New. 
+       (_gcry_random_deinit_external_test): New.
+       * random-fips.c (struct rng_context): Turn TEST_DT_COUNTER into a
+       32 bit integer.
+       (x931_get_dt): Ditto.
+       (selftest_kat): Intialize it accordingly.
+       (_gcry_rngfips_init_external_test): New.
+       (_gcry_rngfips_run_external_test): New. 
+       (_gcry_rngfips_deinit_external_test): New.
+
 2008-09-05  Werner Koch  <wk@g10code.com>
 
        * random.c (_gcry_random_selftest): Return success if not in fips
index e3c0192..269fad9 100644 (file)
@@ -79,6 +79,16 @@ void _gcry_rngfips_create_nonce (void *buffer, size_t length);
 
 gcry_error_t _gcry_rngfips_selftest (selftest_report_func_t report);
 
+gcry_err_code_t _gcry_rngfips_init_external_test (void **r_context, 
+                                                  const void *key,
+                                                  size_t keylen,
+                                                  const void *seed,
+                                                  size_t seedlen,
+                                                  const void *dt,
+                                                  size_t dtlen);
+gcry_err_code_t _gcry_rngfips_run_external_test (void *context,
+                                                 char *buffer, size_t buflen);
+void _gcry_rngfips_deinit_external_test (void *context);
 
 
 
index f81ab46..90499db 100644 (file)
@@ -144,12 +144,12 @@ struct rng_context
   /* To implement a KAT we need to provide a know DT value.  To
      accomplish this the x931_get_dt function checks whether this
      field is not NULL and then uses the 16 bytes at this address for
-     the DT value.  However the last byte is will be replaced by the
-     value of field TEST_DT_COUNTER which will be incremented with
+     the DT value.  However the last 4 bytes are replaced by the
+     value of field TEST_DT_COUNTER which will be incremented after
      each invocation of x931_get_dt. We use a pointer and not a buffer
      because there is no need to put this value into secure  memory.  */
   const unsigned char *test_dt_ptr;
-  unsigned char test_dt_counter;
+  u32 test_dt_counter;
 
   /* We need to keep track of the process which did the initialization
      so that we can detect a fork.  The volatile modifier is required
@@ -283,8 +283,12 @@ x931_get_dt (unsigned char *buffer, size_t length, rng_context_t rng_ctx)
       && rng_ctx != std_rng_context
       && rng_ctx != strong_rng_context)
     {
-      memcpy (buffer, rng_ctx->test_dt_ptr, 15);
-      buffer[15] = rng_ctx->test_dt_counter++;
+      memcpy (buffer, rng_ctx->test_dt_ptr, 16);
+      buffer[12] = (rng_ctx->test_dt_counter >> 24);
+      buffer[13] = (rng_ctx->test_dt_counter >> 16);
+      buffer[14] = (rng_ctx->test_dt_counter >> 8);
+      buffer[15] = rng_ctx->test_dt_counter;
+      rng_ctx->test_dt_counter++;
       return;
     }
 
@@ -792,7 +796,7 @@ _gcry_rngfips_add_bytes (const void *buf, size_t buflen, int quality)
     
 /* Public function to fill the buffer with LENGTH bytes of
    cryptographically strong random bytes.  Level GCRY_WEAK_RANDOM is
-   here mapped to GCRY_STRING_RANDOM, GCRY_STRONG_RANDOM is strong
+   here mapped to GCRY_STRONG_RANDOM, GCRY_STRONG_RANDOM is strong
    enough for most usage, GCRY_VERY_STRONG_RANDOM is good for key
    generation stuff but may be very slow.  */
 void
@@ -915,9 +919,12 @@ selftest_kat (selftest_report_func_t report)
       
       /* Setup a DT value.  */
       test_ctx->test_dt_ptr = tv[tvidx].dt;
-      test_ctx->test_dt_counter = tv[tvidx].dt[15];
+      test_ctx->test_dt_counter = ( (tv[tvidx].dt[12] << 24) 
+                                   |(tv[tvidx].dt[13] << 16)
+                                   |(tv[tvidx].dt[14] << 8)
+                                   |(tv[tvidx].dt[15]) );
 
-      /* Get ant compare the first three results.  */
+      /* Get and compare the first three results.  */
       for (ridx=0; ridx < 3; ridx++)
         {
           /* Compute the next value.  */
@@ -989,3 +996,104 @@ _gcry_rngfips_selftest (selftest_report_func_t report)
   return gpg_error (ec);
 }
 
+
+/* Create a new test context for an external RNG test driver.  On
+   success the test context is stored at R_CONTEXT; on failure NULL is
+   stored at R_CONTEXT and an error code is returned.  */
+gcry_err_code_t
+_gcry_rngfips_init_external_test (void **r_context,
+                                  const void *key, size_t keylen,
+                                  const void *seed, size_t seedlen,
+                                  const void *dt, size_t dtlen)
+{
+  gpg_error_t err;
+  rng_context_t test_ctx;
+
+  _gcry_rngfips_initialize (1);  /* Auto-initialize if needed.  */
+  
+  if (!r_context
+      || !key  || keylen  != 16 
+      || !seed || seedlen != 16
+      || !dt   || dtlen   != 16 )
+    return GPG_ERR_INV_ARG;
+
+  test_ctx = gcry_calloc (1, sizeof *test_ctx + dtlen);
+  if (!test_ctx)
+    return gpg_err_code_from_syserror ();
+  setup_guards (test_ctx);
+  
+  /* Setup the key.  */
+  err = gcry_cipher_open (&test_ctx->cipher_hd,
+                          GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_ECB,
+                          GCRY_CIPHER_SECURE);
+  if (err)
+    goto leave;
+
+  err = gcry_cipher_setkey (test_ctx->cipher_hd, key, keylen);
+  if (err)
+    goto leave;
+
+  test_ctx->key_init_pid = getpid ();
+      
+  /* Setup the seed.  */
+  memcpy (test_ctx->seed_V, seed, seedlen);
+  test_ctx->is_seeded = 1;
+  test_ctx->seed_init_pid = getpid ();
+  
+  /* Setup a DT value.  Because our context structure only stores a
+     pointer we copy the DT value to the extra space we allocated in
+     the test_ctx and set the pointer to tehre.  */
+  memcpy ((char*)test_ctx + sizeof *test_ctx, dt, dtlen);
+  test_ctx->test_dt_ptr = (unsigned char*)test_ctx + sizeof test_ctx; 
+  test_ctx->test_dt_counter = ( (test_ctx->test_dt_ptr[12] << 24) 
+                               |(test_ctx->test_dt_ptr[13] << 16)
+                               |(test_ctx->test_dt_ptr[14] << 8)
+                               |(test_ctx->test_dt_ptr[15]) );
+
+  check_guards (test_ctx);
+  /* All fine.  */
+  err = 0;
+
+ leave:
+  if (err)
+    {
+      gcry_cipher_close (test_ctx->cipher_hd);
+      gcry_free (test_ctx);
+      *r_context = NULL;
+    }
+  else
+    *r_context = test_ctx;
+  return gcry_err_code (err);
+}
+
+
+/* Get BUFLEN bytes from the RNG using the test CONTEXT and store them
+   at BUFFER.  Return 0 on success or an error code.  */
+gcry_err_code_t
+_gcry_rngfips_run_external_test (void *context, char *buffer, size_t buflen)
+{
+  rng_context_t test_ctx = context;
+
+  if (!test_ctx || !buffer || buflen != 16)
+    return GPG_ERR_INV_ARG;
+
+  lock_rng ();
+  get_random (buffer, buflen, test_ctx);
+  unlock_rng ();
+  return 0;
+}
+
+/* Release the test CONTEXT.  */
+void
+_gcry_rngfips_deinit_external_test (void *context)
+{
+  rng_context_t test_ctx = context;
+
+  if (test_ctx)
+    {
+      gcry_cipher_close (test_ctx->cipher_hd);
+      gcry_free (test_ctx);
+    }
+}
+
+
index 7a286b8..9da83cf 100644 (file)
@@ -283,3 +283,41 @@ _gcry_random_selftest (selftest_report_func_t report)
     return 0; /* No selftests yet.  */
 }
 
+
+/* Create a new test context for an external RNG test driver.  On
+   success the test context is stored at R_CONTEXT; on failure NULL is
+   stored at R_CONTEXT and an error code is returned.  */
+gcry_err_code_t
+_gcry_random_init_external_test (void **r_context, 
+                                 unsigned int flags,
+                                 const void *key, size_t keylen,
+                                 const void *seed, size_t seedlen,
+                                 const void *dt, size_t dtlen)
+{
+  (void)flags;
+  if (fips_mode ())
+    return _gcry_rngfips_init_external_test (r_context, key, keylen,
+                                             seed, seedlen,
+                                             dt, dtlen);
+  else
+    return GPG_ERR_NOT_SUPPORTED;
+}
+
+/* Get BUFLEN bytes from the RNG using the test CONTEXT and store them
+   at BUFFER.  Return 0 on success or an error code.  */
+gcry_err_code_t
+_gcry_random_run_external_test (void *context, char *buffer, size_t buflen)
+{
+  if (fips_mode ())
+    return _gcry_rngfips_run_external_test (context, buffer, buflen);
+  else
+    return GPG_ERR_NOT_SUPPORTED;
+}
+
+/* Release the test CONTEXT.  */
+void
+_gcry_random_deinit_external_test (void *context)
+{
+  if (fips_mode ())
+    return _gcry_rngfips_deinit_external_test (context);
+}
index eda44d3..9075d9a 100644 (file)
@@ -39,6 +39,18 @@ 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 );
 
+gcry_err_code_t _gcry_random_init_external_test (void **r_context, 
+                                                 unsigned int flags,
+                                                 const void *key,
+                                                 size_t keylen,
+                                                 const void *seed,
+                                                 size_t seedlen,
+                                                 const void *dt, 
+                                                 size_t dtlen);
+gcry_err_code_t _gcry_random_run_external_test (void *context,
+                                                char *buffer, size_t buflen);
+void            _gcry_random_deinit_external_test (void *context);
+
 
 /*-- rndegd.c --*/
 gpg_error_t _gcry_rndegd_set_socket_name (const char *name);
index d860b54..b902881 100644 (file)
@@ -1,3 +1,18 @@
+2008-09-15  Werner Koch  <wk@g10code.com>
+
+       * fips.c [HAVE_SYSLOG]: Include syslog.h.
+       (_gcry_initialize_fips_mode, lock_fsm, unlock_fsm)
+       (_gcry_fips_signal_error, fips_new_state)
+       (_gcry_fips_noreturn) [HAVE_SYSLOG]: Also log via syslog.
+       * global.h [HAVE_SYSLOG]: Include syslog.h.
+       (_gcry_global_is_operational) [HAVE_SYSLOG]: Print warning.
+
+       * global.c (_gcry_vcontrol): Use GCRYCTL_INITIALIZATION_FINISHED
+       to run power-up tests.  Add unpublished control commands 58-60.
+
+       * global.c (_gcry_global_is_operational): New.
+       * g10lib.h (fips_is_operational): Change to call this function.
+
 2008-09-12  Werner Koch  <wk@g10code.com>
 
        * fips.c (_gcry_fips_run_selftests): Add arg EXTENDED.
index 4aa5024..0f3bdb3 100644 (file)
@@ -26,6 +26,9 @@
 #ifdef ENABLE_HMAC_BINARY_CHECK
 # include <dlfcn.h> 
 #endif
+#ifdef HAVE_SYSLOG
+# include <syslog.h>
+#endif /*HAVE_SYSLOG*/
 
 #include "g10lib.h"
 #include "ath.h"
@@ -148,6 +151,11 @@ _gcry_initialize_fips_mode (int force)
            file system.  We better stop right away. */
         log_info ("FATAL: error reading `%s' in libgcrypt: %s\n",
                   procfname, strerror (saved_errno));
+#ifdef HAVE_SYSLOG
+        syslog (LOG_USER|LOG_ERR, "Libgcrypt error: "
+                "reading `%s' failed: %s - abort",
+                procfname, strerror (saved_errno));
+#endif /*HAVE_SYSLOG*/
         abort ();
       }
   }
@@ -169,6 +177,11 @@ _gcry_initialize_fips_mode (int force)
              get involved.  */
           log_info ("FATAL: failed to create the FSM lock in libgcrypt: %s\n",
                      strerror (err));
+#ifdef HAVE_SYSLOG
+          syslog (LOG_USER|LOG_ERR, "Libgcrypt error: "
+                  "creating FSM lock failed: %s - abort",
+                  strerror (err));
+#endif /*HAVE_SYSLOG*/
           abort ();
         }
       
@@ -189,6 +202,11 @@ lock_fsm (void)
     {
       log_info ("FATAL: failed to acquire the FSM lock in libgrypt: %s\n", 
                 strerror (err));
+#ifdef HAVE_SYSLOG
+      syslog (LOG_USER|LOG_ERR, "Libgcrypt error: "
+              "acquiring FSM lock failed: %s - abort",
+              strerror (err));
+#endif /*HAVE_SYSLOG*/
       abort ();
     }
 }
@@ -203,6 +221,11 @@ unlock_fsm (void)
     {
       log_info ("FATAL: failed to release the FSM lock in libgrypt: %s\n",
                 strerror (err));
+#ifdef HAVE_SYSLOG
+      syslog (LOG_USER|LOG_ERR, "Libgcrypt error: "
+              "releasing FSM lock failed: %s - abort",
+              strerror (err));
+#endif /*HAVE_SYSLOG*/
       abort ();
     }
 }
@@ -619,6 +642,14 @@ _gcry_fips_signal_error (const char *srcfile, int srcline, const char *srcfunc,
             srcfile, srcline, 
             srcfunc? ", function ":"", srcfunc? srcfunc:"",
             description? description : "no description available");
+#ifdef HAVE_SYSLOG
+  syslog (LOG_USER|LOG_ERR, "Libgcrypt error: "
+          "%serror in file %s, line %d%s%s: %s",
+          is_fatal? "fatal ":"",
+          srcfile, srcline, 
+          srcfunc? ", function ":"", srcfunc? srcfunc:"",
+          description? description : "no description available");
+#endif /*HAVE_SYSLOG*/
 }
 
 
@@ -697,8 +728,21 @@ fips_new_state (enum module_states new_state)
   if (!ok)
     {
       /* Invalid state transition.  Halting library. */
+#ifdef HAVE_SYSLOG
+      syslog (LOG_USER|LOG_ERR, 
+              "Libgcrypt error: invalid state transition %s => %s",
+              state2str (last_state), state2str (new_state));
+#endif /*HAVE_SYSLOG*/
       fips_noreturn ();
     }
+  else if (new_state == STATE_ERROR || new_state == STATE_FATALERROR)
+    {
+#ifdef HAVE_SYSLOG
+      syslog (LOG_USER|LOG_WARNING, 
+              "Libgcrypt notice: state transition %s => %s",
+              state2str (last_state), state2str (new_state));
+#endif /*HAVE_SYSLOG*/
+    }
 }
 
 
@@ -709,6 +753,9 @@ fips_new_state (enum module_states new_state)
 void
 _gcry_fips_noreturn (void)
 {
+#ifdef HAVE_SYSLOG
+  syslog (LOG_USER|LOG_ERR, "Libgcrypt terminated the application");
+#endif /*HAVE_SYSLOG*/
   fflush (NULL);
   abort ();
   /*NOTREACHED*/
index 3ef7c94..0d0aea6 100644 (file)
@@ -74,6 +74,7 @@
 \f
 
 /*-- src/global.c -*/
+int _gcry_global_is_operational (void);
 gcry_error_t _gcry_vcontrol (enum gcry_ctl_cmds cmd, va_list arg_ptr);
 void  _gcry_check_heap (const void *a);
 int _gcry_get_debug_flag (unsigned int mask);
@@ -310,7 +311,7 @@ void _gcry_fips_signal_error (const char *srcfile,
 #endif
 
 int _gcry_fips_is_operational (void);
-#define fips_is_operational()   (_gcry_fips_is_operational ())
+#define fips_is_operational()   (_gcry_global_is_operational ())
 #define fips_not_operational()  (GCRY_GPG_ERR_NOT_OPERATIONAL)
 
 int _gcry_fips_test_operational (void);
index 4d8bcd0..af6f85d 100644 (file)
@@ -410,6 +410,7 @@ enum gcry_ctl_cmds
     GCRYCTL_FIPS_MODE_P = 55,
     GCRYCTL_FORCE_FIPS_MODE = 56,
     GCRYCTL_SELFTEST = 57
+    /* Note: 58, 59 and 60 are used internally.  */
   };
 
 /* Perform various operations defined by CMD. */
index 09664ab..ce54f0d 100644 (file)
@@ -28,6 +28,9 @@
 #include <limits.h>
 #include <errno.h>
 #include <unistd.h>
+#ifdef HAVE_SYSLOG
+# include <syslog.h>
+#endif /*HAVE_SYSLOG*/
 
 #include "g10lib.h"
 #include "cipher.h"
@@ -80,7 +83,7 @@ global_init (void)
     return;
   any_init_done = 1;
 
-  /* Initialize our portable theead/mutex wrapper.  */
+  /* Initialize our portable thread/mutex wrapper.  */
   err = ath_init ();
   if (err)
     goto fail;
@@ -119,6 +122,39 @@ global_init (void)
 }
 
 
+/* This function is called by the macro fips_is_operational and makes
+   sure that the minimal initialization has been done.  This is far
+   from a perfect solution and hides problems with an improper
+   initialization but at least in single-threaded mode it should work
+   reliable. 
+
+   The reason we need this is that a lot of applications don't use
+   Libgcrypt properly by not running any initialization code at all.
+   They just call a Libgcrypt function and that is all what they want.
+   Now with the FIPS mode, that has the side effect of entering FIPS
+   mode (for security reasons, FIPS mode is the default if no
+   initialization has been done) and bailing out immediately because
+   the FSM is in the wrong state.  If we always run the init code,
+   Libgcrypt can test for FIPS mode and at least if not in FIPS mode,
+   it will behave as before.  Note that this on-the-fly initialization
+   is only done for the cryptographic functions subject to FIPS mode
+   and thus not all API calls will do such an initialization.  */
+int
+_gcry_global_is_operational (void)
+{
+  if (!any_init_done)
+    {
+#ifdef HAVE_SYSLOG
+      syslog (LOG_USER|LOG_WARNING, "Libgcrypt warning: "
+              "missing initialization - please fix the application");
+#endif /*HAVE_SYSLOG*/
+      global_init ();
+    }
+  return _gcry_fips_is_operational ();
+}
+
+
+
 \f
 /* Version number parsing.  */
 
@@ -392,6 +428,8 @@ _gcry_vcontrol (enum gcry_ctl_cmds cmd, va_list arg_ptr)
              mutexes. */
           _gcry_random_initialize (0);
           init_finished = 1;
+          /* Force us into operational state if in FIPS mode.  */
+          (void)fips_is_operational ();
         }
       break;
 
@@ -478,12 +516,48 @@ _gcry_vcontrol (enum gcry_ctl_cmds cmd, va_list arg_ptr)
     case GCRYCTL_SELFTEST:
       /* Run a selftest.  This works in fips mode as well as in
          standard mode.  In contrast to the power-up tests, we use an
-         extended version ofthe selftests. Returns 0 on success or an
+         extended version of the selftests. Returns 0 on success or an
          error code. */
       global_init ();
       err = _gcry_fips_run_selftests (1);
       break;
 
+    case 58:
+      {
+        void **rctx        = va_arg (arg_ptr, void **);
+        unsigned int flags = va_arg (arg_ptr, unsigned int);
+        const void *key    = va_arg (arg_ptr, const void *);
+        size_t keylen      = va_arg (arg_ptr, size_t);
+        const void *seed   = va_arg (arg_ptr, const void *);
+        size_t seedlen     = va_arg (arg_ptr, size_t);
+        const void *dt     = va_arg (arg_ptr, const void *);
+        size_t dtlen       = va_arg (arg_ptr, size_t);
+        if (!fips_is_operational ())
+          err = fips_not_operational ();
+        else
+          err = _gcry_random_init_external_test (rctx, flags, key, keylen,
+                                                 seed, seedlen, dt, dtlen);
+      }
+      break;
+    case 59:
+      {
+        void *ctx     = va_arg (arg_ptr, void *);
+        void *buffer  = va_arg (arg_ptr, void *);
+        size_t buflen = va_arg (arg_ptr, size_t);
+        if (!fips_is_operational ())
+          err = fips_not_operational ();
+        else
+          err = _gcry_random_run_external_test (ctx, buffer, buflen);
+      }
+      break;
+    case 60:
+      {
+        void *ctx = va_arg (arg_ptr, void *);
+        _gcry_random_deinit_external_test (ctx);
+      }
+      break;
+
+
     default:
       err = GPG_ERR_INV_OP;
     }
index bf3b6b2..f4efdd1 100644 (file)
@@ -1,3 +1,7 @@
+2008-09-15  Werner Koch  <wk@g10code.com>
+
+       * fipsrngdrv.c: New.
+
 2008-09-09  Werner Koch  <wk@g10code.com>
 
        * basic.c (main): New option --selftest.
index 6c39d21..509647a 100644 (file)
@@ -39,7 +39,7 @@ AM_CFLAGS = $(GPG_ERROR_CFLAGS)
 LDADD = ../src/libgcrypt.la $(DL_LIBS)
 
 EXTRA_PROGRAMS = testapi pkbench
-noinst_PROGRAMS = $(TESTS)
+noinst_PROGRAMS = $(TESTS) fipsrngdrv
 
 EXTRA_DIST = README rsa-16k.key
 
diff --git a/tests/fipsrngdrv.c b/tests/fipsrngdrv.c
new file mode 100644 (file)
index 0000000..6100f90
--- /dev/null
@@ -0,0 +1,119 @@
+/* fipsrngdrv.c  -  A driver to test the FIPS RNG.
+   Copyright (C) 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "../src/gcrypt.h"
+
+#define PGM "fipsrngdrv"
+
+
+static void
+die (const char *format, ...)
+{
+  va_list arg_ptr;
+
+  va_start (arg_ptr, format);
+  fputs (PGM ": ", stderr);
+  vfprintf (stderr, format, arg_ptr);
+  va_end (arg_ptr);
+  exit (1);
+}
+
+
+static gcry_error_t
+init_external_test (void **r_context, 
+                    unsigned int flags,
+                    const void *key, size_t keylen,
+                    const void *seed, size_t seedlen,
+                    const void *dt, size_t dtlen)
+{
+  return gcry_control (58, 
+                       r_context, flags,
+                       key, keylen,
+                       seed, seedlen,
+                       dt, dtlen);
+}
+
+static gcry_error_t
+run_external_test (void *context, void *buffer, size_t buflen)
+{
+  return gcry_control (59, context, buffer, buflen);
+}
+
+static void
+deinit_external_test (void *context)
+{
+  gcry_control (60, context);
+}
+
+
+static void
+print_buffer (const unsigned char *buffer, size_t length)
+{
+  while (length--)
+    printf ("%02X", *buffer++);
+}
+
+
+int
+main (int argc, char **argv)
+{
+  void *context;
+  gpg_error_t err;
+  int block;
+  unsigned char buffer[16];
+
+  (void)argc;
+  (void)argv;
+
+  gcry_control (GCRYCTL_FORCE_FIPS_MODE, 0);
+  if (!gcry_check_version (GCRYPT_VERSION))
+    die ("version mismatch\n");
+  gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
+  gcry_control (GCRYCTL_SET_VERBOSITY, 2);
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+
+  err = init_external_test (&context, 0,
+                            "1234567890123456", 16,
+                            "abcdefghijklmnop", 16,
+                            "XXXXXXXXXXXXXXXX", 16);
+  if (err)
+    die ("init external test failed: %s\n", gpg_strerror (err));
+
+  for (block=0; block < 10; block++)
+    {
+      err = run_external_test (context, buffer, sizeof buffer);
+      if (err)
+        die ("run external test failed: %s\n", gpg_strerror (err));
+      print_buffer (buffer, sizeof buffer);
+      putchar ('\n');
+    }
+
+  deinit_external_test (context);
+
+  return 0;
+}
+