diff options
author | Werner Koch <wk@gnupg.org> | 2008-08-19 15:55:46 +0000 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2008-08-19 15:55:46 +0000 |
commit | e77613e7b89ee150ae9e945029f11b04744a6fd3 (patch) | |
tree | bc41644fb0e68f6d353e640eb77a3b7e8b8c4d3e /random/random.c | |
parent | b202832f13ca0ecce65767e4448e7e91eda1214e (diff) | |
download | libgcrypt-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.c | 1342 |
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); } + |