summaryrefslogtreecommitdiff
path: root/lib/randread.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/randread.c')
-rw-r--r--lib/randread.c185
1 files changed, 117 insertions, 68 deletions
diff --git a/lib/randread.c b/lib/randread.c
index 5462d44..ff85d56 100644
--- a/lib/randread.c
+++ b/lib/randread.c
@@ -1,11 +1,11 @@
/* Generate buffers of random data.
- Copyright (C) 2006 Free Software Foundation, Inc.
+ Copyright (C) 2006-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ 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, or (at your option)
- any later version.
+ the Free Software Foundation, either version 3 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
@@ -13,11 +13,13 @@
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. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Paul Eggert. */
+/* FIXME: Improve performance by adding support for the RDRAND machine
+ instruction if available (e.g., Ivy Bridge processors). */
+
#include <config.h>
#include "randread.h"
@@ -25,12 +27,16 @@
#include <errno.h>
#include <error.h>
#include <exitfail.h>
-#include <quotearg.h>
+#include <fcntl.h>
+#include <quote.h>
+#include <stdalign.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/time.h>
+#include <unistd.h>
#include "gettext.h"
#define _(msgid) gettext (msgid)
@@ -40,29 +46,28 @@
#include "unlocked-io.h"
#include "xalloc.h"
-#ifndef MIN
-# define MIN(a, b) ((a) < (b) ? (a) : (b))
-#endif
-
#ifndef __attribute__
-# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
-# define __attribute__(x)
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8)
+# define __attribute__(x) /* empty */
# endif
#endif
-#ifndef ATTRIBUTE_UNUSED
-# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+#ifndef ATTRIBUTE_NORETURN
+# define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__))
+#endif
+
+#ifndef MIN
+# define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
#if _STRING_ARCH_unaligned
# define ALIGNED_POINTER(ptr, type) true
#else
-# define alignof(type) offsetof (struct { char c; type x; }, x)
# define ALIGNED_POINTER(ptr, type) ((size_t) (ptr) % alignof (type) == 0)
#endif
-#ifndef DEFAULT_RANDOM_FILE
-# define DEFAULT_RANDOM_FILE "/dev/urandom"
+#ifndef NAME_OF_NONCE_DEVICE
+# define NAME_OF_NONCE_DEVICE "/dev/urandom"
#endif
/* The maximum buffer size used for reads of random data. Using the
@@ -73,10 +78,8 @@
/* A source of random data for generating random buffers. */
struct randread_source
{
- /* Stream to read random bytes from. If null, the behavior is
- undefined; the current implementation uses ISAAC in this case,
- but this is for old-fashioned implementations that lack
- /dev/urandom and callers should not rely on this. */
+ /* Stream to read random bytes from. If null, the current
+ implementation uses an internal PRNG (ISAAC). */
FILE *source;
/* Function to call, and its argument, if there is an input error or
@@ -107,8 +110,8 @@ struct randread_source
/* Up to a buffer's worth of pseudorandom data. */
union
{
- uint32_t w[ISAAC_WORDS];
- unsigned char b[ISAAC_BYTES];
+ isaac_word w[ISAAC_WORDS];
+ unsigned char b[ISAAC_BYTES];
} data;
} isaac;
} buf;
@@ -117,13 +120,13 @@ struct randread_source
/* The default error handler. */
-static void
+static void ATTRIBUTE_NORETURN
randread_error (void const *file_name)
{
if (file_name)
error (exit_failure, errno,
- _(errno == 0 ? "%s: end of file" : "%s: read error"),
- quotearg_colon (file_name));
+ errno == 0 ? _("%s: end of file") : _("%s: read error"),
+ quote (file_name));
abort ();
}
@@ -140,6 +143,52 @@ simple_new (FILE *source, void const *handler_arg)
return s;
}
+/* Put a nonce value into BUFFER, with size BUFSIZE, but do not get
+ more than BYTES_BOUND bytes' worth of random information from any
+ nonce device. */
+
+static void
+get_nonce (void *buffer, size_t bufsize, size_t bytes_bound)
+{
+ char *buf = buffer;
+ ssize_t seeded = 0;
+
+ /* Get some data from FD if available. */
+ int fd = open (NAME_OF_NONCE_DEVICE, O_RDONLY | O_BINARY);
+ if (0 <= fd)
+ {
+ seeded = read (fd, buf, MIN (bufsize, bytes_bound));
+ if (seeded < 0)
+ seeded = 0;
+ close (fd);
+ }
+
+ /* If there's no nonce device, use a poor approximation
+ by getting the time of day, etc. */
+#define ISAAC_SEED(type, initialize_v) \
+ if (seeded < bufsize) \
+ { \
+ type v; \
+ size_t nbytes = MIN (sizeof v, bufsize - seeded); \
+ initialize_v; \
+ memcpy (buf + seeded, &v, nbytes); \
+ seeded += nbytes; \
+ }
+ ISAAC_SEED (struct timeval, gettimeofday (&v, NULL));
+ ISAAC_SEED (pid_t, v = getpid ());
+ ISAAC_SEED (pid_t, v = getppid ());
+ ISAAC_SEED (uid_t, v = getuid ());
+ ISAAC_SEED (uid_t, v = getgid ());
+
+#ifdef lint
+ /* Normally we like having the extra randomness from uninitialized
+ parts of BUFFER. However, omit this randomness if we want to
+ avoid false-positives from memory-checking debugging tools. */
+ memset (buf + seeded, 0, bufsize - seeded);
+#endif
+}
+
+
/* Create and initialize a random data source from NAME, or use a
reasonable default source if NAME is null. BYTES_BOUND is an upper
bound on the number of bytes that will be needed. If zero, it is a
@@ -158,26 +207,24 @@ randread_new (char const *name, size_t bytes_bound)
return simple_new (NULL, NULL);
else
{
- char const *file_name = (name ? name : DEFAULT_RANDOM_FILE);
- FILE *source = fopen_safer (file_name, "rb");
+ FILE *source = NULL;
struct randread_source *s;
- if (! source)
- {
- if (name)
- return NULL;
- file_name = NULL;
- }
+ if (name)
+ if (! (source = fopen_safer (name, "rb")))
+ return NULL;
- s = simple_new (source, file_name);
+ s = simple_new (source, name);
if (source)
- setvbuf (source, s->buf.c, _IOFBF, MIN (sizeof s->buf.c, bytes_bound));
+ setvbuf (source, s->buf.c, _IOFBF, MIN (sizeof s->buf.c, bytes_bound));
else
- {
- s->buf.isaac.buffered = 0;
- isaac_seed (&s->buf.isaac.state);
- }
+ {
+ s->buf.isaac.buffered = 0;
+ get_nonce (s->buf.isaac.state.m, sizeof s->buf.isaac.state.m,
+ bytes_bound);
+ isaac_seed (&s->buf.isaac.state);
+ }
return s;
}
@@ -210,14 +257,14 @@ randread_set_handler_arg (struct randread_source *s, void const *handler_arg)
static void
readsource (struct randread_source *s, unsigned char *p, size_t size)
{
- for (;;)
+ while (true)
{
size_t inbytes = fread (p, sizeof *p, size, s->source);
int fread_errno = errno;
p += inbytes;
size -= inbytes;
if (size == 0)
- break;
+ break;
errno = (ferror (s->source) ? fread_errno : 0);
s->handler (s->handler_arg);
}
@@ -228,41 +275,43 @@ readsource (struct randread_source *s, unsigned char *p, size_t size)
the buffered ISAAC generator in ISAAC. */
static void
-readisaac (struct isaac *isaac, unsigned char *p, size_t size)
+readisaac (struct isaac *isaac, void *p, size_t size)
{
size_t inbytes = isaac->buffered;
- for (;;)
+ while (true)
{
+ char *char_p = p;
+
if (size <= inbytes)
- {
- memcpy (p, isaac->data.b + ISAAC_BYTES - inbytes, size);
- isaac->buffered = inbytes - size;
- return;
- }
+ {
+ memcpy (p, isaac->data.b + ISAAC_BYTES - inbytes, size);
+ isaac->buffered = inbytes - size;
+ return;
+ }
memcpy (p, isaac->data.b + ISAAC_BYTES - inbytes, inbytes);
- p += inbytes;
+ p = char_p + inbytes;
size -= inbytes;
/* If P is aligned, write to *P directly to avoid the overhead
- of copying from the buffer. */
- if (ALIGNED_POINTER (p, uint32_t))
- {
- uint32_t *wp = (uint32_t *) p;
- while (ISAAC_BYTES <= size)
- {
- isaac_refill (&isaac->state, wp);
- wp += ISAAC_WORDS;
- size -= ISAAC_BYTES;
- if (size == 0)
- {
- isaac->buffered = 0;
- return;
- }
- }
- p = (unsigned char *) wp;
- }
+ of copying from the buffer. */
+ if (ALIGNED_POINTER (p, isaac_word))
+ {
+ isaac_word *wp = p;
+ while (ISAAC_BYTES <= size)
+ {
+ isaac_refill (&isaac->state, wp);
+ wp += ISAAC_WORDS;
+ size -= ISAAC_BYTES;
+ if (size == 0)
+ {
+ isaac->buffered = 0;
+ return;
+ }
+ }
+ p = wp;
+ }
isaac_refill (&isaac->state, isaac->data.w);
inbytes = ISAAC_BYTES;