rand-fortuna.c revision 1.1.1.1
1258945Sroberto/*	$NetBSD: rand-fortuna.c,v 1.1.1.1 2011/04/13 18:14:50 elric Exp $	*/
2258945Sroberto
3258945Sroberto/*
4258945Sroberto * fortuna.c
5258945Sroberto *		Fortuna-like PRNG.
6258945Sroberto *
7258945Sroberto * Copyright (c) 2005 Marko Kreen
8258945Sroberto * All rights reserved.
9258945Sroberto *
10258945Sroberto * Redistribution and use in source and binary forms, with or without
11258945Sroberto * modification, are permitted provided that the following conditions
12258945Sroberto * are met:
13258945Sroberto * 1. Redistributions of source code must retain the above copyright
14258945Sroberto *	  notice, this list of conditions and the following disclaimer.
15258945Sroberto * 2. Redistributions in binary form must reproduce the above copyright
16258945Sroberto *	  notice, this list of conditions and the following disclaimer in the
17258945Sroberto *	  documentation and/or other materials provided with the distribution.
18258945Sroberto *
19258945Sroberto * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20258945Sroberto * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21258945Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22258945Sroberto * ARE DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23258945Sroberto * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24258945Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25258945Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26258945Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27258945Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28258945Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29258945Sroberto * SUCH DAMAGE.
30258945Sroberto *
31258945Sroberto * $PostgreSQL: pgsql/contrib/pgcrypto/fortuna.c,v 1.8 2006/10/04 00:29:46 momjian Exp $
32258945Sroberto */
33258945Sroberto
34258945Sroberto#include <config.h>
35258945Sroberto
36258945Sroberto#include <stdio.h>
37258945Sroberto#include <stdlib.h>
38258945Sroberto#include <rand.h>
39258945Sroberto#include <heim_threads.h>
40258945Sroberto
41258945Sroberto#ifdef KRB5
42258945Sroberto#include <krb5/krb5-types.h>
43258945Sroberto#endif
44258945Sroberto#include <krb5/roken.h>
45258945Sroberto
46258945Sroberto#include "randi.h"
47258945Sroberto#include "aes.h"
48258945Sroberto#include "sha.h"
49258945Sroberto
50258945Sroberto/*
51258945Sroberto * Why Fortuna-like: There does not seem to be any definitive reference
52258945Sroberto * on Fortuna in the net.  Instead this implementation is based on
53258945Sroberto * following references:
54258945Sroberto *
55258945Sroberto * http://en.wikipedia.org/wiki/Fortuna_(PRNG)
56258945Sroberto *	 - Wikipedia article
57258945Sroberto * http://jlcooke.ca/random/
58258945Sroberto *	 - Jean-Luc Cooke Fortuna-based /dev/random driver for Linux.
59258945Sroberto */
60258945Sroberto
61258945Sroberto/*
62258945Sroberto * There is some confusion about whether and how to carry forward
63258945Sroberto * the state of the pools.	Seems like original Fortuna does not
64258945Sroberto * do it, resetting hash after each request.  I guess expecting
65258945Sroberto * feeding to happen more often that requesting.   This is absolutely
66258945Sroberto * unsuitable for pgcrypto, as nothing asynchronous happens here.
67258945Sroberto *
68258945Sroberto * J.L. Cooke fixed this by feeding previous hash to new re-initialized
69258945Sroberto * hash context.
70258945Sroberto *
71258945Sroberto * Fortuna predecessor Yarrow requires ability to query intermediate
72258945Sroberto * 'final result' from hash, without affecting it.
73258945Sroberto *
74258945Sroberto * This implementation uses the Yarrow method - asking intermediate
75258945Sroberto * results, but continuing with old state.
76258945Sroberto */
77258945Sroberto
78258945Sroberto
79258945Sroberto/*
80258945Sroberto * Algorithm parameters
81258945Sroberto */
82258945Sroberto
83258945Sroberto#define NUM_POOLS		32
84258945Sroberto
85258945Sroberto/* in microseconds */
86258945Sroberto#define RESEED_INTERVAL 100000	/* 0.1 sec */
87258945Sroberto
88258945Sroberto/* for one big request, reseed after this many bytes */
89258945Sroberto#define RESEED_BYTES	(1024*1024)
90258945Sroberto
91258945Sroberto/*
92258945Sroberto * Skip reseed if pool 0 has less than this many
93258945Sroberto * bytes added since last reseed.
94258945Sroberto */
95258945Sroberto#define POOL0_FILL		(256/8)
96258945Sroberto
97258945Sroberto/*
98258945Sroberto * Algorithm constants
99258945Sroberto */
100258945Sroberto
101258945Sroberto/* Both cipher key size and hash result size */
102258945Sroberto#define BLOCK			32
103258945Sroberto
104258945Sroberto/* cipher block size */
105258945Sroberto#define CIPH_BLOCK		16
106258945Sroberto
107258945Sroberto/* for internal wrappers */
108258945Sroberto#define MD_CTX			SHA256_CTX
109258945Sroberto#define CIPH_CTX		AES_KEY
110258945Sroberto
111258945Srobertostruct fortuna_state
112258945Sroberto{
113258945Sroberto    unsigned char	counter[CIPH_BLOCK];
114258945Sroberto    unsigned char	result[CIPH_BLOCK];
115258945Sroberto    unsigned char	key[BLOCK];
116258945Sroberto    MD_CTX		pool[NUM_POOLS];
117258945Sroberto    CIPH_CTX		ciph;
118258945Sroberto    unsigned		reseed_count;
119258945Sroberto    struct timeval	last_reseed_time;
120258945Sroberto    unsigned		pool0_bytes;
121258945Sroberto    unsigned		rnd_pos;
122258945Sroberto    int			tricks_done;
123258945Sroberto    pid_t		pid;
124258945Sroberto};
125258945Srobertotypedef struct fortuna_state FState;
126258945Sroberto
127258945Sroberto
128258945Sroberto/*
129258945Sroberto * Use our own wrappers here.
130258945Sroberto * - Need to get intermediate result from digest, without affecting it.
131258945Sroberto * - Need re-set key on a cipher context.
132258945Sroberto * - Algorithms are guaranteed to exist.
133258945Sroberto * - No memory allocations.
134258945Sroberto */
135258945Sroberto
136258945Srobertostatic void
137258945Srobertociph_init(CIPH_CTX * ctx, const unsigned char *key, int klen)
138258945Sroberto{
139258945Sroberto    AES_set_encrypt_key(key, klen * 8, ctx);
140258945Sroberto}
141258945Sroberto
142258945Srobertostatic void
143258945Srobertociph_encrypt(CIPH_CTX * ctx, const unsigned char *in, unsigned char *out)
144258945Sroberto{
145258945Sroberto    AES_encrypt(in, out, ctx);
146258945Sroberto}
147258945Sroberto
148258945Srobertostatic void
149258945Srobertomd_init(MD_CTX * ctx)
150258945Sroberto{
151258945Sroberto    SHA256_Init(ctx);
152258945Sroberto}
153258945Sroberto
154258945Srobertostatic void
155258945Srobertomd_update(MD_CTX * ctx, const unsigned char *data, int len)
156258945Sroberto{
157258945Sroberto    SHA256_Update(ctx, data, len);
158258945Sroberto}
159258945Sroberto
160258945Srobertostatic void
161258945Srobertomd_result(MD_CTX * ctx, unsigned char *dst)
162258945Sroberto{
163258945Sroberto    SHA256_CTX	tmp;
164258945Sroberto
165258945Sroberto    memcpy(&tmp, ctx, sizeof(*ctx));
166258945Sroberto    SHA256_Final(dst, &tmp);
167258945Sroberto    memset(&tmp, 0, sizeof(tmp));
168258945Sroberto}
169258945Sroberto
170258945Sroberto/*
171258945Sroberto * initialize state
172258945Sroberto */
173258945Srobertostatic void
174258945Srobertoinit_state(FState * st)
175258945Sroberto{
176258945Sroberto    int			i;
177258945Sroberto
178258945Sroberto    memset(st, 0, sizeof(*st));
179258945Sroberto    for (i = 0; i < NUM_POOLS; i++)
180258945Sroberto	md_init(&st->pool[i]);
181258945Sroberto    st->pid = getpid();
182258945Sroberto}
183258945Sroberto
184258945Sroberto/*
185258945Sroberto * Endianess does not matter.
186258945Sroberto * It just needs to change without repeating.
187258945Sroberto */
188258945Srobertostatic void
189258945Srobertoinc_counter(FState * st)
190258945Sroberto{
191258945Sroberto    uint32_t   *val = (uint32_t *) st->counter;
192258945Sroberto
193258945Sroberto    if (++val[0])
194258945Sroberto	return;
195258945Sroberto    if (++val[1])
196258945Sroberto	return;
197258945Sroberto    if (++val[2])
198258945Sroberto	return;
199258945Sroberto    ++val[3];
200258945Sroberto}
201258945Sroberto
202258945Sroberto/*
203258945Sroberto * This is called 'cipher in counter mode'.
204258945Sroberto */
205258945Srobertostatic void
206258945Srobertoencrypt_counter(FState * st, unsigned char *dst)
207258945Sroberto{
208258945Sroberto    ciph_encrypt(&st->ciph, st->counter, dst);
209258945Sroberto    inc_counter(st);
210258945Sroberto}
211258945Sroberto
212258945Sroberto
213258945Sroberto/*
214258945Sroberto * The time between reseed must be at least RESEED_INTERVAL
215258945Sroberto * microseconds.
216258945Sroberto */
217258945Srobertostatic int
218258945Srobertoenough_time_passed(FState * st)
219258945Sroberto{
220258945Sroberto    int			ok;
221258945Sroberto    struct timeval tv;
222258945Sroberto    struct timeval *last = &st->last_reseed_time;
223258945Sroberto
224258945Sroberto    gettimeofday(&tv, NULL);
225258945Sroberto
226258945Sroberto    /* check how much time has passed */
227258945Sroberto    ok = 0;
228258945Sroberto    if (tv.tv_sec > last->tv_sec + 1)
229258945Sroberto	ok = 1;
230258945Sroberto    else if (tv.tv_sec == last->tv_sec + 1)
231258945Sroberto    {
232258945Sroberto	if (1000000 + tv.tv_usec - last->tv_usec >= RESEED_INTERVAL)
233258945Sroberto	    ok = 1;
234258945Sroberto    }
235258945Sroberto    else if (tv.tv_usec - last->tv_usec >= RESEED_INTERVAL)
236258945Sroberto	ok = 1;
237258945Sroberto
238258945Sroberto    /* reseed will happen, update last_reseed_time */
239258945Sroberto    if (ok)
240258945Sroberto	memcpy(last, &tv, sizeof(tv));
241258945Sroberto
242258945Sroberto    memset(&tv, 0, sizeof(tv));
243258945Sroberto
244258945Sroberto    return ok;
245258945Sroberto}
246258945Sroberto
247258945Sroberto/*
248258945Sroberto * generate new key from all the pools
249258945Sroberto */
250258945Srobertostatic void
251258945Srobertoreseed(FState * st)
252258945Sroberto{
253258945Sroberto    unsigned	k;
254258945Sroberto    unsigned	n;
255258945Sroberto    MD_CTX		key_md;
256258945Sroberto    unsigned char	buf[BLOCK];
257258945Sroberto
258258945Sroberto    /* set pool as empty */
259258945Sroberto    st->pool0_bytes = 0;
260258945Sroberto
261258945Sroberto    /*
262258945Sroberto     * Both #0 and #1 reseed would use only pool 0. Just skip #0 then.
263258945Sroberto     */
264258945Sroberto    n = ++st->reseed_count;
265258945Sroberto
266258945Sroberto    /*
267258945Sroberto     * The goal: use k-th pool only 1/(2^k) of the time.
268258945Sroberto     */
269258945Sroberto    md_init(&key_md);
270258945Sroberto    for (k = 0; k < NUM_POOLS; k++)
271258945Sroberto    {
272258945Sroberto	md_result(&st->pool[k], buf);
273258945Sroberto	md_update(&key_md, buf, BLOCK);
274258945Sroberto
275258945Sroberto	if (n & 1 || !n)
276258945Sroberto	    break;
277258945Sroberto	n >>= 1;
278258945Sroberto    }
279258945Sroberto
280258945Sroberto    /* add old key into mix too */
281258945Sroberto    md_update(&key_md, st->key, BLOCK);
282258945Sroberto
283258945Sroberto    /* add pid to make output diverse after fork() */
284258945Sroberto    md_update(&key_md, (const unsigned char *)&st->pid, sizeof(st->pid));
285258945Sroberto
286258945Sroberto    /* now we have new key */
287258945Sroberto    md_result(&key_md, st->key);
288258945Sroberto
289258945Sroberto    /* use new key */
290258945Sroberto    ciph_init(&st->ciph, st->key, BLOCK);
291258945Sroberto
292258945Sroberto    memset(&key_md, 0, sizeof(key_md));
293258945Sroberto    memset(buf, 0, BLOCK);
294258945Sroberto}
295258945Sroberto
296258945Sroberto/*
297258945Sroberto * Pick a random pool.	This uses key bytes as random source.
298258945Sroberto */
299258945Srobertostatic unsigned
300258945Srobertoget_rand_pool(FState * st)
301258945Sroberto{
302258945Sroberto    unsigned	rnd;
303258945Sroberto
304258945Sroberto    /*
305258945Sroberto     * This slightly prefers lower pools - thats OK.
306258945Sroberto     */
307258945Sroberto    rnd = st->key[st->rnd_pos] % NUM_POOLS;
308258945Sroberto
309258945Sroberto    st->rnd_pos++;
310258945Sroberto    if (st->rnd_pos >= BLOCK)
311258945Sroberto	st->rnd_pos = 0;
312258945Sroberto
313258945Sroberto    return rnd;
314258945Sroberto}
315258945Sroberto
316258945Sroberto/*
317258945Sroberto * update pools
318258945Sroberto */
319258945Srobertostatic void
320258945Srobertoadd_entropy(FState * st, const unsigned char *data, unsigned len)
321258945Sroberto{
322258945Sroberto    unsigned		pos;
323258945Sroberto    unsigned char	hash[BLOCK];
324258945Sroberto    MD_CTX		md;
325258945Sroberto
326258945Sroberto    /* hash given data */
327258945Sroberto    md_init(&md);
328258945Sroberto    md_update(&md, data, len);
329258945Sroberto    md_result(&md, hash);
330258945Sroberto
331258945Sroberto    /*
332258945Sroberto     * Make sure the pool 0 is initialized, then update randomly.
333258945Sroberto     */
334258945Sroberto    if (st->reseed_count == 0)
335258945Sroberto	pos = 0;
336258945Sroberto    else
337258945Sroberto	pos = get_rand_pool(st);
338258945Sroberto    md_update(&st->pool[pos], hash, BLOCK);
339258945Sroberto
340258945Sroberto    if (pos == 0)
341258945Sroberto	st->pool0_bytes += len;
342258945Sroberto
343258945Sroberto    memset(hash, 0, BLOCK);
344258945Sroberto    memset(&md, 0, sizeof(md));
345258945Sroberto}
346258945Sroberto
347258945Sroberto/*
348258945Sroberto * Just take 2 next blocks as new key
349258945Sroberto */
350258945Srobertostatic void
351258945Srobertorekey(FState * st)
352258945Sroberto{
353258945Sroberto    encrypt_counter(st, st->key);
354258945Sroberto    encrypt_counter(st, st->key + CIPH_BLOCK);
355258945Sroberto    ciph_init(&st->ciph, st->key, BLOCK);
356258945Sroberto}
357258945Sroberto
358258945Sroberto/*
359258945Sroberto * Hide public constants. (counter, pools > 0)
360258945Sroberto *
361258945Sroberto * This can also be viewed as spreading the startup
362258945Sroberto * entropy over all of the components.
363258945Sroberto */
364258945Srobertostatic void
365258945Srobertostartup_tricks(FState * st)
366258945Sroberto{
367258945Sroberto    int			i;
368258945Sroberto    unsigned char	buf[BLOCK];
369258945Sroberto
370258945Sroberto    /* Use next block as counter. */
371258945Sroberto    encrypt_counter(st, st->counter);
372258945Sroberto
373258945Sroberto    /* Now shuffle pools, excluding #0 */
374258945Sroberto    for (i = 1; i < NUM_POOLS; i++)
375258945Sroberto    {
376258945Sroberto	encrypt_counter(st, buf);
377258945Sroberto	encrypt_counter(st, buf + CIPH_BLOCK);
378258945Sroberto	md_update(&st->pool[i], buf, BLOCK);
379258945Sroberto    }
380258945Sroberto    memset(buf, 0, BLOCK);
381258945Sroberto
382258945Sroberto    /* Hide the key. */
383258945Sroberto    rekey(st);
384258945Sroberto
385258945Sroberto    /* This can be done only once. */
386258945Sroberto    st->tricks_done = 1;
387258945Sroberto}
388258945Sroberto
389258945Srobertostatic void
390258945Srobertoextract_data(FState * st, unsigned count, unsigned char *dst)
391258945Sroberto{
392258945Sroberto    unsigned	n;
393258945Sroberto    unsigned	block_nr = 0;
394258945Sroberto    pid_t	pid = getpid();
395258945Sroberto
396258945Sroberto    /* Should we reseed? */
397258945Sroberto    if (st->pool0_bytes >= POOL0_FILL || st->reseed_count == 0)
398258945Sroberto	if (enough_time_passed(st))
399258945Sroberto	    reseed(st);
400258945Sroberto
401258945Sroberto    /* Do some randomization on first call */
402258945Sroberto    if (!st->tricks_done)
403258945Sroberto	startup_tricks(st);
404258945Sroberto
405258945Sroberto    /* If we forked, force a reseed again */
406258945Sroberto    if (pid != st->pid) {
407258945Sroberto	st->pid = pid;
408258945Sroberto	reseed(st);
409293650Sglebius    }
410258945Sroberto
411258945Sroberto    while (count > 0)
412258945Sroberto    {
413258945Sroberto	/* produce bytes */
414258945Sroberto	encrypt_counter(st, st->result);
415258945Sroberto
416258945Sroberto	/* copy result */
417258945Sroberto	if (count > CIPH_BLOCK)
418258945Sroberto	    n = CIPH_BLOCK;
419258945Sroberto	else
420258945Sroberto	    n = count;
421258945Sroberto	memcpy(dst, st->result, n);
422258945Sroberto	dst += n;
423258945Sroberto	count -= n;
424293650Sglebius
425258945Sroberto	/* must not give out too many bytes with one key */
426258945Sroberto	block_nr++;
427258945Sroberto	if (block_nr > (RESEED_BYTES / CIPH_BLOCK))
428258945Sroberto	{
429258945Sroberto	    rekey(st);
430258945Sroberto	    block_nr = 0;
431258945Sroberto	}
432258945Sroberto    }
433258945Sroberto    /* Set new key for next request. */
434258945Sroberto    rekey(st);
435258945Sroberto}
436258945Sroberto
437258945Sroberto/*
438258945Sroberto * public interface
439258945Sroberto */
440258945Sroberto
441258945Srobertostatic FState	main_state;
442258945Srobertostatic int	init_done;
443258945Srobertostatic int	have_entropy;
444258945Sroberto#define FORTUNA_RESEED_BYTE	10000
445258945Srobertostatic unsigned	resend_bytes;
446258945Sroberto
447258945Sroberto/*
448258945Sroberto * This mutex protects all of the above static elements from concurrent
449258945Sroberto * access by multiple threads
450258945Sroberto */
451258945Srobertostatic HEIMDAL_MUTEX fortuna_mutex = HEIMDAL_MUTEX_INITIALIZER;
452258945Sroberto
453258945Sroberto/*
454258945Sroberto * Try our best to do an inital seed
455258945Sroberto */
456258945Sroberto#define INIT_BYTES	128
457258945Sroberto
458258945Sroberto/*
459258945Sroberto * fortuna_mutex must be held across calls to this function
460258945Sroberto */
461258945Sroberto
462258945Srobertostatic int
463258945Srobertofortuna_reseed(void)
464258945Sroberto{
465258945Sroberto    int entropy_p = 0;
466258945Sroberto
467258945Sroberto    if (!init_done)
468258945Sroberto	abort();
469258945Sroberto
470258945Sroberto#ifndef NO_RAND_UNIX_METHOD
471258945Sroberto    {
472258945Sroberto	unsigned char buf[INIT_BYTES];
473258945Sroberto	if ((*hc_rand_unix_method.bytes)(buf, sizeof(buf)) == 1) {
474258945Sroberto	    add_entropy(&main_state, buf, sizeof(buf));
475258945Sroberto	    entropy_p = 1;
476258945Sroberto	    memset(buf, 0, sizeof(buf));
477258945Sroberto	}
478258945Sroberto    }
479258945Sroberto#endif
480258945Sroberto#ifdef HAVE_ARC4RANDOM
481258945Sroberto    {
482258945Sroberto	uint32_t buf[INIT_BYTES / sizeof(uint32_t)];
483258945Sroberto	int i;
484258945Sroberto
485258945Sroberto	for (i = 0; i < sizeof(buf)/sizeof(buf[0]); i++)
486258945Sroberto	    buf[i] = arc4random();
487258945Sroberto	add_entropy(&main_state, (void *)buf, sizeof(buf));
488258945Sroberto	entropy_p = 1;
489258945Sroberto    }
490#endif
491#ifndef NO_RAND_EGD_METHOD
492    /*
493     * Only to get egd entropy if /dev/random or arc4rand failed since
494     * it can be horribly slow to generate new bits.
495     */
496    if (!entropy_p) {
497	unsigned char buf[INIT_BYTES];
498	if ((*hc_rand_egd_method.bytes)(buf, sizeof(buf)) == 1) {
499	    add_entropy(&main_state, buf, sizeof(buf));
500	    entropy_p = 1;
501	    memset(buf, 0, sizeof(buf));
502	}
503    }
504#endif
505    /*
506     * Fall back to gattering data from timer and secret files, this
507     * is really the last resort.
508     */
509    if (!entropy_p) {
510	/* to save stackspace */
511	union {
512	    unsigned char buf[INIT_BYTES];
513	    unsigned char shad[1001];
514	} u;
515	int fd;
516
517	/* add timer info */
518	if ((*hc_rand_timer_method.bytes)(u.buf, sizeof(u.buf)) == 1)
519	    add_entropy(&main_state, u.buf, sizeof(u.buf));
520	/* add /etc/shadow */
521	fd = open("/etc/shadow", O_RDONLY, 0);
522	if (fd >= 0) {
523	    ssize_t n;
524	    rk_cloexec(fd);
525	    /* add_entropy will hash the buf */
526	    while ((n = read(fd, (char *)u.shad, sizeof(u.shad))) > 0)
527		add_entropy(&main_state, u.shad, sizeof(u.shad));
528	    close(fd);
529	}
530
531	memset(&u, 0, sizeof(u));
532
533	entropy_p = 1; /* sure about this ? */
534    }
535    {
536	pid_t pid = getpid();
537	add_entropy(&main_state, (void *)&pid, sizeof(pid));
538    }
539    {
540	struct timeval tv;
541	gettimeofday(&tv, NULL);
542	add_entropy(&main_state, (void *)&tv, sizeof(tv));
543    }
544#ifdef HAVE_GETUID
545    {
546	uid_t u = getuid();
547	add_entropy(&main_state, (void *)&u, sizeof(u));
548    }
549#endif
550    return entropy_p;
551}
552
553/*
554 * fortuna_mutex must be held by callers of this function
555 */
556static int
557fortuna_init(void)
558{
559    if (!init_done)
560    {
561	init_state(&main_state);
562	init_done = 1;
563    }
564    if (!have_entropy)
565	have_entropy = fortuna_reseed();
566    return (init_done && have_entropy);
567}
568
569
570
571static void
572fortuna_seed(const void *indata, int size)
573{
574    HEIMDAL_MUTEX_lock(&fortuna_mutex);
575
576    fortuna_init();
577    add_entropy(&main_state, indata, size);
578    if (size >= INIT_BYTES)
579	have_entropy = 1;
580
581    HEIMDAL_MUTEX_unlock(&fortuna_mutex);
582}
583
584static int
585fortuna_bytes(unsigned char *outdata, int size)
586{
587    int ret = 0;
588
589    HEIMDAL_MUTEX_lock(&fortuna_mutex);
590
591    if (!fortuna_init())
592	goto out;
593
594    resend_bytes += size;
595    if (resend_bytes > FORTUNA_RESEED_BYTE || resend_bytes < size) {
596	resend_bytes = 0;
597	fortuna_reseed();
598    }
599    extract_data(&main_state, size, outdata);
600    ret = 1;
601
602out:
603    HEIMDAL_MUTEX_unlock(&fortuna_mutex);
604
605    return ret;
606}
607
608static void
609fortuna_cleanup(void)
610{
611    HEIMDAL_MUTEX_lock(&fortuna_mutex);
612
613    init_done = 0;
614    have_entropy = 0;
615    memset(&main_state, 0, sizeof(main_state));
616
617    HEIMDAL_MUTEX_unlock(&fortuna_mutex);
618}
619
620static void
621fortuna_add(const void *indata, int size, double entropi)
622{
623    fortuna_seed(indata, size);
624}
625
626static int
627fortuna_pseudorand(unsigned char *outdata, int size)
628{
629    return fortuna_bytes(outdata, size);
630}
631
632static int
633fortuna_status(void)
634{
635    int result;
636
637    HEIMDAL_MUTEX_lock(&fortuna_mutex);
638    result = fortuna_init();
639    HEIMDAL_MUTEX_unlock(&fortuna_mutex);
640
641    return result ? 1 : 0;
642}
643
644const RAND_METHOD hc_rand_fortuna_method = {
645    fortuna_seed,
646    fortuna_bytes,
647    fortuna_cleanup,
648    fortuna_add,
649    fortuna_pseudorand,
650    fortuna_status
651};
652
653const RAND_METHOD *
654RAND_fortuna_method(void)
655{
656    return &hc_rand_fortuna_method;
657}
658