[klibc] [PATCH 1/2] Add minimal arc4random(3) API; self-seeding if /proc is mounted.

Thorsten Glaser tg at mirbsd.de
Sat Jan 29 09:31:23 PST 2011


This patch adds a minimalistic implementation of the
BSD arc4random(3) API on top of jrand48(3) (which is
already there, for size reasons) to have a simple,
self-seeding PRNG (actually SRNG, stretching RNG,
since it uses proper entropy from the kernel, just
with an algorithm not usable for cryptography).

Entropy sources:
- 36 bytes from /proc/sys/kernel/random/uuid
- position of temp. buffer on stack (randomised on recent kernels)

Additional variation (non-random) via:
- PID of last stir call
- PID of current stir call
- count of arc4random() calls since last stir
- time of current stir call
- size of user-provided data
- position of arc4random_addrandom() argument on stack
- filedescriptor of /proc/sys/kernel/random/uuid
- count of bytes read from /proc/sys/kernel/random/uuid

After stirring, it's good for 65535 arc4random() calls.
The first call to arc4random() after fork() re-stirs.

Signed-off-by: Thorsten Glaser <tg at mirbsd.de>
---
 usr/include/stdlib.h   |    6 ++
 usr/klibc/Kbuild       |    1 +
 usr/klibc/arc4random.c |  144 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 151 insertions(+), 0 deletions(-)
 create mode 100644 usr/klibc/arc4random.c

diff --git a/usr/include/stdlib.h b/usr/include/stdlib.h
index 406f446..28c7150 100644
--- a/usr/include/stdlib.h
+++ b/usr/include/stdlib.h
@@ -8,6 +8,7 @@
 #include <klibc/extern.h>
 #include <klibc/compiler.h>
 #include <stddef.h>
+#include <bitsize/stdint.h>
 
 #include <malloc.h>
 
@@ -61,6 +62,11 @@ __extern long lrand48(void);
 __extern unsigned short *seed48(const unsigned short *);
 __extern void srand48(long);
 
