diff options
Diffstat (limited to 'lib/randread.c')
-rw-r--r-- | lib/randread.c | 185 |
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; |