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