[PATCH] urngd: Add support for read()ing entropy

nwfilardo at gmail.com nwfilardo at gmail.com
Sun Jul 12 16:57:39 EDT 2020


From: Nathaniel Wesley Filardo <nwfilardo at gmail.com>

This allows us to attach a hwrng exposed as a character device (e.g.).

At most one sample will be taken every time we also add entropy via the
jitter mechanism.  We won't try reading if the stream hasn't indicated its
readiness, and we'll go back to waiting if ever the stream produces
0 bytes or an error on read.

While here, do some tidying:

  - Refactor out persistant struct rand_pool_info.  There's no need to hold
    one of these structures across invocations of low_entropy_cb.  Just put
    one on the stack and use that.

  - Remove some unused #defines.  These look like there had been plans to
    stay asleep if there was "sufficient" entropy in the system, but the
    code does not do so.

Signed-off-by: Nathaniel Wesley Filardo <nwfilardo at gmail.com>
---
 urngd.c | 134 ++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 86 insertions(+), 48 deletions(-)

diff --git a/urngd.c b/urngd.c
index 35ccdec..cc2b85d 100644
--- a/urngd.c
+++ b/urngd.c
@@ -50,12 +50,10 @@
 #include "jitterentropy.h"
 
 #define ENTROPYBYTES 32
-#define ENTROPYTHRESH 1024
 #define OVERSAMPLINGFACTOR 2
 #define DEV_RANDOM "/dev/random"
-#define ENTROPYAVAIL "/proc/sys/kernel/random/entropy_avail"
-#define ENTROPYPOOLBYTES (sizeof(struct rand_pool_info) + \
-		(ENTROPYBYTES * OVERSAMPLINGFACTOR * sizeof(char)))
+#define ENTROPYPOOLBYTES \
+		(ENTROPYBYTES * OVERSAMPLINGFACTOR * sizeof(char))
 
 #ifdef URNGD_DEBUG
 unsigned int debug;
@@ -63,8 +61,8 @@ unsigned int debug;
 
 struct urngd {
 	struct uloop_fd rnd_fd;
+	struct uloop_fd src_fd;
 	struct rand_data *ec;
-	struct rand_pool_info *rpi;
 };
 
 static struct urngd urngd_service;
@@ -75,53 +73,70 @@ static inline void memset_secure(void *s, int c, size_t n)
 	__asm__ __volatile__("" : : "r" (s) : "memory");
 }
 
-static size_t write_entropy(struct urngd *u, char *buf, size_t len,
-			    size_t entropy_bytes)
+static size_t write_entropy(struct urngd *u, struct rand_pool_info *rpi, char *src)
 {
 	int ret;
-	size_t written = 0;
-
-	/* value is in bits */
-	u->rpi->entropy_count = (entropy_bytes * 8);
-	u->rpi->buf_size = len;
-	memcpy(u->rpi->buf, buf, len);
-	memset(buf, 0, len);
-
-	ret =  ioctl(u->rnd_fd.fd, RNDADDENTROPY, u->rpi);
+	ret =  ioctl(u->rnd_fd.fd, RNDADDENTROPY, rpi);
 	if (0 > ret) {
 		ERROR("error injecting entropy: %s\n", strerror(errno));
+		return 0;
 	} else {
-		DEBUG(1, "injected %zub (%zub of entropy)\n", len, entropy_bytes);
-		written = len;
+		DEBUG(1, "injected %ub (%ub of entropy) from %s\n",
+			rpi->buf_size, rpi->entropy_count/8, src);
+		ret = rpi->buf_size;
 	}
 
-	u->rpi->entropy_count = 0;
-	u->rpi->buf_size = 0;
-	memset(u->rpi->buf, 0, len);
-
-	return written;
+	return ret;
 }
 
