[svn] gcry - r1103 - in branches/LIBGCRYPT-1-2-BRANCH: . cipher tests

svn author wk cvs at cvs.gnupg.org
Tue Sep 13 12:18:55 CEST 2005


Author: wk
Date: 2005-09-13 12:18:54 +0200 (Tue, 13 Sep 2005)
New Revision: 1103

Added:
   branches/LIBGCRYPT-1-2-BRANCH/tests/random.c
Modified:
   branches/LIBGCRYPT-1-2-BRANCH/NEWS
   branches/LIBGCRYPT-1-2-BRANCH/cipher/ChangeLog
   branches/LIBGCRYPT-1-2-BRANCH/cipher/random.c
   branches/LIBGCRYPT-1-2-BRANCH/tests/ChangeLog
   branches/LIBGCRYPT-1-2-BRANCH/tests/Makefile.am
Log:
* random.c (gcry_create_nonce): Detect a fork and re-seed.
(read_pool): Fixed the fork detection; it used to work only for
multi-threaded processes.



Modified: branches/LIBGCRYPT-1-2-BRANCH/NEWS
===================================================================
--- branches/LIBGCRYPT-1-2-BRANCH/NEWS	2005-08-22 09:30:25 UTC (rev 1102)
+++ branches/LIBGCRYPT-1-2-BRANCH/NEWS	2005-09-13 10:18:54 UTC (rev 1103)
@@ -1,6 +1,8 @@
 Noteworthy changes in version 1.2.2
 ------------------------------------------------
 
+ * Made the RNG immune against fork without exec.
+
  * Minor changes to some function declarations.  Buffer arguments are
    now typed as void pointer.  This should not affect any compilation.
    A bug in the definition of gcry_cipher_register has been fixed.

Modified: branches/LIBGCRYPT-1-2-BRANCH/cipher/ChangeLog
===================================================================
--- branches/LIBGCRYPT-1-2-BRANCH/cipher/ChangeLog	2005-08-22 09:30:25 UTC (rev 1102)
+++ branches/LIBGCRYPT-1-2-BRANCH/cipher/ChangeLog	2005-09-13 10:18:54 UTC (rev 1103)
@@ -1,3 +1,9 @@
+2005-09-13  Werner Koch  <wk at g10code.com>
+
+	* random.c (gcry_create_nonce): Detect a fork and re-seed.
+	(read_pool): Fixed the fork detection; it used to work only for
+	multi-threaded processes.
+
 2005-06-16  Werner Koch  <wk at g10code.com>
 
 	* cipher.c (gcry_cipher_register): Changed arg ALGORITHM_ID

Modified: branches/LIBGCRYPT-1-2-BRANCH/cipher/random.c
===================================================================
--- branches/LIBGCRYPT-1-2-BRANCH/cipher/random.c	2005-08-22 09:30:25 UTC (rev 1102)
+++ branches/LIBGCRYPT-1-2-BRANCH/cipher/random.c	2005-09-13 10:18:54 UTC (rev 1103)
@@ -1,6 +1,6 @@
 /* random.c  -	random number generator
  * Copyright (C) 1998, 2000, 2001, 2002, 2003,
- *               2004  Free Software Foundation, Inc.
+ *               2004, 2005  Free Software Foundation, Inc.
  *
  * This file is part of Libgcrypt.
  *
@@ -693,29 +693,46 @@
 
 
 /* Read random out of the pool. This function is the core of the
-   public random fucntions.  Note that Level 0 is not anymore handeld
-   special and in fact an alias for level 1. */
+   public random functions.  Note that Level 0 is special and in fact
+   an alias for level 1. */
 static void
 read_pool (byte *buffer, size_t length, int level)
 {
   int i;
   unsigned long *sp, *dp;
-  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. */
+  size_t n;
+  /* 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;
 
  retry:
   /* Get our own pid, so that we can detect a fork. */
-  my_pid = getpid ();
+  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), 0);
+      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");
+      log_bug("too many random bits requested (%lu)\n", (unsigned long)length);
     }
 
   if (!pool_filled)
@@ -787,7 +804,7 @@
 
   /* Read the required data.  We use a readpointer to read from a
      different position each time */
-  while (length--)
+  for (n=0; n < length; n++)
     {
       *buffer++ = keypool[pool_readpos++];
       if (pool_readpos >= POOLSIZE)
@@ -803,17 +820,14 @@
 
   /* 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.  Obviously this can only happen when
-     running multi-threaded and the pool lock should even catch this.
-     However things do get wrong and thus we better check and retry it
-     here.  We assume that the thread library has no other fatal
-     faults, though.
-   */
-  if ( getpid () != my_pid )
+     the very same random number.  This test here is to detect forks
+     in a multi-threaded process. */
+  if ( getpid () != my_pid2 )
     {
       pid_t x = getpid();
       add_randomness (&x, sizeof(x), 0);
       just_mixed = 0; /* Make sure it will get mixed. */
+      my_pid = x;     /* Also update the static pid. */
       goto retry;
     }
 }
