Patch: Stop using /dev/random

Scott Arciszewski scott at paragonie.com
Thu Nov 3 18:39:40 CET 2016


I'd like to propose this patch (or something similar) be applied to libgcrypt.

What libgcrypt does currently:

- Reads from /dev/random which blocks on Linux.

What the patch does instead:

- Polls /dev/random until it's available, then
- Reads from /dev/urandom (the non-blocking interface) instead.

Because the Linux kernel (which has the offending /dev/random
interface) will make sure /dev/urandom is seeded first, we can rely on
the availability of /dev/random signaling that /dev/urandom has been
seeded. This means we don't have to worry about racing against the
init process and generating insecure random numbers.

This gives us usability and performance (no more waiting for "entropy"
to gather) without sacrificing security.

Most of the code in this patch was lifted from libsodium, which
already does this. Libsodium is ISC Licensed (by Frank Denis).

Thank you for your time,

Scott Arciszewski
Chief Development Officer
Paragon Initiative Enterprises <https://paragonie.com>

The patch follows:

diff --git a/random/rndlinux.c b/random/rndlinux.c
index 2b563bf..59e7fc9 100644
--- a/random/rndlinux.c
+++ b/random/rndlinux.c
@@ -36,6 +36,7 @@
 # include <sys/syscall.h>
 #endif

+#include <poll.h>
 #include "types.h"
 #include "g10lib.h"
 #include "rand-internal.h"
@@ -66,6 +67,7 @@ static int
 open_device (const char *name, int retry)
 {
   int fd;
+  _gcry_block_on_dev_random();

   if (retry)
     _gcry_random_progress ("open_dev_random", 'X', 1, 0);
@@ -102,6 +104,32 @@ open_device (const char *name, int retry)
   return fd;
 }

+/* Block until /dev/random is available. This means that /dev/urandom
+   has already been seeded. */
+static int
+_gcry_block_on_dev_random(void)
+{
+    struct pollfd pfd;
+    int           fd;
+    int           pret;
+
+    fd = open("/dev/random", O_RDONLY);
+    if (fd == -1) {
+        return 0;
+    }
+    pfd.fd = fd;
+    pfd.events = POLLIN;
+    pfd.revents = 0;
+    do {
+        pret = poll(&pfd, 1, -1);
+    } while (pret < 0 && (errno == EINTR || errno == EAGAIN));
+    if (pret != 1) {
+        (void) close(fd);
+        errno = EIO;
+        return -1;
+    }
+    return close(fd);
+}

 /* Note that the caller needs to make sure that this function is only
    called by one thread at a time.  The function returns 0 on success
@@ -114,7 +142,6 @@ _gcry_rndlinux_gather_random (void (*add)(const
void*, size_t,
                               size_t length, int level )
 {
   static int fd_urandom = -1;
-  static int fd_random = -1;
   static unsigned char ever_opened;
   int fd;
   int n;
@@ -128,11 +155,6 @@ _gcry_rndlinux_gather_random (void (*add)(const
void*, size_t,
   if (!add)
     {
       /* Special mode to close the descriptors.  */
-      if (fd_random != -1)
-        {
-          close (fd_random);
-          fd_random = -1;
-        }
       if (fd_urandom != -1)
         {
           close (fd_urandom);
@@ -165,24 +187,12 @@ _gcry_rndlinux_gather_random (void (*add)(const
void*, size_t,
      that we always require the device to be existent but want a more
      graceful behaviour if the rarely needed close operation has been
      used and the device needs to be re-opened later. */
-  if (level >= 2)
-    {
-      if (fd_random == -1)
-        {
-          fd_random = open_device (NAME_OF_DEV_RANDOM, (ever_opened & 1));
-          ever_opened |= 1;
-        }
-      fd = fd_random;
-    }
-  else
-    {
       if (fd_urandom == -1)
         {
           fd_urandom = open_device (NAME_OF_DEV_URANDOM, (ever_opened & 2));
           ever_opened |= 2;
         }
       fd = fd_urandom;
-    }

   /* Enter the read loop.  */
   delay = 0;  /* Start with 0 seconds so that we do no block on the



More information about the Gcrypt-devel mailing list