summaryrefslogtreecommitdiff
path: root/random/random.c
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2008-08-19 15:55:46 +0000
committerWerner Koch <wk@gnupg.org>2008-08-19 15:55:46 +0000
commite77613e7b89ee150ae9e945029f11b04744a6fd3 (patch)
treebc41644fb0e68f6d353e640eb77a3b7e8b8c4d3e /random/random.c
parentb202832f13ca0ecce65767e4448e7e91eda1214e (diff)
downloadlibgcrypt-e77613e7b89ee150ae9e945029f11b04744a6fd3.tar.gz
A whole bunch of changes to eventually support
FIPS restricted mode. Also some documentation improvements and other minor enhancements. See the ChangeLogs. Stay tuned.
Diffstat (limited to 'random/random.c')
-rw-r--r--random/random.c1342
1 files changed, 79 insertions, 1263 deletions
diff --git a/random/random.c b/random/random.c
index 3db72961..020c6518 100644
--- a/random/random.c
+++ b/random/random.c
@@ -1,6 +1,5 @@
-/* random.c - random number generator
- * Copyright (C) 1998, 2000, 2001, 2002, 2003,
- * 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+/* random.c - Random number switch
+ * Copyright (C) 2008 Free Software Foundation, Inc.
*
* This file is part of Libgcrypt.
*
@@ -19,192 +18,21 @@
*/
/*
- This random number generator is modelled after the one described in
- Peter Gutmann's paper: "Software Generation of Practically Strong
- Random Numbers". See also chapter 6 in his book "Cryptographic
- Security Architecture", New York, 2004, ISBN 0-387-95387-6.
+ This module switches between different implementations of random
+ number generators and provides a few help functions.
*/
-
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
-#include <string.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <time.h>
-#ifdef HAVE_GETHRTIME
-#include <sys/times.h>
-#endif
-#ifdef HAVE_GETTIMEOFDAY
-#include <sys/time.h>
-#endif
-#ifdef HAVE_GETRUSAGE
-#include <sys/resource.h>
-#endif
-#ifdef __MINGW32__
-#include <process.h>
-#endif
+
#include "g10lib.h"
-#include "../cipher/rmd.h"
#include "random.h"
#include "rand-internal.h"
-#include "cipher.h" /* Required for the rmd160_hash_buffer() prototype. */
#include "ath.h"
-#ifndef RAND_MAX /* For SunOS. */
-#define RAND_MAX 32767
-#endif
-
-/* Check whether we can lock the seed file read write. */
-#if defined(HAVE_FCNTL) && defined(HAVE_FTRUNCATE) && !defined(HAVE_W32_SYSTEM)
-#define LOCK_SEED_FILE 1
-#else
-#define LOCK_SEED_FILE 0
-#endif
-
-/* Define the constant we use for transforming the pool at read-out. */
-#if SIZEOF_UNSIGNED_LONG == 8
-#define ADD_VALUE 0xa5a5a5a5a5a5a5a5
-#elif SIZEOF_UNSIGNED_LONG == 4
-#define ADD_VALUE 0xa5a5a5a5
-#else
-#error weird size for an unsigned long
-#endif
-
-/* Contstants pertaining to the hash pool. */
-#define BLOCKLEN 64 /* Hash this amount of bytes... */
-#define DIGESTLEN 20 /* ... into a digest of this length (rmd160). */
-/* POOLBLOCKS is the number of digests which make up the pool. */
-#define POOLBLOCKS 30
-/* POOLSIZE must be a multiple of the digest length to make the AND
- operations faster, the size should also be a multiple of unsigned
- long. */
-#define POOLSIZE (POOLBLOCKS*DIGESTLEN)
-#if (POOLSIZE % SIZEOF_UNSIGNED_LONG)
-#error Please make sure that poolsize is a multiple of unsigned long
-#endif
-#define POOLWORDS (POOLSIZE / SIZEOF_UNSIGNED_LONG)
-
-
-/* RNDPOOL is the pool we use to collect the entropy and to stir it
- up. Its allocated size is POOLSIZE+BLOCKLEN. Note that this is
- also an indication on whether the module has been fully
- initialized. */
-static unsigned char *rndpool;
-
-/* KEYPOOL is used as a scratch copy to read out random from RNDPOOL.
- Its allocated size is also POOLSIZE+BLOCKLEN. */
-static unsigned char *keypool;
-
-/* This is the offset into RNDPOOL where the next random bytes are to
- be mixed in. */
-static size_t pool_writepos;
-
-/* When reading data out of KEYPOOL, we start the read at different
- positions. This variable keeps track on where to read next. */
-static size_t pool_readpos;
-
-/* This flag is set to true as soon as the pool has been completely
- filled the first time. This may happen either by rereading a seed
- file or by adding enough entropy. */
-static int pool_filled;
-
-/* This counter is used to track whether the initial seeding has been
- done with enough bytes from a reliable entropy source. */
-static size_t pool_filled_counter;
-
-/* If random of level GCRY_VERY_STRONG_RANDOM has been requested we
- have stricter requirements on what kind of entropy is in the pool.
- In particular POOL_FILLED is not sufficient. Thus we add some
- extra seeding and set this flag to true if the extra seeding has
- been done. */
-static int did_initial_extra_seeding;
-
-/* This variable is used to estimated the amount of fresh entropy
- available in RNDPOOL. */
-static int pool_balance;
-
-/* After a mixing operation this variable will be set to true and
- cleared if new entropy has been added or a remix is required for
- otehr reasons. */
-static int just_mixed;
-
-/* The name of the seed file or NULL if no seed file has been defined.
- The seed file needs to be regsitered at initialiation time. We
- keep a malloced copy here. */
-static char *seed_file_name;
-
-/* If a seed file has been registered and maybe updated on exit this
- flag set. */
-static int allow_seed_file_update;
-
-/* Option flag set at initialiation time to force allocation of the
- pool in secure memory. */
-static int secure_alloc;
-
-/* This function pointer is set to the actual entropy gathering
- function during initailization. After initialization it is
- guaranteed to point to function. (On systems without a random
- gatherer module a dummy function is used).*/
-static int (*slow_gather_fnc)(void (*)(const void*, size_t,
- enum random_origins),
- enum random_origins, size_t, int);
-
-/* This function is set to the actual fast entropy gathering fucntion
- during initialization. If it is NULL, no such function is
- available. */
-static void (*fast_gather_fnc)(void (*)(const void*, size_t,
- enum random_origins),
- enum random_origins);
-
-
-/* Option flag useful for debugging and the test suite. If set
- requests for very strong random are degraded to strong random. Not
- used by regular applications. */
-static int quick_test;
-
-/* On systems without entropy gathering modules, this flag is set to
- indicate that the random generator is not working properly. A
- warning message is issued as well. This is useful only for
- debugging and during development. */
-static int faked_rng;
-
-/* This is the lock we use to protect all pool operations. */
-static ath_mutex_t pool_lock = ATH_MUTEX_INITIALIZER;
-
-/* This is a helper for assert calls. These calls are used to assert
- that functions are called in a locked state. It is not meant to be
- thread-safe but as a method to get aware of missing locks in the
- test suite. */
-static int pool_is_locked;
-
-/* This is the lock we use to protect the buffer used by the nonce
- generation. */
-static ath_mutex_t nonce_buffer_lock = ATH_MUTEX_INITIALIZER;
-
-
-/* We keep some counters in this structure for the sake of the
- _gcry_random_dump_stats () function. */
-static struct
-{
- unsigned long mixrnd;
- unsigned long mixkey;
- unsigned long slowpolls;
- unsigned long fastpolls;
- unsigned long getbytes1;
- unsigned long ngetbytes1;
- unsigned long getbytes2;
- unsigned long ngetbytes2;
- unsigned long addbytes;
- unsigned long naddbytes;
-} rndstats;
-
/* If not NULL a progress function called from certain places and the
opaque value passed along. Registred by
@@ -213,149 +41,13 @@ static void (*progress_cb) (void *,const char*,int,int, int );
static void *progress_cb_data;
-/* --- Stuff pertaining to the random daemon support. --- */
-#ifdef USE_RANDOM_DAEMON
-
-/* If ALLOW_DAEMON is true, the module will try to use the random
- daemon first. If the daemon has failed, this variable is set to
- back to false and the code continues as normal. Note, we don't
- test this flag in a locked state because a wrong value does not
- harm and the trhead will find out itself that the daemon does not
- work and set it (again) to false. */
-static int allow_daemon;
-
-/* During initialization, the user may set a non-default socket name
- for accessing the random daemon. If this value is NULL, the
- default name will be used. */
-static char *daemon_socket_name;
-
-#endif /*USE_RANDOM_DAEMON*/
-
-
-
-/* --- Prototypes --- */
-static void read_pool (byte *buffer, size_t length, int level );
-static void add_randomness (const void *buffer, size_t length,
- enum random_origins origin);
-static void random_poll (void);
-static void do_fast_random_poll (void);
-static int (*getfnc_gather_random (void))(void (*)(const void*, size_t,
- enum random_origins),
- enum random_origins, size_t, int);
-static void (*getfnc_fast_random_poll (void))(void (*)(const void*, size_t,
- enum random_origins),
- enum random_origins);
-static void read_random_source (enum random_origins origin,
- size_t length, int level);
-static int gather_faked (void (*add)(const void*, size_t, enum random_origins),
- enum random_origins, size_t length, int level );
-
/* --- Functions --- */
-/* Basic initialization which is required to initialize mutexes and
- such. It does not run a full initialization so that the filling of
- the random pool can be delayed until it is actually needed. We
- assume that this function is used before any concurrent access
- happens. */
-static void
-initialize_basics(void)
-{
- static int initialized;
- int err;
-
- if (!initialized)
- {
- initialized = 1;
- err = ath_mutex_init (&pool_lock);
- if (err)
- log_fatal ("failed to create the pool lock: %s\n", strerror (err) );
-
- err = ath_mutex_init (&nonce_buffer_lock);
- if (err)
- log_fatal ("failed to create the nonce buffer lock: %s\n",
- strerror (err) );
-
-#ifdef USE_RANDOM_DAEMON
- _gcry_daemon_initialize_basics ();
-#endif /*USE_RANDOM_DAEMON*/
-
- /* Make sure that we are still using the values we have
- traditionally used for the random levels. */
- assert ( GCRY_WEAK_RANDOM == 0
- && GCRY_STRONG_RANDOM == 1
- && GCRY_VERY_STRONG_RANDOM == 2);
- }
-}
-
-/* Take the pool lock. */
-static void
-lock_pool (void)
-{
- int err;
-
- err = ath_mutex_lock (&pool_lock);
- if (err)
- log_fatal ("failed to acquire the pool lock: %s\n", strerror (err));
- pool_is_locked = 1;
-}
-
-/* Release the pool lock. */
-static void
-unlock_pool (void)
-{
- int err;
-
- pool_is_locked = 0;
- err = ath_mutex_unlock (&pool_lock);
- if (err)
- log_fatal ("failed to release the pool lock: %s\n", strerror (err));
-}
-
-
-/* Full initialization of this module. */
-static void
-initialize(void)
-{
- /* Although the basic initialization should have happened already,
- we call it here to make sure that all prerequisites are met. */
- initialize_basics ();
-
- /* Now we can look the pool and complete the initialization if
- necessary. */
- lock_pool ();
- if (!rndpool)
- {
- /* The data buffer is allocated somewhat larger, so that we can
- use this extra space (which is allocated in secure memory) as
- a temporary hash buffer */
- rndpool = (secure_alloc
- ? gcry_xcalloc_secure (1, POOLSIZE + BLOCKLEN)
- : gcry_xcalloc (1, POOLSIZE + BLOCKLEN));
- keypool = (secure_alloc
- ? gcry_xcalloc_secure (1, POOLSIZE + BLOCKLEN)
- : gcry_xcalloc (1, POOLSIZE + BLOCKLEN));
-
- /* Setup the slow entropy gathering function. The code requires
- that this function exists. */
- slow_gather_fnc = getfnc_gather_random ();
- if (!slow_gather_fnc)
- {
- faked_rng = 1;
- slow_gather_fnc = gather_faked;
- }
-
- /* Setup the fast entropy gathering function. */
- fast_gather_fnc = getfnc_fast_random_poll ();
-
- }
- unlock_pool ();
-}
-
-
-/* Used to register a progress callback. */
+/* Used to register a progress callback. This needs to be called
+ before any threads are created. */
void
_gcry_register_random_progress (void (*cb)(void *,const char*,int,int,int),
void *cb_data )
@@ -365,8 +57,8 @@ _gcry_register_random_progress (void (*cb)(void *,const char*,int,int,int),
}
-/* This progress function is currently used by the random modules to give hint
- on how much more entropy is required. */
+/* This progress function is currently used by the random modules to
+ give hints on how much more entropy is required. */
void
_gcry_random_progress (const char *what, int printchar, int current, int total)
{
@@ -375,44 +67,41 @@ _gcry_random_progress (const char *what, int printchar, int current, int total)
}
+
/* Initialize this random subsystem. If FULL is false, this function
- merely calls the initialize and does not do anything more. Doing
- this is not really required but when running in a threaded
- environment we might get a race condition otherwise. */
+ merely calls the basic initialization of the module and does not do
+ anything more. Doing this is not really required but when running
+ in a threaded environment we might get a race condition
+ otherwise. */
void
_gcry_random_initialize (int full)
{
- if (!full)
- initialize_basics ();
+ if ( fips_mode() )
+ _gcry_rngfips_initialize (full);
else
- initialize ();
+ _gcry_rngcsprng_initialize (full);
}
void
-_gcry_random_dump_stats ()
+_gcry_random_dump_stats (void)
{
- /* FIXME: don't we need proper locking here? -mo.
- Yes. However this is usually called during cleanup and thenwe _
- might_ run into problems. Needs to be checked. -wk */
-
- log_info ("random usage: poolsize=%d mixed=%lu polls=%lu/%lu added=%lu/%lu\n"
- " outmix=%lu getlvl1=%lu/%lu getlvl2=%lu/%lu%s\n",
- POOLSIZE, rndstats.mixrnd, rndstats.slowpolls, rndstats.fastpolls,
- rndstats.naddbytes, rndstats.addbytes,
- rndstats.mixkey, rndstats.ngetbytes1, rndstats.getbytes1,
- rndstats.ngetbytes2, rndstats.getbytes2,
- _gcry_rndhw_failed_p()? " (hwrng failed)":"");
+ if ( fips_mode() )
+ _gcry_rngfips_dump_stats ();
+ else
+ _gcry_rngcsprng_dump_stats ();
}
/* This function should be called during initialization and beore
- intialization of this module to palce the random pools into secure
+ intialization of this module to place the random pools into secure
memory. */
void
-_gcry_secure_random_alloc()
+_gcry_secure_random_alloc (void)
{
- secure_alloc = 1;
+ if ( fips_mode() )
+ return; /* Not used; the fips rng is allows in secure mode. */
+ _gcry_rngcsprng_secure_alloc ();
}
@@ -421,21 +110,18 @@ _gcry_secure_random_alloc()
void
_gcry_enable_quick_random_gen (void)
{
- quick_test = 1;
+ if ( fips_mode() )
+ return; /* Not used. */
+ _gcry_rngcsprng_enable_quick_gen ();
}
void
_gcry_set_random_daemon_socket (const char *socketname)
{
-#ifdef USE_RANDOM_DAEMON
- if (daemon_socket_name)
- BUG ();
-
- daemon_socket_name = gcry_xstrdup (socketname);
-#else /*!USE_RANDOM_DAEMON*/
- (void)socketname;
-#endif /*!USE_RANDOM_DAEMON*/
+ if ( fips_mode() )
+ return; /* Not used. */
+ _gcry_rngcsprng_set_daemon_socket (socketname);
}
/* With ONOFF set to 1, enable the use of the daemon. With ONOFF set
@@ -444,31 +130,20 @@ _gcry_set_random_daemon_socket (const char *socketname)
int
_gcry_use_random_daemon (int onoff)
{
-#ifdef USE_RANDOM_DAEMON
- int last;
-
- /* FIXME: This is not really thread safe. */
- last = allow_daemon;
- if (onoff != -1)
- allow_daemon = onoff;
-
- return last;
-#else /*!USE_RANDOM_DAEMON*/
- (void)onoff;
- return 0;
-#endif /*!USE_RANDOM_DAEMON*/
+ if (fips_mode ())
+ return 0; /* Never enabled in fips mode. */
+ return _gcry_rngcsprng_use_daemon (onoff);
}
/* This function returns true if no real RNG is available or the
quality of the RNG has been degraded for test purposes. */
int
-_gcry_random_is_faked()
+_gcry_random_is_faked (void)
{
- /* We need to initialize due to the runtime determination of
- available entropy gather modules. */
- initialize();
- return (faked_rng || quick_test);
+ if (fips_mode ())
+ return 0; /* Never faked in fips mode. */
+ return _gcry_rngcsprng_is_faked ();
}
@@ -478,42 +153,22 @@ _gcry_random_is_faked()
gcry_error_t
gcry_random_add_bytes (const void *buf, size_t buflen, int quality)
{
- size_t nbytes;
- const char *bufptr;
-
- if (quality == -1)
- quality = 35;
- else if (quality > 100)
- quality = 100;
- else if (quality < 0)
- quality = 0;
-
- if (!buf)
- return gpg_error (GPG_ERR_INV_ARG);
-
- if (!buflen || quality < 10)
- return 0; /* Take a shortcut. */
-
- /* Because we don't increment the entropy estimation with FASTPOLL,
- we don't need to take lock that estimation while adding from an
- external source. This limited entropy estimation also means that
- we can't take QUALITY into account. */
- initialize_basics ();
- bufptr = buf;
- while (buflen)
- {
- nbytes = buflen > POOLSIZE? POOLSIZE : buflen;
- lock_pool ();
- if (rndpool)
- add_randomness (bufptr, nbytes, RANDOM_ORIGIN_EXTERNAL);
- unlock_pool ();
- bufptr += nbytes;
- buflen -= nbytes;
- }
- return 0;
+ if (fips_mode ())
+ return 0; /* No need for this in fips mode. */
+ return _gcry_rngcsprng_add_bytes (buf, buflen, quality);
}
-
+
+/* Helper function. */
+static void
+do_randomize (void *buffer, size_t length, enum gcry_random_level level)
+{
+ if (fips_mode ())
+ _gcry_rngfips_randomize (buffer, length, level);
+ else
+ _gcry_rngcsprng_randomize (buffer, length, level);
+}
+
/* The public function to return random data of the quality LEVEL.
Returns a pointer to a newly allocated and randomized buffer of
LEVEL and NBYTES length. Caller must free the buffer. */
@@ -522,11 +177,8 @@ gcry_random_bytes (size_t nbytes, enum gcry_random_level level)
{
void *buffer;
- initialize();
-
buffer = gcry_xmalloc (nbytes);
- gcry_randomize (buffer, nbytes, level);
-
+ do_randomize (buffer, nbytes, level);
return buffer;
}
@@ -535,16 +187,15 @@ gcry_random_bytes (size_t nbytes, enum gcry_random_level level)
this version of the function returns the random in a buffer allocated
in secure memory. Caller must free the buffer. */
void *
-gcry_random_bytes_secure( size_t nbytes, enum gcry_random_level level )
+gcry_random_bytes_secure (size_t nbytes, enum gcry_random_level level)
{
void *buffer;
- initialize();
-
- buffer = secure_alloc ? gcry_xmalloc_secure (nbytes)
- : gcry_xmalloc (nbytes);
- gcry_randomize (buffer, nbytes, level);
-
+ /* Historical note (1.3.0--1.4.1): The buffer was only allocated
+ in secure memory if the pool in random-csprng.c was also set to
+ use secure memory. */
+ buffer = gcry_xmalloc_secure (nbytes);
+ do_randomize (buffer, nbytes, level);
return buffer;
}
@@ -557,741 +208,36 @@ gcry_random_bytes_secure( size_t nbytes, enum gcry_random_level level )
void
gcry_randomize (void *buffer, size_t length, enum gcry_random_level level)
{
- unsigned char *p;
-
- /* Make sure we are initialized. */
- initialize ();
-
- /* Handle our hack used for regression tests of Libgcrypt. */
- if ( quick_test && level > GCRY_STRONG_RANDOM )
- level = GCRY_STRONG_RANDOM;
-
- /* Make sure the level is okay. */
- level &= 3;
-
-#ifdef USE_RANDOM_DAEMON
- if (allow_daemon
- && !_gcry_daemon_randomize (daemon_socket_name, buffer, length, level))
- return; /* The daemon succeeded. */
- allow_daemon = 0; /* Daemon failed - switch off. */
-#endif /*USE_RANDOM_DAEMON*/
-
- /* Acquire the pool lock. */
- lock_pool ();
-
- /* Update the statistics. */
- if (level >= GCRY_VERY_STRONG_RANDOM)
- {
- rndstats.getbytes2 += length;
- rndstats.ngetbytes2++;
- }
- else
- {
- rndstats.getbytes1 += length;
- rndstats.ngetbytes1++;
- }
-
- /* Read the random into the provided buffer. */
- for (p = buffer; length > 0;)
- {
- size_t n;
-
- n = length > POOLSIZE? POOLSIZE : length;
- read_pool (p, n, level);
- length -= n;
- p += n;
- }
-
- /* Release the pool lock. */
- unlock_pool ();
-}
-
-
-
-
-/*
- Mix the pool:
-
- |........blocks*20byte........|20byte|..44byte..|
- <..44byte..> <20byte>
- | |
- | +------+
- +---------------------------|----------+
- v v
- |........blocks*20byte........|20byte|..44byte..|
- <.....64bytes.....>
- |
- +----------------------------------+
- Hash
- v
- |.............................|20byte|..44byte..|
- <20byte><20byte><..44byte..>
- | |
- | +---------------------+
- +-----------------------------+ |
- v v
- |.............................|20byte|..44byte..|
- <.....64byte......>
- |
- +-------------------------+
- Hash
- v
- |.............................|20byte|..44byte..|
- <20byte><20byte><..44byte..>
-
- and so on until we did this for all blocks.
-
- To better protect against implementation errors in this code, we
- xor a digest of the entire pool into the pool before mixing.
-
- Note: this function must only be called with a locked pool.
- */
-static void
-mix_pool(unsigned char *pool)
-{
- static unsigned char failsafe_digest[DIGESTLEN];
- static int failsafe_digest_valid;
-
- unsigned char *hashbuf = pool + POOLSIZE;
- unsigned char *p, *pend;
- int i, n;
- RMD160_CONTEXT md;
-
-#if DIGESTLEN != 20
-#error must have a digest length of 20 for ripe-md-160
-#endif
-
- assert (pool_is_locked);
- _gcry_rmd160_init( &md );
-
- /* Loop over the pool. */
- pend = pool + POOLSIZE;
- memcpy(hashbuf, pend - DIGESTLEN, DIGESTLEN );
- memcpy(hashbuf+DIGESTLEN, pool, BLOCKLEN-DIGESTLEN);
- _gcry_rmd160_mixblock( &md, hashbuf);
- memcpy(pool, hashbuf, 20 );
-
- if (failsafe_digest_valid && pool == rndpool)
- {
- for (i=0; i < 20; i++)
- pool[i] ^= failsafe_digest[i];
- }
-
- p = pool;
- for (n=1; n < POOLBLOCKS; n++)
- {
- memcpy (hashbuf, p, DIGESTLEN);
-
- p += DIGESTLEN;
- if (p+DIGESTLEN+BLOCKLEN < pend)
- memcpy (hashbuf+DIGESTLEN, p+DIGESTLEN, BLOCKLEN-DIGESTLEN);
- else
- {
- unsigned char *pp = p + DIGESTLEN;
-
- for (i=DIGESTLEN; i < BLOCKLEN; i++ )
- {
- if ( pp >= pend )
- pp = pool;
- hashbuf[i] = *pp++;
- }
- }
-
- _gcry_rmd160_mixblock ( &md, hashbuf);
- memcpy(p, hashbuf, 20 );
- }
-
- /* Our hash implementation does only leave small parts (64 bytes)
- of the pool on the stack, so it is okay not to require secure
- memory here. Before we use this pool, it will be copied to the
- help buffer anyway. */
- if ( pool == rndpool)
- {
- _gcry_rmd160_hash_buffer (failsafe_digest, pool, POOLSIZE);
- failsafe_digest_valid = 1;
- }
-
- _gcry_burn_stack (384); /* for the rmd160_mixblock(), rmd160_hash_buffer */
+ do_randomize (buffer, length, level);
}
+/* This function may be used to specify the file to be used as a seed
+ file for the PRNG. This fucntion should be called prior to the
+ initialization of the random module. NAME may not be NULL. */
void
-_gcry_set_random_seed_file( const char *name )
+_gcry_set_random_seed_file (const char *name)
{
- if (seed_file_name)
- BUG ();
- seed_file_name = gcry_xstrdup (name);
-}
-
-
-/* Lock an open file identified by file descriptor FD and wait a
- reasonable time to succeed. With FOR_WRITE set to true a write
- lock will be taken. FNAME is used only for diagnostics. Returns 0
- on success or -1 on error. */
-static int
-lock_seed_file (int fd, const char *fname, int for_write)
-{
-#if LOCK_SEED_FILE
- struct flock lck;
- struct timeval tv;
- int backoff=0;
-
- /* We take a lock on the entire file. */
- memset (&lck, 0, sizeof lck);
- lck.l_type = for_write? F_WRLCK : F_RDLCK;
- lck.l_whence = SEEK_SET;
-
- while (fcntl (fd, F_SETLK, &lck) == -1)
- {
- if (errno != EAGAIN && errno != EACCES)
- {
- log_info (_("can't lock `%s': %s\n"), fname, strerror (errno));
- return -1;
- }
-
- if (backoff > 2) /* Show the first message after ~2.25 seconds. */
- log_info( _("waiting for lock on `%s'...\n"), fname);
-
- tv.tv_sec = backoff;
- tv.tv_usec = 250000;
- select (0, NULL, NULL, NULL, &tv);
- if (backoff < 10)
- backoff++ ;
- }
-#endif /*LOCK_SEED_FILE*/
- return 0;
-}
-
-
-/* Read in a seed from the random_seed file and return true if this
- was successful.
-
- Note: Multiple instances of applications sharing the same random
- seed file can be started in parallel, in which case they will read
- out the same pool and then race for updating it (the last update
- overwrites earlier updates). They will differentiate only by the
- weak entropy that is added in read_seed_file based on the PID and
- clock, and up to 16 bytes of weak random non-blockingly. The
- consequence is that the output of these different instances is
- correlated to some extent. In the perfect scenario, the attacker
- can control (or at least guess) the PID and clock of the
- application, and drain the system's entropy pool to reduce the "up
- to 16 bytes" above to 0. Then the dependencies of the inital
- states of the pools are completely known. */
-static int
-read_seed_file (void)
-{
- int fd;
- struct stat sb;
- unsigned char buffer[POOLSIZE];
- int n;
-
- assert (pool_is_locked);
-
- if (!seed_file_name)
- return 0;
-
-#ifdef HAVE_DOSISH_SYSTEM
- fd = open( seed_file_name, O_RDONLY | O_BINARY );
-#else
- fd = open( seed_file_name, O_RDONLY );
-#endif
- if( fd == -1 && errno == ENOENT)
- {
- allow_seed_file_update = 1;
- return 0;
- }
-
- if (fd == -1 )
- {
- log_info(_("can't open `%s': %s\n"), seed_file_name, strerror(errno) );
- return 0;
- }
- if (lock_seed_file (fd, seed_file_name, 0))
- {
- close (fd);
- return 0;
- }
- if (fstat( fd, &sb ) )
- {
- log_info(_("can't stat `%s': %s\n"), seed_file_name, strerror(errno) );
- close(fd);
- return 0;
- }
- if (!S_ISREG(sb.st_mode) )
- {
- log_info(_("`%s' is not a regular file - ignored\n"), seed_file_name );
- close(fd);
- return 0;
- }
- if (!sb.st_size )
- {
- log_info(_("note: random_seed file is empty\n") );
- close(fd);
- allow_seed_file_update = 1;
- return 0;
- }
- if (sb.st_size != POOLSIZE )
- {
- log_info(_("warning: invalid size of random_seed file - not used\n") );
- close(fd);
- return 0;
- }
-
- do
- {
- n = read( fd, buffer, POOLSIZE );
- }
- while (n == -1 && errno == EINTR );
-
- if (n != POOLSIZE)
- {
- log_fatal(_("can't read `%s': %s\n"), seed_file_name,strerror(errno) );
- close(fd);/*NOTREACHED*/
- return 0;
- }
-
- close(fd);
-
- add_randomness( buffer, POOLSIZE, RANDOM_ORIGIN_INIT );
- /* add some minor entropy to the pool now (this will also force a mixing) */
- {
- pid_t x = getpid();
- add_randomness( &x, sizeof(x), RANDOM_ORIGIN_INIT );
- }
- {
- time_t x = time(NULL);
- add_randomness( &x, sizeof(x), RANDOM_ORIGIN_INIT );
- }
- {
- clock_t x = clock();
- add_randomness( &x, sizeof(x), RANDOM_ORIGIN_INIT );
- }
-
- /* And read a few bytes from our entropy source. By using a level
- * of 0 this will not block and might not return anything with some
- * entropy drivers, however the rndlinux driver will use
- * /dev/urandom and return some stuff - Do not read too much as we
- * want to be friendly to the scare system entropy resource. */
- read_random_source ( RANDOM_ORIGIN_INIT, 16, GCRY_WEAK_RANDOM );
-
- allow_seed_file_update = 1;
- return 1;
+ _gcry_rngcsprng_set_seed_file (name);
}
+/* If a seed file has been setup, this function may be used to write
+ back the random numbers entropy pool. */
void
-_gcry_update_random_seed_file()
-{
- unsigned long *sp, *dp;
- int fd, i;
-
- /* We do only a basic initialization so that we can lock the pool.
- This is required to cope with the case that this function is
- called by some cleanup code at a point where the RNG has never
- been initialized. */
- initialize_basics ();
- lock_pool ();
-
- if ( !seed_file_name || !rndpool || !pool_filled )
- {
- unlock_pool ();
- return;
- }
- if ( !allow_seed_file_update )
- {
- unlock_pool ();
- log_info(_("note: random_seed file not updated\n"));
- return;
- }
-
- /* At this point we know that there is something in the pool and
- thus we can conclude that the pool has been fully initialized. */
-
-
- /* Copy the entropy pool to a scratch pool and mix both of them. */
- for (i=0,dp=(unsigned long*)keypool, sp=(unsigned long*)rndpool;
- i < POOLWORDS; i++, dp++, sp++ )
- {
- *dp = *sp + ADD_VALUE;
- }
- mix_pool(rndpool); rndstats.mixrnd++;
- mix_pool(keypool); rndstats.mixkey++;
-
-#if defined(HAVE_DOSISH_SYSTEM) || defined(__CYGWIN__)
- fd = open (seed_file_name, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,
- S_IRUSR|S_IWUSR );
-#else
-# if LOCK_SEED_FILE
- fd = open (seed_file_name, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR );
-# else
- fd = open (seed_file_name, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR );
-# endif
-#endif
-
- if (fd == -1 )
- log_info (_("can't create `%s': %s\n"), seed_file_name, strerror(errno) );
- else if (lock_seed_file (fd, seed_file_name, 1))
- {
- close (fd);
- }
-#if LOCK_SEED_FILE
- else if (ftruncate (fd, 0))
- {
- log_info(_("can't write `%s': %s\n"), seed_file_name, strerror(errno));
- close (fd);
- }
-#endif /*LOCK_SEED_FILE*/
- else
- {
- do
- {
- i = write (fd, keypool, POOLSIZE );
- }
- while (i == -1 && errno == EINTR);
- if (i != POOLSIZE)
- log_info (_("can't write `%s': %s\n"),seed_file_name, strerror(errno));
- if (close(fd))
- log_info (_("can't close `%s': %s\n"),seed_file_name, strerror(errno));
- }
-
- unlock_pool ();
-}
-
-
-/* Read random out of the pool. This function is the core of the
- public random functions. Note that Level GCRY_WEAK_RANDOM is not
- anymore handled special and in fact is an alias in the API for
- level GCRY_STRONG_RANDOM. Must be called with the pool already
- locked. */
-static void
-read_pool (byte *buffer, size_t length, int level)
+_gcry_update_random_seed_file (void)
{
- int i;
- unsigned long *sp, *dp;
- /* The volatile is there to make sure the compiler does not optimize
- the code away in case the getpid function is badly attributed.
- Note that we keep a pid in a static variable as well as in a
- stack based one; the latter is to detect ill behaving thread
- libraries, ignoring the pool mutexes. */
- static volatile pid_t my_pid = (pid_t)(-1);
- volatile pid_t my_pid2;
-
- assert (pool_is_locked);
-
- retry:
- /* Get our own pid, so that we can detect a fork. */
- my_pid2 = getpid ();
- if (my_pid == (pid_t)(-1))
- my_pid = my_pid2;
- if ( my_pid != my_pid2 )
- {
- /* We detected a plain fork; i.e. we are now the child. Update
- the static pid and add some randomness. */
- pid_t x;
-
- my_pid = my_pid2;
- x = my_pid;
- add_randomness (&x, sizeof(x), RANDOM_ORIGIN_INIT);
- just_mixed = 0; /* Make sure it will get mixed. */
- }
-
- assert (pool_is_locked);
-
- /* Our code does not allow to extract more than POOLSIZE. Better
- check it here. */
- if (length > POOLSIZE)
- {
- log_bug("too many random bits requested\n");
- }
-
- if (!pool_filled)
- {
- if (read_seed_file() )
- pool_filled = 1;
- }
-
- /* For level 2 quality (key generation) we always make sure that the
- pool has been seeded enough initially. */
- if (level == GCRY_VERY_STRONG_RANDOM && !did_initial_extra_seeding)
- {
- size_t needed;
-
- pool_balance = 0;
- needed = length - pool_balance;
- if (needed < POOLSIZE/2)
- needed = POOLSIZE/2;
- else if( needed > POOLSIZE )
- BUG ();
- read_random_source (RANDOM_ORIGIN_EXTRAPOLL, needed,
- GCRY_VERY_STRONG_RANDOM);
- pool_balance += needed;
- did_initial_extra_seeding = 1;
- }
-
- /* For level 2 make sure that there is enough random in the pool. */
- if (level == GCRY_VERY_STRONG_RANDOM && pool_balance < length)
- {
- size_t needed;
-
- if (pool_balance < 0)
- pool_balance = 0;
- needed = length - pool_balance;
- if (needed > POOLSIZE)
- BUG ();
- read_random_source (RANDOM_ORIGIN_EXTRAPOLL, needed,
- GCRY_VERY_STRONG_RANDOM);
- pool_balance += needed;
- }
-
- /* Make sure the pool is filled. */
- while (!pool_filled)
- random_poll();
-
- /* Always do a fast random poll (we have to use the unlocked version). */
- do_fast_random_poll();
-
- /* Mix the pid in so that we for sure won't deliver the same random
- after a fork. */
- {
- pid_t apid = my_pid;
- add_randomness (&apid, sizeof (apid), RANDOM_ORIGIN_INIT);
- }
-
- /* Mix the pool (if add_randomness() didn't it). */
- if (!just_mixed)
- {
- mix_pool(rndpool);
- rndstats.mixrnd++;
- }
-
- /* Create a new pool. */
- for(i=0,dp=(unsigned long*)keypool, sp=(unsigned long*)rndpool;
- i < POOLWORDS; i++, dp++, sp++ )
- *dp = *sp + ADD_VALUE;
-
- /* Mix both pools. */
- mix_pool(rndpool); rndstats.mixrnd++;
- mix_pool(keypool); rndstats.mixkey++;
-
- /* Read the requested data. We use a read pointer to read from a
- different position each time. */
- while (length--)
- {
- *buffer++ = keypool[pool_readpos++];
- if (pool_readpos >= POOLSIZE)
- pool_readpos = 0;
- pool_balance--;
- }
-
- if (pool_balance < 0)
- pool_balance = 0;
-
- /* Clear the keypool. */
- memset (keypool, 0, POOLSIZE);
-
- /* We need to detect whether a fork has happened. A fork might have
- an identical pool and thus the child and the parent could emit
- the very same random number. This test here is to detect forks
- in a multi-threaded process. It does not work with all thread
- implementations in particular not with pthreads. However it is
- good enough for GNU Pth. */
- if ( getpid () != my_pid2 )
- {
- pid_t x = getpid();
- add_randomness (&x, sizeof(x), RANDOM_ORIGIN_INIT);
- just_mixed = 0; /* Make sure it will get mixed. */
- my_pid = x; /* Also update the static pid. */
- goto retry;
- }
-}
-
-
-
-/* Add LENGTH bytes of randomness from buffer to the pool. ORIGIN is
- used to specify the randomness origin. This is one of the
- RANDOM_ORIGIN_* values. */
-static void
-add_randomness (const void *buffer, size_t length, enum random_origins origin)
-{
- const unsigned char *p = buffer;
- size_t count = 0;
-
- assert (pool_is_locked);
+ if (!fips_is_operational ())
+ return;
- rndstats.addbytes += length;
- rndstats.naddbytes++;
- while (length-- )
- {
- rndpool[pool_writepos++] ^= *p++;
- count++;
- if (pool_writepos >= POOLSIZE )
- {
- /* It is possible that we are invoked before the pool is
- filled using an unreliable origin of entropy, for example
- the fast random poll. To avoid flagging the pool as
- filled in this case, we track the initial filling state
- separately. See also the remarks about the seed file. */
- if (origin >= RANDOM_ORIGIN_SLOWPOLL && !pool_filled)
- {
- pool_filled_counter += count;
- count = 0;
- if (pool_filled_counter >= POOLSIZE)
- pool_filled = 1;
- }
- pool_writepos = 0;
- mix_pool(rndpool); rndstats.mixrnd++;
- just_mixed = !length;
- }
- }
+ _gcry_rngcsprng_update_seed_file ();
}
-static void
-random_poll()
-{
- rndstats.slowpolls++;
- read_random_source (RANDOM_ORIGIN_SLOWPOLL, POOLSIZE/5, GCRY_STRONG_RANDOM);
-}
-
-
-/* Runtime determination of the slow entropy gathering module. */
-static int (*
-getfnc_gather_random (void))(void (*)(const void*, size_t,
- enum random_origins),
- enum random_origins, size_t, int)
-{
- int (*fnc)(void (*)(const void*, size_t, enum random_origins),
- enum random_origins, size_t, int);
-
-#if USE_RNDLINUX
- if ( !access (NAME_OF_DEV_RANDOM, R_OK)
- && !access (NAME_OF_DEV_URANDOM, R_OK))
- {
- fnc = _gcry_rndlinux_gather_random;
- return fnc;
- }
-#endif
-
-#if USE_RNDEGD
- if ( _gcry_rndegd_connect_socket (1) != -1 )
- {
- fnc = _gcry_rndegd_gather_random;
- return fnc;
- }
-#endif
-
-#if USE_RNDUNIX
- fnc = _gcry_rndunix_gather_random;
- return fnc;
-#endif
-
-#if USE_RNDW32
- fnc = _gcry_rndw32_gather_random;
- return fnc;
-#endif
-
- log_fatal (_("no entropy gathering module detected\n"));
-
- return NULL; /*NOTREACHED*/
-}
-
-/* Runtime determination of the fast entropy gathering function.
- (Currently a compile time method is used.) */
-static void (*
-getfnc_fast_random_poll (void))( void (*)(const void*, size_t,
- enum random_origins),
- enum random_origins)
-{
-#if USE_RNDW32
- return _gcry_rndw32_gather_random_fast;
-#endif
- return NULL;
-}
-
-
-
-static void
-do_fast_random_poll (void)
-{
- assert (pool_is_locked);
-
- rndstats.fastpolls++;
-
- if (fast_gather_fnc)
- fast_gather_fnc (add_randomness, RANDOM_ORIGIN_FASTPOLL);
-
- /* Continue with the generic functions. */
-#if HAVE_GETHRTIME
- {
- hrtime_t tv;
- tv = gethrtime();
- add_randomness( &tv, sizeof(tv), RANDOM_ORIGIN_FASTPOLL );
- }
-#elif HAVE_GETTIMEOFDAY
- {
- struct timeval tv;
- if( gettimeofday( &tv, NULL ) )
- BUG();
- add_randomness( &tv.tv_sec, sizeof(tv.tv_sec), RANDOM_ORIGIN_FASTPOLL );
- add_randomness( &tv.tv_usec, sizeof(tv.tv_usec), RANDOM_ORIGIN_FASTPOLL );
- }
-#elif HAVE_CLOCK_GETTIME
- { struct timespec tv;
- if( clock_gettime( CLOCK_REALTIME, &tv ) == -1 )
- BUG();
- add_randomness( &tv.tv_sec, sizeof(tv.tv_sec), RANDOM_ORIGIN_FASTPOLL );
- add_randomness( &tv.tv_nsec, sizeof(tv.tv_nsec), RANDOM_ORIGIN_FASTPOLL );
- }
-#else /* use times */
-# ifndef HAVE_DOSISH_SYSTEM
- { struct tms buf;
- times( &buf );
- add_randomness( &buf, sizeof buf, RANDOM_ORIGIN_FASTPOLL );
- }
-# endif
-#endif
-
-#ifdef HAVE_GETRUSAGE
-# ifdef RUSAGE_SELF
- {
- struct rusage buf;
- /* QNX/Neutrino does return ENOSYS - so we just ignore it and add
- whatever is in buf. In a chroot environment it might not work
- at all (i.e. because /proc/ is not accessible), so we better
- ignore all error codes and hope for the best. */
- getrusage (RUSAGE_SELF, &buf );
- add_randomness( &buf, sizeof buf, RANDOM_ORIGIN_FASTPOLL );
- memset( &buf, 0, sizeof buf );
- }
-# else /*!RUSAGE_SELF*/
-# ifdef __GCC__
-# warning There is no RUSAGE_SELF on this system
-# endif
-# endif /*!RUSAGE_SELF*/
-#endif /*HAVE_GETRUSAGE*/
-
- /* Time and clock are availabe on all systems - so we better do it
- just in case one of the above functions didn't work. */
- {
- time_t x = time(NULL);
- add_randomness( &x, sizeof(x), RANDOM_ORIGIN_FASTPOLL );
- }
- {
- clock_t x = clock();
- add_randomness( &x, sizeof(x), RANDOM_ORIGIN_FASTPOLL );
- }
-
- /* If the system features a fast hardware RNG, read some bytes from
- there. */
- _gcry_rndhw_poll_fast (add_randomness, RANDOM_ORIGIN_FASTPOLL);
-}
-
-
/* The fast random pool function as called at some places in
libgcrypt. This is merely a wrapper to make sure that this module
- is initalized and to look the pool. Note, that this function is a
+ is initalized and to lock the pool. Note, that this function is a
NOP unless a random function has been used or _gcry_initialize (1)
has been used. We use this hack so that the internal use of this
function in cipher_open and md_open won't start filling up the
@@ -1299,148 +245,18 @@ do_fast_random_poll (void)
void
_gcry_fast_random_poll (void)
{
- initialize_basics ();
-
- lock_pool ();
- if (rndpool)
- {
- /* Yes, we are fully initialized. */
- do_fast_random_poll ();
- }
- unlock_pool ();
-}
-
-
-
-static void
-read_random_source (enum random_origins orgin, size_t length, int level )
-{
- if ( !slow_gather_fnc )
- log_fatal ("Slow entropy gathering module not yet initialized\n");
+ if (!fips_is_operational ())
+ return;
- if ( slow_gather_fnc (add_randomness, orgin, length, level) < 0)
- log_fatal ("No way to gather entropy for the RNG\n");
+ _gcry_rngcsprng_fast_poll ();
}
-static int
-gather_faked (void (*add)(const void*, size_t, enum random_origins),
- enum random_origins origin, size_t length, int level )
-{
- static int initialized=0;
- size_t n;
- char *buffer, *p;
-
- (void)add;
- (void)level;
-
- if ( !initialized )
- {
- log_info(_("WARNING: using insecure random number generator!!\n"));
- initialized=1;
-#ifdef HAVE_RAND
- srand( time(NULL)*getpid());
-#else
- srandom( time(NULL)*getpid());
-#endif
- }
-
- p = buffer = gcry_xmalloc( length );
- n = length;
-#ifdef HAVE_RAND
- while ( n-- )
- *p++ = ((unsigned)(1 + (int) (256.0*rand()/(RAND_MAX+1.0)))-1);
-#else
- while ( n-- )
- *p++ = ((unsigned)(1 + (int) (256.0*random()/(RAND_MAX+1.0)))-1);
-#endif
- add_randomness ( buffer, length, origin );
- gcry_free (buffer);
- return 0; /* okay */
-}
-
/* Create an unpredicable nonce of LENGTH bytes in BUFFER. */
void
gcry_create_nonce (void *buffer, size_t length)
{
- static unsigned char nonce_buffer[20+8];
- static int nonce_buffer_initialized = 0;
- static volatile pid_t my_pid; /* The volatile is there to make sure the
- compiler does not optimize the code away
- in case the getpid function is badly
- attributed. */
- volatile pid_t apid;
- unsigned char *p;
- size_t n;
- int err;
-
- /* Make sure we are initialized. */
- initialize ();
-
-#ifdef USE_RANDOM_DAEMON
- if (allow_daemon
- && !_gcry_daemon_create_nonce (daemon_socket_name, buffer, length))
- return; /* The daemon succeeded. */
- allow_daemon = 0; /* Daemon failed - switch off. */
-#endif /*USE_RANDOM_DAEMON*/
-
- /* Acquire the nonce buffer lock. */
- err = ath_mutex_lock (&nonce_buffer_lock);
- if (err)
- log_fatal ("failed to acquire the nonce buffer lock: %s\n",
- strerror (err));
-
- apid = getpid ();
- /* The first time intialize our buffer. */
- if (!nonce_buffer_initialized)
- {
- time_t atime = time (NULL);
- pid_t xpid = apid;
-
- my_pid = apid;
-
- if ((sizeof apid + sizeof atime) > sizeof nonce_buffer)
- BUG ();
-
- /* Initialize the first 20 bytes with a reasonable value so that
- a failure of gcry_randomize won't affect us too much. Don't
- care about the uninitialized remaining bytes. */
- p = nonce_buffer;
- memcpy (p, &xpid, sizeof xpid);
- p += sizeof xpid;
- memcpy (p, &atime, sizeof atime);
-
- /* Initialize the never changing private part of 64 bits. */
- gcry_randomize (nonce_buffer+20, 8, GCRY_WEAK_RANDOM);
-
- nonce_buffer_initialized = 1;
- }
- else if ( my_pid != apid )
- {
- /* We forked. Need to reseed the buffer - doing this for the
- private part should be sufficient. */
- gcry_randomize (nonce_buffer+20, 8, GCRY_WEAK_RANDOM);
- /* Update the pid so that we won't run into here again and
- again. */
- my_pid = apid;
- }
-
- /* Create the nonce by hashing the entire buffer, returning the hash
- and updating the first 20 bytes of the buffer with this hash. */
- for (p = buffer; length > 0; length -= n, p += n)
- {
- _gcry_sha1_hash_buffer (nonce_buffer,
- nonce_buffer, sizeof nonce_buffer);
- n = length > 20? 20 : length;
- memcpy (p, nonce_buffer, n);
- }
-
-
- /* Release the nonce buffer lock. */
- err = ath_mutex_unlock (&nonce_buffer_lock);
- if (err)
- log_fatal ("failed to release the nonce buffer lock: %s\n",
- strerror (err));
-
+ _gcry_rngcsprng_create_nonce (buffer, length);
}
+