]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/random.c
kernel: xoroshiro128+ generator for rand()/nrand()
[plan9front.git] / sys / src / 9 / port / random.c
1 #include        "u.h"
2 #include        "../port/lib.h"
3 #include        "mem.h"
4 #include        "dat.h"
5 #include        "fns.h"
6 #include        "../port/error.h"
7
8 #include        <libsec.h>
9
10 /* machine specific hardware random number generator */
11 void (*hwrandbuf)(void*, ulong) = nil;
12
13 static struct
14 {
15         QLock;
16         Chachastate;
17 } *rs;
18
19 typedef struct Seedbuf Seedbuf;
20 struct Seedbuf
21 {
22         ulong           randomcount;
23         uchar           buf[64];
24         uchar           nbuf;
25         uchar           next;
26         ushort          bits;
27
28         SHA2_512state   ds;
29 };
30
31 static void
32 randomsample(Ureg*, Timer *t)
33 {
34         Seedbuf *s = t->ta;
35
36         if(s->randomcount == 0 || s->nbuf >= sizeof(s->buf))
37                 return;
38         s->bits = (s->bits<<2) ^ s->randomcount;
39         s->randomcount = 0;
40         if(++s->next < 8/2)
41                 return;
42         s->next = 0;
43         s->buf[s->nbuf++] ^= s->bits;
44 }
45
46 static void
47 randomseed(void*)
48 {
49         Seedbuf *s;
50
51         s = secalloc(sizeof(Seedbuf));
52
53         if(hwrandbuf != nil)
54                 (*hwrandbuf)(s->buf, sizeof(s->buf));
55
56         /* Frequency close but not equal to HZ */
57         up->tns = (vlong)(MS2HZ+3)*1000000LL;
58         up->tmode = Tperiodic;
59         up->tt = nil;
60         up->ta = s;
61         up->tf = randomsample;
62         timeradd(up);
63         while(s->nbuf < sizeof(s->buf)){
64                 if(++s->randomcount <= 100000)
65                         continue;
66                 if(anyhigher())
67                         sched();
68         }
69         timerdel(up);
70
71         sha2_512(s->buf, sizeof(s->buf), s->buf, &s->ds);
72         setupChachastate(rs, s->buf, 32, s->buf+32, 12, 20);
73         qunlock(rs);
74
75         secfree(s);
76
77         pexit("", 1);
78 }
79
80 void
81 randominit(void)
82 {
83         rs = secalloc(sizeof(*rs));
84         qlock(rs);      /* randomseed() unlocks once seeded */
85         kproc("randomseed", randomseed, nil);
86 }
87
88 ulong
89 randomread(void *p, ulong n)
90 {
91         Chachastate c;
92         ulong b;
93
94         if(n == 0)
95                 return 0;
96
97         if(hwrandbuf != nil)
98                 (*hwrandbuf)(p, n);
99
100         /* copy chacha state and advance block counter */
101         qlock(rs);
102         c = *rs;
103         b = rs->input[12];
104         rs->input[12] += (n + ChachaBsize-1)/ChachaBsize;
105         if(rs->input[12] < b) rs->input[13]++;
106         qunlock(rs);
107
108         /* encrypt the buffer, can fault */
109         chacha_encrypt((uchar*)p, n, &c);
110
111         /* prevent state leakage */
112         memset(&c, 0, sizeof(c));
113
114         return n;
115 }
116
117 /* used by fastrand() */
118 void
119 genrandom(uchar *p, int n)
120 {
121         randomread(p, n);
122 }
123
124 /* used by rand(),nrand() */
125 long
126 lrand(void)
127 {
128         /* xoroshiro128+ algorithm */
129         static int seeded = 0;
130         static uvlong s[2];
131         static Lock lk;
132         ulong r;
133
134         if(seeded == 0){
135                 randomread(s, sizeof(s));
136                 seeded = (s[0] | s[1]) != 0;
137         }
138
139         lock(&lk);
140         r = (s[0] + s[1]) >> 33;
141         s[1] ^= s[0];
142         s[0] = (s[0] << 55 | s[0] >> 9) ^ s[1] ^ (s[1] << 14);
143         s[1] = (s[1] << 36 | s[1] >> 28);
144         unlock(&lk);
145
146         return r;
147 }