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