@@ -1103,6 +1117,10 @@
 {
   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. */
   unsigned char *p;
   size_t n;
   int err;
@@ -1123,6 +1141,8 @@
       pid_t apid = getpid ();
       time_t atime = time (NULL);
 
+      my_pid = apid;
+
       if ((sizeof apid + sizeof atime) > sizeof nonce_buffer)
         BUG ();
 
@@ -1139,6 +1159,12 @@
 
       nonce_buffer_initialized = 1;
     }
+  else if ( my_pid != getpid () )
+    {
+      /* 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);
+    }
 
   /* Create the nonce by hashing the entire buffer, returning the hash
      and updating the first 20 bytes of the buffer with this hash. */

Modified: branches/LIBGCRYPT-1-2-BRANCH/tests/ChangeLog
===================================================================
--- branches/LIBGCRYPT-1-2-BRANCH/tests/ChangeLog	2005-08-22 09:30:25 UTC (rev 1102)
+++ branches/LIBGCRYPT-1-2-BRANCH/tests/ChangeLog	2005-09-13 10:18:54 UTC (rev 1103)
@@ -1,3 +1,7 @@
+2005-09-13  Werner Koch  <wk at g10code.com>
+
+	* random.c: New.
+
 2005-06-16  Werner Koch  <wk at g10code.com>
 
 	* basic.c (check_one_md): Made P unsigned.

Modified: branches/LIBGCRYPT-1-2-BRANCH/tests/Makefile.am
===================================================================
--- branches/LIBGCRYPT-1-2-BRANCH/tests/Makefile.am	2005-08-22 09:30:25 UTC (rev 1102)
+++ branches/LIBGCRYPT-1-2-BRANCH/tests/Makefile.am	2005-09-13 10:18:54 UTC (rev 1103)
@@ -18,7 +18,7 @@
 
 ## Process this file with automake to produce Makefile.in
 
-TESTS = prime register ac basic tsexp keygen pubkey benchmark
+TESTS = prime register ac basic tsexp keygen pubkey benchmark random
 
 INCLUDES = -I$(top_srcdir)/src
 LDADD = ../src/libgcrypt.la

Added: branches/LIBGCRYPT-1-2-BRANCH/tests/random.c
===================================================================
--- branches/LIBGCRYPT-1-2-BRANCH/tests/random.c	2005-08-22 09:30:25 UTC (rev 1102)
+++ branches/LIBGCRYPT-1-2-BRANCH/tests/random.c	2005-09-13 10:18:54 UTC (rev 1103)
@@ -0,0 +1,252 @@
+/* random.c - part of the Libgcrypt test suite.
+   Copyright (C) 2005 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+   USA.  */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include "../src/gcrypt.h"
+
+static int verbose;
+
+static void
+die (const char *format, ...)
+{
+  va_list arg_ptr;
+
+  va_start (arg_ptr, format);
+  vfprintf (stderr, format, arg_ptr);
+  va_end (arg_ptr);
+  exit (1);
+}
+
+
+static void
+print_hex (const char *text, const void *buf, size_t n)
+{
+  const unsigned char *p = buf;
+
+  fputs (text, stdout);
+  for (; n; n--, p++)
+    printf ("%02X", *p);
+  putchar ('\n');
+}
+
+
+static int
+writen (int fd, const void *buf, size_t nbytes)
+{
+  size_t nleft = nbytes;
+  int nwritten;
+  
+  while (nleft > 0)
+    {
+      nwritten = write (fd, buf, nleft);
+      if (nwritten < 0)
+        {
+          if (errno == EINTR)
+            nwritten = 0;
+          else 
+            return -1;
+        }
+      nleft -= nwritten;
+      buf = (const char*)buf + nwritten;
+    }
+    
+  return 0;
+}
+
+static int
+readn (int fd, void *buf, size_t buflen, size_t *ret_nread)
+{
+  size_t nleft = buflen;
+  int nread;
+  char *p;
+  
+  p = buf;
+  while ( nleft > 0 )
+    {
+      nread = read ( fd, buf, nleft );
+      if (nread < 0)
+        {
+          if (nread == EINTR)
+            nread = 0;
+          else 
+            return -1;
+        }
+      else if (!nread)
+        break; /* EOF */
+      nleft -= nread;
+      buf = (char*)buf + nread;
+    }
+  if (ret_nread)
+    *ret_nread = buflen - nleft;
+  return 0;
+}
+
+
+
+/* Check that forking won't return the same random. */
+static void
+check_forking (void)
+{
+  pid_t pid;
+  int rp[2];
+  int i, status;
+  size_t nread;
+  char tmp1[16], tmp1c[16], tmp1p[16];
+  
+  /* We better make sure that the RNG has been initialzied. */
+  gcry_randomize (tmp1, sizeof tmp1, GCRY_STRONG_RANDOM);
+  if (verbose)
+    print_hex ("initial random: ", tmp1, sizeof tmp1);
+
+  if (pipe (rp) == -1)
+    die ("pipe failed: %s\n", strerror (errno));
+
+  pid = fork ();
+  if (pid == (pid_t)(-1))
+    die ("fork failed: %s\n", strerror (errno));
+  if (!pid)
+    {
+      gcry_randomize (tmp1c, sizeof tmp1c, GCRY_STRONG_RANDOM);
+      if (writen (rp[1], tmp1c, sizeof tmp1c))
+        die ("write failed: %s\n", strerror (errno));
+      if (verbose)
+        {
+          print_hex ("  child random: ", tmp1c, sizeof tmp1c);
+          fflush (stdout);
+        }
+      _exit (0);
+    }
+  gcry_randomize (tmp1p, sizeof tmp1p, GCRY_STRONG_RANDOM);
+  if (verbose)
+    print_hex (" parent random: ", tmp1p, sizeof tmp1p);
+
+  close (rp[1]);
+  if (readn (rp[0], tmp1c, sizeof tmp1c, &nread))
+    die ("read failed: %s\n", strerror (errno)); 
+  if (nread != sizeof tmp1c)
+    die ("read too short\n");
+
+  while ( (i=waitpid (pid, &status, 0)) == -1 && errno == EINTR)
+    ;
+  if (i != (pid_t)(-1)
+      && WIFEXITED (status) && !WEXITSTATUS (status))
+    ;
+  else
+    die ("child failed\n");
+
+  if (!memcmp (tmp1p, tmp1c, sizeof tmp1c))
+    die ("parent and child got the same random number\n");
+}
+
+
+
+/* Check that forking won't return the same nonce. */
+static void
+check_nonce_forking (void)
+{
+  pid_t pid;
+  int rp[2];
+  int i, status;
+  size_t nread;
+  char nonce1[10], nonce1c[10], nonce1p[10];
+  
+  /* We won't get the same nonce back if we never initialized the
+     nonce subsystem, thus we get one nonce here and forget about
+     it. */
+  gcry_create_nonce (nonce1, sizeof nonce1);
+  if (verbose)
+    print_hex ("initial nonce: ", nonce1, sizeof nonce1);
+
+  if (pipe (rp) == -1)
+    die ("pipe failed: %s\n", strerror (errno));
+
+  pid = fork ();
+  if (pid == (pid_t)(-1))
+    die ("fork failed: %s\n", strerror (errno));
+  if (!pid)
+    {
+      gcry_create_nonce (nonce1c, sizeof nonce1c);
+      if (writen (rp[1], nonce1c, sizeof nonce1c))
+        die ("write failed: %s\n", strerror (errno));
+      if (verbose)
+        {
+          print_hex ("  child nonce: ", nonce1c, sizeof nonce1c);
+          fflush (stdout);
+        }
+      _exit (0);
+    }
+  gcry_create_nonce (nonce1p, sizeof nonce1p);
+  if (verbose)
+    print_hex (" parent nonce: ", nonce1p, sizeof nonce1p);
+
+  close (rp[1]);
+  if (readn (rp[0], nonce1c, sizeof nonce1c, &nread))
+    die ("read failed: %s\n", strerror (errno)); 
+  if (nread != sizeof nonce1c)
+    die ("read too short\n");
+
+  while ( (i=waitpid (pid, &status, 0)) == -1 && errno == EINTR)
+    ;
+  if (i != (pid_t)(-1)
+      && WIFEXITED (status) && !WEXITSTATUS (status))
+    ;
+  else
+    die ("child failed\n");
+
+  if (!memcmp (nonce1p, nonce1c, sizeof nonce1c))
+    die ("parent and child got the same nonce\n");
+}
+
+
+
+
+
+
+int
+main (int argc, char **argv)
+{
+  int debug = 0;
+
+  if ((argc > 1) && (! strcmp (argv[1], "--verbose")))
+    verbose = 1;
+  else if ((argc > 1) && (! strcmp (argv[1], "--debug")))
+    verbose = debug = 1;
+
+  signal (SIGPIPE, SIG_IGN);
+
+  gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
+  if (!gcry_check_version (GCRYPT_VERSION))
+    die ("version mismatch\n");
+
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+  if (debug)
+    gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
+
+  check_forking ();
+  check_nonce_forking ();
+
+  return 0;
+}




More information about the Gnupg-commits mailing list