aboutsummaryrefslogtreecommitdiffstats
path: root/librecrypt_rng_.c
diff options
context:
space:
mode:
Diffstat (limited to 'librecrypt_rng_.c')
-rw-r--r--librecrypt_rng_.c67
1 files changed, 55 insertions, 12 deletions
diff --git a/librecrypt_rng_.c b/librecrypt_rng_.c
index 8b9f138..d7fbee3 100644
--- a/librecrypt_rng_.c
+++ b/librecrypt_rng_.c
@@ -6,7 +6,7 @@
ssize_t
librecrypt_rng_(void *out, size_t n, void *user)
{
- static unsigned int seed = 0u;
+ static volatile unsigned int seed = 0u;
unsigned char *buf = out;
int v, fd, saved_errno = errno;
unsigned int state;
@@ -15,17 +15,21 @@ librecrypt_rng_(void *out, size_t n, void *user)
struct timespec ts;
#if defined(__linux__) && defined(AT_RANDOM)
const unsigned char *at_random;
- uintptr_t at_random_addr;
#endif
+ void *random_ptr;
+ uintptr_t random_addr;
(void) user;
+ /* Done if nothing was requested */
if (!n)
return 0;
+ /* Make sure we don't return more than in range for the return value */
if (n > (size_t)SSIZE_MAX)
n = (size_t)SSIZE_MAX;
+ /* Use Linux's system call version of /dev/urandom */
#if defined(__linux__)
for (;;) {
r = getrandom(buf, n, 0u);
@@ -45,6 +49,7 @@ librecrypt_rng_(void *out, size_t n, void *user)
}
#endif
+ /* Use getentropy(3posix) */
for (;;) {
min = n < 256u ? n : 256u;
if (getentropy(buf, min)) {
@@ -60,6 +65,7 @@ librecrypt_rng_(void *out, size_t n, void *user)
}
}
+ /* Use /dev/urandom if available */
#ifndef O_CLOEXEC
# define O_CLOEXEC 0
#endif
@@ -88,29 +94,61 @@ librecrypt_rng_(void *out, size_t n, void *user)
}
}
+ /* Last restort, use non-entropy based RNG */
+ /* but try to use a good seed */
state = seed;
if (!state) {
+ /* Hopefully the application has already called srand(3) */
state = (unsigned int)rand();
- state ^= (unsigned int)time(NULL);
- if (!clock_gettime(CLOCK_MONOTONIC, &ts)) {
- state ^= (unsigned int)ts.tv_sec;
- state ^= (unsigned int)ts.tv_nsec;
- }
#if defined(__linux__) && defined(AT_RANDOM)
- at_random_addr = (uintptr_t)getauxval(AT_RANDOM);
- if (at_random_addr) {
- at_random = (const void *)at_random_addr;
+ /* Otherwise can we use random data the kernel gave to the process */
+ random_addr = (uintptr_t)getauxval(AT_RANDOM);
+ if (random_addr) {
+ at_random = (const void *)random_addr;
for (i = 0u; i < 16u; i++)
state ^= (unsigned int)at_random[i] << (i % sizeof(unsigned int) * 8u);
+ goto have_initial_seed;
}
- }
#endif
+#ifndef MAP_UNINITIALIZED
+# define MAP_UNINITIALIZED 0
+#endif
+ /* But where that is not supported, we can hopefully
+ * get a random address: here mmap(2) is much better than
+ * malloc(3), as malloc(3) may be less random when the
+ * allocation is small, and we don't want to make a big
+ * allocation. A few bit's are always 0, but that's not
+ * a big deal. */
+ random_ptr = mmap(NULL, 1u, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_UNINITIALIZED, -1, 0);
+ if (random_ptr != MAP_FAILED) {
+ random_addr = (uintptr_t)random_ptr;
+ state ^= (unsigned int)random_addr;
+ munmap(random_ptr, 1u);
+ goto have_initial_seed; /* just using goto to simplify the #if'ing */
+ }
+ random_ptr = malloc(1u); /* NULL is OK (MAP_FAILED was actually also OK) */
+ random_addr = (uintptr_t)random_ptr;
+ state ^= (unsigned int)random_addr;
+ free(random_ptr);
+ }
+have_initial_seed:
+ /* and always do a time-based reseed in case of multithreading,
+ * so multiple passwords don't end up using the same salt */
+ if (!clock_gettime(CLOCK_MONOTONIC, &ts)) {
+ state ^= (unsigned int)ts.tv_sec;
+ state ^= (unsigned int)ts.tv_nsec;
+ } else {
+ state ^= (unsigned int)time(NULL);
+ }
+ /* rand(3) is good enough on modern systems: all bits are equality random */
for (i = 0u; i < n; i++) {
v = rand_r(&state);
v = (int)((uintmax_t)v * 255u / (uintmax_t)RAND_MAX);
buf[i] = (unsigned char)v;
}
ret += n;
+ /* Of course we have to save the RNG state so next run
+ * depends both of the this run and the time */
seed = state;
out:
@@ -130,15 +168,20 @@ main(void)
unsigned char buf1[1024u];
unsigned char buf2[sizeof(buf1)];
ssize_t n1, n2;
+ void *user = NULL;
SET_UP_ALARM();
+ /* Check that output is random */
n1 = librecrypt_rng_(buf1, sizeof(buf1), NULL);
- n2 = librecrypt_rng_(buf2, sizeof(buf2), NULL);
+ n2 = librecrypt_rng_(buf2, sizeof(buf2), &user);
EXPECT(n1 >= 128 && (size_t)n1 <= sizeof(buf1));
EXPECT(n2 >= 128 && (size_t)n2 <= sizeof(buf2));
EXPECT(memcmp(buf1, buf2, (size_t)(n1 < n2 ? n1 : n2)));
+ /* Check zero-request */
+ EXPECT(librecrypt_rng_(NULL, 0u, NULL) == 0u);
+
return 0;
}