Replace ath based mutexes by gpgrt based locks.
[libgcrypt.git] / random / random.c
index ff7c280..41d4cb3 100644 (file)
@@ -1,5 +1,5 @@
 /* random.c - Random number switch
- * Copyright (C) 2008  Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2006, 2008, 2012  Free Software Foundation, Inc.
  *
  * This file is part of Libgcrypt.
  *
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
+#include <time.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include "g10lib.h"
 #include "random.h"
 #include "rand-internal.h"
-#include "ath.h"
+#include "cipher.h"         /* For _gcry_sha1_hash_buffer().  */
 
 
 /* If not NULL a progress function called from certain places and the
-   opaque value passed along.  Registred by
+   opaque value passed along.  Registered by
    _gcry_register_random_progress (). */
 static void (*progress_cb) (void *,const char*,int,int, int );
 static void *progress_cb_data;
 
+/* Flags indicating the requested RNG types.  */
+static struct
+{
+  int standard;
+  int fips;
+  int system;
+} rng_types;
+
+
+/* This is the lock we use to protect the buffer used by the nonce
+   generation.  */
+GPGRT_LOCK_DEFINE (nonce_buffer_lock);
 
 
 \f
@@ -66,6 +81,55 @@ _gcry_random_progress (const char *what, int printchar, int current, int total)
 }
 
 
+/* Set the preferred RNG type.  This may be called at any time even
+   before gcry_check_version.  Thus we can't assume any thread system
+   initialization.  A type of 0 is used to indicate that any Libgcrypt
+   initialization has been done.*/
+void
+_gcry_set_preferred_rng_type (int type)
+{
+  static int any_init;
+
+  if (!type)
+    {
+      any_init = 1;
+    }
+  else if (type == GCRY_RNG_TYPE_STANDARD)
+    {
+      rng_types.standard = 1;
+    }
+  else if (any_init)
+    {
+      /* After any initialization has been done we only allow to
+         upgrade to the standard RNG (handled above).  All other
+         requests are ignored.  The idea is that the application needs
+         to declare a preference for a weaker RNG as soon as possible
+         and before any library sets a preference.  We assume that a
+         library which uses Libgcrypt calls an init function very
+         early.  This way --- even if the library gets initialized
+         early by the application --- it is unlikely that it can
+         select a lower priority RNG.
+
+         This scheme helps to ensure that existing unmodified
+         applications (e.g. gpg2), which don't known about the new RNG
+         selection system, will continue to use the standard RNG and
+         not be tricked by some library to use a lower priority RNG.
+         There are some loopholes here but at least most GnuPG stuff
+         should be save because it calls src_c{gcry_control
+         (GCRYCTL_SUSPEND_SECMEM_WARN);} quite early and thus inhibits
+         switching to a low priority RNG.
+       */
+    }
+  else if (type == GCRY_RNG_TYPE_FIPS)
+    {
+      rng_types.fips = 1;
+    }
+  else if (type == GCRY_RNG_TYPE_SYSTEM)
+    {
+      rng_types.system = 1;
+    }
+}
+
 
 /* Initialize this random subsystem.  If FULL is false, this function
    merely calls the basic initialization of the module and does not do
@@ -75,32 +139,82 @@ _gcry_random_progress (const char *what, int printchar, int current, int total)
 void
 _gcry_random_initialize (int full)
 {
-  if ( fips_mode() )
+  if (fips_mode ())
     _gcry_rngfips_initialize (full);
+  else if (rng_types.standard)
+    _gcry_rngcsprng_initialize (full);
+  else if (rng_types.fips)
+    _gcry_rngfips_initialize (full);
+  else if (rng_types.system)
+    _gcry_rngsystem_initialize (full);
   else
     _gcry_rngcsprng_initialize (full);
 }
 
 
+/* If possible close file descriptors used by the RNG. */
+void
+_gcry_random_close_fds (void)
+{
+  /* Note that we can't do that directly because each random system
+     has its own lock functions which need to be used for accessing
+     the entropy gatherer.  */
+
+  if (fips_mode ())
+    _gcry_rngfips_close_fds ();
+  else if (rng_types.standard)
+    _gcry_rngcsprng_close_fds ();
+  else if (rng_types.fips)
+    _gcry_rngfips_close_fds ();
+  else if (rng_types.system)
+    _gcry_rngsystem_close_fds ();
+  else
+    _gcry_rngcsprng_close_fds ();
+}
+
+
+/* Return the current RNG type.  IGNORE_FIPS_MODE is a flag used to
+   skip the test for FIPS.  This is useful, so that we are able to
+   return the type of the RNG even before we have setup FIPS mode
+   (note that FIPS mode is enabled by default until it is switched off
+   by the initialization).  This is mostly useful for the regression
+   test.  */
+int
+_gcry_get_rng_type (int ignore_fips_mode)
+{
+  if (!ignore_fips_mode && fips_mode ())
+    return GCRY_RNG_TYPE_FIPS;
+  else if (rng_types.standard)
+    return GCRY_RNG_TYPE_STANDARD;
+  else if (rng_types.fips)
+    return GCRY_RNG_TYPE_FIPS;
+  else if (rng_types.system)
+    return GCRY_RNG_TYPE_SYSTEM;
+  else
+    return GCRY_RNG_TYPE_STANDARD;
+}
+
+
 void
 _gcry_random_dump_stats (void)
 {
-  if ( fips_mode() )
+  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 place the random pools into secure
+/* This function should be called during initialization and before
+   initialization of this module to place the random pools into secure
    memory.  */
 void
 _gcry_secure_random_alloc (void)
 {
-  if ( fips_mode() )
-    return;  /* Not used; the fips rng is allows in secure mode.  */
-  _gcry_rngcsprng_secure_alloc ();
+  if (fips_mode ())
+    ;  /* Not used; the FIPS RNG is always in secure mode.  */
+  else
+    _gcry_rngcsprng_secure_alloc ();
 }
 
 
@@ -109,18 +223,20 @@ _gcry_secure_random_alloc (void)
 void
 _gcry_enable_quick_random_gen (void)
 {
-  if ( fips_mode() )
-    return;  /* Not used.  */
-  _gcry_rngcsprng_enable_quick_gen ();
+  if (fips_mode ())
+    ;  /* Not used.  */
+  else
+    _gcry_rngcsprng_enable_quick_gen ();
 }
 
 
 void
 _gcry_set_random_daemon_socket (const char *socketname)
 {
-  if ( fips_mode() )
-    return;  /* Not used.  */
-  _gcry_rngcsprng_set_daemon_socket (socketname);
+  if (fips_mode ())
+    ;  /* Not used.  */
+  else
+    _gcry_rngcsprng_set_daemon_socket (socketname);
 }
 
 /* With ONOFF set to 1, enable the use of the daemon.  With ONOFF set
@@ -131,7 +247,8 @@ _gcry_use_random_daemon (int onoff)
 {
   if (fips_mode ())
     return 0; /* Never enabled in fips mode.  */
-  return _gcry_rngcsprng_use_daemon (onoff);
+  else
+    return _gcry_rngcsprng_use_daemon (onoff);
 }
 
 
