#include "includes.h"
#include "buffer.h"
-#include "util.h"
+#include "dbutil.h"
+#include "bignum.h"
-int donerandinit = 0;
+static int donerandinit = 0;
/* this is used to generate unique output from the same hashpool */
-unsigned int counter = 0;
+static unsigned int counter = 0;
#define MAX_COUNTER 1000000/* the max value for the counter, so it won't loop */
-unsigned char hashpool[SHA1_HASH_SIZE];
+static unsigned char hashpool[SHA1_HASH_SIZE];
#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_URANDOM or PRNGD and hash it
+/* 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
* data into the pool.
static void readrand(unsigned char* buf, unsigned int buflen) {
+ static int already_blocked = 0;
int readfd;
- unsigned int readpos, readlen;
-#ifdef DROPBEAR_EGD
+ unsigned int readpos;
+ int readlen;
+#ifdef DROPBEAR_PRNGD_SOCKET
struct sockaddr_un egdsock;
+ char egdcmd[2];
#endif
-#ifdef DROPBEAR_DEV_URANDOM
- readfd = open(DEV_URANDOM, O_RDONLY);
- if (!readfd) {
+#ifdef DROPBEAR_RANDOM_DEV
+ readfd = open(DROPBEAR_RANDOM_DEV, O_RDONLY);
+ if (readfd < 0) {
dropbear_exit("couldn't open random device");
}
#endif
-#ifdef DROPBEAR_EGD
+#ifdef DROPBEAR_PRNGD_SOCKET
+ memset((void*)&egdsock, 0x0, sizeof(egdsock));
egdsock.sun_family = AF_UNIX;
- strlcpy(egdsock.sun_path, DROPBEAR_EGD_SOCKET,
+ strlcpy(egdsock.sun_path, DROPBEAR_PRNGD_SOCKET,
sizeof(egdsock.sun_path));
- if ((readfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+ readfd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (readfd < 0) {
dropbear_exit("couldn't open random device");
}
/* todo - try various common locations */
sizeof(struct sockaddr_un)) < 0) {
dropbear_exit("couldn't open random device");
}
+
+ 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
/* read the actual random data */
readpos = 0;
do {
+ if (!already_blocked)
+ {
+ int ret;
+ struct timeval timeout;
+ 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.");
+ already_blocked = 1;
+ }
+ }
readlen = read(readfd, &buf[readpos], buflen - readpos);
if (readlen <= 0) {
- if (errno == EINTR) {
+ if (readlen < 0 && errno == EINTR) {
continue;
}
dropbear_exit("error reading random source");
close (readfd);
}
-/* initialise the prng from /dev/urandom or prngd */
+/* initialise the prng from /dev/(u)random or prngd */
void seedrandom() {
unsigned char readbuf[INIT_SEED_SIZE];
m_burn(hash, sizeof(hash));
}
-/* Adds entropy to the PRNG state. As long as the hash is strong, then we
- * don't need to worry about entropy being added "diluting" the current
- * state - it should only make it stronger. */
-void addrandom(unsigned char* buf, unsigned int len) {
+/* Generates a random mp_int.
+ * max is a *mp_int specifying an upper bound.
+ * rand must be an initialised *mp_int for the result.
+ * the result rand satisfies: 0 < rand < max
+ * */
+void gen_random_mpint(mp_int *max, mp_int *rand) {
- hash_state hs;
- if (!donerandinit) {
- dropbear_exit("seedrandom not done");
- }
+ unsigned char *randbuf = NULL;
+ unsigned int len = 0;
+ const char masks[] = {0xff, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f};
- sha1_init(&hs);
- sha1_process(&hs, (void*)buf, len);
- sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
- sha1_done(&hs, hashpool);
- counter = 0;
+ const int size_bits = mp_count_bits(max);
+ len = size_bits / 8;
+ if ((size_bits % 8) != 0) {
+ len += 1;
+ }
+
+ randbuf = (unsigned char*)m_malloc(len);
+ do {
+ genrandom(randbuf, len);
+ /* Mask out the unrequired bits - mp_read_unsigned_bin expects
+ * MSB first.*/
+ randbuf[0] &= masks[size_bits % 8];
+
+ bytes_to_mp(rand, randbuf, len);
+
+ /* keep regenerating until we get one satisfying
+ * 0 < rand < max */
+ } while ( ( (max != NULL) && (mp_cmp(rand, max) != MP_LT) )
+ || (mp_cmp_d(rand, 0) != MP_GT) );
+ m_burn(randbuf, len);
+ m_free(randbuf);
}