[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