@@ -141,30 +258,44 @@ int
 _gcry_random_is_faked (void)
 {
   if (fips_mode ())
-    return 0; /* Never faked in fips mode.  */
-  return _gcry_rngcsprng_is_faked ();
+    return _gcry_rngfips_is_faked ();
+  else
+    return _gcry_rngcsprng_is_faked ();
 }
 
 
 /* Add BUFLEN bytes from BUF to the internal random pool.  QUALITY
    should be in the range of 0..100 to indicate the goodness of the
    entropy added, or -1 for goodness not known.  */
-gcry_error_t
-gcry_random_add_bytes (const void *buf, size_t buflen, int quality)
+gcry_err_code_t
+_gcry_random_add_bytes (const void *buf, size_t buflen, int quality)
 {
   if (fips_mode ())
     return 0; /* No need for this in fips mode.  */
-  return _gcry_rngcsprng_add_bytes (buf, buflen, quality);
-}   
+  else if (rng_types.standard)
+    return gpg_err_code (_gcry_rngcsprng_add_bytes (buf, buflen, quality));
+  else if (rng_types.fips)
+    return 0;
+  else if (rng_types.system)
+    return 0;
+  else /* default */
+    return gpg_err_code (_gcry_rngcsprng_add_bytes (buf, buflen, quality));
+}
+
 
-  
-/* Helper function.  */ 
+/* 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
+  else if (rng_types.standard)
+    _gcry_rngcsprng_randomize (buffer, length, level);
+  else if (rng_types.fips)
+    _gcry_rngfips_randomize (buffer, length, level);
+  else if (rng_types.system)
+    _gcry_rngsystem_randomize (buffer, length, level);
+  else /* default */
     _gcry_rngcsprng_randomize (buffer, length, level);
 }
 
@@ -172,11 +303,11 @@ do_randomize (void *buffer, size_t length, enum gcry_random_level level)
    Returns a pointer to a newly allocated and randomized buffer of
    LEVEL and NBYTES length.  Caller must free the buffer.  */
 void *
