1258945Sroberto/*
2280849Scy * Copyright (C) 2004-2007, 2009, 2010  Internet Systems Consortium, Inc. ("ISC")
3258945Sroberto * Copyright (C) 2000-2003  Internet Software Consortium.
4258945Sroberto *
5258945Sroberto * Permission to use, copy, modify, and/or distribute this software for any
6258945Sroberto * purpose with or without fee is hereby granted, provided that the above
7258945Sroberto * copyright notice and this permission notice appear in all copies.
8258945Sroberto *
9258945Sroberto * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10258945Sroberto * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11258945Sroberto * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12258945Sroberto * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13258945Sroberto * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14258945Sroberto * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15258945Sroberto * PERFORMANCE OF THIS SOFTWARE.
16258945Sroberto */
17258945Sroberto
18280849Scy/* $Id: entropy.c,v 1.22 2010/08/10 23:48:19 tbox Exp $ */
19258945Sroberto
20258945Sroberto/*! \file
21258945Sroberto * \brief
22258945Sroberto * This is the system independent part of the entropy module.  It is
23258945Sroberto * compiled via inclusion from the relevant OS source file, ie,
24258945Sroberto * \link unix/entropy.c unix/entropy.c \endlink or win32/entropy.c.
25258945Sroberto *
26258945Sroberto * \author Much of this code is modeled after the NetBSD /dev/random implementation,
27258945Sroberto * written by Michael Graff <explorer@netbsd.org>.
28258945Sroberto */
29258945Sroberto
30258945Sroberto#include <errno.h>
31258945Sroberto#include <fcntl.h>
32258945Sroberto#include <stdio.h>
33258945Sroberto
34258945Sroberto#include <isc/buffer.h>
35258945Sroberto#include <isc/entropy.h>
36258945Sroberto#include <isc/keyboard.h>
37258945Sroberto#include <isc/list.h>
38258945Sroberto#include <isc/magic.h>
39258945Sroberto#include <isc/mem.h>
40258945Sroberto#include <isc/msgs.h>
41258945Sroberto#include <isc/mutex.h>
42258945Sroberto#include <isc/platform.h>
43258945Sroberto#include <isc/region.h>
44258945Sroberto#include <isc/sha1.h>
45258945Sroberto#include <isc/string.h>
46258945Sroberto#include <isc/time.h>
47258945Sroberto#include <isc/util.h>
48258945Sroberto
49258945Sroberto
50258945Sroberto#define ENTROPY_MAGIC		ISC_MAGIC('E', 'n', 't', 'e')
51258945Sroberto#define SOURCE_MAGIC		ISC_MAGIC('E', 'n', 't', 's')
52258945Sroberto
53258945Sroberto#define VALID_ENTROPY(e)	ISC_MAGIC_VALID(e, ENTROPY_MAGIC)
54258945Sroberto#define VALID_SOURCE(s)		ISC_MAGIC_VALID(s, SOURCE_MAGIC)
55258945Sroberto
56258945Sroberto/***
57258945Sroberto *** "constants."  Do not change these unless you _really_ know what
58258945Sroberto *** you are doing.
59258945Sroberto ***/
60258945Sroberto
61258945Sroberto/*%
62258945Sroberto * Size of entropy pool in 32-bit words.  This _MUST_ be a power of 2.
63258945Sroberto */
64258945Sroberto#define RND_POOLWORDS	128
65258945Sroberto/*% Pool in bytes. */
66258945Sroberto#define RND_POOLBYTES	(RND_POOLWORDS * 4)
67258945Sroberto/*% Pool in bits. */
68258945Sroberto#define RND_POOLBITS	(RND_POOLWORDS * 32)
69258945Sroberto
70258945Sroberto/*%
71258945Sroberto * Number of bytes returned per hash.  This must be true:
72258945Sroberto *	threshold * 2 <= digest_size_in_bytes
73258945Sroberto */
74258945Sroberto#define RND_ENTROPY_THRESHOLD	10
75258945Sroberto#define THRESHOLD_BITS		(RND_ENTROPY_THRESHOLD * 8)
76258945Sroberto
77258945Sroberto/*%
78258945Sroberto * Size of the input event queue in samples.
79258945Sroberto */
80258945Sroberto#define RND_EVENTQSIZE	32
81258945Sroberto
82258945Sroberto/*%
83258945Sroberto * The number of times we'll "reseed" for pseudorandom seeds.  This is an
84258945Sroberto * extremely weak pseudorandom seed.  If the caller is using lots of
85258945Sroberto * pseudorandom data and they cannot provide a stronger random source,
86258945Sroberto * there is little we can do other than hope they're smart enough to
87258945Sroberto * call _adddata() with something better than we can come up with.
88258945Sroberto */
89258945Sroberto#define RND_INITIALIZE	128
90258945Sroberto
91258945Sroberto/*% Entropy Pool */
92258945Srobertotypedef struct {
93258945Sroberto	isc_uint32_t	cursor;		/*%< current add point in the pool */
94258945Sroberto	isc_uint32_t	entropy;	/*%< current entropy estimate in bits */
95258945Sroberto	isc_uint32_t	pseudo;		/*%< bits extracted in pseudorandom */
96258945Sroberto	isc_uint32_t	rotate;		/*%< how many bits to rotate by */
97258945Sroberto	isc_uint32_t	pool[RND_POOLWORDS];	/*%< random pool data */
98258945Sroberto} isc_entropypool_t;
99258945Sroberto
100258945Srobertostruct isc_entropy {
101258945Sroberto	unsigned int			magic;
102258945Sroberto	isc_mem_t		       *mctx;
103258945Sroberto	isc_mutex_t			lock;
104258945Sroberto	unsigned int			refcnt;
105258945Sroberto	isc_uint32_t			initialized;
106258945Sroberto	isc_uint32_t			initcount;
107258945Sroberto	isc_entropypool_t		pool;
108258945Sroberto	unsigned int			nsources;
109258945Sroberto	isc_entropysource_t	       *nextsource;
110258945Sroberto	ISC_LIST(isc_entropysource_t)	sources;
111258945Sroberto};
112258945Sroberto
113258945Sroberto/*% Sample Queue */
114258945Srobertotypedef struct {
115258945Sroberto	isc_uint32_t	last_time;	/*%< last time recorded */
116258945Sroberto	isc_uint32_t	last_delta;	/*%< last delta value */
117258945Sroberto	isc_uint32_t	last_delta2;	/*%< last delta2 value */
118258945Sroberto	isc_uint32_t	nsamples;	/*%< number of samples filled in */
119258945Sroberto	isc_uint32_t   *samples;	/*%< the samples */
120258945Sroberto	isc_uint32_t   *extra;		/*%< extra samples added in */
121258945Sroberto} sample_queue_t;
122258945Sroberto
123258945Srobertotypedef struct {
124258945Sroberto	sample_queue_t	samplequeue;
125258945Sroberto} isc_entropysamplesource_t;
126258945Sroberto
127258945Srobertotypedef struct {
128258945Sroberto	isc_boolean_t		start_called;
129258945Sroberto	isc_entropystart_t	startfunc;
130258945Sroberto	isc_entropyget_t	getfunc;
131258945Sroberto	isc_entropystop_t	stopfunc;
132258945Sroberto	void		       *arg;
133258945Sroberto	sample_queue_t		samplequeue;
134258945Sroberto} isc_cbsource_t;
135258945Sroberto
136258945Srobertotypedef struct {
137258945Sroberto	FILESOURCE_HANDLE_TYPE handle;
138258945Sroberto} isc_entropyfilesource_t;
139258945Sroberto
140258945Srobertostruct isc_entropysource {
141258945Sroberto	unsigned int	magic;
142258945Sroberto	unsigned int	type;
143258945Sroberto	isc_entropy_t  *ent;
144258945Sroberto	isc_uint32_t	total;		/*%< entropy from this source */
145258945Sroberto	ISC_LINK(isc_entropysource_t)	link;
146258945Sroberto	char		name[32];
147258945Sroberto	isc_boolean_t	bad;
148258945Sroberto	isc_boolean_t	warn_keyboard;
149258945Sroberto	isc_keyboard_t	kbd;
150258945Sroberto	union {
151258945Sroberto		isc_entropysamplesource_t	sample;
152258945Sroberto		isc_entropyfilesource_t		file;
153258945Sroberto		isc_cbsource_t			callback;
154258945Sroberto		isc_entropyusocketsource_t	usocket;
155258945Sroberto	} sources;
156258945Sroberto};
157258945Sroberto
158258945Sroberto#define ENTROPY_SOURCETYPE_SAMPLE	1	/*%< Type is a sample source */
159258945Sroberto#define ENTROPY_SOURCETYPE_FILE		2	/*%< Type is a file source */
160258945Sroberto#define ENTROPY_SOURCETYPE_CALLBACK	3	/*%< Type is a callback source */
161258945Sroberto#define ENTROPY_SOURCETYPE_USOCKET	4	/*%< Type is a Unix socket source */
162258945Sroberto
163258945Sroberto/*@{*/
164258945Sroberto/*%
165258945Sroberto * The random pool "taps"
166258945Sroberto */
167258945Sroberto#define TAP1	99
168258945Sroberto#define TAP2	59
169258945Sroberto#define TAP3	31
170258945Sroberto#define TAP4	 9
171258945Sroberto#define TAP5	 7
172258945Sroberto/*@}*/
173258945Sroberto
174258945Sroberto/*@{*/
175258945Sroberto/*%
176258945Sroberto * Declarations for function provided by the system dependent sources that
177258945Sroberto * include this file.
178258945Sroberto */
179258945Srobertostatic void
180258945Srobertofillpool(isc_entropy_t *, unsigned int, isc_boolean_t);
181258945Sroberto
182258945Srobertostatic int
183258945Srobertowait_for_sources(isc_entropy_t *);
184258945Sroberto
185258945Srobertostatic void
186258945Srobertodestroyfilesource(isc_entropyfilesource_t *source);
187258945Sroberto
188258945Srobertostatic void
189258945Srobertodestroyusocketsource(isc_entropyusocketsource_t *source);
190258945Sroberto
191258945Sroberto/*@}*/
192258945Sroberto
193258945Srobertostatic void
194258945Srobertosamplequeue_release(isc_entropy_t *ent, sample_queue_t *sq) {
195258945Sroberto	REQUIRE(sq->samples != NULL);
196258945Sroberto	REQUIRE(sq->extra != NULL);
197258945Sroberto
198258945Sroberto	isc_mem_put(ent->mctx, sq->samples, RND_EVENTQSIZE * 4);
199258945Sroberto	isc_mem_put(ent->mctx, sq->extra, RND_EVENTQSIZE * 4);
200258945Sroberto	sq->samples = NULL;
201258945Sroberto	sq->extra = NULL;
202258945Sroberto}
203258945Sroberto
204258945Srobertostatic isc_result_t
205258945Srobertosamplesource_allocate(isc_entropy_t *ent, sample_queue_t *sq) {
206258945Sroberto	sq->samples = isc_mem_get(ent->mctx, RND_EVENTQSIZE * 4);
207258945Sroberto	if (sq->samples == NULL)
208258945Sroberto		return (ISC_R_NOMEMORY);
209258945Sroberto
210258945Sroberto	sq->extra = isc_mem_get(ent->mctx, RND_EVENTQSIZE * 4);
211258945Sroberto	if (sq->extra == NULL) {
212258945Sroberto		isc_mem_put(ent->mctx, sq->samples, RND_EVENTQSIZE * 4);
213258945Sroberto		sq->samples = NULL;
214258945Sroberto		return (ISC_R_NOMEMORY);
215258945Sroberto	}
216258945Sroberto
217258945Sroberto	sq->nsamples = 0;
218258945Sroberto
219258945Sroberto	return (ISC_R_SUCCESS);
220258945Sroberto}
221258945Sroberto
222258945Sroberto/*%
223258945Sroberto * Add in entropy, even when the value we're adding in could be
224258945Sroberto * very large.
225258945Sroberto */
226258945Srobertostatic inline void
227258945Srobertoadd_entropy(isc_entropy_t *ent, isc_uint32_t entropy) {
228258945Sroberto	/* clamp input.  Yes, this must be done. */
229258945Sroberto	entropy = ISC_MIN(entropy, RND_POOLBITS);
230258945Sroberto	/* Add in the entropy we already have. */
231258945Sroberto	entropy += ent->pool.entropy;
232258945Sroberto	/* Clamp. */
233258945Sroberto	ent->pool.entropy = ISC_MIN(entropy, RND_POOLBITS);
234258945Sroberto}
235258945Sroberto
236258945Sroberto/*%
237258945Sroberto * Decrement the amount of entropy the pool has.
238258945Sroberto */
239258945Srobertostatic inline void
240258945Srobertosubtract_entropy(isc_entropy_t *ent, isc_uint32_t entropy) {
241258945Sroberto	entropy = ISC_MIN(entropy, ent->pool.entropy);
242258945Sroberto	ent->pool.entropy -= entropy;
243258945Sroberto}
244258945Sroberto
245258945Sroberto/*!
246258945Sroberto * Add in entropy, even when the value we're adding in could be
247258945Sroberto * very large.
248258945Sroberto */
249258945Srobertostatic inline void
250258945Srobertoadd_pseudo(isc_entropy_t *ent, isc_uint32_t pseudo) {
251258945Sroberto	/* clamp input.  Yes, this must be done. */
252258945Sroberto	pseudo = ISC_MIN(pseudo, RND_POOLBITS * 8);
253258945Sroberto	/* Add in the pseudo we already have. */
254258945Sroberto	pseudo += ent->pool.pseudo;
255258945Sroberto	/* Clamp. */
256258945Sroberto	ent->pool.pseudo = ISC_MIN(pseudo, RND_POOLBITS * 8);
257258945Sroberto}
258258945Sroberto
259258945Sroberto/*!
260258945Sroberto * Decrement the amount of pseudo the pool has.
261258945Sroberto */
262258945Srobertostatic inline void
263258945Srobertosubtract_pseudo(isc_entropy_t *ent, isc_uint32_t pseudo) {
264258945Sroberto	pseudo = ISC_MIN(pseudo, ent->pool.pseudo);
265258945Sroberto	ent->pool.pseudo -= pseudo;
266258945Sroberto}
267258945Sroberto
268258945Sroberto/*!
269258945Sroberto * Add one word to the pool, rotating the input as needed.
270258945Sroberto */
271258945Srobertostatic inline void
272258945Srobertoentropypool_add_word(isc_entropypool_t *rp, isc_uint32_t val) {
273258945Sroberto	/*
274258945Sroberto	 * Steal some values out of the pool, and xor them into the
275258945Sroberto	 * word we were given.
276258945Sroberto	 *
277258945Sroberto	 * Mix the new value into the pool using xor.  This will
278258945Sroberto	 * prevent the actual values from being known to the caller
279258945Sroberto	 * since the previous values are assumed to be unknown as well.
280258945Sroberto	 */
281258945Sroberto	val ^= rp->pool[(rp->cursor + TAP1) & (RND_POOLWORDS - 1)];
282258945Sroberto	val ^= rp->pool[(rp->cursor + TAP2) & (RND_POOLWORDS - 1)];
283258945Sroberto	val ^= rp->pool[(rp->cursor + TAP3) & (RND_POOLWORDS - 1)];
284258945Sroberto	val ^= rp->pool[(rp->cursor + TAP4) & (RND_POOLWORDS - 1)];
285258945Sroberto	val ^= rp->pool[(rp->cursor + TAP5) & (RND_POOLWORDS - 1)];
286280849Scy	if (rp->rotate == 0)
287280849Scy		rp->pool[rp->cursor++] ^= val;
288280849Scy	else
289280849Scy		rp->pool[rp->cursor++] ^=
290280849Scy		  ((val << rp->rotate) | (val >> (32 - rp->rotate)));
291258945Sroberto
292258945Sroberto	/*
293258945Sroberto	 * If we have looped around the pool, increment the rotate
294258945Sroberto	 * variable so the next value will get xored in rotated to
295258945Sroberto	 * a different position.
296258945Sroberto	 * Increment by a value that is relatively prime to the word size
297258945Sroberto	 * to try to spread the bits throughout the pool quickly when the
298258945Sroberto	 * pool is empty.
299258945Sroberto	 */
300258945Sroberto	if (rp->cursor == RND_POOLWORDS) {
301258945Sroberto		rp->cursor = 0;
302258945Sroberto		rp->rotate = (rp->rotate + 7) & 31;
303258945Sroberto	}
304258945Sroberto}
305258945Sroberto
306258945Sroberto/*!
307258945Sroberto * Add a buffer's worth of data to the pool.
308258945Sroberto *
309258945Sroberto * Requires that the lock is held on the entropy pool.
310258945Sroberto */
311258945Srobertostatic void
312258945Srobertoentropypool_adddata(isc_entropy_t *ent, void *p, unsigned int len,
313258945Sroberto		    isc_uint32_t entropy)
314258945Sroberto{
315258945Sroberto	isc_uint32_t val;
316258945Sroberto	unsigned long addr;
317258945Sroberto	isc_uint8_t *buf;
318258945Sroberto
319258945Sroberto	addr = (unsigned long)p;
320258945Sroberto	buf = p;
321258945Sroberto
322258945Sroberto	if ((addr & 0x03U) != 0U) {
323258945Sroberto		val = 0;
324258945Sroberto		switch (len) {
325258945Sroberto		case 3:
326258945Sroberto			val = *buf++;
327258945Sroberto			len--;
328258945Sroberto		case 2:
329258945Sroberto			val = val << 8 | *buf++;
330258945Sroberto			len--;
331258945Sroberto		case 1:
332258945Sroberto			val = val << 8 | *buf++;
333258945Sroberto			len--;
334258945Sroberto		}
335258945Sroberto
336258945Sroberto		entropypool_add_word(&ent->pool, val);
337258945Sroberto	}
338258945Sroberto
339258945Sroberto	for (; len > 3; len -= 4) {
340258945Sroberto		val = *((isc_uint32_t *)buf);
341258945Sroberto
342258945Sroberto		entropypool_add_word(&ent->pool, val);
343258945Sroberto		buf += 4;
344258945Sroberto	}
345258945Sroberto
346258945Sroberto	if (len != 0) {
347258945Sroberto		val = 0;
348258945Sroberto		switch (len) {
349258945Sroberto		case 3:
350258945Sroberto			val = *buf++;
351258945Sroberto		case 2:
352258945Sroberto			val = val << 8 | *buf++;
353258945Sroberto		case 1:
354258945Sroberto			val = val << 8 | *buf++;
355258945Sroberto		}
356258945Sroberto
357258945Sroberto		entropypool_add_word(&ent->pool, val);
358258945Sroberto	}
359258945Sroberto
360258945Sroberto	add_entropy(ent, entropy);
361258945Sroberto	subtract_pseudo(ent, entropy);
362258945Sroberto}
363258945Sroberto
364258945Srobertostatic inline void
365258945Srobertoreseed(isc_entropy_t *ent) {
366258945Sroberto	isc_time_t t;
367258945Sroberto	pid_t pid;
368258945Sroberto
369258945Sroberto	if (ent->initcount == 0) {
370258945Sroberto		pid = getpid();
371258945Sroberto		entropypool_adddata(ent, &pid, sizeof(pid), 0);
372258945Sroberto		pid = getppid();
373258945Sroberto		entropypool_adddata(ent, &pid, sizeof(pid), 0);
374258945Sroberto	}
375258945Sroberto
376258945Sroberto	/*!
377258945Sroberto	 * After we've reseeded 100 times, only add new timing info every
378258945Sroberto	 * 50 requests.  This will keep us from using lots and lots of
379258945Sroberto	 * CPU just to return bad pseudorandom data anyway.
380258945Sroberto	 */
381258945Sroberto	if (ent->initcount > 100)
382258945Sroberto		if ((ent->initcount % 50) != 0)
383258945Sroberto			return;
384258945Sroberto
385258945Sroberto	TIME_NOW(&t);
386258945Sroberto	entropypool_adddata(ent, &t, sizeof(t), 0);
387258945Sroberto	ent->initcount++;
388258945Sroberto}
389258945Sroberto
390258945Srobertostatic inline unsigned int
391258945Srobertoestimate_entropy(sample_queue_t *sq, isc_uint32_t t) {
392258945Sroberto	isc_int32_t		delta;
393258945Sroberto	isc_int32_t		delta2;
394258945Sroberto	isc_int32_t		delta3;
395258945Sroberto
396258945Sroberto	/*!
397258945Sroberto	 * If the time counter has overflowed, calculate the real difference.
398258945Sroberto	 * If it has not, it is simpler.
399258945Sroberto	 */
400258945Sroberto	if (t < sq->last_time)
401258945Sroberto		delta = UINT_MAX - sq->last_time + t;
402258945Sroberto	else
403258945Sroberto		delta = sq->last_time - t;
404258945Sroberto
405258945Sroberto	if (delta < 0)
406258945Sroberto		delta = -delta;
407258945Sroberto
408258945Sroberto	/*
409258945Sroberto	 * Calculate the second and third order differentials
410258945Sroberto	 */
411258945Sroberto	delta2 = sq->last_delta - delta;
412258945Sroberto	if (delta2 < 0)
413258945Sroberto		delta2 = -delta2;
414258945Sroberto
415258945Sroberto	delta3 = sq->last_delta2 - delta2;
416258945Sroberto	if (delta3 < 0)
417258945Sroberto		delta3 = -delta3;
418258945Sroberto
419258945Sroberto	sq->last_time = t;
420258945Sroberto	sq->last_delta = delta;
421258945Sroberto	sq->last_delta2 = delta2;
422258945Sroberto
423258945Sroberto	/*
424258945Sroberto	 * If any delta is 0, we got no entropy.  If all are non-zero, we
425258945Sroberto	 * might have something.
426258945Sroberto	 */
427258945Sroberto	if (delta == 0 || delta2 == 0 || delta3 == 0)
428258945Sroberto		return 0;
429258945Sroberto
430258945Sroberto	/*
431258945Sroberto	 * We could find the smallest delta and claim we got log2(delta)
432258945Sroberto	 * bits, but for now return that we found 1 bit.
433258945Sroberto	 */
434258945Sroberto	return 1;
435258945Sroberto}
436258945Sroberto
437258945Srobertostatic unsigned int
438258945Srobertocrunchsamples(isc_entropy_t *ent, sample_queue_t *sq) {
439258945Sroberto	unsigned int ns;
440258945Sroberto	unsigned int added;
441258945Sroberto
442258945Sroberto	if (sq->nsamples < 6)
443258945Sroberto		return (0);
444258945Sroberto
445258945Sroberto	added = 0;
446258945Sroberto	sq->last_time = sq->samples[0];
447258945Sroberto	sq->last_delta = 0;
448258945Sroberto	sq->last_delta2 = 0;
449258945Sroberto
450258945Sroberto	/*
451258945Sroberto	 * Prime the values by adding in the first 4 samples in.  This
452258945Sroberto	 * should completely initialize the delta calculations.
453258945Sroberto	 */
454258945Sroberto	for (ns = 0; ns < 4; ns++)
455258945Sroberto		(void)estimate_entropy(sq, sq->samples[ns]);
456258945Sroberto
457258945Sroberto	for (ns = 4; ns < sq->nsamples; ns++)
458258945Sroberto		added += estimate_entropy(sq, sq->samples[ns]);
459258945Sroberto
460258945Sroberto	entropypool_adddata(ent, sq->samples, sq->nsamples * 4, added);
461258945Sroberto	entropypool_adddata(ent, sq->extra, sq->nsamples * 4, 0);
462258945Sroberto
463258945Sroberto	/*
464258945Sroberto	 * Move the last 4 samples into the first 4 positions, and start
465258945Sroberto	 * adding new samples from that point.
466258945Sroberto	 */
467258945Sroberto	for (ns = 0; ns < 4; ns++) {
468258945Sroberto		sq->samples[ns] = sq->samples[sq->nsamples - 4 + ns];
469258945Sroberto		sq->extra[ns] = sq->extra[sq->nsamples - 4 + ns];
470258945Sroberto	}
471258945Sroberto
472258945Sroberto	sq->nsamples = 4;
473258945Sroberto
474258945Sroberto	return (added);
475258945Sroberto}
476258945Sroberto
477258945Srobertostatic unsigned int
478258945Srobertoget_from_callback(isc_entropysource_t *source, unsigned int desired,
479258945Sroberto		  isc_boolean_t blocking)
480258945Sroberto{
481258945Sroberto	isc_entropy_t *ent = source->ent;
482258945Sroberto	isc_cbsource_t *cbs = &source->sources.callback;
483258945Sroberto	unsigned int added;
484258945Sroberto	unsigned int got;
485258945Sroberto	isc_result_t result;
486258945Sroberto
487258945Sroberto	if (desired == 0)
488258945Sroberto		return (0);
489258945Sroberto
490258945Sroberto	if (source->bad)
491258945Sroberto		return (0);
492258945Sroberto
493258945Sroberto	if (!cbs->start_called && cbs->startfunc != NULL) {
494258945Sroberto		result = cbs->startfunc(source, cbs->arg, blocking);
495258945Sroberto		if (result != ISC_R_SUCCESS)
496258945Sroberto			return (0);
497258945Sroberto		cbs->start_called = ISC_TRUE;
498258945Sroberto	}
499258945Sroberto
500258945Sroberto	added = 0;
501258945Sroberto	result = ISC_R_SUCCESS;
502258945Sroberto	while (desired > 0 && result == ISC_R_SUCCESS) {
503258945Sroberto		result = cbs->getfunc(source, cbs->arg, blocking);
504258945Sroberto		if (result == ISC_R_QUEUEFULL) {
505258945Sroberto			got = crunchsamples(ent, &cbs->samplequeue);
506258945Sroberto			added += got;
507258945Sroberto			desired -= ISC_MIN(got, desired);
508258945Sroberto			result = ISC_R_SUCCESS;
509258945Sroberto		} else if (result != ISC_R_SUCCESS &&
510258945Sroberto			   result != ISC_R_NOTBLOCKING)
511258945Sroberto			source->bad = ISC_TRUE;
512258945Sroberto
513258945Sroberto	}
514258945Sroberto
515258945Sroberto	return (added);
516258945Sroberto}
517258945Sroberto
518258945Sroberto/*
519258945Sroberto * Extract some number of bytes from the random pool, decreasing the
520258945Sroberto * estimate of randomness as each byte is extracted.
521258945Sroberto *
522258945Sroberto * Do this by stiring the pool and returning a part of hash as randomness.
523258945Sroberto * Note that no secrets are given away here since parts of the hash are
524258945Sroberto * xored together before returned.
525258945Sroberto *
526258945Sroberto * Honor the request from the caller to only return good data, any data,
527258945Sroberto * etc.
528258945Sroberto */
529258945Srobertoisc_result_t
530258945Srobertoisc_entropy_getdata(isc_entropy_t *ent, void *data, unsigned int length,
531258945Sroberto		    unsigned int *returned, unsigned int flags)
532258945Sroberto{
533258945Sroberto	unsigned int i;
534258945Sroberto	isc_sha1_t hash;
535258945Sroberto	unsigned char digest[ISC_SHA1_DIGESTLENGTH];
536258945Sroberto	isc_uint32_t remain, deltae, count, total;
537258945Sroberto	isc_uint8_t *buf;
538258945Sroberto	isc_boolean_t goodonly, partial, blocking;
539258945Sroberto
540258945Sroberto	REQUIRE(VALID_ENTROPY(ent));
541258945Sroberto	REQUIRE(data != NULL);
542258945Sroberto	REQUIRE(length > 0);
543258945Sroberto
544258945Sroberto	goodonly = ISC_TF((flags & ISC_ENTROPY_GOODONLY) != 0);
545258945Sroberto	partial = ISC_TF((flags & ISC_ENTROPY_PARTIAL) != 0);
546258945Sroberto	blocking = ISC_TF((flags & ISC_ENTROPY_BLOCKING) != 0);
547258945Sroberto
548258945Sroberto	REQUIRE(!partial || returned != NULL);
549258945Sroberto
550258945Sroberto	LOCK(&ent->lock);
551258945Sroberto
552258945Sroberto	remain = length;
553258945Sroberto	buf = data;
554258945Sroberto	total = 0;
555258945Sroberto	while (remain != 0) {
556258945Sroberto		count = ISC_MIN(remain, RND_ENTROPY_THRESHOLD);
557258945Sroberto
558258945Sroberto		/*
559258945Sroberto		 * If we are extracting good data only, make certain we
560258945Sroberto		 * have enough data in our pool for this pass.  If we don't,
561258945Sroberto		 * get some, and fail if we can't, and partial returns
562258945Sroberto		 * are not ok.
563258945Sroberto		 */
564258945Sroberto		if (goodonly) {
565258945Sroberto			unsigned int fillcount;
566258945Sroberto
567258945Sroberto			fillcount = ISC_MAX(remain * 8, count * 8);
568258945Sroberto
569258945Sroberto			/*
570258945Sroberto			 * If, however, we have at least THRESHOLD_BITS
571258945Sroberto			 * of entropy in the pool, don't block here.  It is
572258945Sroberto			 * better to drain the pool once in a while and
573258945Sroberto			 * then refill it than it is to constantly keep the
574258945Sroberto			 * pool full.
575258945Sroberto			 */
576258945Sroberto			if (ent->pool.entropy >= THRESHOLD_BITS)
577258945Sroberto				fillpool(ent, fillcount, ISC_FALSE);
578258945Sroberto			else
579258945Sroberto				fillpool(ent, fillcount, blocking);
580258945Sroberto
581258945Sroberto			/*
582258945Sroberto			 * Verify that we got enough entropy to do one
583258945Sroberto			 * extraction.  If we didn't, bail.
584258945Sroberto			 */
585258945Sroberto			if (ent->pool.entropy < THRESHOLD_BITS) {
586258945Sroberto				if (!partial)
587258945Sroberto					goto zeroize;
588258945Sroberto				else
589258945Sroberto					goto partial_output;
590258945Sroberto			}
591258945Sroberto		} else {
592258945Sroberto			/*
593258945Sroberto			 * If we've extracted half our pool size in bits
594258945Sroberto			 * since the last refresh, try to refresh here.
595258945Sroberto			 */
596258945Sroberto			if (ent->initialized < THRESHOLD_BITS)
597258945Sroberto				fillpool(ent, THRESHOLD_BITS, blocking);
598258945Sroberto			else
599258945Sroberto				fillpool(ent, 0, ISC_FALSE);
600258945Sroberto
601258945Sroberto			/*
602258945Sroberto			 * If we've not initialized with enough good random
603258945Sroberto			 * data, seed with our crappy code.
604258945Sroberto			 */
605258945Sroberto			if (ent->initialized < THRESHOLD_BITS)
606258945Sroberto				reseed(ent);
607258945Sroberto		}
608258945Sroberto
609258945Sroberto		isc_sha1_init(&hash);
610258945Sroberto		isc_sha1_update(&hash, (void *)(ent->pool.pool),
611258945Sroberto				RND_POOLBYTES);
612258945Sroberto		isc_sha1_final(&hash, digest);
613258945Sroberto
614258945Sroberto		/*
615258945Sroberto		 * Stir the extracted data (all of it) back into the pool.
616258945Sroberto		 */
617258945Sroberto		entropypool_adddata(ent, digest, ISC_SHA1_DIGESTLENGTH, 0);
618258945Sroberto
619258945Sroberto		for (i = 0; i < count; i++)
620258945Sroberto			buf[i] = digest[i] ^ digest[i + RND_ENTROPY_THRESHOLD];
621258945Sroberto
622258945Sroberto		buf += count;
623258945Sroberto		remain -= count;
624258945Sroberto
625258945Sroberto		deltae = count * 8;
626258945Sroberto		deltae = ISC_MIN(deltae, ent->pool.entropy);
627258945Sroberto		total += deltae;
628258945Sroberto		subtract_entropy(ent, deltae);
629258945Sroberto		add_pseudo(ent, count * 8);
630258945Sroberto	}
631258945Sroberto
632258945Sroberto partial_output:
633258945Sroberto	memset(digest, 0, sizeof(digest));
634258945Sroberto
635258945Sroberto	if (returned != NULL)
636258945Sroberto		*returned = (length - remain);
637258945Sroberto
638258945Sroberto	UNLOCK(&ent->lock);
639258945Sroberto
640258945Sroberto	return (ISC_R_SUCCESS);
641258945Sroberto
642258945Sroberto zeroize:
643258945Sroberto	/* put the entropy we almost extracted back */
644258945Sroberto	add_entropy(ent, total);
645258945Sroberto	memset(data, 0, length);
646258945Sroberto	memset(digest, 0, sizeof(digest));
647258945Sroberto	if (returned != NULL)
648258945Sroberto		*returned = 0;
649258945Sroberto
650258945Sroberto	UNLOCK(&ent->lock);
651258945Sroberto
652258945Sroberto	return (ISC_R_NOENTROPY);
653258945Sroberto}
654258945Sroberto
655258945Srobertostatic void
656258945Srobertoisc_entropypool_init(isc_entropypool_t *pool) {
657258945Sroberto	pool->cursor = RND_POOLWORDS - 1;
658258945Sroberto	pool->entropy = 0;
659258945Sroberto	pool->pseudo = 0;
660258945Sroberto	pool->rotate = 0;
661258945Sroberto	memset(pool->pool, 0, RND_POOLBYTES);
662258945Sroberto}
663258945Sroberto
664258945Srobertostatic void
665258945Srobertoisc_entropypool_invalidate(isc_entropypool_t *pool) {
666258945Sroberto	pool->cursor = 0;
667258945Sroberto	pool->entropy = 0;
668258945Sroberto	pool->pseudo = 0;
669258945Sroberto	pool->rotate = 0;
670258945Sroberto	memset(pool->pool, 0, RND_POOLBYTES);
671258945Sroberto}
672258945Sroberto
673258945Srobertoisc_result_t
674258945Srobertoisc_entropy_create(isc_mem_t *mctx, isc_entropy_t **entp) {
675258945Sroberto	isc_result_t result;
676258945Sroberto	isc_entropy_t *ent;
677258945Sroberto
678258945Sroberto	REQUIRE(mctx != NULL);
679258945Sroberto	REQUIRE(entp != NULL && *entp == NULL);
680258945Sroberto
681258945Sroberto	ent = isc_mem_get(mctx, sizeof(isc_entropy_t));
682258945Sroberto	if (ent == NULL)
683258945Sroberto		return (ISC_R_NOMEMORY);
684258945Sroberto
685258945Sroberto	/*
686258945Sroberto	 * We need a lock.
687258945Sroberto	 */
688258945Sroberto	result = isc_mutex_init(&ent->lock);
689258945Sroberto	if (result != ISC_R_SUCCESS)
690258945Sroberto		goto errout;
691258945Sroberto
692258945Sroberto	/*
693258945Sroberto	 * From here down, no failures will/can occur.
694258945Sroberto	 */
695258945Sroberto	ISC_LIST_INIT(ent->sources);
696258945Sroberto	ent->nextsource = NULL;
697258945Sroberto	ent->nsources = 0;
698258945Sroberto	ent->mctx = NULL;
699258945Sroberto	isc_mem_attach(mctx, &ent->mctx);
700258945Sroberto	ent->refcnt = 1;
701258945Sroberto	ent->initialized = 0;
702258945Sroberto	ent->initcount = 0;
703258945Sroberto	ent->magic = ENTROPY_MAGIC;
704258945Sroberto
705258945Sroberto	isc_entropypool_init(&ent->pool);
706258945Sroberto
707258945Sroberto	*entp = ent;
708258945Sroberto	return (ISC_R_SUCCESS);
709258945Sroberto
710258945Sroberto errout:
711258945Sroberto	isc_mem_put(mctx, ent, sizeof(isc_entropy_t));
712258945Sroberto
713258945Sroberto	return (result);
714258945Sroberto}
715258945Sroberto
716258945Sroberto/*!
717258945Sroberto * Requires "ent" be locked.
718258945Sroberto */
719258945Srobertostatic void
720258945Srobertodestroysource(isc_entropysource_t **sourcep) {
721258945Sroberto	isc_entropysource_t *source;
722258945Sroberto	isc_entropy_t *ent;
723258945Sroberto	isc_cbsource_t *cbs;
724258945Sroberto
725258945Sroberto	source = *sourcep;
726258945Sroberto	*sourcep = NULL;
727258945Sroberto	ent = source->ent;
728258945Sroberto
729258945Sroberto	ISC_LIST_UNLINK(ent->sources, source, link);
730258945Sroberto	ent->nextsource = NULL;
731258945Sroberto	REQUIRE(ent->nsources > 0);
732258945Sroberto	ent->nsources--;
733258945Sroberto
734258945Sroberto	switch (source->type) {
735258945Sroberto	case ENTROPY_SOURCETYPE_FILE:
736258945Sroberto		if (! source->bad)
737258945Sroberto			destroyfilesource(&source->sources.file);
738258945Sroberto		break;
739258945Sroberto	case ENTROPY_SOURCETYPE_USOCKET:
740258945Sroberto		if (! source->bad)
741258945Sroberto			destroyusocketsource(&source->sources.usocket);
742258945Sroberto		break;
743258945Sroberto	case ENTROPY_SOURCETYPE_SAMPLE:
744258945Sroberto		samplequeue_release(ent, &source->sources.sample.samplequeue);
745258945Sroberto		break;
746258945Sroberto	case ENTROPY_SOURCETYPE_CALLBACK:
747258945Sroberto		cbs = &source->sources.callback;
748258945Sroberto		if (cbs->start_called && cbs->stopfunc != NULL) {
749258945Sroberto			cbs->stopfunc(source, cbs->arg);
750258945Sroberto			cbs->start_called = ISC_FALSE;
751258945Sroberto		}
752258945Sroberto		samplequeue_release(ent, &cbs->samplequeue);
753258945Sroberto		break;
754258945Sroberto	}
755258945Sroberto
756258945Sroberto	memset(source, 0, sizeof(isc_entropysource_t));
757258945Sroberto
758258945Sroberto	isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t));
759258945Sroberto}
760258945Sroberto
761258945Srobertostatic inline isc_boolean_t
762258945Srobertodestroy_check(isc_entropy_t *ent) {
763258945Sroberto	isc_entropysource_t *source;
764258945Sroberto
765258945Sroberto	if (ent->refcnt > 0)
766258945Sroberto		return (ISC_FALSE);
767258945Sroberto
768258945Sroberto	source = ISC_LIST_HEAD(ent->sources);
769258945Sroberto	while (source != NULL) {
770258945Sroberto		switch (source->type) {
771258945Sroberto		case ENTROPY_SOURCETYPE_FILE:
772258945Sroberto		case ENTROPY_SOURCETYPE_USOCKET:
773258945Sroberto			break;
774258945Sroberto		default:
775258945Sroberto			return (ISC_FALSE);
776258945Sroberto		}
777258945Sroberto		source = ISC_LIST_NEXT(source, link);
778258945Sroberto	}
779258945Sroberto
780258945Sroberto	return (ISC_TRUE);
781258945Sroberto}
782258945Sroberto
783258945Srobertostatic void
784258945Srobertodestroy(isc_entropy_t **entp) {
785258945Sroberto	isc_entropy_t *ent;
786258945Sroberto	isc_entropysource_t *source;
787258945Sroberto	isc_mem_t *mctx;
788258945Sroberto
789258945Sroberto	REQUIRE(entp != NULL && *entp != NULL);
790258945Sroberto	ent = *entp;
791258945Sroberto	*entp = NULL;
792258945Sroberto
793258945Sroberto	LOCK(&ent->lock);
794258945Sroberto
795258945Sroberto	REQUIRE(ent->refcnt == 0);
796258945Sroberto
797258945Sroberto	/*
798258945Sroberto	 * Here, detach non-sample sources.
799258945Sroberto	 */
800258945Sroberto	source = ISC_LIST_HEAD(ent->sources);
801258945Sroberto	while (source != NULL) {
802258945Sroberto		switch(source->type) {
803258945Sroberto		case ENTROPY_SOURCETYPE_FILE:
804258945Sroberto		case ENTROPY_SOURCETYPE_USOCKET:
805258945Sroberto			destroysource(&source);
806258945Sroberto			break;
807258945Sroberto		}
808258945Sroberto		source = ISC_LIST_HEAD(ent->sources);
809258945Sroberto	}
810258945Sroberto
811258945Sroberto	/*
812258945Sroberto	 * If there are other types of sources, we've found a bug.
813258945Sroberto	 */
814258945Sroberto	REQUIRE(ISC_LIST_EMPTY(ent->sources));
815258945Sroberto
816258945Sroberto	mctx = ent->mctx;
817258945Sroberto
818258945Sroberto	isc_entropypool_invalidate(&ent->pool);
819258945Sroberto
820258945Sroberto	UNLOCK(&ent->lock);
821258945Sroberto
822258945Sroberto	DESTROYLOCK(&ent->lock);
823258945Sroberto
824258945Sroberto	memset(ent, 0, sizeof(isc_entropy_t));
825258945Sroberto	isc_mem_put(mctx, ent, sizeof(isc_entropy_t));
826258945Sroberto	isc_mem_detach(&mctx);
827258945Sroberto}
828258945Sroberto
829258945Srobertovoid
830258945Srobertoisc_entropy_destroysource(isc_entropysource_t **sourcep) {
831258945Sroberto	isc_entropysource_t *source;
832258945Sroberto	isc_entropy_t *ent;
833258945Sroberto	isc_boolean_t killit;
834258945Sroberto
835258945Sroberto	REQUIRE(sourcep != NULL);
836258945Sroberto	REQUIRE(VALID_SOURCE(*sourcep));
837258945Sroberto
838258945Sroberto	source = *sourcep;
839258945Sroberto	*sourcep = NULL;
840258945Sroberto
841258945Sroberto	ent = source->ent;
842258945Sroberto	REQUIRE(VALID_ENTROPY(ent));
843258945Sroberto
844258945Sroberto	LOCK(&ent->lock);
845258945Sroberto
846258945Sroberto	destroysource(&source);
847258945Sroberto
848258945Sroberto	killit = destroy_check(ent);
849258945Sroberto
850258945Sroberto	UNLOCK(&ent->lock);
851258945Sroberto
852258945Sroberto	if (killit)
853258945Sroberto		destroy(&ent);
854258945Sroberto}
855258945Sroberto
856258945Srobertoisc_result_t
857258945Srobertoisc_entropy_createcallbacksource(isc_entropy_t *ent,
858258945Sroberto				 isc_entropystart_t start,
859258945Sroberto				 isc_entropyget_t get,
860258945Sroberto				 isc_entropystop_t stop,
861258945Sroberto				 void *arg,
862258945Sroberto				 isc_entropysource_t **sourcep)
863258945Sroberto{
864258945Sroberto	isc_result_t result;
865258945Sroberto	isc_entropysource_t *source;
866258945Sroberto	isc_cbsource_t *cbs;
867258945Sroberto
868258945Sroberto	REQUIRE(VALID_ENTROPY(ent));
869258945Sroberto	REQUIRE(get != NULL);
870258945Sroberto	REQUIRE(sourcep != NULL && *sourcep == NULL);
871258945Sroberto
872258945Sroberto	LOCK(&ent->lock);
873258945Sroberto
874258945Sroberto	source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
875258945Sroberto	if (source == NULL) {
876258945Sroberto		result = ISC_R_NOMEMORY;
877258945Sroberto		goto errout;
878258945Sroberto	}
879258945Sroberto	source->bad = ISC_FALSE;
880258945Sroberto
881258945Sroberto	cbs = &source->sources.callback;
882258945Sroberto
883258945Sroberto	result = samplesource_allocate(ent, &cbs->samplequeue);
884258945Sroberto	if (result != ISC_R_SUCCESS)
885258945Sroberto		goto errout;
886258945Sroberto
887258945Sroberto	cbs->start_called = ISC_FALSE;
888258945Sroberto	cbs->startfunc = start;
889258945Sroberto	cbs->getfunc = get;
890258945Sroberto	cbs->stopfunc = stop;
891258945Sroberto	cbs->arg = arg;
892258945Sroberto
893258945Sroberto	/*
894258945Sroberto	 * From here down, no failures can occur.
895258945Sroberto	 */
896258945Sroberto	source->magic = SOURCE_MAGIC;
897258945Sroberto	source->type = ENTROPY_SOURCETYPE_CALLBACK;
898258945Sroberto	source->ent = ent;
899258945Sroberto	source->total = 0;
900258945Sroberto	memset(source->name, 0, sizeof(source->name));
901258945Sroberto	ISC_LINK_INIT(source, link);
902258945Sroberto
903258945Sroberto	/*
904258945Sroberto	 * Hook it into the entropy system.
905258945Sroberto	 */
906258945Sroberto	ISC_LIST_APPEND(ent->sources, source, link);
907258945Sroberto	ent->nsources++;
908258945Sroberto
909258945Sroberto	*sourcep = source;
910258945Sroberto
911258945Sroberto	UNLOCK(&ent->lock);
912258945Sroberto	return (ISC_R_SUCCESS);
913258945Sroberto
914258945Sroberto errout:
915258945Sroberto	if (source != NULL)
916258945Sroberto		isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t));
917258945Sroberto
918258945Sroberto	UNLOCK(&ent->lock);
919258945Sroberto
920258945Sroberto	return (result);
921258945Sroberto}
922258945Sroberto
923258945Srobertovoid
924258945Srobertoisc_entropy_stopcallbacksources(isc_entropy_t *ent) {
925258945Sroberto	isc_entropysource_t *source;
926258945Sroberto	isc_cbsource_t *cbs;
927258945Sroberto
928258945Sroberto	REQUIRE(VALID_ENTROPY(ent));
929258945Sroberto
930258945Sroberto	LOCK(&ent->lock);
931258945Sroberto
932258945Sroberto	source = ISC_LIST_HEAD(ent->sources);
933258945Sroberto	while (source != NULL) {
934258945Sroberto		if (source->type == ENTROPY_SOURCETYPE_CALLBACK) {
935258945Sroberto			cbs = &source->sources.callback;
936258945Sroberto			if (cbs->start_called && cbs->stopfunc != NULL) {
937258945Sroberto				cbs->stopfunc(source, cbs->arg);
938258945Sroberto				cbs->start_called = ISC_FALSE;
939258945Sroberto			}
940258945Sroberto		}
941258945Sroberto
942258945Sroberto		source = ISC_LIST_NEXT(source, link);
943258945Sroberto	}
944258945Sroberto
945258945Sroberto	UNLOCK(&ent->lock);
946258945Sroberto}
947258945Sroberto
948258945Srobertoisc_result_t
949258945Srobertoisc_entropy_createsamplesource(isc_entropy_t *ent,
950258945Sroberto			       isc_entropysource_t **sourcep)
951258945Sroberto{
952258945Sroberto	isc_result_t result;
953258945Sroberto	isc_entropysource_t *source;
954258945Sroberto	sample_queue_t *sq;
955258945Sroberto
956258945Sroberto	REQUIRE(VALID_ENTROPY(ent));
957258945Sroberto	REQUIRE(sourcep != NULL && *sourcep == NULL);
958258945Sroberto
959258945Sroberto	LOCK(&ent->lock);
960258945Sroberto
961258945Sroberto	source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
962258945Sroberto	if (source == NULL) {
963258945Sroberto		result = ISC_R_NOMEMORY;
964258945Sroberto		goto errout;
965258945Sroberto	}
966258945Sroberto
967258945Sroberto	sq = &source->sources.sample.samplequeue;
968258945Sroberto	result = samplesource_allocate(ent, sq);
969258945Sroberto	if (result != ISC_R_SUCCESS)
970258945Sroberto		goto errout;
971258945Sroberto
972258945Sroberto	/*
973258945Sroberto	 * From here down, no failures can occur.
974258945Sroberto	 */
975258945Sroberto	source->magic = SOURCE_MAGIC;
976258945Sroberto	source->type = ENTROPY_SOURCETYPE_SAMPLE;
977258945Sroberto	source->ent = ent;
978258945Sroberto	source->total = 0;
979258945Sroberto	memset(source->name, 0, sizeof(source->name));
980258945Sroberto	ISC_LINK_INIT(source, link);
981258945Sroberto
982258945Sroberto	/*
983258945Sroberto	 * Hook it into the entropy system.
984258945Sroberto	 */
985258945Sroberto	ISC_LIST_APPEND(ent->sources, source, link);
986258945Sroberto	ent->nsources++;
987258945Sroberto
988258945Sroberto	*sourcep = source;
989258945Sroberto
990258945Sroberto	UNLOCK(&ent->lock);
991258945Sroberto	return (ISC_R_SUCCESS);
992258945Sroberto
993258945Sroberto errout:
994258945Sroberto	if (source != NULL)
995258945Sroberto		isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t));
996258945Sroberto
997258945Sroberto	UNLOCK(&ent->lock);
998258945Sroberto
999258945Sroberto	return (result);
1000258945Sroberto}
1001258945Sroberto
1002258945Sroberto/*!
1003258945Sroberto * Add a sample, and return ISC_R_SUCCESS if the queue has become full,
1004258945Sroberto * ISC_R_NOENTROPY if it has space remaining, and ISC_R_NOMORE if the
1005258945Sroberto * queue was full when this function was called.
1006258945Sroberto */
1007258945Srobertostatic isc_result_t
1008258945Srobertoaddsample(sample_queue_t *sq, isc_uint32_t sample, isc_uint32_t extra) {
1009258945Sroberto	if (sq->nsamples >= RND_EVENTQSIZE)
1010258945Sroberto		return (ISC_R_NOMORE);
1011258945Sroberto
1012258945Sroberto	sq->samples[sq->nsamples] = sample;
1013258945Sroberto	sq->extra[sq->nsamples] = extra;
1014258945Sroberto	sq->nsamples++;
1015258945Sroberto
1016258945Sroberto	if (sq->nsamples >= RND_EVENTQSIZE)
1017258945Sroberto		return (ISC_R_QUEUEFULL);
1018258945Sroberto
1019258945Sroberto	return (ISC_R_SUCCESS);
1020258945Sroberto}
1021258945Sroberto
1022258945Srobertoisc_result_t
1023258945Srobertoisc_entropy_addsample(isc_entropysource_t *source, isc_uint32_t sample,
1024258945Sroberto		      isc_uint32_t extra)
1025258945Sroberto{
1026258945Sroberto	isc_entropy_t *ent;
1027258945Sroberto	sample_queue_t *sq;
1028258945Sroberto	unsigned int entropy;
1029258945Sroberto	isc_result_t result;
1030258945Sroberto
1031258945Sroberto	REQUIRE(VALID_SOURCE(source));
1032258945Sroberto
1033258945Sroberto	ent = source->ent;
1034258945Sroberto
1035258945Sroberto	LOCK(&ent->lock);
1036258945Sroberto
1037258945Sroberto	sq = &source->sources.sample.samplequeue;
1038258945Sroberto	result = addsample(sq, sample, extra);
1039258945Sroberto	if (result == ISC_R_QUEUEFULL) {
1040258945Sroberto		entropy = crunchsamples(ent, sq);
1041258945Sroberto		add_entropy(ent, entropy);
1042258945Sroberto	}
1043258945Sroberto
1044258945Sroberto	UNLOCK(&ent->lock);
1045258945Sroberto
1046258945Sroberto	return (result);
1047258945Sroberto}
1048258945Sroberto
1049258945Srobertoisc_result_t
1050258945Srobertoisc_entropy_addcallbacksample(isc_entropysource_t *source, isc_uint32_t sample,
1051258945Sroberto			      isc_uint32_t extra)
1052258945Sroberto{
1053258945Sroberto	sample_queue_t *sq;
1054258945Sroberto	isc_result_t result;
1055258945Sroberto
1056258945Sroberto	REQUIRE(VALID_SOURCE(source));
1057258945Sroberto	REQUIRE(source->type == ENTROPY_SOURCETYPE_CALLBACK);
1058258945Sroberto
1059258945Sroberto	sq = &source->sources.callback.samplequeue;
1060258945Sroberto	result = addsample(sq, sample, extra);
1061258945Sroberto
1062258945Sroberto	return (result);
1063258945Sroberto}
1064258945Sroberto
1065258945Srobertovoid
1066258945Srobertoisc_entropy_putdata(isc_entropy_t *ent, void *data, unsigned int length,
1067258945Sroberto		    isc_uint32_t entropy)
1068258945Sroberto{
1069258945Sroberto	REQUIRE(VALID_ENTROPY(ent));
1070258945Sroberto
1071258945Sroberto	LOCK(&ent->lock);
1072258945Sroberto
1073258945Sroberto	entropypool_adddata(ent, data, length, entropy);
1074258945Sroberto
1075258945Sroberto	if (ent->initialized < THRESHOLD_BITS)
1076258945Sroberto		ent->initialized = THRESHOLD_BITS;
1077258945Sroberto
1078258945Sroberto	UNLOCK(&ent->lock);
1079258945Sroberto}
1080258945Sroberto
1081258945Srobertostatic void
1082258945Srobertodumpstats(isc_entropy_t *ent, FILE *out) {
1083258945Sroberto	fprintf(out,
1084258945Sroberto		isc_msgcat_get(isc_msgcat, ISC_MSGSET_ENTROPY,
1085258945Sroberto			       ISC_MSG_ENTROPYSTATS,
1086258945Sroberto			       "Entropy pool %p:  refcnt %u cursor %u,"
1087258945Sroberto			       " rotate %u entropy %u pseudo %u nsources %u"
1088258945Sroberto			       " nextsource %p initialized %u initcount %u\n"),
1089258945Sroberto		ent, ent->refcnt,
1090258945Sroberto		ent->pool.cursor, ent->pool.rotate,
1091258945Sroberto		ent->pool.entropy, ent->pool.pseudo,
1092258945Sroberto		ent->nsources, ent->nextsource, ent->initialized,
1093258945Sroberto		ent->initcount);
1094258945Sroberto}
1095258945Sroberto
1096258945Sroberto/*
1097258945Sroberto * This function ignores locking.  Use at your own risk.
1098258945Sroberto */
1099258945Srobertovoid
1100258945Srobertoisc_entropy_stats(isc_entropy_t *ent, FILE *out) {
1101258945Sroberto	REQUIRE(VALID_ENTROPY(ent));
1102258945Sroberto
1103258945Sroberto	LOCK(&ent->lock);
1104258945Sroberto	dumpstats(ent, out);
1105258945Sroberto	UNLOCK(&ent->lock);
1106258945Sroberto}
1107258945Sroberto
1108258945Srobertounsigned int
1109258945Srobertoisc_entropy_status(isc_entropy_t *ent) {
1110258945Sroberto	unsigned int estimate;
1111258945Sroberto
1112258945Sroberto	LOCK(&ent->lock);
1113258945Sroberto	estimate = ent->pool.entropy;
1114258945Sroberto	UNLOCK(&ent->lock);
1115258945Sroberto
1116258945Sroberto	return estimate;
1117258945Sroberto}
1118258945Sroberto
1119258945Srobertovoid
1120258945Srobertoisc_entropy_attach(isc_entropy_t *ent, isc_entropy_t **entp) {
1121258945Sroberto	REQUIRE(VALID_ENTROPY(ent));
1122258945Sroberto	REQUIRE(entp != NULL && *entp == NULL);
1123258945Sroberto
1124258945Sroberto	LOCK(&ent->lock);
1125258945Sroberto
1126258945Sroberto	ent->refcnt++;
1127258945Sroberto	*entp = ent;
1128258945Sroberto
1129258945Sroberto	UNLOCK(&ent->lock);
1130258945Sroberto}
1131258945Sroberto
1132258945Srobertovoid
1133258945Srobertoisc_entropy_detach(isc_entropy_t **entp) {
1134258945Sroberto	isc_entropy_t *ent;
1135258945Sroberto	isc_boolean_t killit;
1136258945Sroberto
1137258945Sroberto	REQUIRE(entp != NULL && VALID_ENTROPY(*entp));
1138258945Sroberto	ent = *entp;
1139258945Sroberto	*entp = NULL;
1140258945Sroberto
1141258945Sroberto	LOCK(&ent->lock);
1142258945Sroberto
1143258945Sroberto	REQUIRE(ent->refcnt > 0);
1144258945Sroberto	ent->refcnt--;
1145258945Sroberto
1146258945Sroberto	killit = destroy_check(ent);
1147258945Sroberto
1148258945Sroberto	UNLOCK(&ent->lock);
1149258945Sroberto
1150258945Sroberto	if (killit)
1151258945Sroberto		destroy(&ent);
1152258945Sroberto}
1153258945Sroberto
1154258945Srobertostatic isc_result_t
1155258945Srobertokbdstart(isc_entropysource_t *source, void *arg, isc_boolean_t blocking) {
1156258945Sroberto	/*
1157258945Sroberto	 * The intent of "first" is to provide a warning message only once
1158258945Sroberto	 * during the run of a program that might try to gather keyboard
1159258945Sroberto	 * entropy multiple times.
1160258945Sroberto	 */
1161258945Sroberto	static isc_boolean_t first = ISC_TRUE;
1162258945Sroberto
1163258945Sroberto	UNUSED(arg);
1164258945Sroberto
1165258945Sroberto	if (! blocking)
1166258945Sroberto		return (ISC_R_NOENTROPY);
1167258945Sroberto
1168258945Sroberto	if (first) {
1169258945Sroberto		if (source->warn_keyboard)
1170258945Sroberto			fprintf(stderr, "You must use the keyboard to create "
1171258945Sroberto				"entropy, since your system is lacking\n"
1172258945Sroberto				"/dev/random (or equivalent)\n\n");
1173258945Sroberto		first = ISC_FALSE;
1174258945Sroberto	}
1175258945Sroberto	fprintf(stderr, "start typing:\n");
1176258945Sroberto
1177258945Sroberto	return (isc_keyboard_open(&source->kbd));
1178258945Sroberto}
1179258945Sroberto
1180258945Srobertostatic void
1181258945Srobertokbdstop(isc_entropysource_t *source, void *arg) {
1182258945Sroberto
1183258945Sroberto	UNUSED(arg);
1184258945Sroberto
1185258945Sroberto	if (! isc_keyboard_canceled(&source->kbd))
1186258945Sroberto		fprintf(stderr, "stop typing.\r\n");
1187258945Sroberto
1188258945Sroberto	(void)isc_keyboard_close(&source->kbd, 3);
1189258945Sroberto}
1190258945Sroberto
1191258945Srobertostatic isc_result_t
1192258945Srobertokbdget(isc_entropysource_t *source, void *arg, isc_boolean_t blocking) {
1193258945Sroberto	isc_result_t result;
1194258945Sroberto	isc_time_t t;
1195258945Sroberto	isc_uint32_t sample;
1196258945Sroberto	isc_uint32_t extra;
1197258945Sroberto	unsigned char c;
1198258945Sroberto
1199258945Sroberto	UNUSED(arg);
1200258945Sroberto
1201258945Sroberto	if (!blocking)
1202258945Sroberto		return (ISC_R_NOTBLOCKING);
1203258945Sroberto
1204258945Sroberto	result = isc_keyboard_getchar(&source->kbd, &c);
1205258945Sroberto	if (result != ISC_R_SUCCESS)
1206258945Sroberto		return (result);
1207258945Sroberto
1208258945Sroberto	TIME_NOW(&t);
1209258945Sroberto
1210258945Sroberto	sample = isc_time_nanoseconds(&t);
1211258945Sroberto	extra = c;
1212258945Sroberto
1213258945Sroberto	result = isc_entropy_addcallbacksample(source, sample, extra);
1214258945Sroberto	if (result != ISC_R_SUCCESS) {
1215258945Sroberto		fprintf(stderr, "\r\n");
1216258945Sroberto		return (result);
1217258945Sroberto	}
1218258945Sroberto
1219258945Sroberto	fprintf(stderr, ".");
1220258945Sroberto	fflush(stderr);
1221258945Sroberto
1222258945Sroberto	return (result);
1223258945Sroberto}
1224258945Sroberto
1225258945Srobertoisc_result_t
1226258945Srobertoisc_entropy_usebestsource(isc_entropy_t *ectx, isc_entropysource_t **source,
1227258945Sroberto			  const char *randomfile, int use_keyboard)
1228258945Sroberto{
1229258945Sroberto	isc_result_t result;
1230258945Sroberto	isc_result_t final_result = ISC_R_NOENTROPY;
1231258945Sroberto	isc_boolean_t userfile = ISC_TRUE;
1232258945Sroberto
1233258945Sroberto	REQUIRE(VALID_ENTROPY(ectx));
1234258945Sroberto	REQUIRE(source != NULL && *source == NULL);
1235258945Sroberto	REQUIRE(use_keyboard == ISC_ENTROPY_KEYBOARDYES ||
1236258945Sroberto		use_keyboard == ISC_ENTROPY_KEYBOARDNO  ||
1237258945Sroberto		use_keyboard == ISC_ENTROPY_KEYBOARDMAYBE);
1238258945Sroberto
1239258945Sroberto#ifdef PATH_RANDOMDEV
1240258945Sroberto	if (randomfile == NULL) {
1241258945Sroberto		randomfile = PATH_RANDOMDEV;
1242258945Sroberto		userfile = ISC_FALSE;
1243258945Sroberto	}
1244258945Sroberto#endif
1245258945Sroberto
1246258945Sroberto	if (randomfile != NULL && use_keyboard != ISC_ENTROPY_KEYBOARDYES) {
1247258945Sroberto		result = isc_entropy_createfilesource(ectx, randomfile);
1248258945Sroberto		if (result == ISC_R_SUCCESS &&
1249258945Sroberto		    use_keyboard == ISC_ENTROPY_KEYBOARDMAYBE)
1250258945Sroberto			use_keyboard = ISC_ENTROPY_KEYBOARDNO;
1251258945Sroberto		if (result != ISC_R_SUCCESS && userfile)
1252258945Sroberto			return (result);
1253258945Sroberto
1254258945Sroberto		final_result = result;
1255258945Sroberto	}
1256258945Sroberto
1257258945Sroberto	if (use_keyboard != ISC_ENTROPY_KEYBOARDNO) {
1258258945Sroberto		result = isc_entropy_createcallbacksource(ectx, kbdstart,
1259258945Sroberto							  kbdget, kbdstop,
1260258945Sroberto							  NULL, source);
1261258945Sroberto		if (result == ISC_R_SUCCESS)
1262258945Sroberto			(*source)->warn_keyboard =
1263258945Sroberto				ISC_TF(use_keyboard ==
1264258945Sroberto				       ISC_ENTROPY_KEYBOARDMAYBE);
1265258945Sroberto
1266258945Sroberto		if (final_result != ISC_R_SUCCESS)
1267258945Sroberto			final_result = result;
1268258945Sroberto	}
1269258945Sroberto
1270258945Sroberto	/*
1271258945Sroberto	 * final_result is ISC_R_SUCCESS if at least one source of entropy
1272258945Sroberto	 * could be started, otherwise it is the error from the most recently
1273258945Sroberto	 * failed operation (or ISC_R_NOENTROPY if PATH_RANDOMDEV is not
1274258945Sroberto	 * defined and use_keyboard is ISC_ENTROPY_KEYBOARDNO).
1275258945Sroberto	 */
1276258945Sroberto	return (final_result);
1277258945Sroberto}
1278