+/* arc4random API emulation on top of jrand48 algorithm */
+__extern uint32_t arc4random(void);
+__extern void arc4random_stir(void);
+__extern void arc4random_addrandom(unsigned char *, int);
+
 #define RAND_MAX 0x7fffffff
 static __inline__ int rand(void)
 {
diff --git a/usr/klibc/Kbuild b/usr/klibc/Kbuild
index af40367..1138e6d 100644
--- a/usr/klibc/Kbuild
+++ b/usr/klibc/Kbuild
@@ -47,6 +47,7 @@ klib-y := vsnprintf.o snprintf.o vsprintf.o sprintf.o \
 	  time.o utime.o llseek.o nice.o getpriority.o \
 	  qsort.o bsearch.o \
 	  lrand48.o jrand48.o mrand48.o nrand48.o srand48.o seed48.o \
+	  arc4random.o \
 	  inet/inet_ntoa.o inet/inet_aton.o inet/inet_addr.o \
 	  inet/inet_ntop.o inet/inet_pton.o inet/bindresvport.o \
 	  send.o recv.o \
diff --git a/usr/klibc/arc4random.c b/usr/klibc/arc4random.c
new file mode 100644
index 0000000..1be3629
--- /dev/null
+++ b/usr/klibc/arc4random.c
@@ -0,0 +1,144 @@
+/*-
+ * Minimum self-seeding PRNG/SRNG implementation, exposed
+ * via the standard BSD arc4random(3) API, for klibc
+ *
+ * Copyright (c) 2009, 2011
+ *	Thorsten Glaser <tg at mirbsd.de>
+ *
+ * This file is available under the same terms ("historic
+ * permission clause") as klibc itself or under the terms
+ * of The MirOS Licence (dual licenced).
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static struct arc4random_state {
+	unsigned short seed[3];		/* for jrand48 */
+	unsigned short cnt;		/* invocation count */
+	pid_t proc;			/* PID of last seed */
+} arc4_state = { { 0, 0, 0 }, 0, 0 };
+
+static void arc4_stir(pid_t);
+
+/* Jenkins one-at-a-time hash */
+#define OAAT_Update(h, buf, siz) do {		\
+	register const uint8_t *OAATcp;		\
+	register int OAATn = (siz);		\
+						\
+	OAATcp = (const void *)(buf);		\
+	while (OAATn--) {			\
+		(h) += *OAATcp++;		\
+		(h) += (h) << 10;		\
+		(h) ^= (h) >> 6;		\
+	}					\
+} while (/* CONSTCOND */ 0)
+#define OAAT_Final(h) do {			\
+	(h) += (h) << 3;			\
+	(h) ^= (h) >> 11;			\
+	(h) += (h) << 15;			\
+} while (/* CONSTCOND */ 0)
+
+void
+arc4random_addrandom(unsigned char *dat, int datlen)
+{
+	register uint32_t h;
+	struct {
+		struct timeval tv;	/* brings variety if not entropy */
+		const void *sp;		/* stack is randomised by kernel */
+		uint32_t v;		/* datlen on first pass */
+	} additional_data;
+
+	gettimeofday(&additional_data.tv, NULL);
+	additional_data.sp = &additional_data;
+	additional_data.v = (uint32_t)datlen;
+
+	h = 0x100;			/* ideal start value for OAAT */
+	/* hash over additional data which introduces variety */
+	OAAT_Update(h, &additional_data, sizeof(additional_data));
+	/* and the data passed */
+	OAAT_Update(h, dat, datlen);
+	/* and the old state (including PID) */
+	OAAT_Update(h, &arc4_state, sizeof(arc4_state));
+	/* reassign part of additional_data for more variety */
+	additional_data.sp = dat;	/* argument address also varies */
+	additional_data.v = h;		/* keep some of h (dat, old state) */
+
+	/* split the hash into the new seed (first pass) */
+	OAAT_Final(h);
+	arc4_state.seed[1] = (unsigned short)(h & 0xFFFF);
+	arc4_state.seed[2] = (unsigned short)(h >> 16);
+
+	/* split half a hash of new additional_data into the new seed */
+	h = 0x100;
+	OAAT_Update(h, &additional_data, sizeof(additional_data));
+	OAAT_Final(h);
+	arc4_state.seed[0] = (unsigned short)(h & 0xFFFF);
+}
+
+static void
+arc4_stir(pid_t ourpid)
+{
+	/* define a "stir buffer" */
+#define UUID_LEN	36
+	struct {
+		union {
+			ssize_t numb;
+			pid_t newpid;
+		} u;
+		int fd;
+		int count;
+		char uuid[UUID_LEN];
+	} s;
+
+	if ((s.fd = open("/proc/sys/kernel/random/uuid", O_RDONLY)) != -1) {
+		s.count = 0;
+		while (s.count < UUID_LEN) {
+			s.u.numb = read(s.fd, s.uuid + s.count,
+			    UUID_LEN - s.count);
+			if (s.u.numb == -1) {
+				if (errno == EINTR) {
+					errno = 0;
+					continue;
+				}
+				break;
+			} else if (s.u.numb == 0)
+				break;
+			s.count += s.u.numb;
+		}
+		close(s.fd);
+	}
+	/* else, we are desperate and just use the stack content */
+#undef UUID_LEN
+
+	/* s.u.newpid != arc4_state.proc directly after fork(2) */
+	s.u.newpid = ourpid;
+
+	/* hash the stir buffer into the PRNG state */
+	arc4random_addrandom((unsigned char *)&s, sizeof(s));
+
+	/* ready to be used for a while */
+	arc4_state.proc = ourpid;
+	arc4_state.cnt = 0xFFFF;
+}
+
+void
+arc4random_stir(void)
+{
+	arc4_stir(getpid());
+}
+
+uint32_t
+arc4random(void)
+{
+	pid_t ourpid;
+
+	if (arc4_state.proc != (ourpid = getpid()) || arc4_state.cnt-- == 0)
+		arc4_stir(ourpid);
+	return ((uint32_t)(jrand48(arc4_state.seed) & 0xFFFFFFFF));
+}
-- 
1.7.2.3



More information about the klibc mailing list