kernel: switch to fast portable chacha based seed-once random number generator

This commit is contained in:
cinap_lenrek 2016-08-27 20:42:31 +02:00
parent 71ac88392f
commit 0a5f81a442
7 changed files with 83 additions and 267 deletions

View file

@ -872,6 +872,11 @@ cpuidentify(void)
fprestore = fpx87restore; fprestore = fpx87restore;
} }
if(strcmp(m->cpuidid, "GenuineIntel") == 0 && (m->cpuidcx & Rdrnd) != 0)
hwrandbuf = rdrandbuf;
else
hwrandbuf = nil;
cputype = t; cputype = t;
return t->family; return t->family;
} }

View file

@ -32,6 +32,7 @@ PORT=\
proc.$O\ proc.$O\
qio.$O\ qio.$O\
qlock.$O\ qlock.$O\
random.$O\
rdb.$O\ rdb.$O\
rebootcmd.$O\ rebootcmd.$O\
segment.$O\ segment.$O\
@ -52,7 +53,6 @@ OBJ=\
memory.$O\ memory.$O\
mmu.$O\ mmu.$O\
trap.$O\ trap.$O\
pcrandom.$O\
$CONF.root.$O\ $CONF.root.$O\
$CONF.rootc.$O\ $CONF.rootc.$O\
$DEVS\ $DEVS\

View file

@ -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;
}

View file

@ -29,6 +29,7 @@ PORT=\
proc.$O\ proc.$O\
qio.$O\ qio.$O\
qlock.$O\ qlock.$O\
random.$O\
rdb.$O\ rdb.$O\
rebootcmd.$O\ rebootcmd.$O\
segment.$O\ segment.$O\
@ -49,7 +50,6 @@ OBJ=\
memory.$O\ memory.$O\
mmu.$O\ mmu.$O\
trap.$O\ trap.$O\
pcrandom.$O\
$CONF.root.$O\ $CONF.root.$O\
$CONF.rootc.$O\ $CONF.rootc.$O\
$DEVS\ $DEVS\

View file

