yarrow.c revision 119418
1/*-
2 * Copyright (c) 2000 Mark R V Murray
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer
10 *    in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/sys/dev/random/yarrow.c 119418 2003-08-24 17:55:58Z obrien $");
30
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/kernel.h>
34#include <sys/lock.h>
35#include <sys/mutex.h>
36#include <sys/random.h>
37#include <sys/sysctl.h>
38
39#include <crypto/rijndael/rijndael.h>
40#include <crypto/sha2/sha2.h>
41
42#include <dev/random/hash.h>
43#include <dev/random/randomdev.h>
44#include <dev/random/yarrow.h>
45
46/* #define DEBUG */
47
48RANDOM_CHECK_UINT(gengateinterval, 4, 64);
49RANDOM_CHECK_UINT(bins, 2, 16);
50RANDOM_CHECK_UINT(fastthresh, BLOCKSIZE/4, BLOCKSIZE);
51RANDOM_CHECK_UINT(slowthresh, BLOCKSIZE/4, BLOCKSIZE);
52RANDOM_CHECK_UINT(slowoverthresh, 1, 5);
53
54/* Structure holding the entropy state */
55static struct random_state random_state;
56
57SYSCTL_NODE(_kern_random, OID_AUTO, yarrow, CTLFLAG_RW, 0, "Yarrow Parameters");
58SYSCTL_PROC(_kern_random_yarrow, OID_AUTO, gengateinterval,
59	CTLTYPE_INT|CTLFLAG_RW, &random_state.gengateinterval, 10,
60	random_check_uint_gengateinterval, "I", "Generator Gate Interval");
61SYSCTL_PROC(_kern_random_yarrow, OID_AUTO, bins,
62	CTLTYPE_INT|CTLFLAG_RW, &random_state.bins, 10,
63	random_check_uint_bins, "I", "Execution time tuner");
64SYSCTL_PROC(_kern_random_yarrow, OID_AUTO, fastthresh,
65	CTLTYPE_INT|CTLFLAG_RW, &random_state.pool[0].thresh, (3*BLOCKSIZE)/4,
66	random_check_uint_fastthresh, "I", "Fast reseed threshold");
67SYSCTL_PROC(_kern_random_yarrow, OID_AUTO, slowthresh,
68	CTLTYPE_INT|CTLFLAG_RW, &random_state.pool[1].thresh, BLOCKSIZE,
69	random_check_uint_slowthresh, "I", "Slow reseed threshold");
70SYSCTL_PROC(_kern_random_yarrow, OID_AUTO, slowoverthresh,
71	CTLTYPE_INT|CTLFLAG_RW, &random_state.slowoverthresh, 2,
72	random_check_uint_slowoverthresh, "I", "Slow over-threshold reseed");
73
74static void generator_gate(void);
75static void reseed(u_int);
76
77/* The reseed thread mutex */
78static struct mtx random_reseed_mtx;
79
80/* Process a single stochastic event off the harvest queue */
81void
82random_process_event(struct harvest *event)
83{
84	u_int pl, overthreshhold[2];
85	struct source *source;
86	enum esource src;
87
88	/* Unpack the event into the appropriate source accumulator */
89	pl = random_state.which;
90	source = &random_state.pool[pl].source[event->source];
91	yarrow_hash_iterate(&random_state.pool[pl].hash, event->entropy,
92		sizeof(event->entropy));
93	yarrow_hash_iterate(&random_state.pool[pl].hash, &event->somecounter,
94		sizeof(event->somecounter));
95	source->frac += event->frac;
96	source->bits += event->bits + source->frac/1024;
97	source->frac %= 1024;
98
99	/* Count the over-threshold sources in each pool */
100	for (pl = 0; pl < 2; pl++) {
101		overthreshhold[pl] = 0;
102		for (src = RANDOM_START; src < ENTROPYSOURCE; src++) {
103			if (random_state.pool[pl].source[src].bits
104				> random_state.pool[pl].thresh)
105				overthreshhold[pl]++;
106		}
107	}
108
109	/* if any fast source over threshhold, reseed */
110	if (overthreshhold[FAST])
111		reseed(FAST);
112
113	/* if enough slow sources are over threshhold, reseed */
114	if (overthreshhold[SLOW] >= random_state.slowoverthresh)
115		reseed(SLOW);
116
117	/* Invert the fast/slow pool selector bit */
118	random_state.which = !random_state.which;
119}
120
121void
122random_init(void)
123{
124	int i;
125
126	/* Yarrow parameters. Do not adjust these unless you have
127	 * have a very good clue about what they do!
128	 */
129	random_state.gengateinterval = 10;
130	random_state.bins = 10;
131	random_state.pool[0].thresh = (3*BLOCKSIZE)/4;
132	random_state.pool[1].thresh = BLOCKSIZE;
133	random_state.slowoverthresh = 2;
134	random_state.which = FAST;
135
136	/* Initialise the fast and slow entropy pools */
137	for (i = 0; i < 2; i++)
138		yarrow_hash_init(&random_state.pool[i].hash);
139
140	/* Clear the counter */
141	for (i = 0; i < 4; i++)
142		random_state.counter[i] = 0;
143
144	/* Set up a lock for the reseed process */
145	mtx_init(&random_reseed_mtx, "random reseed", NULL, MTX_DEF);
146}
147
148void
149random_deinit(void)
150{
151	mtx_destroy(&random_reseed_mtx);
152}
153
154static void
155reseed(u_int fastslow)
156{
157	/* Interrupt-context stack is a limited resource; make large
158	 * structures static.
159	 */
160	static u_char v[TIMEBIN][KEYSIZE];	/* v[i] */
161	static struct yarrowhash context;
162	u_char hash[KEYSIZE];			/* h' */
163	u_char temp[KEYSIZE];
164	u_int i;
165	enum esource j;
166
167#ifdef DEBUG
168	printf("Reseed type %d\n", fastslow);
169#endif
170
171	/* The reseed task must not be jumped on */
172	mtx_lock(&random_reseed_mtx);
173
174	/* 1. Hash the accumulated entropy into v[0] */
175
176	yarrow_hash_init(&context);
177	/* Feed the slow pool hash in if slow */
178	if (fastslow == SLOW)
179		yarrow_hash_iterate(&context,
180			&random_state.pool[SLOW].hash,
181			sizeof(struct yarrowhash));
182	yarrow_hash_iterate(&context,
183		&random_state.pool[FAST].hash, sizeof(struct yarrowhash));
184	yarrow_hash_finish(&context, v[0]);
185
186	/* 2. Compute hash values for all v. _Supposed_ to be computationally
187	 *    intensive.
188	 */
189
190	if (random_state.bins > TIMEBIN)
191		random_state.bins = TIMEBIN;
192	for (i = 1; i < random_state.bins; i++) {
193		yarrow_hash_init(&context);
194		/* v[i] #= h(v[i - 1]) */
195		yarrow_hash_iterate(&context, v[i - 1], KEYSIZE);
196		/* v[i] #= h(v[0]) */
197		yarrow_hash_iterate(&context, v[0], KEYSIZE);
198		/* v[i] #= h(i) */
199		yarrow_hash_iterate(&context, &i, sizeof(u_int));
200		/* Return the hashval */
201		yarrow_hash_finish(&context, v[i]);
202	}
203
204	/* 3. Compute a new key; h' is the identity function here;
205	 *    it is not being ignored!
206	 */
207
208	yarrow_hash_init(&context);
209	yarrow_hash_iterate(&context, &random_state.key, KEYSIZE);
210	for (i = 1; i < random_state.bins; i++)
211		yarrow_hash_iterate(&context, &v[i], KEYSIZE);
212	yarrow_hash_finish(&context, temp);
213	yarrow_encrypt_init(&random_state.key, temp);
214
215	/* 4. Recompute the counter */
216
217	for (i = 0; i < 4; i++)
218		random_state.counter[i] = 0;
219	yarrow_encrypt(&random_state.key, random_state.counter, temp);
220	memcpy(random_state.counter, temp, sizeof(random_state.counter));
221
222	/* 5. Reset entropy estimate accumulators to zero */
223
224	for (i = 0; i <= fastslow; i++) {
225		for (j = RANDOM_START; j < ENTROPYSOURCE; j++) {
226			random_state.pool[i].source[j].bits = 0;
227			random_state.pool[i].source[j].frac = 0;
228		}
229	}
230
231	/* 6. Wipe memory of intermediate values */
232
233	memset((void *)v, 0, sizeof(v));
234	memset((void *)temp, 0, sizeof(temp));
235	memset((void *)hash, 0, sizeof(hash));
236
237	/* 7. Dump to seed file */
238	/* XXX Not done here yet */
239
240	/* Release the reseed mutex */
241	mtx_unlock(&random_reseed_mtx);
242
243#ifdef DEBUG
244	printf("Reseed finish\n");
245#endif
246
247	/* Unblock the device if it was blocked due to being unseeded */
248	random_unblock();
249}
250
251/* Internal function to return processed entropy from the PRNG */
252int
253read_random_real(void *buf, int count)
254{
255	static int cur = 0;
256	static int gate = 1;
257	static u_char genval[KEYSIZE];
258	size_t tomove;
259	int i;
260	int retval;
261
262	/* The reseed task must not be jumped on */
263	mtx_lock(&random_reseed_mtx);
264
265	if (gate) {
266		generator_gate();
267		random_state.outputblocks = 0;
268		gate = 0;
269	}
270	if (count > 0 && (size_t)count >= sizeof(random_state.counter)) {
271		retval = 0;
272		for (i = 0; i < count; i += (int)sizeof(random_state.counter)) {
273			random_state.counter[0]++;
274			yarrow_encrypt(&random_state.key, random_state.counter,
275				genval);
276			tomove = min(count - i, sizeof(random_state.counter));
277			memcpy((char *)buf + i, genval, tomove);
278			if (++random_state.outputblocks >=
279				random_state.gengateinterval) {
280				generator_gate();
281				random_state.outputblocks = 0;
282			}
283			retval += (int)tomove;
284		}
285	}
286	else {
287		if (!cur) {
288			random_state.counter[0]++;
289			yarrow_encrypt(&random_state.key, random_state.counter,
290				genval);
291			memcpy(buf, genval, (size_t)count);
292			cur = (int)sizeof(random_state.counter) - count;
293			if (++random_state.outputblocks >=
294				random_state.gengateinterval) {
295				generator_gate();
296				random_state.outputblocks = 0;
297			}
298			retval = count;
299		}
300		else {
301			retval = cur < count ? cur : count;
302			memcpy(buf,
303			    &genval[(int)sizeof(random_state.counter) - cur],
304			    (size_t)retval);
305			cur -= retval;
306		}
307	}
308	mtx_unlock(&random_reseed_mtx);
309	return retval;
310}
311
312static void
313generator_gate(void)
314{
315	u_int i;
316	u_char temp[KEYSIZE];
317
318#ifdef DEBUG
319	printf("Generator gate\n");
320#endif
321
322	for (i = 0; i < KEYSIZE; i += sizeof(random_state.counter)) {
323		random_state.counter[0]++;
324		yarrow_encrypt(&random_state.key, random_state.counter,
325			&(temp[i]));
326	}
327
328	yarrow_encrypt_init(&random_state.key, temp);
329	memset((void *)temp, 0, KEYSIZE);
330
331#ifdef DEBUG
332	printf("Generator gate finish\n");
333#endif
334}
335
336/* Helper routine to perform explicit reseeds */
337void
338random_reseed(void)
339{
340	reseed(SLOW);
341}
342