source: trunk/minix/drivers/random/random.c@ 10

Last change on this file since 10 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 4.8 KB
RevLine 
[9]1/*
2random.c
3
4Random number generator.
5
6The random number generator collects data from the kernel and compressed
7that data into a seed for a psuedo random number generator.
8*/
9
10#include "../drivers.h"
11#include "../../kernel/const.h"
12#include "assert.h"
13
14#include "random.h"
15#include "sha2.h"
16#include "aes/rijndael.h"
17
18#define N_DERIV 16
19#define NR_POOLS 32
20#define MIN_SAMPLES 256 /* Number of samples needed in pool 0 for a
21 * re-seed.
22 */
23
24PRIVATE unsigned long deriv[RANDOM_SOURCES][N_DERIV];
25PRIVATE int pool_ind[RANDOM_SOURCES];
26PRIVATE SHA256_CTX pool_ctx[NR_POOLS];
27PRIVATE unsigned samples= 0;
28PRIVATE int got_seeded= 0;
29PRIVATE u8_t random_key[2*AES_BLOCKSIZE];
30PRIVATE u32_t count_lo, count_hi;
31PRIVATE u32_t reseed_count;
32
33FORWARD _PROTOTYPE( void add_sample, (int source, unsigned long sample) );
34FORWARD _PROTOTYPE( void data_block, (rd_keyinstance *keyp,
35 void *data) );
36FORWARD _PROTOTYPE( void reseed, (void) );
37
38PUBLIC void random_init()
39{
40 int i, j;
41
42 assert(&deriv[RANDOM_SOURCES-1][N_DERIV-1] ==
43 &deriv[0][0] + RANDOM_SOURCES*N_DERIV -1);
44
45 for (i= 0; i<RANDOM_SOURCES; i++)
46 {
47 for (j= 0; j<N_DERIV; j++)
48 deriv[i][j]= 0;
49 pool_ind[i]= 0;
50 }
51 for (i= 0; i<NR_POOLS; i++)
52 SHA256_Init(&pool_ctx[i]);
53 count_lo= 0;
54 count_hi= 0;
55 reseed_count= 0;
56}
57
58PUBLIC int random_isseeded()
59{
60 if (got_seeded)
61 return 1;
62 return 0;
63}
64
65PUBLIC void random_update(source, buf, count)
66int source;
67unsigned short *buf;
68int count;
69{
70 int i;
71
72#if 0
73 printf("random_update: got %d samples for source %d\n", count, source);
74#endif
75 if (source < 0 || source >= RANDOM_SOURCES)
76 panic("memory", "random_update: bad source", source);
77 for (i= 0; i<count; i++)
78 add_sample(source, buf[i]);
79 reseed();
80}
81
82PUBLIC void random_getbytes(buf, size)
83void *buf;
84size_t size;
85{
86 int n, r;
87 u8_t *cp;
88 rd_keyinstance key;
89 u8_t output[AES_BLOCKSIZE];
90
91 r= rijndael_makekey(&key, sizeof(random_key), random_key);
92 assert(r == 0);
93
94 cp= buf;
95 while (size > 0)
96 {
97 n= AES_BLOCKSIZE;
98 if (n > size)
99 {
100 n= size;
101 data_block(&key, output);
102 memcpy(cp, output, n);
103 }
104 else
105 data_block(&key, cp);
106 cp += n;
107 size -= n;
108 }
109
110 /* Generate new key */
111 assert(sizeof(random_key) == 2*AES_BLOCKSIZE);
112 data_block(&key, random_key);
113 data_block(&key, random_key+AES_BLOCKSIZE);
114}
115
116PUBLIC void random_putbytes(buf, size)
117void *buf;
118size_t size;
119{
120 /* Add bits to pool zero */
121 SHA256_Update(&pool_ctx[0], buf, size);
122
123 /* Assume that these bits are truely random. Increment samples
124 * with the number of bits.
125 */
126 samples += size*8;
127
128 reseed();
129}
130
131PRIVATE void add_sample(source, sample)
132int source;
133unsigned long sample;
134{
135 int i, pool_nr;
136 unsigned long d, v, di, min;
137
138 /* Delete bad sample. Compute the Nth derivative. Delete the sample
139 * if any derivative is too small.
140 */
141 min= (unsigned long)-1;
142 v= sample;
143 for (i= 0; i<N_DERIV; i++)
144 {
145 di= deriv[source][i];
146
147 /* Compute the difference */
148 if (v >= di)
149 d= v-di;
150 else
151 d= di-v;
152 deriv[source][i]= v;
153 v= d;
154 if (v <min)
155 min= v;
156 }
157 if (min < 2)
158 {
159#if 0
160 printf("ignoring sample '%u' from source %d\n",
161 sample, source);
162#endif
163 return;
164 }
165#if 0
166 printf("accepting sample '%u' from source %d\n", sample, source);
167#endif
168
169 pool_nr= pool_ind[source];
170 assert(pool_nr >= 0 && pool_nr < NR_POOLS);
171
172 SHA256_Update(&pool_ctx[pool_nr], (unsigned char *)&sample,
173 sizeof(sample));
174 if (pool_nr == 0)
175 samples++;
176 pool_nr++;
177 if (pool_nr >= NR_POOLS)
178 pool_nr= 0;
179 pool_ind[source]= pool_nr;
180}
181
182PRIVATE void data_block(keyp, data)
183rd_keyinstance *keyp;
184void *data;
185{
186 int r;
187 u8_t input[AES_BLOCKSIZE];
188
189 memset(input, '\0', sizeof(input));
190
191 /* Do we want the output of the random numbers to be portable
192 * across platforms (for example for RSA signatures)? At the moment
193 * we don't do anything special. Encrypt the counter with the AES
194 * key.
195 */
196 assert(sizeof(count_lo)+sizeof(count_hi) <= AES_BLOCKSIZE);
197 memcpy(input, &count_lo, sizeof(count_lo));
198 memcpy(input+sizeof(count_lo), &count_hi, sizeof(count_hi));
199 r= rijndael_ecb_encrypt(keyp, input, data, AES_BLOCKSIZE, NULL);
200 assert(r == AES_BLOCKSIZE);
201
202 count_lo++;
203 if (count_lo == 0)
204 count_hi++;
205}
206
207PRIVATE void reseed()
208{
209 int i;
210 SHA256_CTX ctx;
211 u8_t digest[SHA256_DIGEST_LENGTH];
212
213 if (samples < MIN_SAMPLES)
214 return;
215
216 reseed_count++;
217 SHA256_Init(&ctx);
218 if (got_seeded)
219 SHA256_Update(&ctx, random_key, sizeof(random_key));
220 SHA256_Final(digest, &pool_ctx[0]);
221 SHA256_Update(&ctx, digest, sizeof(digest));
222 SHA256_Init(&pool_ctx[0]);
223 for (i= 1; i<NR_POOLS; i++)
224 {
225 if ((reseed_count & (1UL << (i-1))) != 0)
226 break;
227 SHA256_Final(digest, &pool_ctx[i]);
228 SHA256_Update(&ctx, digest, sizeof(digest));
229 SHA256_Init(&pool_ctx[i]);
230 }
231 SHA256_Final(digest, &ctx);
232 assert(sizeof(random_key) == sizeof(digest));
233 memcpy(random_key, &digest, sizeof(random_key));
234 samples= 0;
235
236 got_seeded= 1;
237}
238
Note: See TracBrowser for help on using the repository browser.