summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2008-09-15 19:21:57 +0000
committerWerner Koch <wk@gnupg.org>2008-09-15 19:21:57 +0000
commit7627f9646701e88c827bbadd1231221d5f0c89a6 (patch)
tree51d35bfd172c13ebee77936d7c1df25e54b66268
parentf5c7621d2496563dea8b4670056c229bb7f596f6 (diff)
downloadlibgcrypt-7627f9646701e88c827bbadd1231221d5f0c89a6.tar.gz
Use syslog to log important messages.
Add an external RNG test hook.
-rw-r--r--ChangeLog4
-rw-r--r--NEWS9
-rw-r--r--configure.ac2
-rw-r--r--random/ChangeLog13
-rw-r--r--random/rand-internal.h10
-rw-r--r--random/random-fips.c124
-rw-r--r--random/random.c38
-rw-r--r--random/random.h12
-rw-r--r--src/ChangeLog15
-rw-r--r--src/fips.c47
-rw-r--r--src/g10lib.h3
-rw-r--r--src/gcrypt.h.in1
-rw-r--r--src/global.c78
-rw-r--r--tests/ChangeLog4
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/fipsrngdrv.c119
16 files changed, 468 insertions, 13 deletions
diff --git a/ChangeLog b/ChangeLog
index 88429178..2cbd7457 100644
--- 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 c3037d07..2bc44e4d 100644
--- 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.
diff --git a/configure.ac b/configure.ac
index a4dd27cf..e8faa318 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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
diff --git a/random/ChangeLog b/random/ChangeLog
index 19042680..ed101f68 100644
--- a/random/ChangeLog
+++ b/random/ChangeLog
@@ -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
diff --git a/random/rand-internal.h b/random/rand-internal.h
index e3c01920..269fad95 100644
--- a/random/rand-internal.h
+++ b/random/rand-internal.h
@@ -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);
diff --git a/random/random-fips.c b/random/random-fips.c
index f81ab466..90499db2 100644
--- a/random/random-fips.c
+++ b/random/random-fips.c
@@ -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);
+ }
+}
+
+
diff --git a/random/random.c b/random/random.c
index 7a286b8e..9da83cf0 100644
--- a/random/random.c
+++ b/random/random.c
@@ -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);
+}
diff --git a/random/random.h b/random/random.h
index eda44d36..9075d9a3 100644
--- a/random/random.h
+++ b/random/random.h
@@ -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);
diff --git a/src/ChangeLog b/src/ChangeLog
index d860b543..b9028818 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -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.
diff --git a/src/fips.c b/src/fips.c
index 4aa50241..0f3bdb3b 100644
--- a/src/fips.c
+++ b/src/fips.c
@@ -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*/
diff --git a/src/g10lib.h b/src/g10lib.h
index 3ef7c94b..0d0aea68 100644
--- a/src/g10lib.h
+++ b/src/g10lib.h
@@ -74,6 +74,7 @@
/*-- 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);
diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in
index 4d8bcd0a..af6f85db 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -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. */
diff --git a/src/global.c b/src/global.c
index 09664ab1..ce54f0d3 100644
--- a/src/global.c
+++ b/src/global.c
@@ -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 ();
+}
+
+
+
/* 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;
}
diff --git a/tests/ChangeLog b/tests/ChangeLog
index bf3b6b21..f4efdd14 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -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.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 6c39d219..509647a5 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -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
index 00000000..6100f909
--- /dev/null
+++ b/tests/fipsrngdrv.c
@@ -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;
+}
+