From 0a5f81a44230cbd562b6d71a0a5be018e24a5ba6 Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Sat, 27 Aug 2016 20:42:31 +0200 Subject: [PATCH] kernel: switch to fast portable chacha based seed-once random number generator --- sys/src/9/pc/devarch.c | 5 ++ sys/src/9/pc/mkfile | 2 +- sys/src/9/pc/pcrandom.c | 152 -------------------------------------- sys/src/9/pc64/mkfile | 2 +- sys/src/9/port/devcons.c | 32 ++++---- sys/src/9/port/portfns.h | 1 + sys/src/9/port/random.c | 156 ++++++++++++++++----------------------- 7 files changed, 83 insertions(+), 267 deletions(-) delete mode 100644 sys/src/9/pc/pcrandom.c diff --git a/sys/src/9/pc/devarch.c b/sys/src/9/pc/devarch.c index f3be061cb..0b4cad252 100644 --- a/sys/src/9/pc/devarch.c +++ b/sys/src/9/pc/devarch.c @@ -872,6 +872,11 @@ cpuidentify(void) fprestore = fpx87restore; } + if(strcmp(m->cpuidid, "GenuineIntel") == 0 && (m->cpuidcx & Rdrnd) != 0) + hwrandbuf = rdrandbuf; + else + hwrandbuf = nil; + cputype = t; return t->family; } diff --git a/sys/src/9/pc/mkfile b/sys/src/9/pc/mkfile index 96ab2c623..c6e683e22 100644 --- a/sys/src/9/pc/mkfile +++ b/sys/src/9/pc/mkfile @@ -32,6 +32,7 @@ PORT=\ proc.$O\ qio.$O\ qlock.$O\ + random.$O\ rdb.$O\ rebootcmd.$O\ segment.$O\ @@ -52,7 +53,6 @@ OBJ=\ memory.$O\ mmu.$O\ trap.$O\ - pcrandom.$O\ $CONF.root.$O\ $CONF.rootc.$O\ $DEVS\ diff --git a/sys/src/9/pc/pcrandom.c b/sys/src/9/pc/pcrandom.c deleted file mode 100644 index 386d7bf8e..000000000 --- a/sys/src/9/pc/pcrandom.c +++ /dev/null @@ -1,152 +0,0 @@ -#include "u.h" -#include "../port/lib.h" -#include "mem.h" -#include "dat.h" -#include "fns.h" -#include "../port/error.h" - -static int haverdrand; - -struct Rb -{ - QLock; - Rendez producer; - Rendez consumer; - ulong randomcount; - uchar buf[128]; - uchar *ep; - uchar *rp; - uchar *wp; - uchar next; - uchar wakeme; - ushort bits; - ulong randn; -} rb; - -static int -rbnotfull(void*) -{ - int i; - - i = rb.rp - rb.wp; - return i != 1 && i != (1 - sizeof(rb.buf)); -} - -static int -rbnotempty(void*) -{ - return rb.wp != rb.rp; -} - -static void -genrandom(void*) -{ - up->basepri = PriNormal; - up->priority = up->basepri; - - while(waserror()) - ; - for(;;){ - if(++rb.randomcount <= 100000) - continue; - if(anyhigher()) - sched(); - if(!rbnotfull(0)) - sleep(&rb.producer, rbnotfull, 0); - } -} - -/* - * produce random bits in a circular buffer - */ -static void -randomclock(void) -{ - if(rb.randomcount == 0 || !rbnotfull(0)) - return; - - rb.bits = (rb.bits<<2) ^ rb.randomcount; - rb.randomcount = 0; - - rb.next++; - if(rb.next != 8/2) - return; - rb.next = 0; - - *rb.wp ^= rb.bits; - if(rb.wp+1 == rb.ep) - rb.wp = rb.buf; - else - rb.wp = rb.wp+1; - - if(rb.wakeme) - wakeup(&rb.consumer); -} - -void -randominit(void) -{ - if(!strcmp(m->cpuidid, "GenuineIntel") - && (m->cpuidcx & Rdrnd)){ - haverdrand = 1; - } - else{ - /* Frequency close but not equal to HZ */ - addclock0link(randomclock, MS2HZ+3); - rb.ep = rb.buf + sizeof(rb.buf); - rb.rp = rb.wp = rb.buf; - kproc("genrandom", genrandom, 0); - } -} - -/* - * consume random bytes from a circular buffer - */ -ulong -randomread(void *xp, ulong n) -{ - uchar *e, *p; - ulong x; - - p = xp; - - if(haverdrand){ - rdrandbuf(p, n); - return n; - } - - if(waserror()){ - qunlock(&rb); - nexterror(); - } - - qlock(&rb); - for(e = p + n; p < e; ){ - if(rb.wp == rb.rp){ - rb.wakeme = 1; - wakeup(&rb.producer); - sleep(&rb.consumer, rbnotempty, 0); - rb.wakeme = 0; - continue; - } - - /* - * beating clocks will be predictable if - * they are synchronized. Use a cheap pseudo- - * random number generator to obscure any cycles. - */ - x = rb.randn*1103515245 ^ *rb.rp; - *p++ = rb.randn = x; - - if(rb.rp+1 == rb.ep) - rb.rp = rb.buf; - else - rb.rp = rb.rp+1; - } - qunlock(&rb); - poperror(); - - wakeup(&rb.producer); - - return n; -} diff --git a/sys/src/9/pc64/mkfile b/sys/src/9/pc64/mkfile index 30b40daf1..16aae0a68 100644 --- a/sys/src/9/pc64/mkfile +++ b/sys/src/9/pc64/mkfile @@ -29,6 +29,7 @@ PORT=\ proc.$O\ qio.$O\ qlock.$O\ + random.$O\ rdb.$O\ rebootcmd.$O\ segment.$O\ @@ -49,7 +50,6 @@ OBJ=\ memory.$O\ mmu.$O\ trap.$O\ - pcrandom.$O\ $CONF.root.$O\ $CONF.rootc.$O\ $DEVS\ diff --git a/sys/src/9/port/devcons.c b/sys/src/9/port/devcons.c index b09df91f2..7f287610f 100644 --- a/sys/src/9/port/devcons.c +++ b/sys/src/9/port/devcons.c @@ -4,8 +4,8 @@ #include "dat.h" #include "fns.h" #include "../port/error.h" -#include "pool.h" +#include #include void (*consdebug)(void) = nil; @@ -21,7 +21,6 @@ int panicking; char *sysname; vlong fasthz; -static void seedrand(void); static int readtime(ulong, char*, int); static int readbintime(char*, int); static int writetime(char*, int); @@ -616,7 +615,8 @@ consread(Chan *c, void *buf, long n, vlong off) "%lud/%lud user\n" "%lud/%lud swap\n" "%llud/%llud/%llud kernel malloc\n" - "%llud/%llud/%llud kernel draw\n", + "%llud/%llud/%llud kernel draw\n" + "%llud/%llud/%llud kernel secret\n", (uvlong)conf.npage*BY2PG, (uvlong)BY2PG, conf.npage-conf.upages, @@ -627,7 +627,10 @@ consread(Chan *c, void *buf, long n, vlong off) (uvlong)mainmem->maxsize, (uvlong)imagmem->curalloc, (uvlong)imagmem->cursize, - (uvlong)imagmem->maxsize); + (uvlong)imagmem->maxsize, + (uvlong)secrmem->curalloc, + (uvlong)secrmem->cursize, + (uvlong)secrmem->maxsize); return readstr((ulong)offset, buf, n, tmp); @@ -845,31 +848,22 @@ Dev consdevtab = { static ulong randn; -static void -seedrand(void) +int +rand(void) { - if(!waserror()){ + if(randn == 0) randomread((void*)&randn, sizeof(randn)); - poperror(); - } + randn = randn*1103515245 + 12345 + MACHP(0)->ticks; + return randn; } int nrand(int n) { - if(randn == 0) - seedrand(); - randn = randn*1103515245 + 12345 + MACHP(0)->ticks; + rand(); return (randn>>16) % n; } -int -rand(void) -{ - nrand(1); - return randn; -} - static uvlong uvorder = 0x0001020304050607ULL; static uchar* diff --git a/sys/src/9/port/portfns.h b/sys/src/9/port/portfns.h index bd0c6f16c..6f84fc691 100644 --- a/sys/src/9/port/portfns.h +++ b/sys/src/9/port/portfns.h @@ -126,6 +126,7 @@ void gotolabel(Label*); char* getconfenv(void); long hostdomainwrite(char*, int); long hostownerwrite(char*, int); +void (*hwrandbuf)(void*, ulong); void hzsched(void); Block* iallocb(int); void iallocsummary(void); diff --git a/sys/src/9/port/random.c b/sys/src/9/port/random.c index f578d68cc..012424aa6 100644 --- a/sys/src/9/port/random.c +++ b/sys/src/9/port/random.c @@ -5,135 +5,103 @@ #include "fns.h" #include "../port/error.h" -struct Rb +#include + +/* machine specific hardware random number generator */ +void (*hwrandbuf)(void*, ulong) = nil; + +static struct { QLock; - Rendez producer; - Rendez consumer; - ulong randomcount; - uchar buf[128]; - uchar *ep; - uchar *rp; - uchar *wp; - uchar next; - uchar wakeme; - ushort bits; - ulong randn; -} rb; + Chachastate; +} *rs; -static int -rbnotfull(void*) +typedef struct Seedbuf Seedbuf; +struct Seedbuf { - int i; + ulong randomcount; + uchar buf[64]; + uchar nbuf; + uchar next; + ushort bits; - i = rb.rp - rb.wp; - return i != 1 && i != (1 - sizeof(rb.buf)); -} + SHA2_512state ds; +}; -static int -rbnotempty(void*) +static void +randomsample(Ureg*, Timer *t) { - return rb.wp != rb.rp; + Seedbuf *s = t->ta; + + if(s->randomcount == 0 || s->nbuf >= sizeof(s->buf)) + return; + s->bits = (s->bits<<2) ^ s->randomcount; + s->randomcount = 0; + if(++s->next < 8/2) + return; + s->next = 0; + s->buf[s->nbuf++] ^= s->bits; } static void -genrandom(void*) +randomseed(void*) { - up->basepri = PriNormal; - up->priority = up->basepri; + Seedbuf *s; - while(waserror()) - ; - for(;;){ - if(++rb.randomcount <= 100000) + s = secalloc(sizeof(Seedbuf)); + + if(hwrandbuf != nil) + (*hwrandbuf)(s->buf, sizeof(s->buf)); + + /* Frequency close but not equal to HZ */ + up->tns = (vlong)(MS2HZ+3)*1000000LL; + up->tmode = Tperiodic; + up->tt = nil; + up->ta = s; + up->tf = randomsample; + timeradd(up); + while(s->nbuf < sizeof(s->buf)){ + if(++s->randomcount <= 100000) continue; if(anyhigher()) sched(); - if(!rbnotfull(0)) - sleep(&rb.producer, rbnotfull, 0); } -} + timerdel(up); -/* - * produce random bits in a circular buffer - */ -static void -randomclock(void) -{ - if(rb.randomcount == 0 || !rbnotfull(0)) - return; + sha2_512(s->buf, sizeof(s->buf), s->buf, &s->ds); + setupChachastate(rs, s->buf, 32, s->buf+32, 12, 20); + qunlock(rs); - rb.bits = (rb.bits<<2) ^ rb.randomcount; - rb.randomcount = 0; + secfree(s); - rb.next++; - if(rb.next != 8/2) - return; - rb.next = 0; - - *rb.wp ^= rb.bits; - if(rb.wp+1 == rb.ep) - rb.wp = rb.buf; - else - rb.wp = rb.wp+1; - - if(rb.wakeme) - wakeup(&rb.consumer); + pexit("", 1); } void randominit(void) { - /* Frequency close but not equal to HZ */ - addclock0link(randomclock, MS2HZ+3); - rb.ep = rb.buf + sizeof(rb.buf); - rb.rp = rb.wp = rb.buf; - kproc("genrandom", genrandom, 0); + rs = secalloc(sizeof(*rs)); + qlock(rs); /* randomseed() unlocks once seeded */ + kproc("randomseed", randomseed, nil); } -/* - * consume random bytes from a circular buffer - */ ulong randomread(void *xp, ulong n) { - uchar *e, *p; - ulong x; + if(n == 0) + return 0; - p = xp; + if(hwrandbuf != nil) + (*hwrandbuf)(xp, n); if(waserror()){ - qunlock(&rb); + qunlock(rs); nexterror(); } - - qlock(&rb); - for(e = p + n; p < e; ){ - if(rb.wp == rb.rp){ - rb.wakeme = 1; - wakeup(&rb.producer); - sleep(&rb.consumer, rbnotempty, 0); - rb.wakeme = 0; - continue; - } - - /* - * beating clocks will be predictable if - * they are synchronized. Use a cheap pseudo- - * random number generator to obscure any cycles. - */ - x = rb.randn*1103515245 ^ *rb.rp; - *p++ = rb.randn = x; - - if(rb.rp+1 == rb.ep) - rb.rp = rb.buf; - else - rb.rp = rb.rp+1; - } - qunlock(&rb); + qlock(rs); + chacha_encrypt((uchar*)xp, n, rs); + qunlock(rs); poperror(); - wakeup(&rb.producer); - return n; }