Fix PRNG seeding to resist guessing

We seed it with value of time().  It's the traditional way, but it
provides only a few bits of effective entropy when an attacker has a
rough idea when the program started.

Instead, seed with a kernel random number.  If we can't get one, fall
back to a hash of gettimeofday() and getpid().  This should happen
only on old systems or Windows.  Far worse than a kernel random
number, but far better than using time().

Note that fairland used to seed with time() + getpid() until commit
331aac2a (v4.2.20) dropped the getpid(), claiming it didn't improve
the randomness.  Perhaps it didn't under Windows then, but it
certainly did elsewhere, so it was a regression.
This commit is contained in:
Markus Armbruster 2012-12-29 15:06:29 +01:00
parent 39c26f4238
commit 9102ecce54
4 changed files with 68 additions and 2 deletions

View file

@ -32,8 +32,12 @@
#include <config.h>
#include <fcntl.h>
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include "chance.h"
#include "mt19937ar.h"
@ -115,3 +119,56 @@ seed_prng(unsigned seed)
{
init_genrand(seed);
}
static uint32_t
djb_hash(uint32_t hash, void *buf, size_t sz)
{
unsigned char *bp;
for (bp = buf; bp < (unsigned char *)buf + sz; bp++)
hash = hash * 33 ^ *bp;
return hash;
}
/*
* Pick a reasonably random seed for the pseudo-random number generator.
*/
unsigned
pick_seed(void)
{
int fd;
uint32_t seed;
int got_seed = 0;
struct timeval tv;
pid_t pid;
/*
* Modern systems provide random number devices, but the details
* vary. On many systems, /dev/random blocks when the kernel
* entropy pool has been depleted, while /dev/urandom doesn't.
* The former should only be used for generating long-lived
* cryptographic keys. On other systems, both devices behave
* exactly the same, or only /dev/random exists.
*
* Try /dev/urandom first, and if it can't be opened, blindly try
* /dev/random.
*/
fd = open("/dev/urandom", O_RDONLY | O_NONBLOCK);
if (fd < 0)
fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
if (fd >= 0) {
got_seed = read(fd, &seed, sizeof(seed)) == sizeof(seed);
close(fd);
}
if (!got_seed) {
/* Kernel didn't provide, fall back to hashing time and PID */
gettimeofday(&tv, NULL);
seed = djb_hash(5381, &tv, sizeof(tv));
pid = getpid();
seed = djb_hash(seed, &pid, sizeof(pid));
}
return seed;
}