diff options
Diffstat (limited to 'src/util/ldseed.c')
-rw-r--r-- | src/util/ldseed.c | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/src/util/ldseed.c b/src/util/ldseed.c new file mode 100644 index 0000000..c231152 --- /dev/null +++ b/src/util/ldseed.c @@ -0,0 +1,138 @@ +/*++ +/* NAME +/* ldseed 3 +/* SUMMARY +/* seed for non-cryptographic applications +/* SYNOPSIS +/* #include <ldseed.h> +/* +/* void ldseed( +/* void *dst, +/* size_t len) +/* DESCRIPTION +/* ldseed() preferably extracts pseudo-random bits from +/* /dev/urandom, a non-blocking device that is available on +/* modern systems. +/* +/* On systems where /dev/urandom is unavailable or does not +/* immediately return the requested amount of randomness, +/* ldseed() falls back to a combination of wallclock time, +/* the time since boot, and the process ID. +/* BUGS +/* With Linux "the O_NONBLOCK flag has no effect when opening +/* /dev/urandom", but reads "can incur an appreciable delay +/* when requesting large amounts of data". Apparently, "large" +/* means more than 256 bytes. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library + */ +#include <sys_defs.h> +#include <string.h> +#include <sys/time.h> +#include <time.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <limits.h> /* CHAR_BIT */ + + /* + * Utility library. + */ +#include <iostuff.h> +#include <msg.h> +#include <ldseed.h> + + /* + * Different systems have different names for non-wallclock time. + */ +#ifdef CLOCK_UPTIME +#define NON_WALLTIME_CLOCK CLOCK_UPTIME +#elif defined(CLOCK_BOOTTIME) +#define NON_WALLTIME_CLOCK CLOCK_BOOTTIME +#elif defined(CLOCK_MONOTONIC) +#define NON_WALLTIME_CLOCK CLOCK_MONOTONIC +#elif defined(CLOCK_HIGHRES) +#define NON_WALLTIME_CLOCK CLOCK_HIGHRES +#endif + +/* ldseed - best-effort, low-dependency seed */ + +void ldseed(void *dst, size_t len) +{ + int count; + int fd; + int n; + time_t fallback = 0; + + /* + * Medium-quality seed. + */ + if ((fd = open("/dev/urandom", O_RDONLY)) > 0) { + non_blocking(fd, NON_BLOCKING); + count = read(fd, dst, len); + (void) close(fd); + if (count == len) + return; + } + + /* + * Low-quality seed. Based on 1) the time since boot (good when an + * attacker knows the program start time but not the system boot time), + * and 2) absolute time (good when an attacker does not know the program + * start time). Assumes a system with better than microsecond resolution, + * and a network stack that does not leak the time since boot, for + * example, through TCP or ICMP timestamps. With those caveats, this seed + * is good for 20-30 bits of randomness. + */ +#ifdef NON_WALLTIME_CLOCK + { + struct timespec ts; + + if (clock_gettime(NON_WALLTIME_CLOCK, &ts) != 0) + msg_fatal("clock_gettime() failed: %m"); + fallback += ts.tv_sec ^ ts.tv_nsec; + } +#elif defined(USE_GETHRTIME) + fallback += gethrtime(); +#endif + +#ifdef CLOCK_REALTIME + { + struct timespec ts; + + if (clock_gettime(CLOCK_REALTIME, &ts) != 0) + msg_fatal("clock_gettime() failed: %m"); + fallback += ts.tv_sec ^ ts.tv_nsec; + } +#else + { + struct timeval tv; + + if (GETTIMEOFDAY(&tv) != 0) + msg_fatal("gettimeofday() failed: %m"); + fallback += tv.tv_sec + tv.tv_usec; + } +#endif + fallback += getpid(); + + /* + * Copy the least significant bytes first, because those are the most + * volatile. + */ + for (n = 0; n < sizeof(fallback) && n < len; n++) { + *(char *) dst++ ^= (fallback & 0xff); + fallback >>= CHAR_BIT; + } + return; +} |