@ -4,8 +4,8 @@
#include "dat.h" #include "dat.h"
#include "fns.h" #include "fns.h"
#include "../port/error.h" #include "../port/error.h"
#include "pool.h"
#include <pool.h>
#include <authsrv.h> #include <authsrv.h>
void (*consdebug)(void) = nil; void (*consdebug)(void) = nil;
@ -21,7 +21,6 @@ int panicking;
char *sysname; char *sysname;
vlong fasthz; vlong fasthz;
static void seedrand(void);
static int readtime(ulong, char*, int); static int readtime(ulong, char*, int);
static int readbintime(char*, int); static int readbintime(char*, int);
static int writetime(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 user\n"
"%lud/%lud swap\n" "%lud/%lud swap\n"
"%llud/%llud/%llud kernel malloc\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)conf.npage*BY2PG,
(uvlong)BY2PG, (uvlong)BY2PG,
conf.npage-conf.upages, conf.npage-conf.upages,
@ -627,7 +627,10 @@ consread(Chan *c, void *buf, long n, vlong off)
(uvlong)mainmem->maxsize, (uvlong)mainmem->maxsize,
(uvlong)imagmem->curalloc, (uvlong)imagmem->curalloc,
(uvlong)imagmem->cursize, (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); return readstr((ulong)offset, buf, n, tmp);
@ -845,31 +848,22 @@ Dev consdevtab = {
static ulong randn; static ulong randn;
static void int
seedrand(void) rand(void)
{ {
if(!waserror()){ if(randn == 0)
randomread((void*)&randn, sizeof(randn)); randomread((void*)&randn, sizeof(randn));
poperror(); randn = randn*1103515245 + 12345 + MACHP(0)->ticks;
} return randn;
} }
int int
nrand(int n) nrand(int n)
{ {
if(randn == 0) rand();
seedrand();
randn = randn*1103515245 + 12345 + MACHP(0)->ticks;
return (randn>>16) % n; return (randn>>16) % n;
} }
int
rand(void)
{
nrand(1);
return randn;
}
static uvlong uvorder = 0x0001020304050607ULL; static uvlong uvorder = 0x0001020304050607ULL;
static uchar* static uchar*

View file

@ -126,6 +126,7 @@ void gotolabel(Label*);
char* getconfenv(void); char* getconfenv(void);
long hostdomainwrite(char*, int); long hostdomainwrite(char*, int);
long hostownerwrite(char*, int); long hostownerwrite(char*, int);
void (*hwrandbuf)(void*, ulong);
void hzsched(void); void hzsched(void);
Block* iallocb(int); Block* iallocb(int);
void iallocsummary(void); void iallocsummary(void);

View file

@ -5,135 +5,103 @@
#include "fns.h" #include "fns.h"
#include "../port/error.h" #include "../port/error.h"
struct Rb #include <libsec.h>
/* machine specific hardware random number generator */
void (*hwrandbuf)(void*, ulong) = nil;
static struct
{ {
QLock; QLock;
Rendez producer; Chachastate;
Rendez consumer; } *rs;
ulong randomcount;
uchar buf[128];
uchar *ep;
uchar *rp;
uchar *wp;
uchar next;
uchar wakeme;
ushort bits;
ulong randn;
} rb;
static int typedef struct Seedbuf Seedbuf;
rbnotfull(void*) struct Seedbuf
{ {
int i; ulong randomcount;
uchar buf[64];
uchar nbuf;
uchar next;
ushort bits;
i = rb.rp - rb.wp; SHA2_512state ds;
return i != 1 && i != (1 - sizeof(rb.buf)); };
}
static int static void
rbnotempty(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 static void
genrandom(void*) randomseed(void*)
{ {
up->basepri = PriNormal; Seedbuf *s;
up->priority = up->basepri;
while(waserror()) s = secalloc(sizeof(Seedbuf));
;
for(;;){ if(hwrandbuf != nil)
if(++rb.randomcount <= 100000) (*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; continue;
if(anyhigher()) if(anyhigher())
sched(); sched();
if(!rbnotfull(0))
sleep(&rb.producer, rbnotfull, 0);
} }
} timerdel(up);
/* sha2_512(s->buf, sizeof(s->buf), s->buf, &s->ds);
* produce random bits in a circular buffer setupChachastate(rs, s->buf, 32, s->buf+32, 12, 20);
*/ qunlock(rs);
static void
randomclock(void)
{
if(rb.randomcount == 0 || !rbnotfull(0))
return;
rb.bits = (rb.bits<<2) ^ rb.randomcount; secfree(s);
rb.randomcount = 0;
rb.next++; pexit("", 1);
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 void
randominit(void) randominit(void)
{ {
/* Frequency close but not equal to HZ */ rs = secalloc(sizeof(*rs));
addclock0link(randomclock, MS2HZ+3); qlock(rs); /* randomseed() unlocks once seeded */
rb.ep = rb.buf + sizeof(rb.buf); kproc("randomseed", randomseed, nil);
rb.rp = rb.wp = rb.buf;
kproc("genrandom", genrandom, 0);
} }
/*
* consume random bytes from a circular buffer
*/
ulong ulong
randomread(void *xp, ulong n) randomread(void *xp, ulong n)
{ {
uchar *e, *p; if(n == 0)
ulong x; return 0;
p = xp; if(hwrandbuf != nil)
(*hwrandbuf)(xp, n);
if(waserror()){ if(waserror()){
qunlock(&rb); qunlock(rs);
nexterror(); nexterror();
} }
qlock(rs);
qlock(&rb); chacha_encrypt((uchar*)xp, n, rs);
for(e = p + n; p < e; ){ qunlock(rs);
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(); poperror();
wakeup(&rb.producer);
return n; return n;
} }