entropy.c revision 43f117d5f0bc9d3e5d95798a9c25ef2c66c6da34
e334405421979688f2d838805ac67ee47bd62976Mark Andrews/*
6fb9b25791778f69002eb72be6235e20d98ec452Tinderbox User * Copyright (C) 2000, 2001 Internet Software Consortium.
b8a9a7bef2c3060204c68aef4f3fce04afc1aaeeAutomatic Updater *
0c27b3fe77ac1d5094ba3521e8142d9e7973133fMark Andrews * Permission to use, copy, modify, and distribute this software for any
0c27b3fe77ac1d5094ba3521e8142d9e7973133fMark Andrews * purpose with or without fee is hereby granted, provided that the above
0c27b3fe77ac1d5094ba3521e8142d9e7973133fMark Andrews * copyright notice and this permission notice appear in all copies.
e334405421979688f2d838805ac67ee47bd62976Mark Andrews *
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
c1aef54e14bb92518b1c062ba8c0292a7cb949cbAutomatic Updater * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
b8a9a7bef2c3060204c68aef4f3fce04afc1aaeeAutomatic Updater * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
e334405421979688f2d838805ac67ee47bd62976Mark Andrews */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews/* $Id: entropy.c,v 1.4 2001/09/01 00:46:04 gson Exp $ */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews/*
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * This is the system independent part of the entropy module. It is
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * compiled via inclusion from the relevant OS source file, ie,
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * unix/entropy.c or win32/entropy.c.
e334405421979688f2d838805ac67ee47bd62976Mark Andrews */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#include <errno.h>
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#include <fcntl.h>
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#include <stdio.h>
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#include <isc/buffer.h>
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#include <isc/entropy.h>
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#include <isc/keyboard.h>
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#include <isc/list.h>
b8a9a7bef2c3060204c68aef4f3fce04afc1aaeeAutomatic Updater#include <isc/magic.h>
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#include <isc/mem.h>
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#include <isc/msgs.h>
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#include <isc/mutex.h>
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#include <isc/platform.h>
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#include <isc/region.h>
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#include <isc/sha1.h>
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#include <isc/string.h>
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#include <isc/time.h>
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#include <isc/util.h>
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews/*
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * Much of this code is modeled after the NetBSD /dev/random implementation,
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * written by Michael Graff <explorer@netbsd.org>.
e334405421979688f2d838805ac67ee47bd62976Mark Andrews */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#define ENTROPY_MAGIC ISC_MAGIC('E', 'n', 't', 'e')
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#define SOURCE_MAGIC ISC_MAGIC('E', 'n', 't', 's')
bfde61d5194a534d800f3b90008d1f52261922c5Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#define VALID_ENTROPY(e) ISC_MAGIC_VALID(e, ENTROPY_MAGIC)
bfde61d5194a534d800f3b90008d1f52261922c5Mark Andrews#define VALID_SOURCE(s) ISC_MAGIC_VALID(s, SOURCE_MAGIC)
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews/***
e334405421979688f2d838805ac67ee47bd62976Mark Andrews *** "constants." Do not change these unless you _really_ know what
f2ea8c2f965be7ff4c59f805712c12d469226b7bEvan Hunt *** you are doing.
e334405421979688f2d838805ac67ee47bd62976Mark Andrews ***/
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews/*
bfde61d5194a534d800f3b90008d1f52261922c5Mark Andrews * size of entropy pool in 32-bit words. This _MUST_ be a power of 2.
e334405421979688f2d838805ac67ee47bd62976Mark Andrews */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#define RND_POOLWORDS 128
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#define RND_POOLBYTES (RND_POOLWORDS * 4)
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#define RND_POOLBITS (RND_POOLWORDS * 32)
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
f2ea8c2f965be7ff4c59f805712c12d469226b7bEvan Hunt/*
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * Number of bytes returned per hash. This must be true:
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * threshold * 2 <= digest_size_in_bytes
e334405421979688f2d838805ac67ee47bd62976Mark Andrews */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#define RND_ENTROPY_THRESHOLD 10
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#define THRESHOLD_BITS (RND_ENTROPY_THRESHOLD * 8)
bfde61d5194a534d800f3b90008d1f52261922c5Mark Andrews
bfde61d5194a534d800f3b90008d1f52261922c5Mark Andrews/*
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * Size of the input event queue in samples.
bfde61d5194a534d800f3b90008d1f52261922c5Mark Andrews */
bfde61d5194a534d800f3b90008d1f52261922c5Mark Andrews#define RND_EVENTQSIZE 32
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
bfde61d5194a534d800f3b90008d1f52261922c5Mark Andrews/*
e851ea826066ac5a5b01c2c23218faa0273a12e8Evan Hunt * The number of times we'll "reseed" for pseudorandom seeds. This is an
bfde61d5194a534d800f3b90008d1f52261922c5Mark Andrews * extremely weak pseudorandom seed. If the caller is using lots of
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * pseudorandom data and they cannot provide a stronger random source,
bfde61d5194a534d800f3b90008d1f52261922c5Mark Andrews * there is little we can do other than hope they're smart enough to
bfde61d5194a534d800f3b90008d1f52261922c5Mark Andrews * call _adddata() with something better than we can come up with.
e334405421979688f2d838805ac67ee47bd62976Mark Andrews */
bfde61d5194a534d800f3b90008d1f52261922c5Mark Andrews#define RND_INITIALIZE 128
bfde61d5194a534d800f3b90008d1f52261922c5Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrewstypedef struct {
bfde61d5194a534d800f3b90008d1f52261922c5Mark Andrews isc_uint32_t cursor; /* current add point in the pool */
bfde61d5194a534d800f3b90008d1f52261922c5Mark Andrews isc_uint32_t entropy; /* current entropy estimate in bits */
bfde61d5194a534d800f3b90008d1f52261922c5Mark Andrews isc_uint32_t pseudo; /* bits extracted in pseudorandom */
bfde61d5194a534d800f3b90008d1f52261922c5Mark Andrews isc_uint32_t rotate; /* how many bits to rotate by */
bfde61d5194a534d800f3b90008d1f52261922c5Mark Andrews isc_uint32_t pool[RND_POOLWORDS]; /* random pool data */
bfde61d5194a534d800f3b90008d1f52261922c5Mark Andrews} isc_entropypool_t;
bfde61d5194a534d800f3b90008d1f52261922c5Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrewsstruct isc_entropy {
44c0cfd2be65cebdfce66e0911c4da11186ee651Mark Andrews unsigned int magic;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_mem_t *mctx;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_mutex_t lock;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews unsigned int refcnt;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_uint32_t initialized;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_uint32_t initcount;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_entropypool_t pool;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews unsigned int nsources;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_entropysource_t *nextsource;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews ISC_LIST(isc_entropysource_t) sources;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews};
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrewstypedef struct {
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_uint32_t last_time; /* last time recorded */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_uint32_t last_delta; /* last delta value */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_uint32_t last_delta2; /* last delta2 value */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_uint32_t nsamples; /* number of samples filled in */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_uint32_t *samples; /* the samples */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_uint32_t *extra; /* extra samples added in */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews} sample_queue_t;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrewstypedef struct {
e334405421979688f2d838805ac67ee47bd62976Mark Andrews sample_queue_t samplequeue;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews} isc_entropysamplesource_t;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrewstypedef struct {
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_boolean_t start_called;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_entropystart_t startfunc;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_entropyget_t getfunc;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_entropystop_t stopfunc;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews void *arg;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews sample_queue_t samplequeue;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews} isc_cbsource_t;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrewstypedef struct {
e334405421979688f2d838805ac67ee47bd62976Mark Andrews FILESOURCE_HANDLE_TYPE handle;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews} isc_entropyfilesource_t;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrewsstruct isc_entropysource {
e334405421979688f2d838805ac67ee47bd62976Mark Andrews unsigned int magic;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews unsigned int type;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_entropy_t *ent;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_uint32_t total; /* entropy from this source */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews ISC_LINK(isc_entropysource_t) link;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews char name[32];
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_boolean_t bad;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_boolean_t warn_keyboard;
b8a9a7bef2c3060204c68aef4f3fce04afc1aaeeAutomatic Updater isc_keyboard_t kbd;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews union {
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_entropysamplesource_t sample;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_entropyfilesource_t file;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_cbsource_t callback;
e851ea826066ac5a5b01c2c23218faa0273a12e8Evan Hunt } sources;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews};
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#define ENTROPY_SOURCETYPE_SAMPLE 1 /* Type is a sample source */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#define ENTROPY_SOURCETYPE_FILE 2 /* Type is a file source */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#define ENTROPY_SOURCETYPE_CALLBACK 3 /* Type is a callback source */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews/*
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * The random pool "taps"
e334405421979688f2d838805ac67ee47bd62976Mark Andrews */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#define TAP1 99
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#define TAP2 59
e334405421979688f2d838805ac67ee47bd62976Mark Andrews#define TAP3 31
e851ea826066ac5a5b01c2c23218faa0273a12e8Evan Hunt#define TAP4 9
f2ea8c2f965be7ff4c59f805712c12d469226b7bEvan Hunt#define TAP5 7
1e442d19949b84d448742672e2ed8cab1177abb6Mark Andrews
1e442d19949b84d448742672e2ed8cab1177abb6Mark Andrews/*
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * Declarations for function provided by the system dependent sources that
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * include this file.
e334405421979688f2d838805ac67ee47bd62976Mark Andrews */
f2ea8c2f965be7ff4c59f805712c12d469226b7bEvan Huntstatic void
e334405421979688f2d838805ac67ee47bd62976Mark Andrewsfillpool(isc_entropy_t *, unsigned int, isc_boolean_t);
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrewsstatic int
e334405421979688f2d838805ac67ee47bd62976Mark Andrewswait_for_sources(isc_entropy_t *);
e851ea826066ac5a5b01c2c23218faa0273a12e8Evan Hunt
e334405421979688f2d838805ac67ee47bd62976Mark Andrewsstatic void
e334405421979688f2d838805ac67ee47bd62976Mark Andrewsdestroyfilesource(isc_entropyfilesource_t *source);
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrewsstatic void
e334405421979688f2d838805ac67ee47bd62976Mark Andrewssamplequeue_release(isc_entropy_t *ent, sample_queue_t *sq) {
e334405421979688f2d838805ac67ee47bd62976Mark Andrews REQUIRE(sq->samples != NULL);
e334405421979688f2d838805ac67ee47bd62976Mark Andrews REQUIRE(sq->extra != NULL);
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_mem_put(ent->mctx, sq->samples, RND_EVENTQSIZE * 4);
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_mem_put(ent->mctx, sq->extra, RND_EVENTQSIZE * 4);
e334405421979688f2d838805ac67ee47bd62976Mark Andrews sq->samples = NULL;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews sq->extra = NULL;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews}
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrewsstatic isc_result_t
e334405421979688f2d838805ac67ee47bd62976Mark Andrewssamplesource_allocate(isc_entropy_t *ent, sample_queue_t *sq) {
e334405421979688f2d838805ac67ee47bd62976Mark Andrews sq->samples = isc_mem_get(ent->mctx, RND_EVENTQSIZE * 4);
e334405421979688f2d838805ac67ee47bd62976Mark Andrews if (sq->samples == NULL)
e334405421979688f2d838805ac67ee47bd62976Mark Andrews return (ISC_R_NOMEMORY);
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
b8a9a7bef2c3060204c68aef4f3fce04afc1aaeeAutomatic Updater sq->extra = isc_mem_get(ent->mctx, RND_EVENTQSIZE * 4);
e334405421979688f2d838805ac67ee47bd62976Mark Andrews if (sq->extra == NULL) {
e334405421979688f2d838805ac67ee47bd62976Mark Andrews isc_mem_put(ent->mctx, sq->samples, RND_EVENTQSIZE * 4);
e334405421979688f2d838805ac67ee47bd62976Mark Andrews sq->samples = NULL;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews return (ISC_R_NOMEMORY);
e334405421979688f2d838805ac67ee47bd62976Mark Andrews }
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews sq->nsamples = 0;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews return (ISC_R_SUCCESS);
e334405421979688f2d838805ac67ee47bd62976Mark Andrews}
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews/*
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * Add in entropy, even when the value we're adding in could be
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * very large.
e334405421979688f2d838805ac67ee47bd62976Mark Andrews */
e334405421979688f2d838805ac67ee47bd62976Mark Andrewsstatic inline void
b8a9a7bef2c3060204c68aef4f3fce04afc1aaeeAutomatic Updateradd_entropy(isc_entropy_t *ent, isc_uint32_t entropy) {
e334405421979688f2d838805ac67ee47bd62976Mark Andrews /* clamp input. Yes, this must be done. */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews entropy = ISC_MIN(entropy, RND_POOLBITS);
e334405421979688f2d838805ac67ee47bd62976Mark Andrews /* Add in the entropy we already have. */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews entropy += ent->pool.entropy;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews /* Clamp. */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews ent->pool.entropy = ISC_MIN(entropy, RND_POOLBITS);
e334405421979688f2d838805ac67ee47bd62976Mark Andrews}
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews/*
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * Decrement the amount of entropy the pool has.
e334405421979688f2d838805ac67ee47bd62976Mark Andrews */
e334405421979688f2d838805ac67ee47bd62976Mark Andrewsstatic inline void
e334405421979688f2d838805ac67ee47bd62976Mark Andrewssubtract_entropy(isc_entropy_t *ent, isc_uint32_t entropy) {
e334405421979688f2d838805ac67ee47bd62976Mark Andrews entropy = ISC_MIN(entropy, ent->pool.entropy);
e334405421979688f2d838805ac67ee47bd62976Mark Andrews ent->pool.entropy -= entropy;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews}
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews/*
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * Add in entropy, even when the value we're adding in could be
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * very large.
e334405421979688f2d838805ac67ee47bd62976Mark Andrews */
e334405421979688f2d838805ac67ee47bd62976Mark Andrewsstatic inline void
e334405421979688f2d838805ac67ee47bd62976Mark Andrewsadd_pseudo(isc_entropy_t *ent, isc_uint32_t pseudo) {
e334405421979688f2d838805ac67ee47bd62976Mark Andrews /* clamp input. Yes, this must be done. */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews pseudo = ISC_MIN(pseudo, RND_POOLBITS * 8);
e334405421979688f2d838805ac67ee47bd62976Mark Andrews /* Add in the pseudo we already have. */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews pseudo += ent->pool.pseudo;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews /* Clamp. */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews ent->pool.pseudo = ISC_MIN(pseudo, RND_POOLBITS * 8);
e334405421979688f2d838805ac67ee47bd62976Mark Andrews}
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews/*
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * Decrement the amount of pseudo the pool has.
cf4ceeee5fee2ebdca74f82514c14fd50939f85bMark Andrews */
cf4ceeee5fee2ebdca74f82514c14fd50939f85bMark Andrewsstatic inline void
e334405421979688f2d838805ac67ee47bd62976Mark Andrewssubtract_pseudo(isc_entropy_t *ent, isc_uint32_t pseudo) {
e334405421979688f2d838805ac67ee47bd62976Mark Andrews pseudo = ISC_MIN(pseudo, ent->pool.pseudo);
e334405421979688f2d838805ac67ee47bd62976Mark Andrews ent->pool.pseudo -= pseudo;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews}
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews/*
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * Add one word to the pool, rotating the input as needed.
e334405421979688f2d838805ac67ee47bd62976Mark Andrews */
e334405421979688f2d838805ac67ee47bd62976Mark Andrewsstatic inline void
e334405421979688f2d838805ac67ee47bd62976Mark Andrewsentropypool_add_word(isc_entropypool_t *rp, isc_uint32_t val) {
e334405421979688f2d838805ac67ee47bd62976Mark Andrews /*
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * Steal some values out of the pool, and xor them into the
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * word we were given.
e851ea826066ac5a5b01c2c23218faa0273a12e8Evan Hunt *
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * Mix the new value into the pool using xor. This will
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * prevent the actual values from being known to the caller
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * since the previous values are assumed to be unknown as well.
e334405421979688f2d838805ac67ee47bd62976Mark Andrews */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews val ^= rp->pool[(rp->cursor + TAP1) & (RND_POOLWORDS - 1)];
e334405421979688f2d838805ac67ee47bd62976Mark Andrews val ^= rp->pool[(rp->cursor + TAP2) & (RND_POOLWORDS - 1)];
e334405421979688f2d838805ac67ee47bd62976Mark Andrews val ^= rp->pool[(rp->cursor + TAP3) & (RND_POOLWORDS - 1)];
e334405421979688f2d838805ac67ee47bd62976Mark Andrews val ^= rp->pool[(rp->cursor + TAP4) & (RND_POOLWORDS - 1)];
e334405421979688f2d838805ac67ee47bd62976Mark Andrews val ^= rp->pool[(rp->cursor + TAP5) & (RND_POOLWORDS - 1)];
e334405421979688f2d838805ac67ee47bd62976Mark Andrews rp->pool[rp->cursor++] ^=
e334405421979688f2d838805ac67ee47bd62976Mark Andrews ((val << rp->rotate) | (val >> (32 - rp->rotate)));
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews /*
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * If we have looped around the pool, increment the rotate
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * variable so the next value will get xored in rotated to
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * a different position.
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * Increment by a value that is relativly prime to the word size
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * to try to spread the bits throughout the pool quickly when the
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * pool is empty.
e334405421979688f2d838805ac67ee47bd62976Mark Andrews */
e334405421979688f2d838805ac67ee47bd62976Mark Andrews if (rp->cursor == RND_POOLWORDS) {
e334405421979688f2d838805ac67ee47bd62976Mark Andrews rp->cursor = 0;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews rp->rotate = (rp->rotate + 7) & 31;
e334405421979688f2d838805ac67ee47bd62976Mark Andrews }
e334405421979688f2d838805ac67ee47bd62976Mark Andrews}
e334405421979688f2d838805ac67ee47bd62976Mark Andrews
e334405421979688f2d838805ac67ee47bd62976Mark Andrews/*
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * Add a buffer's worth of data to the pool.
e334405421979688f2d838805ac67ee47bd62976Mark Andrews *
e334405421979688f2d838805ac67ee47bd62976Mark Andrews * Requires that the lock is held on the entropy pool.
*/
static void
entropypool_adddata(isc_entropy_t *ent, void *p, unsigned int len,
isc_uint32_t entropy)
{
isc_uint32_t val;
unsigned long addr;
isc_uint8_t *buf;
addr = (unsigned long)p;
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--;
}
entropypool_add_word(&ent->pool, val);
}
for (; len > 3 ; len -= 4) {
val = *((isc_uint32_t *)buf);
entropypool_add_word(&ent->pool, val);
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++;
}
entropypool_add_word(&ent->pool, val);
}
add_entropy(ent, entropy);
subtract_pseudo(ent, entropy);
}
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++;
}
}
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 simpler.
*/
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;
if (sq->nsamples < 6)
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);
/*
* 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];
}
sq->nsamples = 4;
return (added);
}
static unsigned int
get_from_callback(isc_entropysource_t *source, unsigned int desired,
isc_boolean_t blocking)
{
isc_entropy_t *ent = source->ent;
isc_cbsource_t *cbs = &source->sources.callback;
unsigned int added;
unsigned int got;
isc_result_t result;
if (desired == 0)
return (0);
if (source->bad)
return (0);
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;
} else if (result != ISC_R_SUCCESS &&
result != ISC_R_NOTBLOCKING)
source->bad = ISC_TRUE;
}
return (added);
}
/*
* 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,
* etc.
*/
isc_result_t
isc_entropy_getdata(isc_entropy_t *ent, void *data, unsigned int length,
unsigned int *returned, unsigned int flags)
{
unsigned int i;
isc_sha1_t hash;
unsigned char digest[ISC_SHA1_DIGESTLENGTH];
isc_uint32_t remain, deltae, count, total;
isc_uint8_t *buf;
isc_boolean_t goodonly, partial, blocking;
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);
REQUIRE(!partial || returned != NULL);
LOCK(&ent->lock);
remain = length;
buf = data;
total = 0;
while (remain != 0) {
count = ISC_MIN(remain, RND_ENTROPY_THRESHOLD);
/*
* 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.
*/
if (goodonly) {
unsigned int fillcount;
fillcount = ISC_MAX(remain * 8, count * 8);
/*
* 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);
/*
* Verify that we got enough entropy to do one
* extraction. If we didn't, bail.
*/
if (ent->pool.entropy < THRESHOLD_BITS) {
if (!partial)
goto zeroize;
else
goto partial_output;
}
} else {
/*
* If we've extracted half our pool size in bits
* since the last refresh, try to refresh here.
*/
if (ent->initialized < THRESHOLD_BITS)
fillpool(ent, THRESHOLD_BITS, blocking);
else
fillpool(ent, 0, ISC_FALSE);
/*
* If we've not initialized with enough good random
* data, seed with our crappy code.
*/
if (ent->initialized < THRESHOLD_BITS)
reseed(ent);
}
isc_sha1_init(&hash);
isc_sha1_update(&hash, (void *)(ent->pool.pool),
RND_POOLBYTES);
isc_sha1_final(&hash, digest);
/*
* Stir the extracted data (all of it) back into the pool.
*/
entropypool_adddata(ent, digest, ISC_SHA1_DIGESTLENGTH, 0);
for (i = 0; i < count; i++)
buf[i] = digest[i] ^ digest[i + RND_ENTROPY_THRESHOLD];
buf += count;
remain -= count;
deltae = count * 8;
deltae = ISC_MIN(deltae, ent->pool.entropy);
total += deltae;
subtract_entropy(ent, deltae);
add_pseudo(ent, count * 8);
}
partial_output:
memset(digest, 0, sizeof(digest));
if (returned != NULL)
*returned = (length - remain);
UNLOCK(&ent->lock);
return (ISC_R_SUCCESS);
zeroize:
/* put the entropy we almost extracted back */
add_entropy(ent, total);
memset(data, 0, length);
memset(digest, 0, sizeof(digest));
if (returned != NULL)
*returned = 0;
UNLOCK(&ent->lock);
return (ISC_R_NOENTROPY);
}
static void
isc_entropypool_init(isc_entropypool_t *pool) {
pool->cursor = RND_POOLWORDS - 1;
pool->entropy = 0;
pool->pseudo = 0;
pool->rotate = 0;
memset(pool->pool, 0, RND_POOLBYTES);
}
static void
isc_entropypool_invalidate(isc_entropypool_t *pool) {
pool->cursor = 0;
pool->entropy = 0;
pool->pseudo = 0;
pool->rotate = 0;
memset(pool->pool, 0, RND_POOLBYTES);
}
isc_result_t
isc_entropy_create(isc_mem_t *mctx, isc_entropy_t **entp) {
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);
ent->nextsource = NULL;
ent->nsources = 0;
ent->mctx = NULL;
isc_mem_attach(mctx, &ent->mctx);
ent->refcnt = 1;
ent->initialized = 0;
ent->initcount = 0;
ent->magic = ENTROPY_MAGIC;
isc_entropypool_init(&ent->pool);
*entp = ent;
return (ISC_R_SUCCESS);
errout:
isc_mem_put(mctx, ent, sizeof(isc_entropy_t));
return (ret);
}
/*
* Requires "ent" be locked.
*/
static void
destroysource(isc_entropysource_t **sourcep) {
isc_entropysource_t *source;
isc_entropy_t *ent;
isc_cbsource_t *cbs;
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:
if (! source->bad)
destroyfilesource(&source->sources.file);
break;
case ENTROPY_SOURCETYPE_SAMPLE:
samplequeue_release(ent, &source->sources.sample.samplequeue);
break;
case ENTROPY_SOURCETYPE_CALLBACK:
cbs = &source->sources.callback;
if (cbs->start_called && cbs->stopfunc != NULL) {
cbs->stopfunc(source, cbs->arg);
cbs->start_called = ISC_FALSE;
}
samplequeue_release(ent, &cbs->samplequeue);
break;
}
memset(source, 0, sizeof(isc_entropysource_t));
isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t));
}
static inline isc_boolean_t
destroy_check(isc_entropy_t *ent) {
isc_entropysource_t *source;
if (ent->refcnt > 0)
return (ISC_FALSE);
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);
}
return (ISC_TRUE);
}
static void
destroy(isc_entropy_t **entp) {
isc_entropy_t *ent;
isc_entropysource_t *source;
isc_mem_t *mctx;
REQUIRE(entp != NULL && *entp != NULL);
ent = *entp;
*entp = NULL;
LOCK(&ent->lock);
REQUIRE(ent->refcnt == 0);
/*
* 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.
*/
REQUIRE(ISC_LIST_EMPTY(ent->sources));
mctx = ent->mctx;
isc_entropypool_invalidate(&ent->pool);
UNLOCK(&ent->lock);
DESTROYLOCK(&ent->lock);
memset(ent, 0, sizeof(isc_entropy_t));
isc_mem_put(mctx, ent, sizeof(isc_entropy_t));
isc_mem_detach(&mctx);
}
void
isc_entropy_destroysource(isc_entropysource_t **sourcep) {
isc_entropysource_t *source;
isc_entropy_t *ent;
isc_boolean_t killit;
REQUIRE(sourcep != NULL);
REQUIRE(VALID_SOURCE(*sourcep));
source = *sourcep;
*sourcep = NULL;
ent = source->ent;
REQUIRE(VALID_ENTROPY(ent));
LOCK(&ent->lock);
destroysource(&source);
killit = destroy_check(ent);
UNLOCK(&ent->lock);
if (killit)
destroy(&ent);
}
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)
{
isc_result_t ret;
isc_entropysource_t *source;
isc_cbsource_t *cbs;
REQUIRE(VALID_ENTROPY(ent));
REQUIRE(get != NULL);
REQUIRE(sourcep != NULL && *sourcep == NULL);
LOCK(&ent->lock);
source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
if (source == NULL) {
ret = ISC_R_NOMEMORY;
goto errout;
}
source->bad = ISC_FALSE;
cbs = &source->sources.callback;
ret = samplesource_allocate(ent, &cbs->samplequeue);
if (ret != ISC_R_SUCCESS)
goto errout;
cbs->start_called = ISC_FALSE;
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);
ent->nsources++;
*sourcep = source;
UNLOCK(&ent->lock);
return (ISC_R_SUCCESS);
errout:
if (source != NULL)
isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t));
UNLOCK(&ent->lock);
return (ret);
}
void
isc_entropy_stopcallbacksources(isc_entropy_t *ent) {
isc_entropysource_t *source;
isc_cbsource_t *cbs;
REQUIRE(VALID_ENTROPY(ent));
LOCK(&ent->lock);
source = ISC_LIST_HEAD(ent->sources);
while (source != NULL) {
if (source->type == ENTROPY_SOURCETYPE_CALLBACK) {
cbs = &source->sources.callback;
if (cbs->start_called && cbs->stopfunc != NULL) {
cbs->stopfunc(source, cbs->arg);
cbs->start_called = ISC_FALSE;
}
}
source = ISC_LIST_NEXT(source, link);
}
UNLOCK(&ent->lock);
}
isc_result_t
isc_entropy_createsamplesource(isc_entropy_t *ent,
isc_entropysource_t **sourcep)
{
isc_result_t ret;
isc_entropysource_t *source;
sample_queue_t *sq;
REQUIRE(VALID_ENTROPY(ent));
REQUIRE(sourcep != NULL && *sourcep == NULL);
LOCK(&ent->lock);
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);
ent->nsources++;
*sourcep = source;
UNLOCK(&ent->lock);
return (ISC_R_SUCCESS);
errout:
if (source != NULL)
isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t));
UNLOCK(&ent->lock);
return (ret);
}
/*
* 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)
return (ISC_R_QUEUEFULL);
return (ISC_R_SUCCESS);
}
isc_result_t
isc_entropy_addsample(isc_entropysource_t *source, isc_uint32_t sample,
isc_uint32_t extra)
{
isc_entropy_t *ent;
sample_queue_t *sq;
unsigned int entropy;
isc_result_t result;
REQUIRE(VALID_SOURCE(source));
ent = source->ent;
LOCK(&ent->lock);
sq = &source->sources.sample.samplequeue;
result = addsample(sq, sample, extra);
if (result == ISC_R_QUEUEFULL) {
entropy = crunchsamples(ent, sq);
add_entropy(ent, entropy);
}
UNLOCK(&ent->lock);
return (result);
}
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);
}
void
isc_entropy_putdata(isc_entropy_t *ent, void *data, unsigned int length,
isc_uint32_t entropy)
{
REQUIRE(VALID_ENTROPY(ent));
LOCK(&ent->lock);
entropypool_adddata(ent, data, length, entropy);
if (ent->initialized < THRESHOLD_BITS)
ent->initialized = THRESHOLD_BITS;
UNLOCK(&ent->lock);
}
static void
dumpstats(isc_entropy_t *ent, FILE *out) {
fprintf(out,
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"),
ent, ent->refcnt,
ent->pool.cursor, ent->pool.rotate,
ent->pool.entropy, ent->pool.pseudo,
ent->nsources, ent->nextsource, ent->initialized,
ent->initcount);
}
/*
* 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);
}
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);
}
static isc_result_t
kbdstart(isc_entropysource_t *source, void *arg, isc_boolean_t blocking) {
/*
* The intent of "first" is to provide a warning message only once
* during the run of a program that might try to gather keyboard
* entropy multiple times.
*/
static isc_boolean_t first = ISC_TRUE;
UNUSED(arg);
if (! blocking)
return (ISC_R_NOENTROPY);
if (first) {
if (source->warn_keyboard)
fprintf(stderr, "You must use the keyboard to create "
"entropy, since your system is lacking\n"
"/dev/random (or equivalent)\n\n");
first = ISC_FALSE;
}
fprintf(stderr, "start typing:\n");
return (isc_keyboard_open(&source->kbd));
}
static void
kbdstop(isc_entropysource_t *source, void *arg) {
UNUSED(arg);
if (! isc_keyboard_canceled(&source->kbd))
fprintf(stderr, "stop typing.\r\n");
(void)isc_keyboard_close(&source->kbd, 3);
}
static isc_result_t
kbdget(isc_entropysource_t *source, void *arg, isc_boolean_t blocking) {
isc_result_t result;
isc_time_t t;
isc_uint32_t sample;
isc_uint32_t extra;
unsigned char c;
UNUSED(arg);
if (!blocking)
return (ISC_R_NOTBLOCKING);
result = isc_keyboard_getchar(&source->kbd, &c);
if (result != ISC_R_SUCCESS)
return (result);
result = isc_time_now(&t);
if (result != ISC_R_SUCCESS)
return (result);
sample = isc_time_nanoseconds(&t);
extra = c;
result = isc_entropy_addcallbacksample(source, sample, extra);
if (result != ISC_R_SUCCESS) {
fprintf(stderr, "\r\n");
return (result);
}
fprintf(stderr, ".");
fflush(stderr);
return (result);
}
isc_result_t
isc_entropy_usebestsource(isc_entropy_t *ectx, isc_entropysource_t **source,
const char *randomfile, int use_keyboard)
{
isc_result_t result;
isc_result_t final_result = ISC_R_NOENTROPY;
REQUIRE(VALID_ENTROPY(ectx));
REQUIRE(source != NULL && *source == NULL);
REQUIRE(use_keyboard == ISC_ENTROPY_KEYBOARDYES ||
use_keyboard == ISC_ENTROPY_KEYBOARDNO ||
use_keyboard == ISC_ENTROPY_KEYBOARDMAYBE);
#ifdef PATH_RANDOMDEV
if (randomfile == NULL)
randomfile = PATH_RANDOMDEV;
#endif
if (randomfile != NULL) {
result = isc_entropy_createfilesource(ectx, randomfile);
if (result == ISC_R_SUCCESS &&
use_keyboard == ISC_ENTROPY_KEYBOARDMAYBE)
use_keyboard = ISC_ENTROPY_KEYBOARDNO;
final_result = result;
}
if (use_keyboard != ISC_ENTROPY_KEYBOARDNO) {
result = isc_entropy_createcallbacksource(ectx, kbdstart,
kbdget, kbdstop,
NULL, source);
if (result == ISC_R_SUCCESS)
(*source)->warn_keyboard =
ISC_TF(use_keyboard ==
ISC_ENTROPY_KEYBOARDMAYBE);
if (final_result != ISC_R_SUCCESS)
final_result = result;
}
/*
* final_result is ISC_R_SUCCESS if at least one source of entropy
* could be started, otherwise it is the error from the most recently
* failed operation (or ISC_R_NOENTROPY if PATH_RANDOMDEV is not
* defined and use_keyboard is ISC_ENTROPY_KEYBOARDNO).
*/
return (final_result);
}