-static size_t gather_entropy(struct urngd *u)
+static size_t gather_jitter_entropy(struct urngd *u)
 {
+	ssize_t ent;
 	size_t ret = 0;
-	char buf[(ENTROPYBYTES * OVERSAMPLINGFACTOR)];
+	struct rand_pool_info *rpi = alloca(sizeof(*rpi) + ENTROPYPOOLBYTES);
 
-	if (jent_read_entropy(u->ec, buf, sizeof(buf)) < 0) {
+	ent = jent_read_entropy(u->ec, (char *)&rpi->buf[0], ENTROPYPOOLBYTES);
+	if (ent < 0) {
 		ERROR("cannot read entropy\n");
 		return 0;
 	}
 
-	ret = write_entropy(u, buf, sizeof(buf), ENTROPYBYTES);
-	if (sizeof(buf) != ret) {
-		ERROR("injected %zub of entropy, less then %zub expected\n",
-		      ret, sizeof(buf));
+	rpi->buf_size = ENTROPYPOOLBYTES;
+	rpi->entropy_count = 8 * ENTROPYBYTES;
+
+	ret = write_entropy(u, rpi, "jitter");
+
+	memset_secure(&rpi->buf, 0, ENTROPYPOOLBYTES);
+
+	return ret;
+}
+
+static size_t gather_src_entropy(struct urngd *u) {
+	static const size_t src_bytes = 1024;
+	struct rand_pool_info *rpi = alloca(sizeof(*rpi) + src_bytes);
+	ssize_t ent;
+	size_t ret;
+
+	if ((u->src_fd.fd < 0) || (u->src_fd.registered)) {
+		/* No source or source still waiting for available bytes */
+		return 0;
+	}
+
+	ent = read(u->src_fd.fd, (char *)&rpi->buf[0], src_bytes);
+	if (ent > 0) {
+		/* Read some bytes from the source; stir those in, too */
+		rpi->buf_size = ent;
+		rpi->entropy_count = 8 * ent;
+		ret = write_entropy(u, rpi, "source");
 	} else {
-		ret = sizeof(buf);
+		/* No luck this time around */
+		ret = 0;
+
+		/* Go back to waiting for the source to be ready */
+		uloop_fd_add(&u->src_fd, ULOOP_READ);
 	}
 
-	memset_secure(buf, 0, sizeof(buf));
-	DEBUG(2, DEV_RANDOM " fed with %zub of entropy\n", ret);
+	memset_secure(&rpi->buf, 0, ent);
 
 	return ret;
 }
@@ -131,7 +146,13 @@ static void low_entropy_cb(struct uloop_fd *ufd, unsigned int events)
 	struct urngd *u = container_of(ufd, struct urngd, rnd_fd);
 
 	DEBUG(2, DEV_RANDOM " signals low entropy\n");
-	gather_entropy(u);
+	gather_jitter_entropy(u);
+	gather_src_entropy(u);
+}
+
+static void src_ready_cb(struct uloop_fd *ufd, unsigned int events)
+{
+	uloop_fd_delete(ufd);
 }
 
 static void urngd_done(struct urngd *u)
@@ -141,16 +162,15 @@ static void urngd_done(struct urngd *u)
 		u->ec = NULL;
 	}
 
-	if (u->rpi) {
-		memset(u->rpi, 0, ENTROPYPOOLBYTES);
-		free(u->rpi);
-		u->rpi = NULL;
-	}
-
 	if (u->rnd_fd.fd) {
 		close(u->rnd_fd.fd);
 		u->rnd_fd.fd = 0;
 	}
+
+	if (u->src_fd.fd >= 0) {
+		close(u->src_fd.fd);
+		u->src_fd.fd = -1;
+	}
 }
 
 static bool urngd_init(struct urngd *u)
@@ -167,12 +187,6 @@ static bool urngd_init(struct urngd *u)
 		return false;
 	}
 
-	u->rpi = malloc(ENTROPYPOOLBYTES);
-	if (!u->rpi) {
-		ERROR("rand pool alloc failed\n");
-		return false;
-	}
-
 	u->rnd_fd.cb = low_entropy_cb;
 	u->rnd_fd.fd = open(DEV_RANDOM, O_WRONLY);
 	if (u->rnd_fd.fd < 1) {
@@ -182,6 +196,18 @@ static bool urngd_init(struct urngd *u)
 
 	uloop_fd_add(&u->rnd_fd, ULOOP_WRITE);
 
+	if (u->src_fd.fd >= 0) {
+		int ret;
+
+		u->src_fd.cb = src_ready_cb;
+		ret = uloop_fd_add(&u->src_fd, ULOOP_READ);
+		if (ret == -1 && errno == EPERM) {
+			LOG("Source (-f) does not support polling;"
+				" assuming that's OK.");
+			u->src_fd.registered = false;
+		}
+	}
+
 	return true;
 }
 
@@ -192,6 +218,7 @@ static int usage(const char *prog)
 #ifdef URNGD_DEBUG
 		"	-d <level>	Enable debug messages\n"
 #endif
+		"	-f <file>	Source entropy from <file>\n"
 		"	-S		Print messages to stdout\n"
 		"\n", prog);
 	return 1;
@@ -210,13 +237,24 @@ int main(int argc, char **argv)
 	}
 #endif
 
-	while ((ch = getopt(argc, argv, "d:S")) != -1) {
+	urngd_service.src_fd.fd = -1;
+
+	while ((ch = getopt(argc, argv, "d:f:S")) != -1) {
 		switch (ch) {
 #ifdef URNGD_DEBUG
 		case 'd':
 			debug = atoi(optarg);
 			break;
 #endif
+		case 'f':
+			urngd_service.src_fd.fd =
+				open(optarg, O_RDONLY | O_NONBLOCK);
+			if (urngd_service.src_fd.fd < 0) {
+				ERROR("%s open failed: %s\n",
+					optarg, strerror(errno));
+				return -1;
+			}
+			break;
 		case 'S':
 			ulog_channels = ULOG_STDIO;
 			break;
@@ -235,7 +273,7 @@ int main(int argc, char **argv)
 
 	LOG("v%s started.\n", URNGD_VERSION);
 
-	gather_entropy(&urngd_service);
+	gather_jitter_entropy(&urngd_service);
 
 	uloop_run();
 	uloop_done();
-- 
2.27.0




More information about the openwrt-devel mailing list