2000-06-03 02:15:08 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2000 Internet Software Consortium.
|
2000-08-01 01:33:37 +00:00
|
|
|
*
|
2000-06-03 02:15:08 +00:00
|
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
2000-08-01 01:33:37 +00:00
|
|
|
*
|
2000-07-27 09:55:03 +00:00
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
|
|
|
|
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
|
|
|
* INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
|
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
|
|
|
|
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
|
|
|
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
|
|
|
|
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
2000-06-03 02:15:08 +00:00
|
|
|
*/
|
|
|
|
|
2000-12-06 00:30:32 +00:00
|
|
|
/* $Id: entropy.c,v 1.52 2000/12/06 00:30:21 tale Exp $ */
|
2000-06-22 22:00:42 +00:00
|
|
|
|
2000-06-03 02:15:08 +00:00
|
|
|
#include <config.h>
|
|
|
|
|
2000-06-08 23:20:01 +00:00
|
|
|
#include <errno.h>
|
2000-06-08 22:18:53 +00:00
|
|
|
#include <fcntl.h>
|
2000-06-03 02:15:08 +00:00
|
|
|
#include <stdio.h>
|
2000-06-08 22:18:53 +00:00
|
|
|
#include <unistd.h>
|
2000-06-03 02:15:08 +00:00
|
|
|
|
|
|
|
#include <isc/buffer.h>
|
|
|
|
#include <isc/entropy.h>
|
2000-06-08 06:35:49 +00:00
|
|
|
#include <isc/list.h>
|
2000-06-09 17:25:41 +00:00
|
|
|
#include <isc/magic.h>
|
2000-06-08 06:35:49 +00:00
|
|
|
#include <isc/mem.h>
|
2000-12-06 00:30:32 +00:00
|
|
|
#include <isc/msgs.h>
|
2000-06-08 06:35:49 +00:00
|
|
|
#include <isc/mutex.h>
|
2000-06-03 02:15:08 +00:00
|
|
|
#include <isc/region.h>
|
2000-06-08 06:35:49 +00:00
|
|
|
#include <isc/sha1.h>
|
2000-06-03 02:15:08 +00:00
|
|
|
#include <isc/string.h>
|
2000-06-21 22:12:23 +00:00
|
|
|
#include <isc/time.h>
|
2000-06-03 02:15:08 +00:00
|
|
|
#include <isc/util.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Much of this code is modeled after the NetBSD /dev/random implementation,
|
|
|
|
* written by Michael Graff <explorer@netbsd.org>.
|
|
|
|
*/
|
|
|
|
|
2000-06-09 17:25:41 +00:00
|
|
|
#define ENTROPY_MAGIC ISC_MAGIC('E', 'n', 't', 'e')
|
|
|
|
#define SOURCE_MAGIC ISC_MAGIC('E', 'n', 't', 's')
|
2000-06-09 02:00:22 +00:00
|
|
|
|
2000-06-09 17:25:41 +00:00
|
|
|
#define VALID_ENTROPY(e) ISC_MAGIC_VALID(e, ENTROPY_MAGIC)
|
|
|
|
#define VALID_SOURCE(s) ISC_MAGIC_VALID(s, SOURCE_MAGIC)
|
2000-06-08 22:18:53 +00:00
|
|
|
|
2000-06-03 02:15:08 +00:00
|
|
|
/***
|
|
|
|
*** "constants." Do not change these unless you _really_ know what
|
|
|
|
*** you are doing.
|
|
|
|
***/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* size of entropy pool in 32-bit words. This _MUST_ be a power of 2.
|
|
|
|
*/
|
2000-06-12 22:28:05 +00:00
|
|
|
#define RND_POOLWORDS 128
|
2000-06-09 21:36:51 +00:00
|
|
|
#define RND_POOLBITS (RND_POOLWORDS * 32)
|
2000-06-03 02:15:08 +00:00
|
|
|
|
|
|
|
/*
|
2000-06-08 06:35:49 +00:00
|
|
|
* Number of bytes returned per hash. This must be true:
|
|
|
|
* threshold * 2 <= digest_size_in_bytes
|
2000-06-03 02:15:08 +00:00
|
|
|
*/
|
2000-06-08 06:35:49 +00:00
|
|
|
#define RND_ENTROPY_THRESHOLD 10
|
2000-06-12 22:28:05 +00:00
|
|
|
#define THRESHOLD_BITS (RND_ENTROPY_THRESHOLD * 8)
|
2000-06-03 02:15:08 +00:00
|
|
|
|
|
|
|
/*
|
2000-06-08 06:35:49 +00:00
|
|
|
* Size of the input event queue in samples.
|
2000-06-03 02:15:08 +00:00
|
|
|
*/
|
2000-06-08 06:35:49 +00:00
|
|
|
#define RND_EVENTQSIZE 32
|
2000-06-03 02:15:08 +00:00
|
|
|
|
2000-06-21 22:12:23 +00:00
|
|
|
/*
|
|
|
|
* The number of times we'll "reseed" for pseudorandom seeds. This is an
|
|
|
|
* extremely weak pseudorandom seed. If the caller is using lots of
|
|
|
|
* pseudorandom data and they cannot provide a stronger random source,
|
|
|
|
* there is little we can do other than hope they're smart enough to
|
|
|
|
* call _adddata() with something better than we can come up with.
|
|
|
|
*/
|
|
|
|
#define RND_INITIALIZE 128
|
|
|
|
|
2000-06-03 02:15:08 +00:00
|
|
|
typedef struct {
|
2000-06-09 02:00:22 +00:00
|
|
|
isc_uint32_t magic;
|
2000-06-03 05:38:29 +00:00
|
|
|
isc_uint32_t cursor; /* current add point in the pool */
|
|
|
|
isc_uint32_t entropy; /* current entropy estimate in bits */
|
2000-06-08 23:42:17 +00:00
|
|
|
isc_uint32_t pseudo; /* bits extracted in pseudorandom */
|
2000-06-03 05:38:29 +00:00
|
|
|
isc_uint32_t rotate; /* how many bits to rotate by */
|
|
|
|
isc_uint32_t pool[RND_POOLWORDS]; /* random pool data */
|
2000-06-08 06:35:49 +00:00
|
|
|
} isc_entropypool_t;
|
|
|
|
|
|
|
|
struct isc_entropy {
|
2000-06-09 02:00:22 +00:00
|
|
|
isc_uint32_t magic;
|
2000-06-12 18:19:43 +00:00
|
|
|
isc_mem_t *mctx;
|
2000-06-08 06:35:49 +00:00
|
|
|
isc_mutex_t lock;
|
2000-06-09 19:18:56 +00:00
|
|
|
unsigned int refcnt;
|
2000-06-12 18:19:43 +00:00
|
|
|
isc_uint32_t initialized;
|
2000-06-21 22:12:23 +00:00
|
|
|
isc_uint32_t initcount;
|
2000-06-08 06:35:49 +00:00
|
|
|
isc_entropypool_t pool;
|
2000-06-15 18:15:26 +00:00
|
|
|
unsigned int nsources;
|
|
|
|
isc_entropysource_t *nextsource;
|
2000-06-08 06:35:49 +00:00
|
|
|
ISC_LIST(isc_entropysource_t) sources;
|
|
|
|
};
|
2000-06-03 02:15:08 +00:00
|
|
|
|
|
|
|
typedef struct {
|
2000-06-03 05:38:29 +00:00
|
|
|
isc_uint32_t last_time; /* last time recorded */
|
|
|
|
isc_uint32_t last_delta; /* last delta value */
|
|
|
|
isc_uint32_t last_delta2; /* last delta2 value */
|
2000-06-07 22:15:55 +00:00
|
|
|
isc_uint32_t nsamples; /* number of samples filled in */
|
|
|
|
isc_uint32_t *samples; /* the samples */
|
|
|
|
isc_uint32_t *extra; /* extra samples added in */
|
2000-06-17 01:30:40 +00:00
|
|
|
} sample_queue_t;
|
2000-06-07 22:15:55 +00:00
|
|
|
|
2000-06-16 01:38:13 +00:00
|
|
|
typedef struct {
|
2000-06-17 01:30:40 +00:00
|
|
|
sample_queue_t samplequeue;
|
|
|
|
} isc_entropysamplesource_t;
|
|
|
|
|
|
|
|
typedef struct {
|
2000-06-20 19:01:13 +00:00
|
|
|
isc_boolean_t start_called;
|
2000-06-17 01:30:40 +00:00
|
|
|
isc_entropystart_t startfunc;
|
|
|
|
isc_entropyget_t getfunc;
|
|
|
|
isc_entropystop_t stopfunc;
|
|
|
|
void *arg;
|
|
|
|
sample_queue_t samplequeue;
|
|
|
|
} isc_cbsource_t;
|
2000-06-16 01:38:13 +00:00
|
|
|
|
2000-06-07 22:15:55 +00:00
|
|
|
typedef struct {
|
|
|
|
int fd; /* fd for the file, or -1 if closed */
|
2000-06-08 06:35:49 +00:00
|
|
|
} isc_entropyfilesource_t;
|
2000-06-07 22:15:55 +00:00
|
|
|
|
2000-06-08 06:35:49 +00:00
|
|
|
struct isc_entropysource {
|
2000-06-09 02:00:22 +00:00
|
|
|
isc_uint32_t magic;
|
2000-06-07 22:15:55 +00:00
|
|
|
unsigned int type;
|
2000-06-08 22:18:53 +00:00
|
|
|
isc_entropy_t *ent;
|
2000-06-03 05:38:29 +00:00
|
|
|
isc_uint32_t total; /* entropy from this source */
|
2000-06-08 22:18:53 +00:00
|
|
|
ISC_LINK(isc_entropysource_t) link;
|
2000-06-07 22:15:55 +00:00
|
|
|
char name[32];
|
2000-11-23 01:04:00 +00:00
|
|
|
isc_boolean_t bad;
|
2000-06-07 22:15:55 +00:00
|
|
|
union {
|
2000-06-08 22:18:53 +00:00
|
|
|
isc_entropysamplesource_t sample;
|
|
|
|
isc_entropyfilesource_t file;
|
2000-11-23 01:04:00 +00:00
|
|
|
isc_cbsource_t callback;
|
2000-06-07 22:15:55 +00:00
|
|
|
} sources;
|
2000-06-08 06:35:49 +00:00
|
|
|
};
|
2000-06-03 02:15:08 +00:00
|
|
|
|
2000-06-08 22:18:53 +00:00
|
|
|
#define ENTROPY_SOURCETYPE_SAMPLE 1 /* Type is a sample source */
|
|
|
|
#define ENTROPY_SOURCETYPE_FILE 2 /* Type is a file source */
|
2000-06-16 01:38:13 +00:00
|
|
|
#define ENTROPY_SOURCETYPE_CALLBACK 3 /* Type is a callback source */
|
2000-06-03 02:15:08 +00:00
|
|
|
|
2000-06-08 06:35:49 +00:00
|
|
|
/*
|
|
|
|
* The random pool "taps"
|
|
|
|
*/
|
|
|
|
#define TAP1 99
|
|
|
|
#define TAP2 59
|
|
|
|
#define TAP3 31
|
|
|
|
#define TAP4 9
|
|
|
|
#define TAP5 7
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
entropypool_add_word(isc_entropypool_t *, isc_uint32_t);
|
|
|
|
|
2000-06-08 22:18:53 +00:00
|
|
|
static void
|
|
|
|
fillpool(isc_entropy_t *, unsigned int, isc_boolean_t);
|
|
|
|
|
2000-06-09 21:36:51 +00:00
|
|
|
static int
|
|
|
|
wait_for_sources(isc_entropy_t *);
|
|
|
|
|
2000-06-20 19:01:13 +00:00
|
|
|
static unsigned int
|
|
|
|
crunchsamples(isc_entropy_t *, sample_queue_t *sq);
|
|
|
|
|
2000-06-21 22:12:23 +00:00
|
|
|
static inline void
|
|
|
|
reseed(isc_entropy_t *ent);
|
|
|
|
|
2000-06-17 01:30:40 +00:00
|
|
|
static void
|
|
|
|
samplequeue_release(isc_entropy_t *ent, sample_queue_t *sq) {
|
|
|
|
REQUIRE(sq->samples != NULL);
|
|
|
|
REQUIRE(sq->extra != NULL);
|
|
|
|
|
|
|
|
isc_mem_put(ent->mctx, sq->samples, RND_EVENTQSIZE * 4);
|
|
|
|
isc_mem_put(ent->mctx, sq->extra, RND_EVENTQSIZE * 4);
|
|
|
|
sq->samples = NULL;
|
|
|
|
sq->extra = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static isc_result_t
|
|
|
|
samplesource_allocate(isc_entropy_t *ent, sample_queue_t *sq) {
|
|
|
|
sq->samples = isc_mem_get(ent->mctx, RND_EVENTQSIZE * 4);
|
|
|
|
if (sq->samples == NULL)
|
|
|
|
return (ISC_R_NOMEMORY);
|
|
|
|
|
|
|
|
sq->extra = isc_mem_get(ent->mctx, RND_EVENTQSIZE * 4);
|
|
|
|
if (sq->extra == NULL) {
|
|
|
|
isc_mem_put(ent->mctx, sq->samples, RND_EVENTQSIZE * 4);
|
|
|
|
sq->samples = NULL;
|
|
|
|
return (ISC_R_NOMEMORY);
|
|
|
|
}
|
|
|
|
|
2000-06-21 00:04:57 +00:00
|
|
|
sq->nsamples = 0;
|
|
|
|
|
2000-06-17 01:30:40 +00:00
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
}
|
|
|
|
|
2000-06-09 18:22:11 +00:00
|
|
|
/*
|
|
|
|
* Add in entropy, even when the value we're adding in could be
|
|
|
|
* very large.
|
|
|
|
*/
|
|
|
|
static inline void
|
|
|
|
add_entropy(isc_entropy_t *ent, isc_uint32_t entropy) {
|
|
|
|
/* clamp input. Yes, this must be done. */
|
|
|
|
entropy = ISC_MIN(entropy, RND_POOLBITS);
|
|
|
|
/* Add in the entropy we already have. */
|
|
|
|
entropy += ent->pool.entropy;
|
|
|
|
/* Clamp. */
|
|
|
|
ent->pool.entropy = ISC_MIN(entropy, RND_POOLBITS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decrement the amount of entropy the pool has.
|
|
|
|
*/
|
|
|
|
static inline void
|
|
|
|
subtract_entropy(isc_entropy_t *ent, isc_uint32_t entropy) {
|
|
|
|
entropy = ISC_MIN(entropy, ent->pool.entropy);
|
|
|
|
ent->pool.entropy -= entropy;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add in entropy, even when the value we're adding in could be
|
|
|
|
* very large.
|
|
|
|
*/
|
|
|
|
static inline void
|
2000-06-15 18:15:26 +00:00
|
|
|
add_pseudo(isc_entropy_t *ent, isc_uint32_t pseudo) {
|
2000-06-09 18:22:11 +00:00
|
|
|
/* clamp input. Yes, this must be done. */
|
2000-06-15 18:15:26 +00:00
|
|
|
pseudo = ISC_MIN(pseudo, RND_POOLBITS * 8);
|
|
|
|
/* Add in the pseudo we already have. */
|
|
|
|
pseudo += ent->pool.pseudo;
|
2000-06-09 18:22:11 +00:00
|
|
|
/* Clamp. */
|
2000-06-15 18:15:26 +00:00
|
|
|
ent->pool.pseudo = ISC_MIN(pseudo, RND_POOLBITS * 8);
|
2000-06-09 18:22:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2000-06-15 18:15:26 +00:00
|
|
|
* Decrement the amount of pseudo the pool has.
|
2000-06-09 18:22:11 +00:00
|
|
|
*/
|
|
|
|
static inline void
|
2000-06-15 18:15:26 +00:00
|
|
|
subtract_pseudo(isc_entropy_t *ent, isc_uint32_t pseudo) {
|
|
|
|
pseudo = ISC_MIN(pseudo, ent->pool.pseudo);
|
|
|
|
ent->pool.pseudo -= pseudo;
|
2000-06-09 18:22:11 +00:00
|
|
|
}
|
2000-06-08 22:18:53 +00:00
|
|
|
|
2000-06-08 06:35:49 +00:00
|
|
|
/*
|
|
|
|
* Add one word to the pool, rotating the input as needed.
|
|
|
|
*/
|
|
|
|
static inline void
|
2000-06-09 18:22:11 +00:00
|
|
|
entropypool_add_word(isc_entropypool_t *rp, isc_uint32_t val) {
|
2000-06-08 06:35:49 +00:00
|
|
|
/*
|
|
|
|
* Steal some values out of the pool, and xor them into the
|
|
|
|
* word we were given.
|
|
|
|
*
|
|
|
|
* Mix the new value into the pool using xor. This will
|
|
|
|
* prevent the actual values from being known to the caller
|
|
|
|
* since the previous values are assumed to be unknown as well.
|
|
|
|
*/
|
|
|
|
val ^= rp->pool[(rp->cursor + TAP1) & (RND_POOLWORDS - 1)];
|
|
|
|
val ^= rp->pool[(rp->cursor + TAP2) & (RND_POOLWORDS - 1)];
|
|
|
|
val ^= rp->pool[(rp->cursor + TAP3) & (RND_POOLWORDS - 1)];
|
|
|
|
val ^= rp->pool[(rp->cursor + TAP4) & (RND_POOLWORDS - 1)];
|
|
|
|
val ^= rp->pool[(rp->cursor + TAP5) & (RND_POOLWORDS - 1)];
|
|
|
|
rp->pool[rp->cursor++] ^=
|
|
|
|
((val << rp->rotate) | (val >> (32 - rp->rotate)));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we have looped around the pool, increment the rotate
|
|
|
|
* variable so the next value will get xored in rotated to
|
|
|
|
* a different position.
|
|
|
|
* Increment by a value that is relativly prime to the word size
|
|
|
|
* to try to spread the bits throughout the pool quickly when the
|
|
|
|
* pool is empty.
|
|
|
|
*/
|
|
|
|
if (rp->cursor == RND_POOLWORDS) {
|
|
|
|
rp->cursor = 0;
|
|
|
|
rp->rotate = (rp->rotate + 7) & 31;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2000-06-08 22:18:53 +00:00
|
|
|
* Add a buffer's worth of data to the pool.
|
|
|
|
*
|
|
|
|
* Requires that the lock is held on the entropy pool.
|
2000-06-08 06:35:49 +00:00
|
|
|
*/
|
2000-06-08 22:18:53 +00:00
|
|
|
static void
|
2000-06-09 18:22:11 +00:00
|
|
|
entropypool_adddata(isc_entropy_t *ent, void *p, unsigned int len,
|
2000-06-08 06:35:49 +00:00
|
|
|
isc_uint32_t entropy)
|
|
|
|
{
|
|
|
|
isc_uint32_t val;
|
2000-06-26 18:41:06 +00:00
|
|
|
unsigned long addr;
|
2000-06-08 06:35:49 +00:00
|
|
|
isc_uint8_t *buf;
|
|
|
|
|
2000-06-26 18:41:06 +00:00
|
|
|
addr = (unsigned long)p;
|
2000-06-08 06:35:49 +00:00
|
|
|
buf = p;
|
|
|
|
|
|
|
|
if ((addr & 0x03) != 0) {
|
|
|
|
val = 0;
|
|
|
|
switch (len) {
|
|
|
|
case 3:
|
|
|
|
val = *buf++;
|
|
|
|
len--;
|
|
|
|
case 2:
|
|
|
|
val = val << 8 | *buf++;
|
|
|
|
len--;
|
|
|
|
case 1:
|
|
|
|
val = val << 8 | *buf++;
|
|
|
|
len--;
|
|
|
|
}
|
|
|
|
|
2000-06-09 18:22:11 +00:00
|
|
|
entropypool_add_word(&ent->pool, val);
|
2000-06-08 06:35:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (; len > 3 ; len -= 4) {
|
|
|
|
val = *((isc_uint32_t *)buf);
|
|
|
|
|
2000-06-09 18:22:11 +00:00
|
|
|
entropypool_add_word(&ent->pool, val);
|
2000-06-08 06:35:49 +00:00
|
|
|
buf += 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len != 0) {
|
|
|
|
val = 0;
|
|
|
|
switch (len) {
|
|
|
|
case 3:
|
|
|
|
val = *buf++;
|
|
|
|
case 2:
|
|
|
|
val = val << 8 | *buf++;
|
|
|
|
case 1:
|
|
|
|
val = val << 8 | *buf++;
|
|
|
|
}
|
|
|
|
|
2000-06-09 18:22:11 +00:00
|
|
|
entropypool_add_word(&ent->pool, val);
|
2000-06-08 06:35:49 +00:00
|
|
|
}
|
|
|
|
|
2000-06-09 18:22:11 +00:00
|
|
|
add_entropy(ent, entropy);
|
2000-06-17 00:43:11 +00:00
|
|
|
subtract_pseudo(ent, entropy);
|
2000-06-08 06:35:49 +00:00
|
|
|
}
|
|
|
|
|
2000-06-21 22:12:23 +00:00
|
|
|
static inline void
|
|
|
|
reseed(isc_entropy_t *ent) {
|
|
|
|
isc_result_t result;
|
|
|
|
isc_time_t t;
|
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
if (ent->initcount == 0) {
|
|
|
|
pid = getpid();
|
|
|
|
entropypool_adddata(ent, &pid, sizeof pid, 0);
|
|
|
|
pid = getppid();
|
|
|
|
entropypool_adddata(ent, &pid, sizeof pid, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* After we've reseeded 100 times, only add new timing info every
|
|
|
|
* 50 requests. This will keep us from using lots and lots of
|
|
|
|
* CPU just to return bad pseudorandom data anyway.
|
|
|
|
*/
|
|
|
|
if (ent->initcount > 100)
|
|
|
|
if ((ent->initcount % 50) != 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
result = isc_time_now(&t);
|
|
|
|
if (result == ISC_R_SUCCESS) {
|
|
|
|
entropypool_adddata(ent, &t, sizeof t, 0);
|
|
|
|
ent->initcount++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-06-20 19:01:13 +00:00
|
|
|
static unsigned int
|
2000-06-08 23:42:17 +00:00
|
|
|
get_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) {
|
2000-06-09 18:22:11 +00:00
|
|
|
isc_entropy_t *ent = source->ent;
|
2000-06-08 23:42:17 +00:00
|
|
|
unsigned char buf[128];
|
|
|
|
int fd = source->sources.file.fd;
|
|
|
|
ssize_t n, ndesired;
|
2000-06-20 19:01:13 +00:00
|
|
|
unsigned int added;
|
2000-06-08 23:42:17 +00:00
|
|
|
|
2000-11-23 01:04:00 +00:00
|
|
|
if (source->bad)
|
2000-06-08 23:42:17 +00:00
|
|
|
return (0);
|
|
|
|
|
2000-06-09 02:00:22 +00:00
|
|
|
desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
|
2000-06-08 23:42:17 +00:00
|
|
|
|
2000-06-09 17:48:08 +00:00
|
|
|
added = 0;
|
2000-06-08 23:42:17 +00:00
|
|
|
while (desired > 0) {
|
|
|
|
ndesired = ISC_MIN(desired, sizeof(buf));
|
|
|
|
n = read(fd, buf, ndesired);
|
|
|
|
if (n < 0) {
|
2000-06-09 22:24:22 +00:00
|
|
|
if (errno == EAGAIN)
|
2000-06-08 23:42:17 +00:00
|
|
|
goto out;
|
|
|
|
close(fd);
|
2000-11-23 01:04:00 +00:00
|
|
|
source->bad = ISC_TRUE;
|
2000-06-09 21:36:51 +00:00
|
|
|
goto out;
|
2000-06-08 23:42:17 +00:00
|
|
|
}
|
2000-06-09 21:36:51 +00:00
|
|
|
if (n == 0) {
|
|
|
|
close(fd);
|
2000-11-23 01:04:00 +00:00
|
|
|
source->bad = ISC_TRUE;
|
2000-06-08 23:42:17 +00:00
|
|
|
goto out;
|
2000-06-09 21:36:51 +00:00
|
|
|
}
|
2000-06-08 23:42:17 +00:00
|
|
|
|
2000-06-09 18:22:11 +00:00
|
|
|
entropypool_adddata(ent, buf, n, n * 8);
|
2000-06-08 23:42:17 +00:00
|
|
|
added += n * 8;
|
2000-06-09 02:00:22 +00:00
|
|
|
desired -= n;
|
2000-06-08 23:42:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
return (added);
|
|
|
|
}
|
|
|
|
|
2000-06-20 19:01:13 +00:00
|
|
|
static unsigned int
|
2000-06-21 00:04:57 +00:00
|
|
|
get_from_callback(isc_entropysource_t *source, unsigned int desired,
|
|
|
|
isc_boolean_t blocking)
|
|
|
|
{
|
2000-06-20 19:01:13 +00:00
|
|
|
isc_entropy_t *ent = source->ent;
|
|
|
|
isc_cbsource_t *cbs = &source->sources.callback;
|
|
|
|
unsigned int added;
|
2000-06-21 00:04:57 +00:00
|
|
|
unsigned int got;
|
2000-06-20 19:01:13 +00:00
|
|
|
isc_result_t result;
|
|
|
|
|
2000-06-21 00:04:57 +00:00
|
|
|
if (desired == 0)
|
|
|
|
return (0);
|
2000-06-20 19:01:13 +00:00
|
|
|
|
2000-11-23 01:04:00 +00:00
|
|
|
if (source->bad)
|
|
|
|
return (0);
|
|
|
|
|
2000-06-21 00:04:57 +00:00
|
|
|
if (!cbs->start_called && cbs->startfunc != NULL) {
|
|
|
|
result = cbs->startfunc(source, cbs->arg, blocking);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (0);
|
|
|
|
cbs->start_called = ISC_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
added = 0;
|
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
while (desired > 0 && result == ISC_R_SUCCESS) {
|
|
|
|
result = cbs->getfunc(source, cbs->arg, blocking);
|
|
|
|
if (result == ISC_R_QUEUEFULL) {
|
|
|
|
got = crunchsamples(ent, &cbs->samplequeue);
|
|
|
|
added += got;
|
|
|
|
desired -= ISC_MIN(got, desired);
|
|
|
|
result = ISC_R_SUCCESS;
|
2000-11-23 01:04:00 +00:00
|
|
|
} else if (result != ISC_R_SUCCESS)
|
|
|
|
source->bad = ISC_TRUE;
|
|
|
|
|
2000-06-20 19:01:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return (added);
|
|
|
|
}
|
|
|
|
|
2000-06-08 22:18:53 +00:00
|
|
|
/*
|
|
|
|
* Poll each source, trying to get data from it to stuff into the entropy
|
|
|
|
* pool.
|
|
|
|
*/
|
|
|
|
static void
|
2000-06-20 19:01:13 +00:00
|
|
|
fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) {
|
|
|
|
unsigned int added;
|
|
|
|
unsigned int remaining;
|
|
|
|
unsigned int needed;
|
2000-06-15 18:15:26 +00:00
|
|
|
unsigned int nsource;
|
2000-06-08 23:42:17 +00:00
|
|
|
isc_entropysource_t *source;
|
|
|
|
|
2000-06-09 02:00:22 +00:00
|
|
|
REQUIRE(VALID_ENTROPY(ent));
|
2000-06-08 23:42:17 +00:00
|
|
|
|
2000-06-20 19:01:13 +00:00
|
|
|
needed = desired;
|
|
|
|
|
2000-06-09 02:00:22 +00:00
|
|
|
/*
|
2000-06-09 17:48:08 +00:00
|
|
|
* This logic is a little strange, so an explanation is in order.
|
|
|
|
*
|
|
|
|
* If needed is 0, it means we are being asked to "fill to whatever
|
|
|
|
* we think is best." This means that if we have at least a
|
|
|
|
* partially full pool (say, > 1/4th of the pool) we probably don't
|
|
|
|
* need to add anything.
|
|
|
|
*
|
|
|
|
* Also, we will check to see if the "pseudo" count is too high.
|
|
|
|
* If it is, try to mix in better data. Too high is currently
|
|
|
|
* defined as 1/4th of the pool.
|
|
|
|
*
|
|
|
|
* Next, if we are asked to add a specific bit of entropy, make
|
|
|
|
* certain that we will do so. Clamp how much we try to add to
|
|
|
|
* (DIGEST_SIZE * 8 < needed < POOLBITS - entropy).
|
2000-06-09 21:36:51 +00:00
|
|
|
*
|
|
|
|
* Note that if we are in a blocking mode, we will only try to
|
|
|
|
* get as much data as we need, not as much as we might want
|
|
|
|
* to build up.
|
2000-06-09 02:00:22 +00:00
|
|
|
*/
|
2000-06-09 17:48:08 +00:00
|
|
|
if (needed == 0) {
|
|
|
|
REQUIRE(!blocking);
|
|
|
|
|
2000-06-09 02:00:22 +00:00
|
|
|
if ((ent->pool.entropy >= RND_POOLBITS / 4)
|
|
|
|
&& (ent->pool.pseudo <= RND_POOLBITS / 4))
|
|
|
|
return;
|
2000-06-21 00:04:57 +00:00
|
|
|
|
|
|
|
needed = THRESHOLD_BITS * 4;
|
2000-06-09 17:48:08 +00:00
|
|
|
} else {
|
2000-06-12 22:28:05 +00:00
|
|
|
needed = ISC_MAX(needed, THRESHOLD_BITS);
|
2000-06-09 17:48:08 +00:00
|
|
|
needed = ISC_MIN(needed, RND_POOLBITS);
|
2000-06-09 02:00:22 +00:00
|
|
|
}
|
2000-06-08 23:42:17 +00:00
|
|
|
|
2000-06-09 21:36:51 +00:00
|
|
|
/*
|
|
|
|
* In any case, clamp how much we need to how much we can add.
|
|
|
|
*/
|
|
|
|
needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
|
|
|
|
|
2000-06-12 18:19:43 +00:00
|
|
|
/*
|
2000-06-12 22:28:05 +00:00
|
|
|
* But wait! If we're not yet initialized, we need at least
|
|
|
|
* THRESHOLD_BITS
|
2000-06-15 18:15:26 +00:00
|
|
|
* of randomness.
|
2000-06-12 18:19:43 +00:00
|
|
|
*/
|
2000-06-12 22:28:05 +00:00
|
|
|
if (ent->initialized < THRESHOLD_BITS)
|
|
|
|
needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
|
2000-06-12 18:19:43 +00:00
|
|
|
|
2000-06-08 23:42:17 +00:00
|
|
|
/*
|
|
|
|
* Poll each file source to see if we can read anything useful from
|
|
|
|
* it. XXXMLG When where are multiple sources, we should keep a
|
|
|
|
* record of which one we last used so we can start from it (or the
|
|
|
|
* next one) to avoid letting some sources build up entropy while
|
|
|
|
* others are always drained.
|
|
|
|
*/
|
|
|
|
|
2000-06-10 01:09:22 +00:00
|
|
|
added = 0;
|
2000-06-10 01:47:06 +00:00
|
|
|
remaining = needed;
|
2000-06-15 18:15:26 +00:00
|
|
|
if (ent->nextsource == NULL) {
|
|
|
|
ent->nextsource = ISC_LIST_HEAD(ent->sources);
|
|
|
|
if (ent->nextsource == NULL)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
source = ent->nextsource;
|
2000-06-20 19:01:13 +00:00
|
|
|
again_file:
|
2000-06-15 18:15:26 +00:00
|
|
|
for (nsource = 0 ; nsource < ent->nsources ; nsource++) {
|
2000-06-20 19:01:13 +00:00
|
|
|
unsigned int got;
|
2000-06-08 23:42:17 +00:00
|
|
|
|
2000-06-15 18:15:26 +00:00
|
|
|
if (remaining == 0)
|
|
|
|
break;
|
|
|
|
|
2000-06-10 01:47:06 +00:00
|
|
|
got = 0;
|
|
|
|
|
|
|
|
if (source->type == ENTROPY_SOURCETYPE_FILE)
|
|
|
|
got = get_from_filesource(source, remaining);
|
2000-06-08 23:42:17 +00:00
|
|
|
|
2000-06-10 01:47:06 +00:00
|
|
|
added += got;
|
|
|
|
|
2000-06-21 00:04:57 +00:00
|
|
|
remaining -= ISC_MIN(remaining, got);
|
2000-06-10 01:47:06 +00:00
|
|
|
|
2000-06-15 18:15:26 +00:00
|
|
|
source = ISC_LIST_NEXT(source, link);
|
|
|
|
if (source == NULL)
|
|
|
|
source = ISC_LIST_HEAD(ent->sources);
|
|
|
|
|
2000-06-20 19:01:13 +00:00
|
|
|
#if 0
|
2000-06-10 01:47:06 +00:00
|
|
|
if (added >= needed)
|
|
|
|
break;
|
2000-06-20 19:01:13 +00:00
|
|
|
#endif
|
2000-06-08 23:42:17 +00:00
|
|
|
}
|
2000-06-15 18:15:26 +00:00
|
|
|
ent->nextsource = source;
|
2000-06-08 23:42:17 +00:00
|
|
|
|
2000-06-10 01:47:06 +00:00
|
|
|
if (blocking && remaining != 0) {
|
2000-06-10 01:09:22 +00:00
|
|
|
int fds;
|
|
|
|
fds = wait_for_sources(ent);
|
|
|
|
if (fds > 0)
|
2000-06-20 19:01:13 +00:00
|
|
|
goto again_file;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Here, if there are bits remaining to be had and we can block,
|
|
|
|
* check to see if we have a callback source. If so, call them.
|
|
|
|
*/
|
|
|
|
source = ISC_LIST_HEAD(ent->sources);
|
2000-06-21 00:04:57 +00:00
|
|
|
while ((remaining != 0) && (source != NULL)) {
|
2000-06-20 19:01:13 +00:00
|
|
|
unsigned int got;
|
|
|
|
|
|
|
|
got = 0;
|
|
|
|
|
|
|
|
if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
|
2000-06-21 00:04:57 +00:00
|
|
|
got = get_from_callback(source, remaining, blocking);
|
2000-06-20 19:01:13 +00:00
|
|
|
|
|
|
|
added += got;
|
2000-06-21 00:04:57 +00:00
|
|
|
remaining -= ISC_MIN(remaining, got);
|
2000-06-20 19:01:13 +00:00
|
|
|
|
|
|
|
if (added >= needed)
|
|
|
|
break;
|
|
|
|
|
|
|
|
source = ISC_LIST_NEXT(source, link);
|
2000-06-09 21:36:51 +00:00
|
|
|
}
|
|
|
|
|
2000-06-12 18:19:43 +00:00
|
|
|
/*
|
|
|
|
* Mark as initialized if we've added enough data.
|
|
|
|
*/
|
2000-06-12 22:28:05 +00:00
|
|
|
if (ent->initialized < THRESHOLD_BITS)
|
2000-06-12 18:19:43 +00:00
|
|
|
ent->initialized += added;
|
2000-06-08 22:18:53 +00:00
|
|
|
}
|
|
|
|
|
2000-06-09 21:36:51 +00:00
|
|
|
static int
|
|
|
|
wait_for_sources(isc_entropy_t *ent) {
|
|
|
|
isc_entropysource_t *source;
|
|
|
|
int maxfd, fd;
|
|
|
|
int cc;
|
|
|
|
fd_set reads;
|
|
|
|
|
|
|
|
maxfd = -1;
|
|
|
|
FD_ZERO(&reads);
|
|
|
|
|
|
|
|
source = ISC_LIST_HEAD(ent->sources);
|
|
|
|
while (source != NULL) {
|
|
|
|
if (source->type == ENTROPY_SOURCETYPE_FILE) {
|
|
|
|
fd = source->sources.file.fd;
|
|
|
|
if (fd >= 0) {
|
|
|
|
maxfd = ISC_MAX(maxfd, fd);
|
|
|
|
FD_SET(fd, &reads);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
source = ISC_LIST_NEXT(source, link);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (maxfd < 0)
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
cc = select(maxfd + 1, &reads, NULL, NULL, NULL);
|
|
|
|
if (cc < 0)
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
return (cc);
|
|
|
|
}
|
|
|
|
|
2000-06-08 06:35:49 +00:00
|
|
|
/*
|
|
|
|
* Extract some number of bytes from the random pool, decreasing the
|
|
|
|
* estimate of randomness as each byte is extracted.
|
|
|
|
*
|
|
|
|
* Do this by stiring the pool and returning a part of hash as randomness.
|
|
|
|
* Note that no secrets are given away here since parts of the hash are
|
|
|
|
* xored together before returned.
|
|
|
|
*
|
|
|
|
* Honor the request from the caller to only return good data, any data,
|
2000-06-08 22:18:53 +00:00
|
|
|
* etc.
|
2000-06-08 06:35:49 +00:00
|
|
|
*/
|
2000-06-08 22:18:53 +00:00
|
|
|
isc_result_t
|
|
|
|
isc_entropy_getdata(isc_entropy_t *ent, void *data, unsigned int length,
|
|
|
|
unsigned int *returned, unsigned int flags)
|
2000-06-08 06:35:49 +00:00
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
isc_sha1_t hash;
|
|
|
|
unsigned char digest[ISC_SHA1_DIGESTLENGTH];
|
2000-06-08 22:18:53 +00:00
|
|
|
isc_uint32_t remain, deltae, count, total;
|
2000-06-08 06:35:49 +00:00
|
|
|
isc_uint8_t *buf;
|
2000-06-08 22:18:53 +00:00
|
|
|
isc_boolean_t goodonly, partial, blocking;
|
2000-06-08 06:35:49 +00:00
|
|
|
|
2000-06-08 22:18:53 +00:00
|
|
|
REQUIRE(VALID_ENTROPY(ent));
|
|
|
|
REQUIRE(data != NULL);
|
|
|
|
REQUIRE(length > 0);
|
|
|
|
|
|
|
|
goodonly = ISC_TF((flags & ISC_ENTROPY_GOODONLY) != 0);
|
|
|
|
partial = ISC_TF((flags & ISC_ENTROPY_PARTIAL) != 0);
|
|
|
|
blocking = ISC_TF((flags & ISC_ENTROPY_BLOCKING) != 0);
|
|
|
|
|
2000-06-23 02:31:10 +00:00
|
|
|
REQUIRE(!partial || returned != NULL);
|
|
|
|
|
2000-06-08 22:18:53 +00:00
|
|
|
LOCK(&ent->lock);
|
2000-06-08 06:35:49 +00:00
|
|
|
|
2000-06-08 22:18:53 +00:00
|
|
|
remain = length;
|
|
|
|
buf = data;
|
|
|
|
total = 0;
|
|
|
|
while (remain != 0) {
|
|
|
|
count = ISC_MIN(remain, RND_ENTROPY_THRESHOLD);
|
|
|
|
|
2000-06-08 23:42:17 +00:00
|
|
|
/*
|
|
|
|
* If we are extracting good data only, make certain we
|
|
|
|
* have enough data in our pool for this pass. If we don't,
|
|
|
|
* get some, and fail if we can't, and partial returns
|
|
|
|
* are not ok.
|
|
|
|
*/
|
2000-06-08 22:18:53 +00:00
|
|
|
if (goodonly) {
|
2000-06-10 00:50:03 +00:00
|
|
|
unsigned int fillcount;
|
|
|
|
|
|
|
|
fillcount = ISC_MAX(remain * 8, count * 8);
|
|
|
|
|
2000-06-12 22:28:05 +00:00
|
|
|
/*
|
|
|
|
* If, however, we have at least THRESHOLD_BITS
|
|
|
|
* of entropy in the pool, don't block here. It is
|
|
|
|
* better to drain the pool once in a while and
|
|
|
|
* then refill it than it is to constantly keep the
|
|
|
|
* pool full.
|
|
|
|
*/
|
|
|
|
if (ent->pool.entropy >= THRESHOLD_BITS)
|
|
|
|
fillpool(ent, fillcount, ISC_FALSE);
|
|
|
|
else
|
|
|
|
fillpool(ent, fillcount, blocking);
|
2000-06-10 00:50:03 +00:00
|
|
|
|
2000-06-23 02:31:10 +00:00
|
|
|
/*
|
|
|
|
* Verify that we got enough entropy to do one
|
|
|
|
* extraction. If we didn't, bail.
|
|
|
|
*/
|
2000-06-21 00:04:57 +00:00
|
|
|
if (ent->pool.entropy < THRESHOLD_BITS) {
|
2000-06-23 02:31:10 +00:00
|
|
|
if (!partial)
|
2000-06-10 00:50:03 +00:00
|
|
|
goto zeroize;
|
2000-06-23 02:31:10 +00:00
|
|
|
else
|
2000-06-21 00:04:57 +00:00
|
|
|
goto partial_output;
|
|
|
|
}
|
2000-06-08 23:42:17 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* If we've extracted half our pool size in bits
|
|
|
|
* since the last refresh, try to refresh here.
|
|
|
|
*/
|
2000-06-12 22:28:05 +00:00
|
|
|
if (ent->initialized < THRESHOLD_BITS)
|
|
|
|
fillpool(ent, THRESHOLD_BITS, blocking);
|
2000-06-12 18:28:25 +00:00
|
|
|
else
|
|
|
|
fillpool(ent, 0, ISC_FALSE);
|
2000-06-12 18:19:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we've not initialized with enough good random
|
2000-06-21 22:12:23 +00:00
|
|
|
* data, seed with our crappy code.
|
2000-06-12 18:19:43 +00:00
|
|
|
*/
|
2000-06-12 22:28:05 +00:00
|
|
|
if (ent->initialized < THRESHOLD_BITS)
|
2000-06-21 22:12:23 +00:00
|
|
|
reseed(ent);
|
2000-06-08 22:18:53 +00:00
|
|
|
}
|
|
|
|
|
2000-06-08 06:35:49 +00:00
|
|
|
isc_sha1_init(&hash);
|
2000-06-08 22:18:53 +00:00
|
|
|
isc_sha1_update(&hash, (void *)(ent->pool.pool),
|
2000-06-08 06:35:49 +00:00
|
|
|
RND_POOLWORDS * 4);
|
|
|
|
isc_sha1_final(&hash, digest);
|
2000-08-01 01:33:37 +00:00
|
|
|
|
2000-06-08 06:35:49 +00:00
|
|
|
/*
|
2000-06-08 22:18:53 +00:00
|
|
|
* Stir the extracted data (all of it) back into the pool.
|
2000-06-08 06:35:49 +00:00
|
|
|
*/
|
2000-06-09 18:22:11 +00:00
|
|
|
entropypool_adddata(ent, digest, ISC_SHA1_DIGESTLENGTH, 0);
|
2000-06-08 06:35:49 +00:00
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
2000-06-08 22:18:53 +00:00
|
|
|
buf[i] = digest[i] ^ digest[i + RND_ENTROPY_THRESHOLD];
|
2000-06-08 06:35:49 +00:00
|
|
|
|
|
|
|
buf += count;
|
|
|
|
remain -= count;
|
|
|
|
|
2000-06-08 22:18:53 +00:00
|
|
|
deltae = count * 8;
|
2000-06-09 18:22:11 +00:00
|
|
|
deltae = ISC_MIN(deltae, ent->pool.entropy);
|
2000-06-08 22:18:53 +00:00
|
|
|
total += deltae;
|
2000-06-09 18:22:11 +00:00
|
|
|
subtract_entropy(ent, deltae);
|
2000-06-09 21:36:51 +00:00
|
|
|
add_pseudo(ent, count * 8);
|
2000-06-08 22:18:53 +00:00
|
|
|
}
|
|
|
|
|
2000-06-21 00:04:57 +00:00
|
|
|
partial_output:
|
2000-06-08 22:18:53 +00:00
|
|
|
memset(digest, 0, sizeof(digest));
|
2000-06-08 06:35:49 +00:00
|
|
|
|
2000-06-09 02:00:22 +00:00
|
|
|
if (returned != NULL)
|
2000-06-08 23:42:17 +00:00
|
|
|
*returned = (length - remain);
|
2000-06-08 06:35:49 +00:00
|
|
|
|
2000-06-09 02:00:22 +00:00
|
|
|
UNLOCK(&ent->lock);
|
|
|
|
|
2000-06-08 22:18:53 +00:00
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
|
|
|
|
zeroize:
|
|
|
|
/* put the entropy we almost extracted back */
|
2000-06-09 18:22:11 +00:00
|
|
|
add_entropy(ent, total);
|
2000-06-08 22:18:53 +00:00
|
|
|
memset(data, 0, length);
|
2000-06-08 20:42:11 +00:00
|
|
|
memset(digest, 0, sizeof(digest));
|
2000-06-09 02:00:22 +00:00
|
|
|
if (returned != NULL)
|
2000-06-08 22:18:53 +00:00
|
|
|
*returned = 0;
|
2000-06-08 06:35:49 +00:00
|
|
|
|
2000-06-09 02:00:22 +00:00
|
|
|
UNLOCK(&ent->lock);
|
|
|
|
|
2000-06-08 22:18:53 +00:00
|
|
|
return (ISC_R_NOENTROPY);
|
2000-06-08 06:35:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
isc_entropypool_init(isc_entropypool_t *pool) {
|
|
|
|
pool->cursor = RND_POOLWORDS - 1;
|
|
|
|
pool->entropy = 0;
|
2000-06-08 23:42:17 +00:00
|
|
|
pool->pseudo = 0;
|
2000-06-08 06:35:49 +00:00
|
|
|
pool->rotate = 0;
|
|
|
|
memset(pool->pool, 0, RND_POOLWORDS);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
isc_entropypool_invalidate(isc_entropypool_t *pool) {
|
|
|
|
pool->cursor = 0;
|
|
|
|
pool->entropy = 0;
|
2000-06-08 23:42:17 +00:00
|
|
|
pool->pseudo = 0;
|
2000-06-08 06:35:49 +00:00
|
|
|
pool->rotate = 0;
|
|
|
|
memset(pool->pool, 0, RND_POOLWORDS);
|
|
|
|
}
|
|
|
|
|
2000-06-07 22:15:55 +00:00
|
|
|
isc_result_t
|
|
|
|
isc_entropy_create(isc_mem_t *mctx, isc_entropy_t **entp) {
|
2000-06-08 06:35:49 +00:00
|
|
|
isc_result_t ret;
|
|
|
|
isc_entropy_t *ent;
|
|
|
|
|
|
|
|
REQUIRE(mctx != NULL);
|
|
|
|
REQUIRE(entp != NULL && *entp == NULL);
|
|
|
|
|
|
|
|
ent = isc_mem_get(mctx, sizeof(isc_entropy_t));
|
|
|
|
if (ent == NULL)
|
|
|
|
return (ISC_R_NOMEMORY);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We need a lock.
|
|
|
|
*/
|
|
|
|
if (isc_mutex_init(&ent->lock) != ISC_R_SUCCESS) {
|
|
|
|
ret = ISC_R_UNEXPECTED;
|
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* From here down, no failures will/can occur.
|
|
|
|
*/
|
|
|
|
ISC_LIST_INIT(ent->sources);
|
2000-06-15 18:15:26 +00:00
|
|
|
ent->nextsource = NULL;
|
|
|
|
ent->nsources = 0;
|
2000-06-09 19:18:56 +00:00
|
|
|
ent->mctx = NULL;
|
|
|
|
isc_mem_attach(mctx, &ent->mctx);
|
|
|
|
ent->refcnt = 1;
|
2000-06-12 18:19:43 +00:00
|
|
|
ent->initialized = 0;
|
2000-06-21 22:12:23 +00:00
|
|
|
ent->initcount = 0;
|
2000-06-09 17:25:41 +00:00
|
|
|
ent->magic = ENTROPY_MAGIC;
|
2000-06-08 06:35:49 +00:00
|
|
|
|
|
|
|
isc_entropypool_init(&ent->pool);
|
|
|
|
|
|
|
|
*entp = ent;
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
|
|
|
|
errout:
|
|
|
|
isc_mem_put(mctx, ent, sizeof(isc_entropy_t));
|
|
|
|
|
|
|
|
return (ret);
|
2000-06-07 22:15:55 +00:00
|
|
|
}
|
2000-06-03 02:15:08 +00:00
|
|
|
|
2000-06-15 18:15:26 +00:00
|
|
|
/*
|
|
|
|
* Requires "ent" be locked.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
destroysource(isc_entropysource_t **sourcep) {
|
|
|
|
isc_entropysource_t *source;
|
|
|
|
isc_entropy_t *ent;
|
2000-06-17 01:30:40 +00:00
|
|
|
isc_cbsource_t *cbs;
|
2000-06-15 18:15:26 +00:00
|
|
|
int fd;
|
|
|
|
|
|
|
|
source = *sourcep;
|
|
|
|
*sourcep = NULL;
|
|
|
|
ent = source->ent;
|
|
|
|
|
|
|
|
ISC_LIST_UNLINK(ent->sources, source, link);
|
|
|
|
ent->nextsource = NULL;
|
|
|
|
REQUIRE(ent->nsources > 0);
|
|
|
|
ent->nsources--;
|
|
|
|
|
|
|
|
switch (source->type) {
|
|
|
|
case ENTROPY_SOURCETYPE_FILE:
|
|
|
|
fd = source->sources.file.fd;
|
2000-11-23 01:04:00 +00:00
|
|
|
if (!source->bad)
|
2000-06-15 18:15:26 +00:00
|
|
|
close(fd);
|
|
|
|
break;
|
|
|
|
case ENTROPY_SOURCETYPE_SAMPLE:
|
2000-06-17 01:30:40 +00:00
|
|
|
samplequeue_release(ent, &source->sources.sample.samplequeue);
|
|
|
|
break;
|
|
|
|
case ENTROPY_SOURCETYPE_CALLBACK:
|
|
|
|
cbs = &source->sources.callback;
|
2000-06-21 00:04:57 +00:00
|
|
|
if (cbs->start_called && cbs->stopfunc != NULL) {
|
|
|
|
cbs->stopfunc(source, cbs->arg);
|
|
|
|
cbs->start_called = ISC_FALSE;
|
|
|
|
}
|
2000-06-17 01:30:40 +00:00
|
|
|
samplequeue_release(ent, &cbs->samplequeue);
|
2000-06-15 18:15:26 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(source, 0, sizeof(isc_entropysource_t));
|
|
|
|
|
|
|
|
isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t));
|
|
|
|
}
|
|
|
|
|
2000-06-09 19:18:56 +00:00
|
|
|
static inline isc_boolean_t
|
|
|
|
destroy_check(isc_entropy_t *ent) {
|
2000-06-15 18:15:26 +00:00
|
|
|
isc_entropysource_t *source;
|
|
|
|
|
2000-06-09 19:18:56 +00:00
|
|
|
if (ent->refcnt > 0)
|
|
|
|
return (ISC_FALSE);
|
2000-06-15 18:15:26 +00:00
|
|
|
|
|
|
|
source = ISC_LIST_HEAD(ent->sources);
|
|
|
|
while (source != NULL) {
|
|
|
|
switch (source->type) {
|
|
|
|
case ENTROPY_SOURCETYPE_FILE:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return (ISC_FALSE);
|
|
|
|
}
|
|
|
|
source = ISC_LIST_NEXT(source, link);
|
|
|
|
}
|
2000-06-09 19:18:56 +00:00
|
|
|
|
|
|
|
return (ISC_TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
destroy(isc_entropy_t **entp) {
|
2000-06-08 06:35:49 +00:00
|
|
|
isc_entropy_t *ent;
|
2000-06-15 18:15:26 +00:00
|
|
|
isc_entropysource_t *source;
|
2000-06-09 02:00:22 +00:00
|
|
|
isc_mem_t *mctx;
|
2000-06-08 06:35:49 +00:00
|
|
|
|
|
|
|
REQUIRE(entp != NULL && *entp != NULL);
|
|
|
|
ent = *entp;
|
|
|
|
*entp = NULL;
|
|
|
|
|
2000-06-15 18:15:26 +00:00
|
|
|
LOCK(&ent->lock);
|
|
|
|
|
2000-06-09 19:18:56 +00:00
|
|
|
REQUIRE(ent->refcnt == 0);
|
2000-06-15 18:15:26 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Here, detach non-sample sources.
|
|
|
|
*/
|
|
|
|
source = ISC_LIST_HEAD(ent->sources);
|
|
|
|
while (source != NULL) {
|
|
|
|
switch(source->type) {
|
|
|
|
case ENTROPY_SOURCETYPE_FILE:
|
|
|
|
destroysource(&source);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
source = ISC_LIST_HEAD(ent->sources);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If there are other types of sources, we've found a bug.
|
|
|
|
*/
|
2000-06-08 06:35:49 +00:00
|
|
|
REQUIRE(ISC_LIST_EMPTY(ent->sources));
|
2000-06-09 19:18:56 +00:00
|
|
|
|
2000-06-09 02:00:22 +00:00
|
|
|
mctx = ent->mctx;
|
2000-06-08 06:35:49 +00:00
|
|
|
|
|
|
|
isc_entropypool_invalidate(&ent->pool);
|
2000-06-15 18:15:26 +00:00
|
|
|
|
|
|
|
UNLOCK(&ent->lock);
|
|
|
|
|
2000-08-26 01:31:56 +00:00
|
|
|
DESTROYLOCK(&ent->lock);
|
2000-06-08 06:35:49 +00:00
|
|
|
|
|
|
|
memset(ent, 0, sizeof(isc_entropy_t));
|
2000-06-09 02:00:22 +00:00
|
|
|
isc_mem_put(mctx, ent, sizeof(isc_entropy_t));
|
2000-06-09 19:18:56 +00:00
|
|
|
isc_mem_detach(&mctx);
|
2000-06-07 22:15:55 +00:00
|
|
|
}
|
|
|
|
|
2000-06-08 22:18:53 +00:00
|
|
|
/*
|
|
|
|
* Make a fd non-blocking
|
|
|
|
*/
|
|
|
|
static isc_result_t
|
|
|
|
make_nonblock(int fd) {
|
|
|
|
int ret;
|
|
|
|
int flags;
|
|
|
|
|
|
|
|
flags = fcntl(fd, F_GETFL, 0);
|
|
|
|
flags |= O_NONBLOCK;
|
|
|
|
ret = fcntl(fd, F_SETFL, flags);
|
|
|
|
|
|
|
|
if (ret == -1) {
|
|
|
|
UNEXPECTED_ERROR(__FILE__, __LINE__,
|
|
|
|
"fcntl(%d, F_SETFL, %d): %s",
|
|
|
|
fd, flags, strerror(errno));
|
|
|
|
|
|
|
|
return (ISC_R_UNEXPECTED);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
}
|
|
|
|
|
2000-06-07 22:15:55 +00:00
|
|
|
isc_result_t
|
2000-06-17 01:42:21 +00:00
|
|
|
isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
|
2000-06-08 22:18:53 +00:00
|
|
|
int fd;
|
|
|
|
isc_result_t ret;
|
|
|
|
isc_entropysource_t *source;
|
2000-06-08 06:35:49 +00:00
|
|
|
|
2000-06-08 22:18:53 +00:00
|
|
|
REQUIRE(VALID_ENTROPY(ent));
|
|
|
|
REQUIRE(fname != NULL);
|
|
|
|
|
|
|
|
LOCK(&ent->lock);
|
|
|
|
|
|
|
|
source = NULL;
|
|
|
|
|
|
|
|
fd = open(fname, O_RDONLY | O_NONBLOCK, 0);
|
|
|
|
if (fd < 0) {
|
|
|
|
ret = ISC_R_IOERROR;
|
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
ret = make_nonblock(fd);
|
|
|
|
if (ret != ISC_R_SUCCESS)
|
2000-11-23 01:04:00 +00:00
|
|
|
goto closefd;
|
2000-06-08 22:18:53 +00:00
|
|
|
|
|
|
|
source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
|
|
|
|
if (source == NULL) {
|
|
|
|
ret = ISC_R_NOMEMORY;
|
2000-11-23 01:04:00 +00:00
|
|
|
goto closefd;
|
2000-06-08 22:18:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* From here down, no failures can occur.
|
|
|
|
*/
|
2000-06-09 17:25:41 +00:00
|
|
|
source->magic = SOURCE_MAGIC;
|
2000-06-08 22:18:53 +00:00
|
|
|
source->type = ENTROPY_SOURCETYPE_FILE;
|
|
|
|
source->ent = ent;
|
|
|
|
source->total = 0;
|
2000-11-23 01:04:00 +00:00
|
|
|
source->bad = ISC_FALSE;
|
2000-06-08 22:18:53 +00:00
|
|
|
memset(source->name, 0, sizeof(source->name));
|
|
|
|
ISC_LINK_INIT(source, link);
|
|
|
|
source->sources.file.fd = fd;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Hook it into the entropy system.
|
|
|
|
*/
|
|
|
|
ISC_LIST_APPEND(ent->sources, source, link);
|
2000-06-15 18:15:26 +00:00
|
|
|
ent->nsources++;
|
2000-06-08 22:18:53 +00:00
|
|
|
|
|
|
|
UNLOCK(&ent->lock);
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
|
2000-11-23 01:04:00 +00:00
|
|
|
closefd:
|
|
|
|
close(fd);
|
|
|
|
|
2000-06-08 22:18:53 +00:00
|
|
|
errout:
|
|
|
|
if (source != NULL)
|
|
|
|
isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t));
|
|
|
|
|
|
|
|
UNLOCK(&ent->lock);
|
|
|
|
|
|
|
|
return (ret);
|
2000-06-07 22:15:55 +00:00
|
|
|
}
|
|
|
|
|
2000-06-08 06:35:49 +00:00
|
|
|
void
|
2000-06-07 22:15:55 +00:00
|
|
|
isc_entropy_destroysource(isc_entropysource_t **sourcep) {
|
2000-06-08 22:18:53 +00:00
|
|
|
isc_entropysource_t *source;
|
|
|
|
isc_entropy_t *ent;
|
2000-06-09 19:18:56 +00:00
|
|
|
isc_boolean_t killit;
|
2000-06-08 22:18:53 +00:00
|
|
|
|
|
|
|
REQUIRE(sourcep != NULL);
|
|
|
|
REQUIRE(VALID_SOURCE(*sourcep));
|
|
|
|
|
|
|
|
source = *sourcep;
|
|
|
|
*sourcep = NULL;
|
|
|
|
|
|
|
|
ent = source->ent;
|
|
|
|
REQUIRE(VALID_ENTROPY(ent));
|
|
|
|
|
|
|
|
LOCK(&ent->lock);
|
|
|
|
|
2000-06-15 18:15:26 +00:00
|
|
|
destroysource(&source);
|
2000-06-08 22:18:53 +00:00
|
|
|
|
2000-06-09 19:18:56 +00:00
|
|
|
killit = destroy_check(ent);
|
|
|
|
|
2000-06-08 22:18:53 +00:00
|
|
|
UNLOCK(&ent->lock);
|
2000-06-09 19:18:56 +00:00
|
|
|
|
|
|
|
if (killit)
|
|
|
|
destroy(&ent);
|
2000-06-07 22:15:55 +00:00
|
|
|
}
|
|
|
|
|
2000-06-16 01:38:13 +00:00
|
|
|
isc_result_t
|
|
|
|
isc_entropy_createcallbacksource(isc_entropy_t *ent,
|
|
|
|
isc_entropystart_t start,
|
|
|
|
isc_entropyget_t get,
|
|
|
|
isc_entropystop_t stop,
|
|
|
|
void *arg,
|
|
|
|
isc_entropysource_t **sourcep)
|
|
|
|
{
|
2000-06-17 01:30:40 +00:00
|
|
|
isc_result_t ret;
|
|
|
|
isc_entropysource_t *source;
|
|
|
|
isc_cbsource_t *cbs;
|
|
|
|
|
2000-06-16 01:38:13 +00:00
|
|
|
REQUIRE(VALID_ENTROPY(ent));
|
2000-06-17 01:30:40 +00:00
|
|
|
REQUIRE(get != NULL);
|
2000-06-16 01:38:13 +00:00
|
|
|
REQUIRE(sourcep != NULL && *sourcep == NULL);
|
|
|
|
|
|
|
|
LOCK(&ent->lock);
|
|
|
|
|
2000-06-17 01:30:40 +00:00
|
|
|
source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
|
|
|
|
if (source == NULL) {
|
|
|
|
ret = ISC_R_NOMEMORY;
|
|
|
|
goto errout;
|
|
|
|
}
|
2000-11-23 01:04:00 +00:00
|
|
|
source->bad = ISC_FALSE;
|
2000-06-17 01:30:40 +00:00
|
|
|
|
|
|
|
cbs = &source->sources.callback;
|
|
|
|
|
|
|
|
ret = samplesource_allocate(ent, &cbs->samplequeue);
|
|
|
|
if (ret != ISC_R_SUCCESS)
|
|
|
|
goto errout;
|
|
|
|
|
2000-06-20 19:01:13 +00:00
|
|
|
cbs->start_called = ISC_FALSE;
|
2000-06-17 01:30:40 +00:00
|
|
|
cbs->startfunc = start;
|
|
|
|
cbs->getfunc = get;
|
|
|
|
cbs->stopfunc = stop;
|
|
|
|
cbs->arg = arg;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* From here down, no failures can occur.
|
|
|
|
*/
|
|
|
|
source->magic = SOURCE_MAGIC;
|
|
|
|
source->type = ENTROPY_SOURCETYPE_CALLBACK;
|
|
|
|
source->ent = ent;
|
|
|
|
source->total = 0;
|
|
|
|
memset(source->name, 0, sizeof(source->name));
|
|
|
|
ISC_LINK_INIT(source, link);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Hook it into the entropy system.
|
|
|
|
*/
|
|
|
|
ISC_LIST_APPEND(ent->sources, source, link);
|
2000-06-16 01:38:13 +00:00
|
|
|
ent->nsources++;
|
|
|
|
|
2000-06-21 00:04:57 +00:00
|
|
|
*sourcep = source;
|
|
|
|
|
2000-06-16 01:38:13 +00:00
|
|
|
UNLOCK(&ent->lock);
|
2000-06-17 01:30:40 +00:00
|
|
|
return (ISC_R_SUCCESS);
|
2000-06-16 01:38:13 +00:00
|
|
|
|
2000-06-17 01:30:40 +00:00
|
|
|
errout:
|
|
|
|
if (source != NULL)
|
|
|
|
isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t));
|
|
|
|
|
|
|
|
UNLOCK(&ent->lock);
|
|
|
|
|
|
|
|
return (ret);
|
2000-06-16 01:38:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2000-06-17 01:30:40 +00:00
|
|
|
isc_entropy_stopcallbacksources(isc_entropy_t *ent) {
|
|
|
|
isc_entropysource_t *source;
|
|
|
|
isc_cbsource_t *cbs;
|
|
|
|
|
|
|
|
REQUIRE(VALID_ENTROPY(ent));
|
|
|
|
|
2000-06-20 19:10:19 +00:00
|
|
|
LOCK(&ent->lock);
|
|
|
|
|
2000-06-17 01:30:40 +00:00
|
|
|
source = ISC_LIST_HEAD(ent->sources);
|
|
|
|
while (source != NULL) {
|
|
|
|
if (source->type == ENTROPY_SOURCETYPE_CALLBACK) {
|
|
|
|
cbs = &source->sources.callback;
|
2000-06-21 00:04:57 +00:00
|
|
|
if (cbs->start_called && cbs->stopfunc != NULL) {
|
|
|
|
cbs->stopfunc(source, cbs->arg);
|
|
|
|
cbs->start_called = ISC_FALSE;
|
|
|
|
}
|
2000-06-17 01:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
source = ISC_LIST_NEXT(source, link);
|
|
|
|
}
|
2000-06-20 19:10:19 +00:00
|
|
|
|
|
|
|
UNLOCK(&ent->lock);
|
2000-06-16 01:38:13 +00:00
|
|
|
}
|
|
|
|
|
2000-06-07 22:15:55 +00:00
|
|
|
isc_result_t
|
|
|
|
isc_entropy_createsamplesource(isc_entropy_t *ent,
|
|
|
|
isc_entropysource_t **sourcep)
|
|
|
|
{
|
2000-06-20 19:10:19 +00:00
|
|
|
isc_result_t ret;
|
|
|
|
isc_entropysource_t *source;
|
|
|
|
sample_queue_t *sq;
|
|
|
|
|
2000-06-09 18:45:51 +00:00
|
|
|
REQUIRE(VALID_ENTROPY(ent));
|
|
|
|
REQUIRE(sourcep != NULL && *sourcep == NULL);
|
|
|
|
|
|
|
|
LOCK(&ent->lock);
|
|
|
|
|
2000-06-20 19:10:19 +00:00
|
|
|
source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
|
|
|
|
if (source == NULL) {
|
|
|
|
ret = ISC_R_NOMEMORY;
|
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
|
|
|
|
sq = &source->sources.sample.samplequeue;
|
|
|
|
ret = samplesource_allocate(ent, sq);
|
|
|
|
if (ret != ISC_R_SUCCESS)
|
|
|
|
goto errout;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* From here down, no failures can occur.
|
|
|
|
*/
|
|
|
|
source->magic = SOURCE_MAGIC;
|
|
|
|
source->type = ENTROPY_SOURCETYPE_SAMPLE;
|
|
|
|
source->ent = ent;
|
|
|
|
source->total = 0;
|
|
|
|
memset(source->name, 0, sizeof(source->name));
|
|
|
|
ISC_LINK_INIT(source, link);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Hook it into the entropy system.
|
|
|
|
*/
|
|
|
|
ISC_LIST_APPEND(ent->sources, source, link);
|
2000-06-15 18:15:26 +00:00
|
|
|
ent->nsources++;
|
|
|
|
|
2000-06-21 00:04:57 +00:00
|
|
|
*sourcep = source;
|
|
|
|
|
2000-06-09 18:45:51 +00:00
|
|
|
UNLOCK(&ent->lock);
|
2000-06-20 19:10:19 +00:00
|
|
|
return (ISC_R_SUCCESS);
|
2000-06-08 06:35:49 +00:00
|
|
|
|
2000-06-20 19:10:19 +00:00
|
|
|
errout:
|
|
|
|
if (source != NULL)
|
|
|
|
isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t));
|
|
|
|
|
|
|
|
UNLOCK(&ent->lock);
|
|
|
|
|
|
|
|
return (ret);
|
2000-06-07 22:15:55 +00:00
|
|
|
}
|
|
|
|
|
2000-06-20 19:01:13 +00:00
|
|
|
static inline unsigned int
|
|
|
|
estimate_entropy(sample_queue_t *sq, isc_uint32_t t)
|
|
|
|
{
|
|
|
|
isc_int32_t delta;
|
|
|
|
isc_int32_t delta2;
|
|
|
|
isc_int32_t delta3;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the time counter has overflowed, calculate the real difference.
|
|
|
|
* If it has not, it is simplier.
|
|
|
|
*/
|
|
|
|
if (t < sq->last_time)
|
|
|
|
delta = UINT_MAX - sq->last_time + t;
|
|
|
|
else
|
|
|
|
delta = sq->last_time - t;
|
|
|
|
|
|
|
|
if (delta < 0)
|
|
|
|
delta = -delta;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate the second and third order differentials
|
|
|
|
*/
|
|
|
|
delta2 = sq->last_delta - delta;
|
|
|
|
if (delta2 < 0)
|
|
|
|
delta2 = -delta2;
|
|
|
|
|
|
|
|
delta3 = sq->last_delta2 - delta2;
|
|
|
|
if (delta3 < 0)
|
|
|
|
delta3 = -delta3;
|
|
|
|
|
|
|
|
sq->last_time = t;
|
|
|
|
sq->last_delta = delta;
|
|
|
|
sq->last_delta2 = delta2;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If any delta is 0, we got no entropy. If all are non-zero, we
|
|
|
|
* might have something.
|
|
|
|
*/
|
|
|
|
if (delta == 0 || delta2 == 0 || delta3 == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We could find the smallest delta and claim we got log2(delta)
|
|
|
|
* bits, but for now return that we found 1 bit.
|
|
|
|
*/
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int
|
|
|
|
crunchsamples(isc_entropy_t *ent, sample_queue_t *sq) {
|
|
|
|
unsigned int ns;
|
|
|
|
unsigned int added;
|
|
|
|
|
2000-06-21 00:04:57 +00:00
|
|
|
if (sq->nsamples < 6)
|
2000-06-20 19:01:13 +00:00
|
|
|
return (0);
|
|
|
|
|
|
|
|
added = 0;
|
|
|
|
sq->last_time = sq->samples[0];
|
|
|
|
sq->last_delta = 0;
|
|
|
|
sq->last_delta2 = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Prime the values by adding in the first 4 samples in. This
|
|
|
|
* should completely initialize the delta calculations.
|
|
|
|
*/
|
|
|
|
for (ns = 0 ; ns < 4 ; ns++)
|
|
|
|
(void)estimate_entropy(sq, sq->samples[ns]);
|
|
|
|
|
|
|
|
for (ns = 4 ; ns < sq->nsamples ; ns++)
|
|
|
|
added += estimate_entropy(sq, sq->samples[ns]);
|
|
|
|
|
|
|
|
entropypool_adddata(ent, sq->samples, sq->nsamples * 4, added);
|
|
|
|
entropypool_adddata(ent, sq->extra, sq->nsamples * 4, 0);
|
|
|
|
|
2000-06-21 00:04:57 +00:00
|
|
|
/*
|
|
|
|
* Move the last 4 samples into the first 4 positions, and start
|
|
|
|
* adding new samples from that point.
|
|
|
|
*/
|
|
|
|
for (ns = 0 ; ns < 4 ; ns++) {
|
|
|
|
sq->samples[ns] = sq->samples[sq->nsamples - 4 + ns];
|
|
|
|
sq->extra[ns] = sq->extra[sq->nsamples - 4 + ns];
|
|
|
|
}
|
|
|
|
|
2000-06-23 02:31:10 +00:00
|
|
|
sq->nsamples = 4;
|
2000-06-21 00:04:57 +00:00
|
|
|
|
2000-06-20 19:01:13 +00:00
|
|
|
return (added);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add a sample, and return ISC_R_SUCCESS if the queue has become full,
|
|
|
|
* ISC_R_NOENTROPY if it has space remaining, and ISC_R_NOMORE if the
|
|
|
|
* queue was full when this function was called.
|
|
|
|
*/
|
|
|
|
static isc_result_t
|
|
|
|
addsample(sample_queue_t *sq, isc_uint32_t sample, isc_uint32_t extra) {
|
|
|
|
if (sq->nsamples >= RND_EVENTQSIZE)
|
|
|
|
return (ISC_R_NOMORE);
|
|
|
|
|
|
|
|
sq->samples[sq->nsamples] = sample;
|
|
|
|
sq->extra[sq->nsamples] = extra;
|
|
|
|
sq->nsamples++;
|
|
|
|
|
|
|
|
if (sq->nsamples >= RND_EVENTQSIZE)
|
2000-06-21 00:04:57 +00:00
|
|
|
return (ISC_R_QUEUEFULL);
|
2000-06-20 19:01:13 +00:00
|
|
|
|
2000-06-21 00:04:57 +00:00
|
|
|
return (ISC_R_SUCCESS);
|
2000-06-20 19:01:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
isc_result_t
|
2000-06-07 22:15:55 +00:00
|
|
|
isc_entropy_addsample(isc_entropysource_t *source, isc_uint32_t sample,
|
2000-06-08 22:18:53 +00:00
|
|
|
isc_uint32_t extra)
|
2000-06-07 22:15:55 +00:00
|
|
|
{
|
2000-06-09 18:45:51 +00:00
|
|
|
isc_entropy_t *ent;
|
2000-06-20 19:01:13 +00:00
|
|
|
sample_queue_t *sq;
|
|
|
|
unsigned int entropy;
|
|
|
|
isc_result_t result;
|
2000-06-09 18:45:51 +00:00
|
|
|
|
|
|
|
REQUIRE(VALID_SOURCE(source));
|
2000-06-20 19:01:13 +00:00
|
|
|
|
2000-06-09 18:45:51 +00:00
|
|
|
ent = source->ent;
|
|
|
|
|
|
|
|
LOCK(&ent->lock);
|
|
|
|
|
2000-06-21 00:04:57 +00:00
|
|
|
sq = &source->sources.sample.samplequeue;
|
|
|
|
result = addsample(sq, sample, extra);
|
|
|
|
if (result == ISC_R_QUEUEFULL) {
|
|
|
|
entropy = crunchsamples(ent, sq);
|
|
|
|
add_entropy(ent, entropy);
|
2000-06-20 19:01:13 +00:00
|
|
|
}
|
|
|
|
|
2000-06-09 18:45:51 +00:00
|
|
|
UNLOCK(&ent->lock);
|
2000-06-20 19:01:13 +00:00
|
|
|
|
|
|
|
return (result);
|
2000-06-07 22:15:55 +00:00
|
|
|
}
|
2000-06-09 02:00:22 +00:00
|
|
|
|
2000-06-21 00:04:57 +00:00
|
|
|
isc_result_t
|
|
|
|
isc_entropy_addcallbacksample(isc_entropysource_t *source, isc_uint32_t sample,
|
|
|
|
isc_uint32_t extra)
|
|
|
|
{
|
|
|
|
sample_queue_t *sq;
|
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
REQUIRE(VALID_SOURCE(source));
|
|
|
|
REQUIRE(source->type == ENTROPY_SOURCETYPE_CALLBACK);
|
|
|
|
|
|
|
|
sq = &source->sources.callback.samplequeue;
|
|
|
|
result = addsample(sq, sample, extra);
|
|
|
|
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
2000-06-09 02:00:22 +00:00
|
|
|
void
|
2000-06-17 00:43:11 +00:00
|
|
|
isc_entropy_putdata(isc_entropy_t *ent, void *data, unsigned int length,
|
|
|
|
isc_uint32_t entropy)
|
|
|
|
{
|
2000-06-09 18:45:51 +00:00
|
|
|
REQUIRE(VALID_ENTROPY(ent));
|
|
|
|
|
2000-06-17 00:43:11 +00:00
|
|
|
LOCK(&ent->lock);
|
2000-06-21 22:12:23 +00:00
|
|
|
|
2000-06-17 00:43:11 +00:00
|
|
|
entropypool_adddata(ent, data, length, entropy);
|
|
|
|
|
|
|
|
if (ent->initialized < THRESHOLD_BITS)
|
|
|
|
ent->initialized = THRESHOLD_BITS;
|
2000-06-21 22:12:23 +00:00
|
|
|
|
2000-06-17 00:43:11 +00:00
|
|
|
UNLOCK(&ent->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dumpstats(isc_entropy_t *ent, FILE *out) {
|
2000-06-09 19:18:56 +00:00
|
|
|
fprintf(out,
|
2000-12-06 00:30:32 +00:00
|
|
|
isc_msgcat_get(isc_msgcat, ISC_MSGSET_ENTROPY,
|
|
|
|
ISC_MSG_ENTROPYSTATS,
|
|
|
|
"Entropy pool %p: refcnt %u cursor %u,"
|
|
|
|
" rotate %u entropy %u pseudo %u nsources %u"
|
|
|
|
" nextsource %p initialized %u initcount %u\n"),
|
2000-06-09 19:18:56 +00:00
|
|
|
ent, ent->refcnt,
|
|
|
|
ent->pool.cursor, ent->pool.rotate,
|
2000-06-15 18:15:26 +00:00
|
|
|
ent->pool.entropy, ent->pool.pseudo,
|
2000-06-21 22:12:23 +00:00
|
|
|
ent->nsources, ent->nextsource, ent->initialized,
|
|
|
|
ent->initcount);
|
2000-06-09 02:00:22 +00:00
|
|
|
}
|
2000-06-09 19:18:56 +00:00
|
|
|
|
2000-06-17 00:43:11 +00:00
|
|
|
/*
|
|
|
|
* This function ignores locking. Use at your own risk.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
isc_entropy_stats(isc_entropy_t *ent, FILE *out) {
|
|
|
|
REQUIRE(VALID_ENTROPY(ent));
|
|
|
|
|
|
|
|
LOCK(&ent->lock);
|
|
|
|
dumpstats(ent, out);
|
|
|
|
UNLOCK(&ent->lock);
|
|
|
|
}
|
|
|
|
|
2000-06-09 19:18:56 +00:00
|
|
|
void
|
|
|
|
isc_entropy_attach(isc_entropy_t *ent, isc_entropy_t **entp) {
|
|
|
|
REQUIRE(VALID_ENTROPY(ent));
|
|
|
|
REQUIRE(entp != NULL && *entp == NULL);
|
|
|
|
|
|
|
|
LOCK(&ent->lock);
|
|
|
|
|
|
|
|
ent->refcnt++;
|
|
|
|
*entp = ent;
|
|
|
|
|
|
|
|
UNLOCK(&ent->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
isc_entropy_detach(isc_entropy_t **entp) {
|
|
|
|
isc_entropy_t *ent;
|
|
|
|
isc_boolean_t killit;
|
|
|
|
|
|
|
|
REQUIRE(entp != NULL && VALID_ENTROPY(*entp));
|
|
|
|
ent = *entp;
|
|
|
|
*entp = NULL;
|
|
|
|
|
|
|
|
LOCK(&ent->lock);
|
|
|
|
|
|
|
|
REQUIRE(ent->refcnt > 0);
|
|
|
|
ent->refcnt--;
|
|
|
|
|
|
|
|
killit = destroy_check(ent);
|
|
|
|
|
|
|
|
UNLOCK(&ent->lock);
|
|
|
|
|
|
|
|
if (killit)
|
|
|
|
destroy(&ent);
|
|
|
|
}
|