yarrow.c revision 153575
1/*-
2 * Copyright (c) 2000-2004 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 153575 2005-12-20 21:41:52Z ps $");
30
31#include <sys/param.h>
32#include <sys/kernel.h>
33#include <sys/lock.h>
34#include <sys/malloc.h>
35#include <sys/mutex.h>
36#include <sys/random.h>
37#include <sys/sysctl.h>
38#include <sys/systm.h>
39
40#include <crypto/rijndael/rijndael-api-fst.h>
41#include <crypto/sha2/sha2.h>
42
43#include <dev/random/hash.h>
44#include <dev/random/randomdev_soft.h>
45#include <dev/random/yarrow.h>
46
47RANDOM_CHECK_UINT(gengateinterval, 4, 64);
48RANDOM_CHECK_UINT(bins, 2, 16);
49RANDOM_CHECK_UINT(fastthresh, BLOCKSIZE/4, BLOCKSIZE);
50RANDOM_CHECK_UINT(slowthresh, BLOCKSIZE/4, BLOCKSIZE);
51RANDOM_CHECK_UINT(slowoverthresh, 1, 5);
52
53/* Structure holding the entropy state */
54static struct random_state random_state;
55
56static void generator_gate(void);
57static void reseed(u_int);
58
59/* The reseed thread mutex */
60struct mtx random_reseed_mtx;
61
62/* Process a single stochastic event off the harvest queue */
63void
64random_process_event(struct harvest *event)
65{
66	u_int pl, overthreshhold[2];
67	struct source *source;
68	enum esource src;
69
70	/* Unpack the event into the appropriate source accumulator */
71	pl = random_state.which;
72	source = &random_state.pool[pl].source[event->source];
73	yarrow_hash_iterate(&random_state.pool[pl].hash, event->entropy,
74		sizeof(event->entropy));
75	yarrow_hash_iterate(&random_state.pool[pl].hash, &event->somecounter,
76		sizeof(event->somecounter));
77	source->frac += event->frac;
78	source->bits += event->bits + source->frac/1024;
79	source->frac %= 1024;
80
81	/* Count the over-threshold sources in each pool */
82	for (pl = 0; pl < 2; pl++) {
83		overthreshhold[pl] = 0;
84		for (src = RANDOM_START; src < ENTROPYSOURCE; src++) {
85			if (random_state.pool[pl].source[src].bits
86				> random_state.pool[pl].thresh)
87				overthreshhold[pl]++;
88		}
89	}
90
91	/* if any fast source over threshhold, reseed */
92	if (overthreshhold[FAST])
93		reseed(FAST);
94
95	/* if enough slow sources are over threshhold, reseed */
96	if (overthreshhold[SLOW] >= random_state.slowoverthresh)
97		reseed(SLOW);
98
99	/* Invert the fast/slow pool selector bit */
100	random_state.which = !random_state.which;
101}
102
103void
104random_yarrow_init_alg(struct sysctl_ctx_list *clist, struct sysctl_oid *in_o)
105{
106	int i;
107	struct sysctl_oid *o, *random_yarrow_o;
108
109	/* Yarrow parameters. Do not adjust these unless you have
110	 * have a very good clue about what they do!
111	 */
112	o = SYSCTL_ADD_NODE(clist,
113		SYSCTL_CHILDREN(in_o),
114		OID_AUTO, "yarrow", CTLFLAG_RW, 0,
115		"Yarrow Parameters");
116
117	random_yarrow_o = o;
118
119	o = SYSCTL_ADD_PROC(clist,
120		SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO,
121		"gengateinterval", CTLTYPE_INT|CTLFLAG_RW,
122		&random_state.gengateinterval, 10,
123		random_check_uint_gengateinterval, "I",
124		"Generation gate interval");
125
126	o = SYSCTL_ADD_PROC(clist,
127		SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO,
128		"bins", CTLTYPE_INT|CTLFLAG_RW,
129		&random_state.bins, 10,
130		random_check_uint_bins, "I",
131		"Execution time tuner");
132
133	o = SYSCTL_ADD_PROC(clist,
134		SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO,
135		"fastthresh", CTLTYPE_INT|CTLFLAG_RW,
136		&random_state.pool[0].thresh, (3*BLOCKSIZE)/4,
137		random_check_uint_fastthresh, "I",
138		"Fast reseed threshold");
139
140	o = SYSCTL_ADD_PROC(clist,
141		SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO,
142		"slowthresh", CTLTYPE_INT|CTLFLAG_RW,
143		&random_state.pool[1].thresh, BLOCKSIZE,
144		random_check_uint_slowthresh, "I",
145		"Slow reseed threshold");
146
147	o = SYSCTL_ADD_PROC(clist,
148		SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO,
149		"slowoverthresh", CTLTYPE_INT|CTLFLAG_RW,
150		&random_state.slowoverthresh, 2,
151		random_check_uint_slowoverthresh, "I",
152		"Slow over-threshold reseed");
153
154	random_state.gengateinterval = 10;
155	random_state.bins = 10;
156	random_state.pool[0].thresh = (3*BLOCKSIZE)/4;
157	random_state.pool[1].thresh = BLOCKSIZE;
158	random_state.slowoverthresh = 2;
159	random_state.which = FAST;
160
161	/* Initialise the fast and slow entropy pools */
162	for (i = 0; i < 2; i++)
163		yarrow_hash_init(&random_state.pool[i].hash);
164
165	/* Clear the counter */
166	for (i = 0; i < 4; i++)
167		random_state.counter[i] = 0;
168
169	/* Set up a lock for the reseed process */
170	mtx_init(&random_reseed_mtx, "random reseed", NULL, MTX_DEF);
171}
172
173void
174random_yarrow_deinit_alg(void)
175{
176	mtx_destroy(&random_reseed_mtx);
177}
178
179static void
180reseed(u_int fastslow)
181{
182	/* Interrupt-context stack is a limited resource; make large
183	 * structures static.
184	 */
185	static u_char v[TIMEBIN][KEYSIZE];	/* v[i] */
186	static struct yarrowhash context;
187	u_char hash[KEYSIZE];			/* h' */
188	u_char temp[KEYSIZE];
189	u_int i;
190	enum esource j;
191
192	/* The reseed task must not be jumped on */
193	mtx_lock(&random_reseed_mtx);
194
195	/* 1. Hash the accumulated entropy into v[0] */
196
197	yarrow_hash_init(&context);
198	/* Feed the slow pool hash in if slow */
199	if (fastslow == SLOW)
200		yarrow_hash_iterate(&context,
201			&random_state.pool[SLOW].hash,
202			sizeof(struct yarrowhash));
203	yarrow_hash_iterate(&context,
204		&random_state.pool[FAST].hash, sizeof(struct yarrowhash));
205	yarrow_hash_finish(&context, v[0]);
206
207	/* 2. Compute hash values for all v. _Supposed_ to be computationally
208	 *    intensive.
209	 */
210
211	if (random_state.bins > TIMEBIN)
212		random_state.bins = TIMEBIN;
213	for (i = 1; i < random_state.bins; i++) {
214		yarrow_hash_init(&context);
215		/* v[i] #= h(v[i - 1]) */
216		yarrow_hash_iterate(&context, v[i - 1], KEYSIZE);
217		/* v[i] #= h(v[0]) */
218		yarrow_hash_iterate(&context, v[0], KEYSIZE);
219		/* v[i] #= h(i) */
220		yarrow_hash_iterate(&context, &i, sizeof(u_int));
221		/* Return the hashval */
222		yarrow_hash_finish(&context, v[i]);
223	}
224
225	/* 3. Compute a new key; h' is the identity function here;
226	 *    it is not being ignored!
227	 */
228
229	yarrow_hash_init(&context);
230	yarrow_hash_iterate(&context, &random_state.key, KEYSIZE);
231	for (i = 1; i < random_state.bins; i++)
232		yarrow_hash_iterate(&context, &v[i], KEYSIZE);
233	yarrow_hash_finish(&context, temp);
234	yarrow_encrypt_init(&random_state.key, temp);
235
236	/* 4. Recompute the counter */
237
238	for (i = 0; i < 4; i++)
239		random_state.counter[i] = 0;
240	yarrow_encrypt(&random_state.key, random_state.counter, temp);
241	memcpy(random_state.counter, temp, sizeof(random_state.counter));
242
243	/* 5. Reset entropy estimate accumulators to zero */
244
245	for (i = 0; i <= fastslow; i++) {
246		for (j = RANDOM_START; j < ENTROPYSOURCE; j++) {
247			random_state.pool[i].source[j].bits = 0;
248			random_state.pool[i].source[j].frac = 0;
249		}
250	}
251
252	/* 6. Wipe memory of intermediate values */
253
254	memset((void *)v, 0, sizeof(v));
255	memset((void *)temp, 0, sizeof(temp));
256	memset((void *)hash, 0, sizeof(hash));
257
258	/* 7. Dump to seed file */
259	/* XXX Not done here yet */
260
261	/* Unblock the device if it was blocked due to being unseeded */
262	random_yarrow_unblock();
263
264	/* Release the reseed mutex */
265	mtx_unlock(&random_reseed_mtx);
266}
267
268/* Internal function to return processed entropy from the PRNG */
269int
270random_yarrow_read(void *buf, int count)
271{
272	static int cur = 0;
273	static int gate = 1;
274	static u_char genval[KEYSIZE];
275	size_t tomove;
276	int i;
277	int retval;
278
279	/* The reseed task must not be jumped on */
280	mtx_lock(&random_reseed_mtx);
281
282	if (gate) {
283		generator_gate();
284		random_state.outputblocks = 0;
285		gate = 0;
286	}
287	if (count > 0 && (size_t)count >= sizeof(random_state.counter)) {
288		retval = 0;
289		for (i = 0; i < count; i += (int)sizeof(random_state.counter)) {
290			random_state.counter[0]++;
291			yarrow_encrypt(&random_state.key, random_state.counter,
292				genval);
293			tomove = min(count - i, sizeof(random_state.counter));
294			memcpy((char *)buf + i, genval, tomove);
295			if (++random_state.outputblocks >=
296				random_state.gengateinterval) {
297				generator_gate();
298				random_state.outputblocks = 0;
299			}
300			retval += (int)tomove;
301		}
302	}
303	else {
304		if (!cur) {
305			random_state.counter[0]++;
306			yarrow_encrypt(&random_state.key, random_state.counter,
307				genval);
308			memcpy(buf, genval, (size_t)count);
309			cur = (int)sizeof(random_state.counter) - count;
310			if (++random_state.outputblocks >=
311				random_state.gengateinterval) {
312				generator_gate();
313				random_state.outputblocks = 0;
314			}
315			retval = count;
316		}
317		else {
318			retval = MIN(cur, count);
319			memcpy(buf,
320			    &genval[(int)sizeof(random_state.counter) - cur],
321			    (size_t)retval);
322			cur -= retval;
323		}
324	}
325	mtx_unlock(&random_reseed_mtx);
326	return retval;
327}
328
329static void
330generator_gate(void)
331{
332	u_int i;
333	u_char temp[KEYSIZE];
334
335	for (i = 0; i < KEYSIZE; i += sizeof(random_state.counter)) {
336		random_state.counter[0]++;
337		yarrow_encrypt(&random_state.key, random_state.counter,
338			&(temp[i]));
339	}
340
341	yarrow_encrypt_init(&random_state.key, temp);
342	memset((void *)temp, 0, KEYSIZE);
343
344}
345
346/* Helper routine to perform explicit reseeds */
347void
348random_yarrow_reseed(void)
349{
350	reseed(SLOW);
351}
352