|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <config.h> |
|
|
|
|
|
#include "randread.h" |
|
|
|
|
|
#include <errno.h> |
|
|
#include <error.h> |
|
|
#include <exitfail.h> |
|
|
#include <fcntl.h> |
|
|
#include <quote.h> |
|
|
#include <stdint.h> |
|
|
#include <stdio.h> |
|
|
#include <stdlib.h> |
|
|
#include <string.h> |
|
|
#include <sys/random.h> |
|
|
|
|
|
#include "gettext.h" |
|
|
#define _(msgid) gettext (msgid) |
|
|
|
|
|
#include "assure.h" |
|
|
#include "minmax.h" |
|
|
#include "rand-isaac.h" |
|
|
#include "stdio-safer.h" |
|
|
#include "unlocked-io.h" |
|
|
#include "xalloc.h" |
|
|
|
|
|
#if _STRING_ARCH_unaligned || _STRING_INLINE_unaligned |
|
|
# define POINTER_IS_ALIGNED(ptr, type) true |
|
|
#else |
|
|
# define POINTER_IS_ALIGNED(ptr, type) ((size_t) (ptr) % alignof (type) == 0) |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define RANDREAD_BUFFER_SIZE (2 * ISAAC_BYTES) |
|
|
|
|
|
|
|
|
struct randread_source |
|
|
{ |
|
|
|
|
|
|
|
|
FILE *source; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void (*handler) (void const *); |
|
|
void const *handler_arg; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
union |
|
|
{ |
|
|
|
|
|
char c[RANDREAD_BUFFER_SIZE]; |
|
|
|
|
|
|
|
|
struct isaac |
|
|
{ |
|
|
|
|
|
size_t buffered; |
|
|
|
|
|
|
|
|
struct isaac_state state; |
|
|
|
|
|
|
|
|
union |
|
|
{ |
|
|
isaac_word w[ISAAC_WORDS]; |
|
|
unsigned char b[ISAAC_BYTES]; |
|
|
} data; |
|
|
} isaac; |
|
|
} buf; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
randread_error (void const *file_name) |
|
|
{ |
|
|
affirm (exit_failure); |
|
|
error (exit_failure, errno, |
|
|
errno == 0 ? _("%s: end of file") : _("%s: read error"), |
|
|
quote (file_name)); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static struct randread_source * |
|
|
simple_new (FILE *source, void const *handler_arg) |
|
|
{ |
|
|
struct randread_source *s = xmalloc (sizeof *s); |
|
|
s->source = source; |
|
|
s->handler = randread_error; |
|
|
s->handler_arg = handler_arg; |
|
|
return s; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool |
|
|
get_nonce (void *buffer, size_t bufsize) |
|
|
{ |
|
|
char *buf = buffer, *buflim = buf + bufsize; |
|
|
while (buf < buflim) |
|
|
{ |
|
|
#if defined __sun |
|
|
# define MAX_GETRANDOM 1024 |
|
|
#else |
|
|
# define MAX_GETRANDOM SIZE_MAX |
|
|
#endif |
|
|
size_t max_bytes = MIN (buflim - buf, MAX_GETRANDOM); |
|
|
ssize_t nbytes = getrandom (buf, max_bytes, 0); |
|
|
if (0 <= nbytes) |
|
|
buf += nbytes; |
|
|
else if (errno != EINTR) |
|
|
return false; |
|
|
} |
|
|
return true; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static int |
|
|
randread_free_body (struct randread_source *s) |
|
|
{ |
|
|
FILE *source = s->source; |
|
|
explicit_bzero (s, sizeof *s); |
|
|
free (s); |
|
|
return source ? fclose (source) : 0; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct randread_source * |
|
|
randread_new (char const *name, size_t bytes_bound) |
|
|
{ |
|
|
if (bytes_bound == 0) |
|
|
return simple_new (nullptr, nullptr); |
|
|
else |
|
|
{ |
|
|
FILE *source = nullptr; |
|
|
struct randread_source *s; |
|
|
|
|
|
if (name) |
|
|
if (! (source = fopen_safer (name, "rb"))) |
|
|
return nullptr; |
|
|
|
|
|
s = simple_new (source, name); |
|
|
|
|
|
if (source) |
|
|
setvbuf (source, s->buf.c, _IOFBF, MIN (sizeof s->buf.c, bytes_bound)); |
|
|
else |
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
s->buf.isaac.buffered = 0; |
|
|
if (! get_nonce (s->buf.isaac.state.m, |
|
|
sizeof s->buf.isaac.state.m)) |
|
|
{ |
|
|
int e = errno; |
|
|
randread_free_body (s); |
|
|
errno = e; |
|
|
return nullptr; |
|
|
} |
|
|
isaac_seed (&s->buf.isaac.state); |
|
|
} |
|
|
|
|
|
return s; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void |
|
|
randread_set_handler (struct randread_source *s, void (*handler) (void const *)) |
|
|
{ |
|
|
s->handler = handler; |
|
|
} |
|
|
|
|
|
void |
|
|
randread_set_handler_arg (struct randread_source *s, void const *handler_arg) |
|
|
{ |
|
|
s->handler_arg = handler_arg; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
readsource (struct randread_source *s, unsigned char *p, size_t size) |
|
|
{ |
|
|
while (true) |
|
|
{ |
|
|
size_t inbytes = fread (p, sizeof *p, size, s->source); |
|
|
int fread_errno = errno; |
|
|
p += inbytes; |
|
|
size -= inbytes; |
|
|
if (size == 0) |
|
|
break; |
|
|
errno = (ferror (s->source) ? fread_errno : 0); |
|
|
s->handler (s->handler_arg); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
readisaac (struct isaac *isaac, void *p, size_t size) |
|
|
{ |
|
|
size_t inbytes = isaac->buffered; |
|
|
|
|
|
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, inbytes); |
|
|
p = char_p + inbytes; |
|
|
size -= inbytes; |
|
|
|
|
|
|
|
|
|
|
|
if (POINTER_IS_ALIGNED (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; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void |
|
|
randread (struct randread_source *s, void *buf, size_t size) |
|
|
{ |
|
|
if (s->source) |
|
|
readsource (s, buf, size); |
|
|
else |
|
|
readisaac (&s->buf.isaac, buf, size); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int |
|
|
randread_free (struct randread_source *s) |
|
|
{ |
|
|
return randread_free_body (s); |
|
|
} |
|
|
|