Mercurial > dropbear
changeset 687:167fdc091c05
Improve RNG seeding.
Try to read from /dev/urandom multiple times, take input from extra sources,
and use /dev/random when generating private keys
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Fri, 29 Jun 2012 23:19:43 +0800 |
parents | 63f8d6c469cf |
children | 650c41a4909a |
files | common-runopts.c gendss.c genrsa.c options.h random.c random.h svr-chansession.c svr-main.c svr-session.c sysoptions.h |
diffstat | 10 files changed, 171 insertions(+), 108 deletions(-) [+] |
line wrap: on
line diff
--- a/common-runopts.c Thu May 17 00:26:12 2012 +0800 +++ b/common-runopts.c Fri Jun 29 23:19:43 2012 +0800 @@ -29,6 +29,7 @@ #include "dbutil.h" #include "auth.h" #include "algo.h" +#include "random.h" runopts opts; /* GLOBAL */ @@ -45,6 +46,9 @@ goto out; } buf_setpos(buf, 0); + + addrandom(buf_getptr(buf, buf->len), buf->len); + if (buf_get_priv_key(buf, hostkey, type) == DROPBEAR_FAILURE) { goto out; }
--- a/gendss.c Thu May 17 00:26:12 2012 +0800 +++ b/gendss.c Fri Jun 29 23:19:43 2012 +0800 @@ -57,6 +57,7 @@ m_mp_init_multi(key->p, key->q, key->g, key->y, key->x, NULL); seedrandom(); + seedstrongrandom(); getq(key); getp(key, size);
--- a/genrsa.c Thu May 17 00:26:12 2012 +0800 +++ b/genrsa.c Fri Jun 29 23:19:43 2012 +0800 @@ -56,6 +56,7 @@ &pminus, &lcm, &qminus, NULL); seedrandom(); + seedstrongrandom(); if (mp_set_int(key->e, RSA_E) != MP_OKAY) { fprintf(stderr, "RSA generation failed\n");
--- a/options.h Thu May 17 00:26:12 2012 +0800 +++ b/options.h Fri Jun 29 23:19:43 2012 +0800 @@ -204,20 +204,14 @@ * return the password on standard output */ /*#define ENABLE_CLI_ASKPASS_HELPER*/ -/* Random device to use - define either DROPBEAR_RANDOM_DEV or - * DROPBEAR_PRNGD_SOCKET. - * DROPBEAR_RANDOM_DEV is recommended on hosts with a good /dev/(u)random, - * otherwise use run prngd (or egd if you want), specifying the socket. - * The device will be queried for a few dozen bytes of seed a couple of times - * per session (or more for very long-lived sessions). */ +/* Source for randomness. This must be able to provide hundreds of bytes per SSH + * connection without blocking. In addition /dev/random is used for seeding + * rsa/dss key generation */ +#define DROPBEAR_URANDOM_DEV "/dev/urandom" -/* We'll use /dev/urandom by default, since /dev/random is too much hassle. - * If system developers aren't keeping seeds between boots nor getting - * any entropy from somewhere it's their own fault. */ -#define DROPBEAR_RANDOM_DEV "/dev/urandom" +/* Set this to use PRNGD or EGD instead of /dev/urandom or /dev/random */ +/*#define DROPBEAR_PRNGD_SOCKET "/var/run/dropbear-rng"*/ -/* prngd must be manually set up to produce output */ -/*#define DROPBEAR_PRNGD_SOCKET "/var/run/dropbear-rng"*/ /* Specify the number of clients we will allow to be connected but * not yet authenticated. After this limit, connections are rejected */
--- a/random.c Thu May 17 00:26:12 2012 +0800 +++ b/random.c Fri Jun 29 23:19:43 2012 +0800 @@ -27,19 +27,16 @@ #include "dbutil.h" #include "bignum.h" -static int donerandinit = 0; - /* this is used to generate unique output from the same hashpool */ static uint32_t counter = 0; /* the max value for the counter, so it won't integer overflow */ #define MAX_COUNTER 1<<30 -static unsigned char hashpool[SHA1_HASH_SIZE]; +static unsigned char hashpool[SHA1_HASH_SIZE] = {0}; +static int donerandinit = 0; #define INIT_SEED_SIZE 32 /* 256 bits */ -static void readrand(unsigned char* buf, unsigned int buflen); - /* The basic setup is we read some data from /dev/(u)random or prngd and hash it * into hashpool. To read data, we hash together current hashpool contents, * and a counter. We feed more data in by hashing the current pool and new @@ -50,120 +47,190 @@ * */ -static void readrand(unsigned char* buf, unsigned int buflen) { - +/* Pass len=0 to hash an entire file */ +static int +process_file(hash_state *hs, const char *filename, + unsigned int len, int prngd) +{ static int already_blocked = 0; int readfd; - unsigned int readpos; - int readlen; -#ifdef DROPBEAR_PRNGD_SOCKET - struct sockaddr_un egdsock; - char egdcmd[2]; -#endif - -#ifdef DROPBEAR_RANDOM_DEV - readfd = open(DROPBEAR_RANDOM_DEV, O_RDONLY); - if (readfd < 0) { - dropbear_exit("Couldn't open random device"); - } -#endif + unsigned int readcount; + int ret = DROPBEAR_FAILURE; #ifdef DROPBEAR_PRNGD_SOCKET - readfd = connect_unix(DROPBEAR_PRNGD_SOCKET); - - if (readfd < 0) { - dropbear_exit("Couldn't open random device"); + if (prngd) + { + readfd = connect_unix(filename); + } + else +#endif + { + readfd = open(filename, O_RDONLY); } - if (buflen > 255) - dropbear_exit("Can't request more than 255 bytes from egd"); - egdcmd[0] = 0x02; /* blocking read */ - egdcmd[1] = (unsigned char)buflen; - if (write(readfd, egdcmd, 2) < 0) - dropbear_exit("Can't send command to egd"); -#endif + if (readfd < 0) { + goto out; + } - /* read the actual random data */ - readpos = 0; - do { + readcount = 0; + while (readcount < len) + { + int readlen, wantread; + unsigned char readbuf[128]; if (!already_blocked) { int ret; - struct timeval timeout; + struct timeval timeout = { .tv_sec = 2, .tv_usec = 0}; fd_set read_fds; - timeout.tv_sec = 2; /* two seconds should be enough */ - timeout.tv_usec = 0; - FD_ZERO(&read_fds); FD_SET(readfd, &read_fds); ret = select(readfd + 1, &read_fds, NULL, NULL, &timeout); if (ret == 0) { - dropbear_log(LOG_INFO, "Warning: Reading the random source seems to have blocked.\nIf you experience problems, you probably need to find a better entropy source."); + dropbear_log(LOG_WARNING, "Warning: Reading the randomness source '%s' seems to have blocked.\nYou may need to find a better entropy source.", filename); already_blocked = 1; } } - readlen = read(readfd, &buf[readpos], buflen - readpos); + + wantread = MIN(sizeof(readbuf), len-readcount); + +#ifdef DROPBEAR_PRNGD_SOCKET + if (prngd) + { + char egdcmd[2]; + egdcmd[0] = 0x02; /* blocking read */ + egdcmd[1] = (unsigned char)wantread; + if (write(readfd, egdcmd, 2) < 0) + { + dropbear_exit("Can't send command to egd"); + } + } +#endif + + readlen = read(readfd, readbuf, wantread); if (readlen <= 0) { if (readlen < 0 && errno == EINTR) { continue; } - dropbear_exit("Error reading random source"); + if (readlen == 0 && len == 0) + { + /* whole file was read as requested */ + break; + } + goto out; } - readpos += readlen; - } while (readpos < buflen); - - close (readfd); + sha1_process(hs, readbuf, readlen); + readcount += readlen; + } + ret = DROPBEAR_SUCCESS; +out: + close(readfd); + return ret; } -/* initialise the prng from /dev/(u)random or prngd */ -void seedrandom() { - - unsigned char readbuf[INIT_SEED_SIZE]; - +void addrandom(char * buf, int len) +{ hash_state hs; - /* initialise so that things won't warn about - * hashing an undefined buffer */ - if (!donerandinit) { - m_burn(hashpool, sizeof(hashpool)); - } - - /* get the seed data */ - readrand(readbuf, sizeof(readbuf)); - /* hash in the new seed data */ sha1_init(&hs); + /* existing state (zeroes on startup) */ sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); - sha1_process(&hs, (void*)readbuf, sizeof(readbuf)); + + /* new */ + sha1_process(&hs, buf, len); + sha1_done(&hs, hashpool); +} + +static void write_urandom() +{ +#ifndef DROPBEAR_PRNGD_SOCKET + /* This is opportunistic, don't worry about failure */ + unsigned char buf[INIT_SEED_SIZE]; + FILE *f = fopen(DROPBEAR_URANDOM_DEV, "w"); + genrandom(buf, sizeof(buf)); + fwrite(buf, sizeof(buf), 1, f); + fclose(f); +#endif +} + +/* add entropy from the stronger, blocking source /dev/random. Only used + * for generating persistent private keys (RSA and DSS) */ +void seedstrongrandom() +{ + /* We assume that PRNGD is a strong source, so don't need to do anything here */ +#ifndef DROPBEAR_PRNGD_SOCKET + hash_state hs; + + sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); + if (process_file(&hs, "/dev/random", INIT_SEED_SIZE, 0) + != DROPBEAR_SUCCESS) { + dropbear_exit("Failure reading random device %s", "/dev/random"); + } + + sha1_done(&hs, hashpool); +#endif +} + +/* Initialise the prng from /dev/urandom or prngd. This function can