entropy.c revision 193149
1135446Strhodes/* 2193149Sdougb * Copyright (C) 2004-2007, 2009 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 2000-2003 Internet Software Consortium. 4135446Strhodes * 5193149Sdougb * Permission to use, copy, modify, and/or distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18193149Sdougb/* $Id: entropy.c,v 1.18.332.2 2009/01/18 23:47:41 tbox Exp $ */ 19135446Strhodes 20170222Sdougb/*! \file 21170222Sdougb * \brief 22135446Strhodes * This is the system independent part of the entropy module. It is 23135446Strhodes * compiled via inclusion from the relevant OS source file, ie, 24170222Sdougb * \link unix/entropy.c unix/entropy.c \endlink or win32/entropy.c. 25170222Sdougb * 26170222Sdougb * \author Much of this code is modeled after the NetBSD /dev/random implementation, 27170222Sdougb * written by Michael Graff <explorer@netbsd.org>. 28135446Strhodes */ 29135446Strhodes 30135446Strhodes#include <errno.h> 31135446Strhodes#include <fcntl.h> 32135446Strhodes#include <stdio.h> 33135446Strhodes 34135446Strhodes#include <isc/buffer.h> 35135446Strhodes#include <isc/entropy.h> 36135446Strhodes#include <isc/keyboard.h> 37135446Strhodes#include <isc/list.h> 38135446Strhodes#include <isc/magic.h> 39135446Strhodes#include <isc/mem.h> 40135446Strhodes#include <isc/msgs.h> 41135446Strhodes#include <isc/mutex.h> 42135446Strhodes#include <isc/platform.h> 43135446Strhodes#include <isc/region.h> 44135446Strhodes#include <isc/sha1.h> 45135446Strhodes#include <isc/string.h> 46135446Strhodes#include <isc/time.h> 47135446Strhodes#include <isc/util.h> 48135446Strhodes 49135446Strhodes 50135446Strhodes#define ENTROPY_MAGIC ISC_MAGIC('E', 'n', 't', 'e') 51135446Strhodes#define SOURCE_MAGIC ISC_MAGIC('E', 'n', 't', 's') 52135446Strhodes 53135446Strhodes#define VALID_ENTROPY(e) ISC_MAGIC_VALID(e, ENTROPY_MAGIC) 54135446Strhodes#define VALID_SOURCE(s) ISC_MAGIC_VALID(s, SOURCE_MAGIC) 55135446Strhodes 56135446Strhodes/*** 57135446Strhodes *** "constants." Do not change these unless you _really_ know what 58135446Strhodes *** you are doing. 59135446Strhodes ***/ 60135446Strhodes 61170222Sdougb/*% 62170222Sdougb * Size of entropy pool in 32-bit words. This _MUST_ be a power of 2. 63135446Strhodes */ 64135446Strhodes#define RND_POOLWORDS 128 65170222Sdougb/*% Pool in bytes. */ 66135446Strhodes#define RND_POOLBYTES (RND_POOLWORDS * 4) 67170222Sdougb/*% Pool in bits. */ 68135446Strhodes#define RND_POOLBITS (RND_POOLWORDS * 32) 69135446Strhodes 70170222Sdougb/*% 71135446Strhodes * Number of bytes returned per hash. This must be true: 72135446Strhodes * threshold * 2 <= digest_size_in_bytes 73135446Strhodes */ 74135446Strhodes#define RND_ENTROPY_THRESHOLD 10 75135446Strhodes#define THRESHOLD_BITS (RND_ENTROPY_THRESHOLD * 8) 76135446Strhodes 77170222Sdougb/*% 78135446Strhodes * Size of the input event queue in samples. 79135446Strhodes */ 80135446Strhodes#define RND_EVENTQSIZE 32 81135446Strhodes 82170222Sdougb/*% 83135446Strhodes * The number of times we'll "reseed" for pseudorandom seeds. This is an 84135446Strhodes * extremely weak pseudorandom seed. If the caller is using lots of 85135446Strhodes * pseudorandom data and they cannot provide a stronger random source, 86135446Strhodes * there is little we can do other than hope they're smart enough to 87135446Strhodes * call _adddata() with something better than we can come up with. 88135446Strhodes */ 89135446Strhodes#define RND_INITIALIZE 128 90135446Strhodes 91170222Sdougb/*% Entropy Pool */ 92135446Strhodestypedef struct { 93170222Sdougb isc_uint32_t cursor; /*%< current add point in the pool */ 94170222Sdougb isc_uint32_t entropy; /*%< current entropy estimate in bits */ 95170222Sdougb isc_uint32_t pseudo; /*%< bits extracted in pseudorandom */ 96170222Sdougb isc_uint32_t rotate; /*%< how many bits to rotate by */ 97170222Sdougb isc_uint32_t pool[RND_POOLWORDS]; /*%< random pool data */ 98135446Strhodes} isc_entropypool_t; 99135446Strhodes 100135446Strhodesstruct isc_entropy { 101135446Strhodes unsigned int magic; 102135446Strhodes isc_mem_t *mctx; 103135446Strhodes isc_mutex_t lock; 104135446Strhodes unsigned int refcnt; 105135446Strhodes isc_uint32_t initialized; 106135446Strhodes isc_uint32_t initcount; 107135446Strhodes isc_entropypool_t pool; 108135446Strhodes unsigned int nsources; 109135446Strhodes isc_entropysource_t *nextsource; 110135446Strhodes ISC_LIST(isc_entropysource_t) sources; 111135446Strhodes}; 112135446Strhodes 113170222Sdougb/*% Sample Queue */ 114135446Strhodestypedef struct { 115170222Sdougb isc_uint32_t last_time; /*%< last time recorded */ 116170222Sdougb isc_uint32_t last_delta; /*%< last delta value */ 117170222Sdougb isc_uint32_t last_delta2; /*%< last delta2 value */ 118170222Sdougb isc_uint32_t nsamples; /*%< number of samples filled in */ 119170222Sdougb isc_uint32_t *samples; /*%< the samples */ 120170222Sdougb isc_uint32_t *extra; /*%< extra samples added in */ 121135446Strhodes} sample_queue_t; 122135446Strhodes 123135446Strhodestypedef struct { 124135446Strhodes sample_queue_t samplequeue; 125135446Strhodes} isc_entropysamplesource_t; 126135446Strhodes 127135446Strhodestypedef struct { 128135446Strhodes isc_boolean_t start_called; 129135446Strhodes isc_entropystart_t startfunc; 130135446Strhodes isc_entropyget_t getfunc; 131135446Strhodes isc_entropystop_t stopfunc; 132135446Strhodes void *arg; 133135446Strhodes sample_queue_t samplequeue; 134135446Strhodes} isc_cbsource_t; 135135446Strhodes 136135446Strhodestypedef struct { 137135446Strhodes FILESOURCE_HANDLE_TYPE handle; 138135446Strhodes} isc_entropyfilesource_t; 139135446Strhodes 140135446Strhodesstruct isc_entropysource { 141135446Strhodes unsigned int magic; 142135446Strhodes unsigned int type; 143135446Strhodes isc_entropy_t *ent; 144170222Sdougb isc_uint32_t total; /*%< entropy from this source */ 145135446Strhodes ISC_LINK(isc_entropysource_t) link; 146135446Strhodes char name[32]; 147135446Strhodes isc_boolean_t bad; 148135446Strhodes isc_boolean_t warn_keyboard; 149135446Strhodes isc_keyboard_t kbd; 150135446Strhodes union { 151135446Strhodes isc_entropysamplesource_t sample; 152135446Strhodes isc_entropyfilesource_t file; 153135446Strhodes isc_cbsource_t callback; 154135446Strhodes isc_entropyusocketsource_t usocket; 155135446Strhodes } sources; 156135446Strhodes}; 157135446Strhodes 158170222Sdougb#define ENTROPY_SOURCETYPE_SAMPLE 1 /*%< Type is a sample source */ 159170222Sdougb#define ENTROPY_SOURCETYPE_FILE 2 /*%< Type is a file source */ 160170222Sdougb#define ENTROPY_SOURCETYPE_CALLBACK 3 /*%< Type is a callback source */ 161170222Sdougb#define ENTROPY_SOURCETYPE_USOCKET 4 /*%< Type is a Unix socket source */ 162135446Strhodes 163170222Sdougb/*@{*/ 164170222Sdougb/*% 165135446Strhodes * The random pool "taps" 166135446Strhodes */ 167135446Strhodes#define TAP1 99 168135446Strhodes#define TAP2 59 169135446Strhodes#define TAP3 31 170135446Strhodes#define TAP4 9 171135446Strhodes#define TAP5 7 172170222Sdougb/*@}*/ 173135446Strhodes 174170222Sdougb/*@{*/ 175170222Sdougb/*% 176135446Strhodes * Declarations for function provided by the system dependent sources that 177135446Strhodes * include this file. 178135446Strhodes */ 179135446Strhodesstatic void 180135446Strhodesfillpool(isc_entropy_t *, unsigned int, isc_boolean_t); 181135446Strhodes 182135446Strhodesstatic int 183135446Strhodeswait_for_sources(isc_entropy_t *); 184135446Strhodes 185135446Strhodesstatic void 186135446Strhodesdestroyfilesource(isc_entropyfilesource_t *source); 187135446Strhodes 188135446Strhodesstatic void 189135446Strhodesdestroyusocketsource(isc_entropyusocketsource_t *source); 190135446Strhodes 191170222Sdougb/*@}*/ 192135446Strhodes 193135446Strhodesstatic void 194135446Strhodessamplequeue_release(isc_entropy_t *ent, sample_queue_t *sq) { 195135446Strhodes REQUIRE(sq->samples != NULL); 196135446Strhodes REQUIRE(sq->extra != NULL); 197135446Strhodes 198135446Strhodes isc_mem_put(ent->mctx, sq->samples, RND_EVENTQSIZE * 4); 199135446Strhodes isc_mem_put(ent->mctx, sq->extra, RND_EVENTQSIZE * 4); 200135446Strhodes sq->samples = NULL; 201135446Strhodes sq->extra = NULL; 202135446Strhodes} 203135446Strhodes 204135446Strhodesstatic isc_result_t 205135446Strhodessamplesource_allocate(isc_entropy_t *ent, sample_queue_t *sq) { 206135446Strhodes sq->samples = isc_mem_get(ent->mctx, RND_EVENTQSIZE * 4); 207135446Strhodes if (sq->samples == NULL) 208135446Strhodes return (ISC_R_NOMEMORY); 209135446Strhodes 210135446Strhodes sq->extra = isc_mem_get(ent->mctx, RND_EVENTQSIZE * 4); 211135446Strhodes if (sq->extra == NULL) { 212135446Strhodes isc_mem_put(ent->mctx, sq->samples, RND_EVENTQSIZE * 4); 213135446Strhodes sq->samples = NULL; 214135446Strhodes return (ISC_R_NOMEMORY); 215135446Strhodes } 216135446Strhodes 217135446Strhodes sq->nsamples = 0; 218135446Strhodes 219135446Strhodes return (ISC_R_SUCCESS); 220135446Strhodes} 221135446Strhodes 222170222Sdougb/*% 223135446Strhodes * Add in entropy, even when the value we're adding in could be 224135446Strhodes * very large. 225135446Strhodes */ 226135446Strhodesstatic inline void 227135446Strhodesadd_entropy(isc_entropy_t *ent, isc_uint32_t entropy) { 228135446Strhodes /* clamp input. Yes, this must be done. */ 229135446Strhodes entropy = ISC_MIN(entropy, RND_POOLBITS); 230135446Strhodes /* Add in the entropy we already have. */ 231135446Strhodes entropy += ent->pool.entropy; 232135446Strhodes /* Clamp. */ 233135446Strhodes ent->pool.entropy = ISC_MIN(entropy, RND_POOLBITS); 234135446Strhodes} 235135446Strhodes 236170222Sdougb/*% 237135446Strhodes * Decrement the amount of entropy the pool has. 238135446Strhodes */ 239135446Strhodesstatic inline void 240135446Strhodessubtract_entropy(isc_entropy_t *ent, isc_uint32_t entropy) { 241135446Strhodes entropy = ISC_MIN(entropy, ent->pool.entropy); 242135446Strhodes ent->pool.entropy -= entropy; 243135446Strhodes} 244135446Strhodes 245170222Sdougb/*! 246135446Strhodes * Add in entropy, even when the value we're adding in could be 247135446Strhodes * very large. 248135446Strhodes */ 249135446Strhodesstatic inline void 250135446Strhodesadd_pseudo(isc_entropy_t *ent, isc_uint32_t pseudo) { 251135446Strhodes /* clamp input. Yes, this must be done. */ 252135446Strhodes pseudo = ISC_MIN(pseudo, RND_POOLBITS * 8); 253135446Strhodes /* Add in the pseudo we already have. */ 254135446Strhodes pseudo += ent->pool.pseudo; 255135446Strhodes /* Clamp. */ 256135446Strhodes ent->pool.pseudo = ISC_MIN(pseudo, RND_POOLBITS * 8); 257135446Strhodes} 258135446Strhodes 259170222Sdougb/*! 260135446Strhodes * Decrement the amount of pseudo the pool has. 261135446Strhodes */ 262135446Strhodesstatic inline void 263135446Strhodessubtract_pseudo(isc_entropy_t *ent, isc_uint32_t pseudo) { 264135446Strhodes pseudo = ISC_MIN(pseudo, ent->pool.pseudo); 265135446Strhodes ent->pool.pseudo -= pseudo; 266135446Strhodes} 267135446Strhodes 268170222Sdougb/*! 269135446Strhodes * Add one word to the pool, rotating the input as needed. 270135446Strhodes */ 271135446Strhodesstatic inline void 272135446Strhodesentropypool_add_word(isc_entropypool_t *rp, isc_uint32_t val) { 273135446Strhodes /* 274135446Strhodes * Steal some values out of the pool, and xor them into the 275135446Strhodes * word we were given. 276135446Strhodes * 277135446Strhodes * Mix the new value into the pool using xor. This will 278135446Strhodes * prevent the actual values from being known to the caller 279135446Strhodes * since the previous values are assumed to be unknown as well. 280135446Strhodes */ 281135446Strhodes val ^= rp->pool[(rp->cursor + TAP1) & (RND_POOLWORDS - 1)]; 282135446Strhodes val ^= rp->pool[(rp->cursor + TAP2) & (RND_POOLWORDS - 1)]; 283135446Strhodes val ^= rp->pool[(rp->cursor + TAP3) & (RND_POOLWORDS - 1)]; 284135446Strhodes val ^= rp->pool[(rp->cursor + TAP4) & (RND_POOLWORDS - 1)]; 285135446Strhodes val ^= rp->pool[(rp->cursor + TAP5) & (RND_POOLWORDS - 1)]; 286135446Strhodes rp->pool[rp->cursor++] ^= 287135446Strhodes ((val << rp->rotate) | (val >> (32 - rp->rotate))); 288135446Strhodes 289135446Strhodes /* 290135446Strhodes * If we have looped around the pool, increment the rotate 291135446Strhodes * variable so the next value will get xored in rotated to 292135446Strhodes * a different position. 293193149Sdougb * Increment by a value that is relatively prime to the word size 294135446Strhodes * to try to spread the bits throughout the pool quickly when the 295135446Strhodes * pool is empty. 296135446Strhodes */ 297135446Strhodes if (rp->cursor == RND_POOLWORDS) { 298135446Strhodes rp->cursor = 0; 299135446Strhodes rp->rotate = (rp->rotate + 7) & 31; 300135446Strhodes } 301135446Strhodes} 302135446Strhodes 303170222Sdougb/*! 304135446Strhodes * Add a buffer's worth of data to the pool. 305135446Strhodes * 306135446Strhodes * Requires that the lock is held on the entropy pool. 307135446Strhodes */ 308135446Strhodesstatic void 309135446Strhodesentropypool_adddata(isc_entropy_t *ent, void *p, unsigned int len, 310135446Strhodes isc_uint32_t entropy) 311135446Strhodes{ 312135446Strhodes isc_uint32_t val; 313135446Strhodes unsigned long addr; 314135446Strhodes isc_uint8_t *buf; 315135446Strhodes 316135446Strhodes addr = (unsigned long)p; 317135446Strhodes buf = p; 318135446Strhodes 319135446Strhodes if ((addr & 0x03U) != 0U) { 320135446Strhodes val = 0; 321135446Strhodes switch (len) { 322135446Strhodes case 3: 323135446Strhodes val = *buf++; 324135446Strhodes len--; 325135446Strhodes case 2: 326135446Strhodes val = val << 8 | *buf++; 327135446Strhodes len--; 328135446Strhodes case 1: 329135446Strhodes val = val << 8 | *buf++; 330135446Strhodes len--; 331135446Strhodes } 332135446Strhodes 333135446Strhodes entropypool_add_word(&ent->pool, val); 334135446Strhodes } 335135446Strhodes 336135446Strhodes for (; len > 3; len -= 4) { 337135446Strhodes val = *((isc_uint32_t *)buf); 338135446Strhodes 339135446Strhodes entropypool_add_word(&ent->pool, val); 340135446Strhodes buf += 4; 341135446Strhodes } 342135446Strhodes 343135446Strhodes if (len != 0) { 344135446Strhodes val = 0; 345135446Strhodes switch (len) { 346135446Strhodes case 3: 347135446Strhodes val = *buf++; 348135446Strhodes case 2: 349135446Strhodes val = val << 8 | *buf++; 350135446Strhodes case 1: 351135446Strhodes val = val << 8 | *buf++; 352135446Strhodes } 353135446Strhodes 354135446Strhodes entropypool_add_word(&ent->pool, val); 355135446Strhodes } 356135446Strhodes 357135446Strhodes add_entropy(ent, entropy); 358135446Strhodes subtract_pseudo(ent, entropy); 359135446Strhodes} 360135446Strhodes 361135446Strhodesstatic inline void 362135446Strhodesreseed(isc_entropy_t *ent) { 363135446Strhodes isc_time_t t; 364135446Strhodes pid_t pid; 365135446Strhodes 366135446Strhodes if (ent->initcount == 0) { 367135446Strhodes pid = getpid(); 368135446Strhodes entropypool_adddata(ent, &pid, sizeof(pid), 0); 369135446Strhodes pid = getppid(); 370135446Strhodes entropypool_adddata(ent, &pid, sizeof(pid), 0); 371135446Strhodes } 372135446Strhodes 373170222Sdougb /*! 374135446Strhodes * After we've reseeded 100 times, only add new timing info every 375135446Strhodes * 50 requests. This will keep us from using lots and lots of 376135446Strhodes * CPU just to return bad pseudorandom data anyway. 377135446Strhodes */ 378135446Strhodes if (ent->initcount > 100) 379135446Strhodes if ((ent->initcount % 50) != 0) 380135446Strhodes return; 381135446Strhodes 382135446Strhodes TIME_NOW(&t); 383135446Strhodes entropypool_adddata(ent, &t, sizeof(t), 0); 384135446Strhodes ent->initcount++; 385135446Strhodes} 386135446Strhodes 387135446Strhodesstatic inline unsigned int 388135446Strhodesestimate_entropy(sample_queue_t *sq, isc_uint32_t t) { 389135446Strhodes isc_int32_t delta; 390135446Strhodes isc_int32_t delta2; 391135446Strhodes isc_int32_t delta3; 392135446Strhodes 393170222Sdougb /*! 394135446Strhodes * If the time counter has overflowed, calculate the real difference. 395135446Strhodes * If it has not, it is simpler. 396135446Strhodes */ 397135446Strhodes if (t < sq->last_time) 398135446Strhodes delta = UINT_MAX - sq->last_time + t; 399135446Strhodes else 400135446Strhodes delta = sq->last_time - t; 401135446Strhodes 402135446Strhodes if (delta < 0) 403135446Strhodes delta = -delta; 404135446Strhodes 405135446Strhodes /* 406135446Strhodes * Calculate the second and third order differentials 407135446Strhodes */ 408135446Strhodes delta2 = sq->last_delta - delta; 409135446Strhodes if (delta2 < 0) 410135446Strhodes delta2 = -delta2; 411135446Strhodes 412135446Strhodes delta3 = sq->last_delta2 - delta2; 413135446Strhodes if (delta3 < 0) 414135446Strhodes delta3 = -delta3; 415135446Strhodes 416135446Strhodes sq->last_time = t; 417135446Strhodes sq->last_delta = delta; 418135446Strhodes sq->last_delta2 = delta2; 419135446Strhodes 420135446Strhodes /* 421135446Strhodes * If any delta is 0, we got no entropy. If all are non-zero, we 422135446Strhodes * might have something. 423135446Strhodes */ 424135446Strhodes if (delta == 0 || delta2 == 0 || delta3 == 0) 425135446Strhodes return 0; 426135446Strhodes 427135446Strhodes /* 428135446Strhodes * We could find the smallest delta and claim we got log2(delta) 429135446Strhodes * bits, but for now return that we found 1 bit. 430135446Strhodes */ 431135446Strhodes return 1; 432135446Strhodes} 433135446Strhodes 434135446Strhodesstatic unsigned int 435135446Strhodescrunchsamples(isc_entropy_t *ent, sample_queue_t *sq) { 436135446Strhodes unsigned int ns; 437135446Strhodes unsigned int added; 438135446Strhodes 439135446Strhodes if (sq->nsamples < 6) 440135446Strhodes return (0); 441135446Strhodes 442135446Strhodes added = 0; 443135446Strhodes sq->last_time = sq->samples[0]; 444135446Strhodes sq->last_delta = 0; 445135446Strhodes sq->last_delta2 = 0; 446135446Strhodes 447135446Strhodes /* 448135446Strhodes * Prime the values by adding in the first 4 samples in. This 449135446Strhodes * should completely initialize the delta calculations. 450135446Strhodes */ 451135446Strhodes for (ns = 0; ns < 4; ns++) 452135446Strhodes (void)estimate_entropy(sq, sq->samples[ns]); 453135446Strhodes 454135446Strhodes for (ns = 4; ns < sq->nsamples; ns++) 455135446Strhodes added += estimate_entropy(sq, sq->samples[ns]); 456135446Strhodes 457135446Strhodes entropypool_adddata(ent, sq->samples, sq->nsamples * 4, added); 458135446Strhodes entropypool_adddata(ent, sq->extra, sq->nsamples * 4, 0); 459135446Strhodes 460135446Strhodes /* 461135446Strhodes * Move the last 4 samples into the first 4 positions, and start 462135446Strhodes * adding new samples from that point. 463135446Strhodes */ 464135446Strhodes for (ns = 0; ns < 4; ns++) { 465135446Strhodes sq->samples[ns] = sq->samples[sq->nsamples - 4 + ns]; 466135446Strhodes sq->extra[ns] = sq->extra[sq->nsamples - 4 + ns]; 467135446Strhodes } 468135446Strhodes 469135446Strhodes sq->nsamples = 4; 470135446Strhodes 471135446Strhodes return (added); 472135446Strhodes} 473135446Strhodes 474135446Strhodesstatic unsigned int 475135446Strhodesget_from_callback(isc_entropysource_t *source, unsigned int desired, 476135446Strhodes isc_boolean_t blocking) 477135446Strhodes{ 478135446Strhodes isc_entropy_t *ent = source->ent; 479135446Strhodes isc_cbsource_t *cbs = &source->sources.callback; 480135446Strhodes unsigned int added; 481135446Strhodes unsigned int got; 482135446Strhodes isc_result_t result; 483135446Strhodes 484135446Strhodes if (desired == 0) 485135446Strhodes return (0); 486135446Strhodes 487135446Strhodes if (source->bad) 488135446Strhodes return (0); 489135446Strhodes 490135446Strhodes if (!cbs->start_called && cbs->startfunc != NULL) { 491135446Strhodes result = cbs->startfunc(source, cbs->arg, blocking); 492135446Strhodes if (result != ISC_R_SUCCESS) 493135446Strhodes return (0); 494135446Strhodes cbs->start_called = ISC_TRUE; 495135446Strhodes } 496135446Strhodes 497135446Strhodes added = 0; 498135446Strhodes result = ISC_R_SUCCESS; 499135446Strhodes while (desired > 0 && result == ISC_R_SUCCESS) { 500135446Strhodes result = cbs->getfunc(source, cbs->arg, blocking); 501135446Strhodes if (result == ISC_R_QUEUEFULL) { 502135446Strhodes got = crunchsamples(ent, &cbs->samplequeue); 503135446Strhodes added += got; 504135446Strhodes desired -= ISC_MIN(got, desired); 505135446Strhodes result = ISC_R_SUCCESS; 506135446Strhodes } else if (result != ISC_R_SUCCESS && 507135446Strhodes result != ISC_R_NOTBLOCKING) 508135446Strhodes source->bad = ISC_TRUE; 509135446Strhodes 510135446Strhodes } 511135446Strhodes 512135446Strhodes return (added); 513135446Strhodes} 514135446Strhodes 515135446Strhodes/* 516135446Strhodes * Extract some number of bytes from the random pool, decreasing the 517135446Strhodes * estimate of randomness as each byte is extracted. 518135446Strhodes * 519135446Strhodes * Do this by stiring the pool and returning a part of hash as randomness. 520135446Strhodes * Note that no secrets are given away here since parts of the hash are 521135446Strhodes * xored together before returned. 522135446Strhodes * 523135446Strhodes * Honor the request from the caller to only return good data, any data, 524135446Strhodes * etc. 525135446Strhodes */ 526135446Strhodesisc_result_t 527135446Strhodesisc_entropy_getdata(isc_entropy_t *ent, void *data, unsigned int length, 528135446Strhodes unsigned int *returned, unsigned int flags) 529135446Strhodes{ 530135446Strhodes unsigned int i; 531135446Strhodes isc_sha1_t hash; 532135446Strhodes unsigned char digest[ISC_SHA1_DIGESTLENGTH]; 533135446Strhodes isc_uint32_t remain, deltae, count, total; 534135446Strhodes isc_uint8_t *buf; 535135446Strhodes isc_boolean_t goodonly, partial, blocking; 536135446Strhodes 537135446Strhodes REQUIRE(VALID_ENTROPY(ent)); 538135446Strhodes REQUIRE(data != NULL); 539135446Strhodes REQUIRE(length > 0); 540135446Strhodes 541135446Strhodes goodonly = ISC_TF((flags & ISC_ENTROPY_GOODONLY) != 0); 542135446Strhodes partial = ISC_TF((flags & ISC_ENTROPY_PARTIAL) != 0); 543135446Strhodes blocking = ISC_TF((flags & ISC_ENTROPY_BLOCKING) != 0); 544135446Strhodes 545135446Strhodes REQUIRE(!partial || returned != NULL); 546135446Strhodes 547135446Strhodes LOCK(&ent->lock); 548135446Strhodes 549135446Strhodes remain = length; 550135446Strhodes buf = data; 551135446Strhodes total = 0; 552135446Strhodes while (remain != 0) { 553135446Strhodes count = ISC_MIN(remain, RND_ENTROPY_THRESHOLD); 554135446Strhodes 555135446Strhodes /* 556135446Strhodes * If we are extracting good data only, make certain we 557135446Strhodes * have enough data in our pool for this pass. If we don't, 558135446Strhodes * get some, and fail if we can't, and partial returns 559135446Strhodes * are not ok. 560135446Strhodes */ 561135446Strhodes if (goodonly) { 562135446Strhodes unsigned int fillcount; 563135446Strhodes 564135446Strhodes fillcount = ISC_MAX(remain * 8, count * 8); 565135446Strhodes 566135446Strhodes /* 567135446Strhodes * If, however, we have at least THRESHOLD_BITS 568135446Strhodes * of entropy in the pool, don't block here. It is 569135446Strhodes * better to drain the pool once in a while and 570135446Strhodes * then refill it than it is to constantly keep the 571135446Strhodes * pool full. 572135446Strhodes */ 573135446Strhodes if (ent->pool.entropy >= THRESHOLD_BITS) 574135446Strhodes fillpool(ent, fillcount, ISC_FALSE); 575135446Strhodes else 576135446Strhodes fillpool(ent, fillcount, blocking); 577135446Strhodes 578135446Strhodes /* 579135446Strhodes * Verify that we got enough entropy to do one 580135446Strhodes * extraction. If we didn't, bail. 581135446Strhodes */ 582135446Strhodes if (ent->pool.entropy < THRESHOLD_BITS) { 583135446Strhodes if (!partial) 584135446Strhodes goto zeroize; 585135446Strhodes else 586135446Strhodes goto partial_output; 587135446Strhodes } 588135446Strhodes } else { 589135446Strhodes /* 590135446Strhodes * If we've extracted half our pool size in bits 591135446Strhodes * since the last refresh, try to refresh here. 592135446Strhodes */ 593135446Strhodes if (ent->initialized < THRESHOLD_BITS) 594135446Strhodes fillpool(ent, THRESHOLD_BITS, blocking); 595135446Strhodes else 596135446Strhodes fillpool(ent, 0, ISC_FALSE); 597135446Strhodes 598135446Strhodes /* 599135446Strhodes * If we've not initialized with enough good random 600135446Strhodes * data, seed with our crappy code. 601135446Strhodes */ 602135446Strhodes if (ent->initialized < THRESHOLD_BITS) 603135446Strhodes reseed(ent); 604135446Strhodes } 605135446Strhodes 606135446Strhodes isc_sha1_init(&hash); 607135446Strhodes isc_sha1_update(&hash, (void *)(ent->pool.pool), 608135446Strhodes RND_POOLBYTES); 609135446Strhodes isc_sha1_final(&hash, digest); 610135446Strhodes 611135446Strhodes /* 612135446Strhodes * Stir the extracted data (all of it) back into the pool. 613135446Strhodes */ 614135446Strhodes entropypool_adddata(ent, digest, ISC_SHA1_DIGESTLENGTH, 0); 615135446Strhodes 616135446Strhodes for (i = 0; i < count; i++) 617135446Strhodes buf[i] = digest[i] ^ digest[i + RND_ENTROPY_THRESHOLD]; 618135446Strhodes 619135446Strhodes buf += count; 620135446Strhodes remain -= count; 621135446Strhodes 622135446Strhodes deltae = count * 8; 623135446Strhodes deltae = ISC_MIN(deltae, ent->pool.entropy); 624135446Strhodes total += deltae; 625135446Strhodes subtract_entropy(ent, deltae); 626135446Strhodes add_pseudo(ent, count * 8); 627135446Strhodes } 628135446Strhodes 629135446Strhodes partial_output: 630135446Strhodes memset(digest, 0, sizeof(digest)); 631135446Strhodes 632135446Strhodes if (returned != NULL) 633135446Strhodes *returned = (length - remain); 634135446Strhodes 635135446Strhodes UNLOCK(&ent->lock); 636135446Strhodes 637135446Strhodes return (ISC_R_SUCCESS); 638135446Strhodes 639135446Strhodes zeroize: 640135446Strhodes /* put the entropy we almost extracted back */ 641135446Strhodes add_entropy(ent, total); 642135446Strhodes memset(data, 0, length); 643135446Strhodes memset(digest, 0, sizeof(digest)); 644135446Strhodes if (returned != NULL) 645135446Strhodes *returned = 0; 646135446Strhodes 647135446Strhodes UNLOCK(&ent->lock); 648135446Strhodes 649135446Strhodes return (ISC_R_NOENTROPY); 650135446Strhodes} 651135446Strhodes 652135446Strhodesstatic void 653135446Strhodesisc_entropypool_init(isc_entropypool_t *pool) { 654135446Strhodes pool->cursor = RND_POOLWORDS - 1; 655135446Strhodes pool->entropy = 0; 656135446Strhodes pool->pseudo = 0; 657135446Strhodes pool->rotate = 0; 658135446Strhodes memset(pool->pool, 0, RND_POOLBYTES); 659135446Strhodes} 660135446Strhodes 661135446Strhodesstatic void 662135446Strhodesisc_entropypool_invalidate(isc_entropypool_t *pool) { 663135446Strhodes pool->cursor = 0; 664135446Strhodes pool->entropy = 0; 665135446Strhodes pool->pseudo = 0; 666135446Strhodes pool->rotate = 0; 667135446Strhodes memset(pool->pool, 0, RND_POOLBYTES); 668135446Strhodes} 669135446Strhodes 670135446Strhodesisc_result_t 671135446Strhodesisc_entropy_create(isc_mem_t *mctx, isc_entropy_t **entp) { 672170222Sdougb isc_result_t result; 673135446Strhodes isc_entropy_t *ent; 674135446Strhodes 675135446Strhodes REQUIRE(mctx != NULL); 676135446Strhodes REQUIRE(entp != NULL && *entp == NULL); 677135446Strhodes 678135446Strhodes ent = isc_mem_get(mctx, sizeof(isc_entropy_t)); 679135446Strhodes if (ent == NULL) 680135446Strhodes return (ISC_R_NOMEMORY); 681135446Strhodes 682135446Strhodes /* 683135446Strhodes * We need a lock. 684135446Strhodes */ 685170222Sdougb result = isc_mutex_init(&ent->lock); 686170222Sdougb if (result != ISC_R_SUCCESS) 687135446Strhodes goto errout; 688135446Strhodes 689135446Strhodes /* 690135446Strhodes * From here down, no failures will/can occur. 691135446Strhodes */ 692135446Strhodes ISC_LIST_INIT(ent->sources); 693135446Strhodes ent->nextsource = NULL; 694135446Strhodes ent->nsources = 0; 695135446Strhodes ent->mctx = NULL; 696135446Strhodes isc_mem_attach(mctx, &ent->mctx); 697135446Strhodes ent->refcnt = 1; 698135446Strhodes ent->initialized = 0; 699135446Strhodes ent->initcount = 0; 700135446Strhodes ent->magic = ENTROPY_MAGIC; 701135446Strhodes 702135446Strhodes isc_entropypool_init(&ent->pool); 703135446Strhodes 704135446Strhodes *entp = ent; 705135446Strhodes return (ISC_R_SUCCESS); 706135446Strhodes 707135446Strhodes errout: 708135446Strhodes isc_mem_put(mctx, ent, sizeof(isc_entropy_t)); 709135446Strhodes 710170222Sdougb return (result); 711135446Strhodes} 712135446Strhodes 713170222Sdougb/*! 714135446Strhodes * Requires "ent" be locked. 715135446Strhodes */ 716135446Strhodesstatic void 717135446Strhodesdestroysource(isc_entropysource_t **sourcep) { 718135446Strhodes isc_entropysource_t *source; 719135446Strhodes isc_entropy_t *ent; 720135446Strhodes isc_cbsource_t *cbs; 721135446Strhodes 722135446Strhodes source = *sourcep; 723135446Strhodes *sourcep = NULL; 724135446Strhodes ent = source->ent; 725135446Strhodes 726135446Strhodes ISC_LIST_UNLINK(ent->sources, source, link); 727135446Strhodes ent->nextsource = NULL; 728135446Strhodes REQUIRE(ent->nsources > 0); 729135446Strhodes ent->nsources--; 730135446Strhodes 731135446Strhodes switch (source->type) { 732135446Strhodes case ENTROPY_SOURCETYPE_FILE: 733135446Strhodes if (! source->bad) 734135446Strhodes destroyfilesource(&source->sources.file); 735135446Strhodes break; 736135446Strhodes case ENTROPY_SOURCETYPE_USOCKET: 737135446Strhodes if (! source->bad) 738135446Strhodes destroyusocketsource(&source->sources.usocket); 739135446Strhodes break; 740135446Strhodes case ENTROPY_SOURCETYPE_SAMPLE: 741135446Strhodes samplequeue_release(ent, &source->sources.sample.samplequeue); 742135446Strhodes break; 743135446Strhodes case ENTROPY_SOURCETYPE_CALLBACK: 744135446Strhodes cbs = &source->sources.callback; 745135446Strhodes if (cbs->start_called && cbs->stopfunc != NULL) { 746135446Strhodes cbs->stopfunc(source, cbs->arg); 747135446Strhodes cbs->start_called = ISC_FALSE; 748135446Strhodes } 749135446Strhodes samplequeue_release(ent, &cbs->samplequeue); 750135446Strhodes break; 751135446Strhodes } 752135446Strhodes 753135446Strhodes memset(source, 0, sizeof(isc_entropysource_t)); 754135446Strhodes 755135446Strhodes isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t)); 756135446Strhodes} 757135446Strhodes 758135446Strhodesstatic inline isc_boolean_t 759135446Strhodesdestroy_check(isc_entropy_t *ent) { 760135446Strhodes isc_entropysource_t *source; 761135446Strhodes 762135446Strhodes if (ent->refcnt > 0) 763135446Strhodes return (ISC_FALSE); 764135446Strhodes 765135446Strhodes source = ISC_LIST_HEAD(ent->sources); 766135446Strhodes while (source != NULL) { 767135446Strhodes switch (source->type) { 768135446Strhodes case ENTROPY_SOURCETYPE_FILE: 769135446Strhodes case ENTROPY_SOURCETYPE_USOCKET: 770135446Strhodes break; 771135446Strhodes default: 772135446Strhodes return (ISC_FALSE); 773135446Strhodes } 774135446Strhodes source = ISC_LIST_NEXT(source, link); 775135446Strhodes } 776135446Strhodes 777135446Strhodes return (ISC_TRUE); 778135446Strhodes} 779135446Strhodes 780135446Strhodesstatic void 781135446Strhodesdestroy(isc_entropy_t **entp) { 782135446Strhodes isc_entropy_t *ent; 783135446Strhodes isc_entropysource_t *source; 784135446Strhodes isc_mem_t *mctx; 785135446Strhodes 786135446Strhodes REQUIRE(entp != NULL && *entp != NULL); 787135446Strhodes ent = *entp; 788135446Strhodes *entp = NULL; 789135446Strhodes 790135446Strhodes LOCK(&ent->lock); 791135446Strhodes 792135446Strhodes REQUIRE(ent->refcnt == 0); 793135446Strhodes 794135446Strhodes /* 795135446Strhodes * Here, detach non-sample sources. 796135446Strhodes */ 797135446Strhodes source = ISC_LIST_HEAD(ent->sources); 798135446Strhodes while (source != NULL) { 799135446Strhodes switch(source->type) { 800135446Strhodes case ENTROPY_SOURCETYPE_FILE: 801135446Strhodes case ENTROPY_SOURCETYPE_USOCKET: 802135446Strhodes destroysource(&source); 803135446Strhodes break; 804135446Strhodes } 805135446Strhodes source = ISC_LIST_HEAD(ent->sources); 806135446Strhodes } 807135446Strhodes 808135446Strhodes /* 809135446Strhodes * If there are other types of sources, we've found a bug. 810135446Strhodes */ 811135446Strhodes REQUIRE(ISC_LIST_EMPTY(ent->sources)); 812135446Strhodes 813135446Strhodes mctx = ent->mctx; 814135446Strhodes 815135446Strhodes isc_entropypool_invalidate(&ent->pool); 816135446Strhodes 817135446Strhodes UNLOCK(&ent->lock); 818135446Strhodes 819135446Strhodes DESTROYLOCK(&ent->lock); 820135446Strhodes 821135446Strhodes memset(ent, 0, sizeof(isc_entropy_t)); 822135446Strhodes isc_mem_put(mctx, ent, sizeof(isc_entropy_t)); 823135446Strhodes isc_mem_detach(&mctx); 824135446Strhodes} 825135446Strhodes 826135446Strhodesvoid 827135446Strhodesisc_entropy_destroysource(isc_entropysource_t **sourcep) { 828135446Strhodes isc_entropysource_t *source; 829135446Strhodes isc_entropy_t *ent; 830135446Strhodes isc_boolean_t killit; 831135446Strhodes 832135446Strhodes REQUIRE(sourcep != NULL); 833135446Strhodes REQUIRE(VALID_SOURCE(*sourcep)); 834135446Strhodes 835135446Strhodes source = *sourcep; 836135446Strhodes *sourcep = NULL; 837135446Strhodes 838135446Strhodes ent = source->ent; 839135446Strhodes REQUIRE(VALID_ENTROPY(ent)); 840135446Strhodes 841135446Strhodes LOCK(&ent->lock); 842135446Strhodes 843135446Strhodes destroysource(&source); 844135446Strhodes 845135446Strhodes killit = destroy_check(ent); 846135446Strhodes 847135446Strhodes UNLOCK(&ent->lock); 848135446Strhodes 849135446Strhodes if (killit) 850135446Strhodes destroy(&ent); 851135446Strhodes} 852135446Strhodes 853135446Strhodesisc_result_t 854135446Strhodesisc_entropy_createcallbacksource(isc_entropy_t *ent, 855135446Strhodes isc_entropystart_t start, 856135446Strhodes isc_entropyget_t get, 857135446Strhodes isc_entropystop_t stop, 858135446Strhodes void *arg, 859135446Strhodes isc_entropysource_t **sourcep) 860135446Strhodes{ 861170222Sdougb isc_result_t result; 862135446Strhodes isc_entropysource_t *source; 863135446Strhodes isc_cbsource_t *cbs; 864135446Strhodes 865135446Strhodes REQUIRE(VALID_ENTROPY(ent)); 866135446Strhodes REQUIRE(get != NULL); 867135446Strhodes REQUIRE(sourcep != NULL && *sourcep == NULL); 868135446Strhodes 869135446Strhodes LOCK(&ent->lock); 870135446Strhodes 871135446Strhodes source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t)); 872135446Strhodes if (source == NULL) { 873170222Sdougb result = ISC_R_NOMEMORY; 874135446Strhodes goto errout; 875135446Strhodes } 876135446Strhodes source->bad = ISC_FALSE; 877135446Strhodes 878135446Strhodes cbs = &source->sources.callback; 879135446Strhodes 880170222Sdougb result = samplesource_allocate(ent, &cbs->samplequeue); 881170222Sdougb if (result != ISC_R_SUCCESS) 882135446Strhodes goto errout; 883135446Strhodes 884135446Strhodes cbs->start_called = ISC_FALSE; 885135446Strhodes cbs->startfunc = start; 886135446Strhodes cbs->getfunc = get; 887135446Strhodes cbs->stopfunc = stop; 888135446Strhodes cbs->arg = arg; 889135446Strhodes 890135446Strhodes /* 891135446Strhodes * From here down, no failures can occur. 892135446Strhodes */ 893135446Strhodes source->magic = SOURCE_MAGIC; 894135446Strhodes source->type = ENTROPY_SOURCETYPE_CALLBACK; 895135446Strhodes source->ent = ent; 896135446Strhodes source->total = 0; 897135446Strhodes memset(source->name, 0, sizeof(source->name)); 898135446Strhodes ISC_LINK_INIT(source, link); 899135446Strhodes 900135446Strhodes /* 901135446Strhodes * Hook it into the entropy system. 902135446Strhodes */ 903135446Strhodes ISC_LIST_APPEND(ent->sources, source, link); 904135446Strhodes ent->nsources++; 905135446Strhodes 906135446Strhodes *sourcep = source; 907135446Strhodes 908135446Strhodes UNLOCK(&ent->lock); 909135446Strhodes return (ISC_R_SUCCESS); 910135446Strhodes 911135446Strhodes errout: 912135446Strhodes if (source != NULL) 913135446Strhodes isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t)); 914135446Strhodes 915135446Strhodes UNLOCK(&ent->lock); 916135446Strhodes 917170222Sdougb return (result); 918135446Strhodes} 919135446Strhodes 920135446Strhodesvoid 921135446Strhodesisc_entropy_stopcallbacksources(isc_entropy_t *ent) { 922135446Strhodes isc_entropysource_t *source; 923135446Strhodes isc_cbsource_t *cbs; 924135446Strhodes 925135446Strhodes REQUIRE(VALID_ENTROPY(ent)); 926135446Strhodes 927135446Strhodes LOCK(&ent->lock); 928135446Strhodes 929135446Strhodes source = ISC_LIST_HEAD(ent->sources); 930135446Strhodes while (source != NULL) { 931135446Strhodes if (source->type == ENTROPY_SOURCETYPE_CALLBACK) { 932135446Strhodes cbs = &source->sources.callback; 933135446Strhodes if (cbs->start_called && cbs->stopfunc != NULL) { 934135446Strhodes cbs->stopfunc(source, cbs->arg); 935135446Strhodes cbs->start_called = ISC_FALSE; 936135446Strhodes } 937135446Strhodes } 938135446Strhodes 939135446Strhodes source = ISC_LIST_NEXT(source, link); 940135446Strhodes } 941135446Strhodes 942135446Strhodes UNLOCK(&ent->lock); 943135446Strhodes} 944135446Strhodes 945135446Strhodesisc_result_t 946135446Strhodesisc_entropy_createsamplesource(isc_entropy_t *ent, 947135446Strhodes isc_entropysource_t **sourcep) 948135446Strhodes{ 949170222Sdougb isc_result_t result; 950135446Strhodes isc_entropysource_t *source; 951135446Strhodes sample_queue_t *sq; 952135446Strhodes 953135446Strhodes REQUIRE(VALID_ENTROPY(ent)); 954135446Strhodes REQUIRE(sourcep != NULL && *sourcep == NULL); 955135446Strhodes 956135446Strhodes LOCK(&ent->lock); 957135446Strhodes 958135446Strhodes source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t)); 959135446Strhodes if (source == NULL) { 960170222Sdougb result = ISC_R_NOMEMORY; 961135446Strhodes goto errout; 962135446Strhodes } 963135446Strhodes 964135446Strhodes sq = &source->sources.sample.samplequeue; 965170222Sdougb result = samplesource_allocate(ent, sq); 966170222Sdougb if (result != ISC_R_SUCCESS) 967135446Strhodes goto errout; 968135446Strhodes 969135446Strhodes /* 970135446Strhodes * From here down, no failures can occur. 971135446Strhodes */ 972135446Strhodes source->magic = SOURCE_MAGIC; 973135446Strhodes source->type = ENTROPY_SOURCETYPE_SAMPLE; 974135446Strhodes source->ent = ent; 975135446Strhodes source->total = 0; 976135446Strhodes memset(source->name, 0, sizeof(source->name)); 977135446Strhodes ISC_LINK_INIT(source, link); 978135446Strhodes 979135446Strhodes /* 980135446Strhodes * Hook it into the entropy system. 981135446Strhodes */ 982135446Strhodes ISC_LIST_APPEND(ent->sources, source, link); 983135446Strhodes ent->nsources++; 984135446Strhodes 985135446Strhodes *sourcep = source; 986135446Strhodes 987135446Strhodes UNLOCK(&ent->lock); 988135446Strhodes return (ISC_R_SUCCESS); 989135446Strhodes 990135446Strhodes errout: 991135446Strhodes if (source != NULL) 992135446Strhodes isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t)); 993135446Strhodes 994135446Strhodes UNLOCK(&ent->lock); 995135446Strhodes 996170222Sdougb return (result); 997135446Strhodes} 998135446Strhodes 999170222Sdougb/*! 1000135446Strhodes * Add a sample, and return ISC_R_SUCCESS if the queue has become full, 1001135446Strhodes * ISC_R_NOENTROPY if it has space remaining, and ISC_R_NOMORE if the 1002135446Strhodes * queue was full when this function was called. 1003135446Strhodes */ 1004135446Strhodesstatic isc_result_t 1005135446Strhodesaddsample(sample_queue_t *sq, isc_uint32_t sample, isc_uint32_t extra) { 1006135446Strhodes if (sq->nsamples >= RND_EVENTQSIZE) 1007135446Strhodes return (ISC_R_NOMORE); 1008135446Strhodes 1009135446Strhodes sq->samples[sq->nsamples] = sample; 1010135446Strhodes sq->extra[sq->nsamples] = extra; 1011135446Strhodes sq->nsamples++; 1012135446Strhodes 1013135446Strhodes if (sq->nsamples >= RND_EVENTQSIZE) 1014135446Strhodes return (ISC_R_QUEUEFULL); 1015135446Strhodes 1016135446Strhodes return (ISC_R_SUCCESS); 1017135446Strhodes} 1018135446Strhodes 1019135446Strhodesisc_result_t 1020135446Strhodesisc_entropy_addsample(isc_entropysource_t *source, isc_uint32_t sample, 1021135446Strhodes isc_uint32_t extra) 1022135446Strhodes{ 1023135446Strhodes isc_entropy_t *ent; 1024135446Strhodes sample_queue_t *sq; 1025135446Strhodes unsigned int entropy; 1026135446Strhodes isc_result_t result; 1027135446Strhodes 1028135446Strhodes REQUIRE(VALID_SOURCE(source)); 1029135446Strhodes 1030135446Strhodes ent = source->ent; 1031135446Strhodes 1032135446Strhodes LOCK(&ent->lock); 1033135446Strhodes 1034135446Strhodes sq = &source->sources.sample.samplequeue; 1035135446Strhodes result = addsample(sq, sample, extra); 1036135446Strhodes if (result == ISC_R_QUEUEFULL) { 1037135446Strhodes entropy = crunchsamples(ent, sq); 1038135446Strhodes add_entropy(ent, entropy); 1039135446Strhodes } 1040135446Strhodes 1041135446Strhodes UNLOCK(&ent->lock); 1042135446Strhodes 1043135446Strhodes return (result); 1044135446Strhodes} 1045135446Strhodes 1046135446Strhodesisc_result_t 1047135446Strhodesisc_entropy_addcallbacksample(isc_entropysource_t *source, isc_uint32_t sample, 1048135446Strhodes isc_uint32_t extra) 1049135446Strhodes{ 1050135446Strhodes sample_queue_t *sq; 1051135446Strhodes isc_result_t result; 1052135446Strhodes 1053135446Strhodes REQUIRE(VALID_SOURCE(source)); 1054135446Strhodes REQUIRE(source->type == ENTROPY_SOURCETYPE_CALLBACK); 1055135446Strhodes 1056135446Strhodes sq = &source->sources.callback.samplequeue; 1057135446Strhodes result = addsample(sq, sample, extra); 1058135446Strhodes 1059135446Strhodes return (result); 1060135446Strhodes} 1061135446Strhodes 1062135446Strhodesvoid 1063135446Strhodesisc_entropy_putdata(isc_entropy_t *ent, void *data, unsigned int length, 1064135446Strhodes isc_uint32_t entropy) 1065135446Strhodes{ 1066135446Strhodes REQUIRE(VALID_ENTROPY(ent)); 1067135446Strhodes 1068135446Strhodes LOCK(&ent->lock); 1069135446Strhodes 1070135446Strhodes entropypool_adddata(ent, data, length, entropy); 1071135446Strhodes 1072135446Strhodes if (ent->initialized < THRESHOLD_BITS) 1073135446Strhodes ent->initialized = THRESHOLD_BITS; 1074135446Strhodes 1075135446Strhodes UNLOCK(&ent->lock); 1076135446Strhodes} 1077135446Strhodes 1078135446Strhodesstatic void 1079135446Strhodesdumpstats(isc_entropy_t *ent, FILE *out) { 1080135446Strhodes fprintf(out, 1081135446Strhodes isc_msgcat_get(isc_msgcat, ISC_MSGSET_ENTROPY, 1082135446Strhodes ISC_MSG_ENTROPYSTATS, 1083135446Strhodes "Entropy pool %p: refcnt %u cursor %u," 1084135446Strhodes " rotate %u entropy %u pseudo %u nsources %u" 1085135446Strhodes " nextsource %p initialized %u initcount %u\n"), 1086135446Strhodes ent, ent->refcnt, 1087135446Strhodes ent->pool.cursor, ent->pool.rotate, 1088135446Strhodes ent->pool.entropy, ent->pool.pseudo, 1089135446Strhodes ent->nsources, ent->nextsource, ent->initialized, 1090135446Strhodes ent->initcount); 1091135446Strhodes} 1092135446Strhodes 1093135446Strhodes/* 1094135446Strhodes * This function ignores locking. Use at your own risk. 1095135446Strhodes */ 1096135446Strhodesvoid 1097135446Strhodesisc_entropy_stats(isc_entropy_t *ent, FILE *out) { 1098135446Strhodes REQUIRE(VALID_ENTROPY(ent)); 1099135446Strhodes 1100135446Strhodes LOCK(&ent->lock); 1101135446Strhodes dumpstats(ent, out); 1102135446Strhodes UNLOCK(&ent->lock); 1103135446Strhodes} 1104135446Strhodes 1105193149Sdougbunsigned int 1106193149Sdougbisc_entropy_status(isc_entropy_t *ent) { 1107193149Sdougb unsigned int estimate; 1108193149Sdougb 1109193149Sdougb LOCK(&ent->lock); 1110193149Sdougb estimate = ent->pool.entropy; 1111193149Sdougb UNLOCK(&ent->lock); 1112193149Sdougb 1113193149Sdougb return estimate; 1114193149Sdougb} 1115193149Sdougb 1116135446Strhodesvoid 1117135446Strhodesisc_entropy_attach(isc_entropy_t *ent, isc_entropy_t **entp) { 1118135446Strhodes REQUIRE(VALID_ENTROPY(ent)); 1119135446Strhodes REQUIRE(entp != NULL && *entp == NULL); 1120135446Strhodes 1121135446Strhodes LOCK(&ent->lock); 1122135446Strhodes 1123135446Strhodes ent->refcnt++; 1124135446Strhodes *entp = ent; 1125135446Strhodes 1126135446Strhodes UNLOCK(&ent->lock); 1127135446Strhodes} 1128135446Strhodes 1129135446Strhodesvoid 1130135446Strhodesisc_entropy_detach(isc_entropy_t **entp) { 1131135446Strhodes isc_entropy_t *ent; 1132135446Strhodes isc_boolean_t killit; 1133135446Strhodes 1134135446Strhodes REQUIRE(entp != NULL && VALID_ENTROPY(*entp)); 1135135446Strhodes ent = *entp; 1136135446Strhodes *entp = NULL; 1137135446Strhodes 1138135446Strhodes LOCK(&ent->lock); 1139135446Strhodes 1140135446Strhodes REQUIRE(ent->refcnt > 0); 1141135446Strhodes ent->refcnt--; 1142135446Strhodes 1143135446Strhodes killit = destroy_check(ent); 1144135446Strhodes 1145135446Strhodes UNLOCK(&ent->lock); 1146135446Strhodes 1147135446Strhodes if (killit) 1148135446Strhodes destroy(&ent); 1149135446Strhodes} 1150135446Strhodes 1151135446Strhodesstatic isc_result_t 1152135446Strhodeskbdstart(isc_entropysource_t *source, void *arg, isc_boolean_t blocking) { 1153135446Strhodes /* 1154135446Strhodes * The intent of "first" is to provide a warning message only once 1155135446Strhodes * during the run of a program that might try to gather keyboard 1156135446Strhodes * entropy multiple times. 1157135446Strhodes */ 1158135446Strhodes static isc_boolean_t first = ISC_TRUE; 1159135446Strhodes 1160135446Strhodes UNUSED(arg); 1161135446Strhodes 1162135446Strhodes if (! blocking) 1163135446Strhodes return (ISC_R_NOENTROPY); 1164135446Strhodes 1165135446Strhodes if (first) { 1166135446Strhodes if (source->warn_keyboard) 1167135446Strhodes fprintf(stderr, "You must use the keyboard to create " 1168135446Strhodes "entropy, since your system is lacking\n" 1169135446Strhodes "/dev/random (or equivalent)\n\n"); 1170135446Strhodes first = ISC_FALSE; 1171135446Strhodes } 1172135446Strhodes fprintf(stderr, "start typing:\n"); 1173135446Strhodes 1174135446Strhodes return (isc_keyboard_open(&source->kbd)); 1175135446Strhodes} 1176135446Strhodes 1177135446Strhodesstatic void 1178135446Strhodeskbdstop(isc_entropysource_t *source, void *arg) { 1179135446Strhodes 1180135446Strhodes UNUSED(arg); 1181135446Strhodes 1182135446Strhodes if (! isc_keyboard_canceled(&source->kbd)) 1183135446Strhodes fprintf(stderr, "stop typing.\r\n"); 1184135446Strhodes 1185135446Strhodes (void)isc_keyboard_close(&source->kbd, 3); 1186135446Strhodes} 1187135446Strhodes 1188135446Strhodesstatic isc_result_t 1189135446Strhodeskbdget(isc_entropysource_t *source, void *arg, isc_boolean_t blocking) { 1190135446Strhodes isc_result_t result; 1191135446Strhodes isc_time_t t; 1192135446Strhodes isc_uint32_t sample; 1193135446Strhodes isc_uint32_t extra; 1194135446Strhodes unsigned char c; 1195135446Strhodes 1196135446Strhodes UNUSED(arg); 1197135446Strhodes 1198135446Strhodes if (!blocking) 1199135446Strhodes return (ISC_R_NOTBLOCKING); 1200135446Strhodes 1201135446Strhodes result = isc_keyboard_getchar(&source->kbd, &c); 1202135446Strhodes if (result != ISC_R_SUCCESS) 1203135446Strhodes return (result); 1204135446Strhodes 1205135446Strhodes TIME_NOW(&t); 1206135446Strhodes 1207135446Strhodes sample = isc_time_nanoseconds(&t); 1208135446Strhodes extra = c; 1209135446Strhodes 1210135446Strhodes result = isc_entropy_addcallbacksample(source, sample, extra); 1211135446Strhodes if (result != ISC_R_SUCCESS) { 1212135446Strhodes fprintf(stderr, "\r\n"); 1213135446Strhodes return (result); 1214135446Strhodes } 1215135446Strhodes 1216135446Strhodes fprintf(stderr, "."); 1217135446Strhodes fflush(stderr); 1218135446Strhodes 1219135446Strhodes return (result); 1220135446Strhodes} 1221135446Strhodes 1222135446Strhodesisc_result_t 1223135446Strhodesisc_entropy_usebestsource(isc_entropy_t *ectx, isc_entropysource_t **source, 1224135446Strhodes const char *randomfile, int use_keyboard) 1225135446Strhodes{ 1226135446Strhodes isc_result_t result; 1227135446Strhodes isc_result_t final_result = ISC_R_NOENTROPY; 1228135446Strhodes isc_boolean_t userfile = ISC_TRUE; 1229135446Strhodes 1230135446Strhodes REQUIRE(VALID_ENTROPY(ectx)); 1231135446Strhodes REQUIRE(source != NULL && *source == NULL); 1232135446Strhodes REQUIRE(use_keyboard == ISC_ENTROPY_KEYBOARDYES || 1233135446Strhodes use_keyboard == ISC_ENTROPY_KEYBOARDNO || 1234135446Strhodes use_keyboard == ISC_ENTROPY_KEYBOARDMAYBE); 1235135446Strhodes 1236135446Strhodes#ifdef PATH_RANDOMDEV 1237135446Strhodes if (randomfile == NULL) { 1238135446Strhodes randomfile = PATH_RANDOMDEV; 1239135446Strhodes userfile = ISC_FALSE; 1240135446Strhodes } 1241135446Strhodes#endif 1242135446Strhodes 1243135446Strhodes if (randomfile != NULL && use_keyboard != ISC_ENTROPY_KEYBOARDYES) { 1244135446Strhodes result = isc_entropy_createfilesource(ectx, randomfile); 1245135446Strhodes if (result == ISC_R_SUCCESS && 1246135446Strhodes use_keyboard == ISC_ENTROPY_KEYBOARDMAYBE) 1247135446Strhodes use_keyboard = ISC_ENTROPY_KEYBOARDNO; 1248135446Strhodes if (result != ISC_R_SUCCESS && userfile) 1249135446Strhodes return (result); 1250135446Strhodes 1251135446Strhodes final_result = result; 1252135446Strhodes } 1253135446Strhodes 1254135446Strhodes if (use_keyboard != ISC_ENTROPY_KEYBOARDNO) { 1255135446Strhodes result = isc_entropy_createcallbacksource(ectx, kbdstart, 1256135446Strhodes kbdget, kbdstop, 1257135446Strhodes NULL, source); 1258135446Strhodes if (result == ISC_R_SUCCESS) 1259135446Strhodes (*source)->warn_keyboard = 1260135446Strhodes ISC_TF(use_keyboard == 1261135446Strhodes ISC_ENTROPY_KEYBOARDMAYBE); 1262135446Strhodes 1263135446Strhodes if (final_result != ISC_R_SUCCESS) 1264135446Strhodes final_result = result; 1265193149Sdougb } 1266135446Strhodes 1267135446Strhodes /* 1268135446Strhodes * final_result is ISC_R_SUCCESS if at least one source of entropy 1269135446Strhodes * could be started, otherwise it is the error from the most recently 1270135446Strhodes * failed operation (or ISC_R_NOENTROPY if PATH_RANDOMDEV is not 1271135446Strhodes * defined and use_keyboard is ISC_ENTROPY_KEYBOARDNO). 1272135446Strhodes */ 1273135446Strhodes return (final_result); 1274135446Strhodes} 1275