fortuna.c revision 274340
1/*-
2 * Copyright (c) 2013-2014 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/* This implementation of Fortuna is based on the descriptions found in
29 * ISBN 0-471-22357-3 "Practical Cryptography" by Ferguson and Schneier
30 * ("F&S").
31 *
32 * The above book is superseded by ISBN 978-0-470-47424-2 "Cryptography
33 * Engineering" by Ferguson, Schneier and Kohno ("FS&K").  The code has
34 * not yet fully caught up with FS&K.
35 */
36
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD: head/sys/dev/random/fortuna.c 274340 2014-11-10 09:44:38Z des $");
39
40#ifdef _KERNEL
41#include "opt_random.h"
42
43#include <sys/param.h>
44#include <sys/kernel.h>
45#include <sys/lock.h>
46#include <sys/malloc.h>
47#include <sys/mutex.h>
48#include <sys/random.h>
49#include <sys/sysctl.h>
50#include <sys/systm.h>
51
52#include <machine/cpu.h>
53
54#include <crypto/rijndael/rijndael-api-fst.h>
55#include <crypto/sha2/sha2.h>
56
57#include <dev/random/hash.h>
58#include <dev/random/randomdev.h>
59#include <dev/random/random_adaptors.h>
60#include <dev/random/random_harvestq.h>
61#include <dev/random/uint128.h>
62#include <dev/random/fortuna.h>
63#else /* !_KERNEL */
64#include <sys/param.h>
65#include <sys/types.h>
66#include <inttypes.h>
67#include <stdio.h>
68#include <stdlib.h>
69#include <string.h>
70#include <threads.h>
71
72#include "unit_test.h"
73
74#include <crypto/rijndael/rijndael-api-fst.h>
75#include <crypto/sha2/sha2.h>
76
77#include <dev/random/hash.h>
78#include <dev/random/uint128.h>
79#include <dev/random/fortuna.h>
80#endif /* _KERNEL */
81
82#if !defined(RANDOM_YARROW) && !defined(RANDOM_FORTUNA)
83#define RANDOM_YARROW
84#elif defined(RANDOM_YARROW) && defined(RANDOM_FORTUNA)
85#error "Must define either RANDOM_YARROW or RANDOM_FORTUNA"
86#endif
87
88#if defined(RANDOM_FORTUNA)
89
90#define NPOOLS 32
91#define MINPOOLSIZE 64
92#define DEFPOOLSIZE 256
93#define MAXPOOLSIZE 65536
94
95/* This algorithm (and code) presumes that KEYSIZE is twice as large as BLOCKSIZE */
96CTASSERT(BLOCKSIZE == sizeof(uint128_t));
97CTASSERT(KEYSIZE == 2*BLOCKSIZE);
98
99/* This is the beastie that needs protecting. It contains all of the
100 * state that we are excited about.
101 * Exactly one is instantiated.
102 */
103static struct fortuna_state {
104	/* P_i */
105	struct pool {
106		u_int length;
107		struct randomdev_hash hash;
108	} pool[NPOOLS];
109
110	/* ReseedCnt */
111	u_int reseedcount;
112
113	/* C - 128 bits */
114	union {
115		uint8_t byte[BLOCKSIZE];
116		uint128_t whole;
117	} counter;
118
119	/* K */
120	struct randomdev_key key;
121
122	/* Extras */
123	u_int minpoolsize;
124
125	/* Extras for the OS */
126
127#ifdef _KERNEL
128	/* For use when 'pacing' the reseeds */
129	sbintime_t lasttime;
130#endif
131} fortuna_state;
132
133/* The random_reseed_mtx mutex protects seeding and polling/blocking.  */
134static mtx_t random_reseed_mtx;
135
136static struct fortuna_start_cache {
137	uint8_t junk[PAGE_SIZE];
138	size_t length;
139	struct randomdev_hash hash;
140} fortuna_start_cache;
141
142#ifdef _KERNEL
143static struct sysctl_ctx_list random_clist;
144RANDOM_CHECK_UINT(minpoolsize, MINPOOLSIZE, MAXPOOLSIZE);
145#endif
146
147void
148random_fortuna_init_alg(void)
149{
150	int i;
151#ifdef _KERNEL
152	struct sysctl_oid *random_fortuna_o;
153#endif
154
155	memset(fortuna_start_cache.junk, 0, sizeof(fortuna_start_cache.junk));
156	fortuna_start_cache.length = 0U;
157	randomdev_hash_init(&fortuna_start_cache.hash);
158
159	/* Set up a lock for the reseed process */
160#ifdef _KERNEL
161	mtx_init(&random_reseed_mtx, "reseed mutex", NULL, MTX_DEF);
162#else /* !_KERNEL */
163	mtx_init(&random_reseed_mtx, mtx_plain);
164#endif /* _KERNEL */
165
166#ifdef _KERNEL
167	/* Fortuna parameters. Do not adjust these unless you have
168	 * have a very good clue about what they do!
169	 */
170	random_fortuna_o = SYSCTL_ADD_NODE(&random_clist,
171		SYSCTL_STATIC_CHILDREN(_kern_random),
172		OID_AUTO, "fortuna", CTLFLAG_RW, 0,
173		"Fortuna Parameters");
174
175	SYSCTL_ADD_PROC(&random_clist,
176		SYSCTL_CHILDREN(random_fortuna_o), OID_AUTO,
177		"minpoolsize", CTLTYPE_UINT|CTLFLAG_RW,
178		&fortuna_state.minpoolsize, DEFPOOLSIZE,
179		random_check_uint_minpoolsize, "IU",
180		"Minimum pool size necessary to cause a reseed automatically");
181
182	fortuna_state.lasttime = 0U;
183#endif
184
185	fortuna_state.minpoolsize = DEFPOOLSIZE;
186
187	/* F&S - InitializePRNG() */
188
189	/* F&S - P_i = \epsilon */
190	for (i = 0; i < NPOOLS; i++) {
191		randomdev_hash_init(&fortuna_state.pool[i].hash);
192		fortuna_state.pool[i].length = 0U;
193	}
194
195	/* F&S - ReseedCNT = 0 */
196	fortuna_state.reseedcount = 0U;
197
198	/* F&S - InitializeGenerator() */
199
200	/* F&S - C = 0 */
201	uint128_clear(&fortuna_state.counter.whole);
202
203	/* F&S - K = 0 */
204	memset(&fortuna_state.key, 0, sizeof(fortuna_state.key));
205}
206
207void
208random_fortuna_deinit_alg(void)
209{
210
211	mtx_destroy(&random_reseed_mtx);
212	memset(&fortuna_state, 0, sizeof(fortuna_state));
213}
214
215/* F&S - AddRandomEvent() */
216/* Process a single stochastic event off the harvest queue */
217void
218random_fortuna_process_event(struct harvest_event *event)
219{
220	u_int pl;
221
222	/* We must be locked for all this as plenty of state gets messed with */
223	mtx_lock(&random_reseed_mtx);
224
225	/* Accumulate the event into the appropriate pool
226	 * where each event carries the destination information
227	 */
228	/* F&S - P_i = P_i|<harvested stuff> */
229	/* The hash_init and hash_finish are done in random_fortuna_read() below */
230	pl = event->he_destination % NPOOLS;
231	randomdev_hash_iterate(&fortuna_state.pool[pl].hash, event, sizeof(*event));
232	/* No point in counting above the outside maximum */
233	fortuna_state.pool[pl].length += event->he_size;
234	fortuna_state.pool[pl].length = MIN(fortuna_state.pool[pl].length, MAXPOOLSIZE);
235
236	/* Done with state-messing */
237	mtx_unlock(&random_reseed_mtx);
238}
239
240/* F&S - Reseed() */
241/* Reseed Mutex is held */
242static void
243reseed(uint8_t *junk, u_int length)
244{
245	struct randomdev_hash context;
246	uint8_t hash[KEYSIZE];
247
248	KASSERT(fortuna_state.minpoolsize > 0, ("random: Fortuna threshold = 0"));
249#ifdef _KERNEL
250	mtx_assert(&random_reseed_mtx, MA_OWNED);
251#endif
252
253	/* FS&K - K = Hd(K|s) where Hd(m) is H(H(0^512|m)) */
254	randomdev_hash_init(&context);
255	randomdev_hash_iterate(&context, zero_region, 512/8);
256	randomdev_hash_iterate(&context, &fortuna_state.key, sizeof(fortuna_state.key));
257	randomdev_hash_iterate(&context, junk, length);
258	randomdev_hash_finish(&context, hash);
259	randomdev_hash_init(&context);
260	randomdev_hash_iterate(&context, hash, KEYSIZE);
261	randomdev_hash_finish(&context, hash);
262	randomdev_encrypt_init(&fortuna_state.key, hash);
263	memset(hash, 0, sizeof(hash));
264
265	/* Unblock the device if it was blocked due to being unseeded */
266	if (uint128_is_zero(fortuna_state.counter.whole))
267		random_adaptor_unblock();
268	/* FS&K - C = C + 1 */
269	uint128_increment(&fortuna_state.counter.whole);
270}
271
272/* F&S - GenerateBlocks() */
273/* Reseed Mutex is held, and buf points to a whole number of blocks. */
274static __inline void
275random_fortuna_genblocks(uint8_t *buf, u_int blockcount)
276{
277	u_int i;
278
279	for (i = 0u; i < blockcount; i++) {
280		/* F&S - r = r|E(K,C) */
281		randomdev_encrypt(&fortuna_state.key, fortuna_state.counter.byte, buf, BLOCKSIZE);
282		buf += BLOCKSIZE;
283
284		/* F&S - C = C + 1 */
285		uint128_increment(&fortuna_state.counter.whole);
286	}
287}
288
289/* F&S - PseudoRandomData() */
290/* Reseed Mutex is held, and buf points to a whole number of blocks. */
291static __inline void
292random_fortuna_genrandom(uint8_t *buf, u_int bytecount)
293{
294	static uint8_t temp[BLOCKSIZE*(KEYSIZE/BLOCKSIZE)];
295	u_int blockcount;
296
297	/* F&S - assert(n < 2^20) */
298	KASSERT((bytecount <= (1 << 20)), ("invalid single read request to fortuna of %d bytes", bytecount));
299
300	/* F&S - r = first-n-bytes(GenerateBlocks(ceil(n/16))) */
301	blockcount = (bytecount + BLOCKSIZE - 1)/BLOCKSIZE;
302	random_fortuna_genblocks(buf, blockcount);
303
304	/* F&S - K = GenerateBlocks(2) */
305	random_fortuna_genblocks(temp, KEYSIZE/BLOCKSIZE);
306	randomdev_encrypt_init(&fortuna_state.key, temp);
307	memset(temp, 0, sizeof(temp));
308}
309
310/* F&S - RandomData() */
311/* Used to return processed entropy from the PRNG */
312/* The argument buf points to a whole number of blocks. */
313void
314random_fortuna_read(uint8_t *buf, u_int bytecount)
315{
316#ifdef _KERNEL
317	sbintime_t thistime;
318#endif
319	struct randomdev_hash context;
320	uint8_t s[NPOOLS*KEYSIZE], temp[KEYSIZE];
321	int i;
322	u_int seedlength;
323
324	/* We must be locked for all this as plenty of state gets messed with */
325	mtx_lock(&random_reseed_mtx);
326
327	/* if buf == NULL and bytecount == 0 then this is the pre-read. */
328	/* if buf == NULL and bytecount != 0 then this is the post-read; ignore. */
329	if (buf == NULL) {
330		if (bytecount == 0) {
331			if (fortuna_state.pool[0].length >= fortuna_state.minpoolsize
332#ifdef _KERNEL
333			/* F&S - Use 'getsbinuptime()' to prevent reseed-spamming. */
334		    	&& ((thistime = getsbinuptime()) - fortuna_state.lasttime > hz/10)
335#endif
336		    	) {
337#ifdef _KERNEL
338				fortuna_state.lasttime = thistime;
339#endif
340
341				seedlength = 0U;
342				/* F&S - ReseedCNT = ReseedCNT + 1 */
343				fortuna_state.reseedcount++;
344				/* s = \epsilon by default */
345				for (i = 0; i < NPOOLS; i++) {
346					/* F&S - if Divides(ReseedCnt, 2^i) ... */
347					if ((fortuna_state.reseedcount % (1 << i)) == 0U) {
348						seedlength += KEYSIZE;
349						/* F&S - temp = (P_i) */
350						randomdev_hash_finish(&fortuna_state.pool[i].hash, temp);
351						/* F&S - P_i = \epsilon */
352						randomdev_hash_init(&fortuna_state.pool[i].hash);
353						fortuna_state.pool[i].length = 0U;
354						/* F&S - s = s|H(temp) */
355						randomdev_hash_init(&context);
356						randomdev_hash_iterate(&context, temp, KEYSIZE);
357						randomdev_hash_finish(&context, s + i*KEYSIZE);
358					}
359					else
360						break;
361				}
362#ifdef RANDOM_DEBUG
363				printf("random: active reseed: reseedcount [%d] ", fortuna_state.reseedcount);
364				for (i = 0; i < NPOOLS; i++)
365					printf(" %d", fortuna_state.pool[i].length);
366				printf("\n");
367#endif
368				/* F&S */
369				reseed(s, seedlength);
370
371				/* Clean up */
372				memset(s, 0, seedlength);
373				seedlength = 0U;
374				memset(temp, 0, sizeof(temp));
375				memset(&context, 0, sizeof(context));
376			}
377		}
378	}
379	/* if buf != NULL do a regular read. */
380	else
381		random_fortuna_genrandom(buf, bytecount);
382
383	mtx_unlock(&random_reseed_mtx);
384}
385
386/* Internal function to hand external entropy to the PRNG */
387void
388random_fortuna_write(uint8_t *buf, u_int count)
389{
390	uint8_t temp[KEYSIZE];
391	int i;
392	uintmax_t timestamp;
393
394	timestamp = get_cyclecount();
395	randomdev_hash_iterate(&fortuna_start_cache.hash, &timestamp, sizeof(timestamp));
396	randomdev_hash_iterate(&fortuna_start_cache.hash, buf, count);
397	timestamp = get_cyclecount();
398	randomdev_hash_iterate(&fortuna_start_cache.hash, &timestamp, sizeof(timestamp));
399	randomdev_hash_finish(&fortuna_start_cache.hash, temp);
400	for (i = 0; i < KEYSIZE; i++)
401		fortuna_start_cache.junk[(fortuna_start_cache.length + i)%PAGE_SIZE] ^= temp[i];
402	fortuna_start_cache.length += KEYSIZE;
403
404#ifdef RANDOM_DEBUG
405	printf("random: %s - ", __func__);
406	for (i = 0; i < KEYSIZE; i++)
407		printf("%02X", temp[i]);
408	printf("\n");
409#endif
410
411	memset(temp, 0, KEYSIZE);
412
413	/* We must be locked for all this as plenty of state gets messed with */
414	mtx_lock(&random_reseed_mtx);
415
416	randomdev_hash_init(&fortuna_start_cache.hash);
417
418	reseed(fortuna_start_cache.junk, MIN(PAGE_SIZE, fortuna_start_cache.length));
419	memset(fortuna_start_cache.junk, 0, sizeof(fortuna_start_cache.junk));
420
421	mtx_unlock(&random_reseed_mtx);
422}
423
424void
425random_fortuna_reseed(void)
426{
427
428	/* CWOT */
429}
430
431int
432random_fortuna_seeded(void)
433{
434
435	return (!uint128_is_zero(fortuna_state.counter.whole));
436}
437
438#endif /* RANDOM_FORTUNA */
439