summaryrefslogtreecommitdiffstats
path: root/src/util/ldseed.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/ldseed.c')
-rw-r--r--src/util/ldseed.c138
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;
+}