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