-gcry_random_bytes (size_t nbytes, enum gcry_random_level level)
+_gcry_random_bytes (size_t nbytes, enum gcry_random_level level)
 {
   void *buffer;
 
-  buffer = gcry_xmalloc (nbytes);
+  buffer = xmalloc (nbytes);
   do_randomize (buffer, nbytes, level);
   return buffer;
 }
@@ -186,14 +317,14 @@ 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;
 
   /* 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);
+  buffer = xmalloc_secure (nbytes);
   do_randomize (buffer, nbytes, level);
   return buffer;
 }
@@ -205,19 +336,28 @@ gcry_random_bytes_secure (size_t nbytes, enum gcry_random_level level)
    usage, GCRY_VERY_STRONG_RANDOM is good for key generation stuff but
    may be very slow.  */
 void
-gcry_randomize (void *buffer, size_t length, enum gcry_random_level level)
+_gcry_randomize (void *buffer, size_t length, enum gcry_random_level level)
 {
   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
+   file for the PRNG.  This function 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_rngcsprng_set_seed_file (name);
+  if (fips_mode ())
+    ; /* No need for this in fips mode.  */
+  else if (rng_types.standard)
+    _gcry_rngcsprng_set_seed_file (name);
+  else if (rng_types.fips)
+    ;
+  else if (rng_types.system)
+    ;
+  else /* default */
+    _gcry_rngcsprng_set_seed_file (name);
 }
 
 
@@ -226,17 +366,23 @@ _gcry_set_random_seed_file (const char *name)
 void
 _gcry_update_random_seed_file (void)
 {
-  if (!fips_is_operational ())  /* FIXME:  This does no look correct.  */
-    return;
-
-  _gcry_rngcsprng_update_seed_file ();
+  if (fips_mode ())
+    ; /* No need for this in fips mode.  */
+  else if (rng_types.standard)
+    _gcry_rngcsprng_update_seed_file ();
+  else if (rng_types.fips)
+    ;
+  else if (rng_types.system)
+    ;
+  else /* default */
+    _gcry_rngcsprng_update_seed_file ();
 }
 
 
 
 /* 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 lock the pool.  Note, that this function is a
+   is initialized 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
@@ -244,18 +390,157 @@ _gcry_update_random_seed_file (void)
 void
 _gcry_fast_random_poll (void)
 {
-  if (!fips_is_operational ())
-    return;
-
-  _gcry_rngcsprng_fast_poll ();
+  if (fips_mode ())
+    ; /* No need for this in fips mode.  */
+  else if (rng_types.standard)
+    _gcry_rngcsprng_fast_poll ();
+  else if (rng_types.fips)
+    ;
+  else if (rng_types.system)
+    ;
+  else /* default */
+    _gcry_rngcsprng_fast_poll ();
 }
 
 
 
 /* Create an unpredicable nonce of LENGTH bytes in BUFFER. */
 void
-gcry_create_nonce (void *buffer, size_t length)
+_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;
+
+  /* First check whether we shall use the FIPS nonce generator.  This
+     is only done in FIPS mode, in all other modes, we use our own
+     nonce generator which is seeded by the RNG actual in use.  */
+  if (fips_mode ())
+    {
+      _gcry_rngfips_create_nonce (buffer, length);
+      return;
+    }
+
+  /* This is the nonce generator, which formerly lived in
+     random-csprng.c.  It is now used by all RNG types except when in
+     FIPS mode (not that this means it is also used if the FIPS RNG
+     has been selected but we are not in fips mode).  */
+
+  /* Make sure we are initialized. */
+  _gcry_random_initialize (1);
+
+  /* Acquire the nonce buffer lock. */
+  err = gpgrt_lock_lock (&nonce_buffer_lock);
+  if (err)
+    log_fatal ("failed to acquire the nonce buffer lock: %s\n",
+               gpg_strerror (err));
+
+  apid = getpid ();
+  /* The first time initialize 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. */
+      do_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 = gpgrt_lock_unlock (&nonce_buffer_lock);
+  if (err)
+    log_fatal ("failed to release the nonce buffer lock: %s\n",
+               gpg_strerror (err));
+}
+
+
+/* Run the self-tests for the RNG.  This is currently only implemented
+   for the FIPS generator.  */
+gpg_error_t
+_gcry_random_selftest (selftest_report_func_t report)
+{
+  if (fips_mode ())
+    return _gcry_rngfips_selftest (report);
+  else
+    return 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, flags, 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)
 {
-  _gcry_rngcsprng_create_nonce (buffer, length);
+  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 ())
+    _gcry_rngfips_deinit_external_test (context);
+}