entropy.c revision 218384
1135446Strhodes/* 2218384Sdougb * Copyright (C) 2004-2007, 2009, 2010 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 18218384Sdougb/* $Id: entropy.c,v 1.18.332.4 2010-08-10 23:46:54 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)]; 286218384Sdougb if (rp->rotate == 0) 287218384Sdougb rp->pool[rp->cursor++] ^= val; 288218384Sdougb else 289218384Sdougb rp->pool[rp->cursor++] ^= 290218384Sdougb ((val << rp->rotate) | (val >> (32 - rp->rotate))); 291135446Strhodes 292135446Strhodes /* 293135446Strhodes * If we have looped around the pool, increment the rotate 294135446Strhodes * variable so the next value will get xored in rotated to 295135446Strhodes * a different position. 296193149Sdougb * Increment by a value that is relatively prime to the word size 297135446Strhodes * to try to spread the bits throughout the pool quickly when the 298135446Strhodes * pool is empty. 299135446Strhodes */ 300135446Strhodes if (rp->cursor == RND_POOLWORDS) { 301135446Strhodes rp->cursor = 0; 302135446Strhodes rp->rotate = (rp->rotate + 7) & 31; 303135446Strhodes } 304135446Strhodes} 305135446Strhodes 306170222Sdougb/*! 307135446Strhodes * Add a buffer's worth of data to the pool. 308135446Strhodes * 309135446Strhodes * Requires that the lock is held on the entropy pool. 310135446Strhodes */ 311135446Strhodesstatic void 312135446Strhodesentropypool_adddata(isc_entropy_t *ent, void *p, unsigned int len, 313135446Strhodes isc_uint32_t entropy) 314135446Strhodes{ 315135446Strhodes isc_uint32_t val; 316135446Strhodes unsigned long addr; 317135446Strhodes isc_uint8_t *buf; 318135446Strhodes 319135446Strhodes addr = (unsigned long)p; 320135446Strhodes buf = p; 321135446Strhodes 322135446Strhodes if ((addr & 0x03U) != 0U) { 323135446Strhodes val = 0; 324135446Strhodes switch (len) { 325135446Strhodes case 3: 326135446Strhodes val = *buf++; 327135446Strhodes len--; 328135446Strhodes case 2: 329135446Strhodes val = val << 8 | *buf++; 330135446Strhodes len--; 331135446Strhodes case 1: 332135446Strhodes val = val << 8 | *buf++; 333135446Strhodes len--; 334135446Strhodes } 335135446Strhodes 336135446Strhodes entropypool_add_word(&ent->pool, val); 337135446Strhodes } 338135446Strhodes 339135446Strhodes for (; len > 3; len -= 4) { 340135446Strhodes val = *((isc_uint32_t *)buf); 341135446Strhodes 342135446Strhodes entropypool_add_word(&ent->pool, val); 343135446Strhodes buf += 4; 344135446Strhodes } 345135446Strhodes 346135446Strhodes if (len != 0) { 347135446Strhodes val = 0; 348135446Strhodes switch (len) { 349135446Strhodes case 3: 350135446Strhodes val = *buf++; 351135446Strhodes case 2: 352135446Strhodes val = val << 8 | *buf++; 353135446Strhodes case 1: 354135446Strhodes val = val << 8 | *buf++; 355135446Strhodes } 356135446Strhodes 357135446Strhodes entropypool_add_word(&ent->pool, val); 358135446Strhodes } 359135446Strhodes 360135446Strhodes add_entropy(ent, entropy); 361135446Strhodes subtract_pseudo(ent, entropy); 362135446Strhodes} 363135446Strhodes 364135446Strhodesstatic inline void 365135446Strhodesreseed(isc_entropy_t *ent) { 366135446Strhodes isc_time_t t; 367135446Strhodes pid_t pid; 368135446Strhodes 369135446Strhodes if (ent->initcount == 0) { 370135446Strhodes pid = getpid(); 371135446Strhodes entropypool_adddata(ent, &pid, sizeof(pid), 0); 372135446Strhodes pid = getppid(); 373135446Strhodes entropypool_adddata(ent, &pid, sizeof(pid), 0); 374135446Strhodes } 375135446Strhodes 376170222Sdougb /*! 377135446Strhodes * After we've reseeded 100 times, only add new timing info every 378135446Strhodes * 50 requests. This will keep us from using lots and lots of 379135446Strhodes * CPU just to return bad pseudorandom data anyway. 380135446Strhodes */ 381135446Strhodes if (ent->initcount > 100) 382135446Strhodes if ((ent->initcount % 50) != 0) 383135446Strhodes return; 384135446Strhodes 385135446Strhodes TIME_NOW(&t); 386135446Strhodes entropypool_adddata(ent, &t, sizeof(t), 0); 387135446Strhodes ent->initcount++; 388135446Strhodes} 389135446Strhodes 390135446Strhodesstatic inline unsigned int 391135446Strhodesestimate_entropy(sample_queue_t *sq, isc_uint32_t t) { 392135446Strhodes isc_int32_t delta; 393135446Strhodes isc_int32_t delta2; 394135446Strhodes isc_int32_t delta3; 395135446Strhodes 396170222Sdougb /*! 397135446Strhodes * If the time counter has overflowed, calculate the real difference. 398135446Strhodes * If it has not, it is simpler. 399135446Strhodes */ 400135446Strhodes if (t < sq->last_time) 401135446Strhodes delta = UINT_MAX - sq->last_time + t; 402135446Strhodes else 403135446Strhodes delta = sq->last_time - t; 404135446Strhodes 405135446Strhodes if (delta < 0) 406135446Strhodes delta = -delta; 407135446Strhodes 408135446Strhodes /* 409135446Strhodes * Calculate the second and third order differentials 410135446Strhodes */ 411135446Strhodes delta2 = sq->last_delta - delta; 412135446Strhodes if (delta2 < 0) 413135446Strhodes delta2 = -delta2; 414135446Strhodes 415135446Strhodes delta3 = sq->last_delta2 - delta2; 416135446Strhodes if (delta3 < 0) 417135446Strhodes delta3 = -delta3; 418135446Strhodes 419135446Strhodes sq->last_time = t; 420135446Strhodes sq->last_delta = delta; 421135446Strhodes sq->last_delta2 = delta2; 422135446Strhodes 423135446Strhodes /* 424135446Strhodes * If any delta is 0, we got no entropy. If all are non-zero, we 425135446Strhodes * might have something. 426135446Strhodes */ 427135446Strhodes if (delta == 0 || delta2 == 0 || delta3 == 0) 428135446Strhodes return 0; 429135446Strhodes 430135446Strhodes /* 431135446Strhodes * We could find the smallest delta and claim we got log2(delta) 432135446Strhodes * bits, but for now return that we found 1 bit. 433135446Strhodes */ 434135446Strhodes return 1; 435135446Strhodes} 436135446Strhodes 437135446Strhodesstatic unsigned int 438135446Strhodescrunchsamples(isc_entropy_t *ent, sample_queue_t *sq) { 439135446Strhodes unsigned int ns; 440135446Strhodes unsigned int added; 441135446Strhodes 442135446Strhodes if (sq->nsamples < 6) 443135446Strhodes return (0); 444135446Strhodes 445135446Strhodes added = 0; 446135446Strhodes sq->last_time = sq->samples[0]; 447135446Strhodes sq->last_delta = 0; 448135446Strhodes sq->last_delta2 = 0; 449135446Strhodes 450135446Strhodes /* 451135446Strhodes * Prime the values by adding in the first 4 samples in. This 452135446Strhodes * should completely initialize the delta calculations. 453135446Strhodes */ 454135446Strhodes for (ns = 0; ns < 4; ns++) 455135446Strhodes (void)estimate_entropy(sq, sq->samples[ns]); 456135446Strhodes 457135446Strhodes for (ns = 4; ns < sq->nsamples; ns++) 458135446Strhodes added += estimate_entropy(sq, sq->samples[ns]); 459135446Strhodes 460135446Strhodes entropypool_adddata(ent, sq->samples, sq->nsamples * 4, added); 461135446Strhodes entropypool_adddata(ent, sq->extra, sq->nsamples * 4, 0); 462135446Strhodes 463135446Strhodes /* 464135446Strhodes * Move the last 4 samples into the first 4 positions, and start 465135446Strhodes * adding new samples from that point. 466135446Strhodes */ 467135446Strhodes for (ns = 0; ns < 4; ns++) { 468135446Strhodes sq->samples[ns] = sq->samples[sq->nsamples - 4 + ns]; 469135446Strhodes sq->extra[ns] = sq->extra[sq->nsamples - 4 + ns]; 470135446Strhodes } 471135446Strhodes 472135446Strhodes sq->nsamples = 4; 473135446Strhodes 474135446Strhodes return (added); 475135446Strhodes} 476135446Strhodes 477135446Strhodesstatic unsigned int 478135446Strhodesget_from_callback(isc_entropysource_t *source, unsigned int desired, 479135446Strhodes isc_boolean_t blocking) 480135446Strhodes{ 481135446Strhodes isc_entropy_t *ent = source->ent; 482135446Strhodes isc_cbsource_t *cbs = &source->sources.callback; 483135446Strhodes unsigned int added; 484135446Strhodes unsigned int got; 485135446Strhodes isc_result_t result; 486135446Strhodes 487135446Strhodes if (desired == 0) 488135446Strhodes return (0); 489135446Strhodes 490135446Strhodes if (source->bad) 491135446Strhodes return (0); 492135446Strhodes 493135446Strhodes if (!cbs->start_called && cbs->startfunc != NULL) { 494135446Strhodes result = cbs->startfunc(source, cbs->arg, blocking); 495135446Strhodes if (result != ISC_R_SUCCESS) 496135446Strhodes return (0); 497135446Strhodes cbs->start_called = ISC_TRUE; 498135446Strhodes } 499135446Strhodes 500135446Strhodes added = 0; 501135446Strhodes result = ISC_R_SUCCESS; 502135446Strhodes while (desired > 0 && result == ISC_R_SUCCESS) { 503135446Strhodes result = cbs->getfunc(source, cbs->arg, blocking); 504135446Strhodes if (result == ISC_R_QUEUEFULL) { 505135446Strhodes got = crunchsamples(ent, &cbs->samplequeue); 506135446Strhodes added += got; 507135446Strhodes desired -= ISC_MIN(got, desired); 508135446Strhodes result = ISC_R_SUCCESS; 509135446Strhodes } else if (result != ISC_R_SUCCESS && 510135446Strhodes result != ISC_R_NOTBLOCKING) 511135446Strhodes source->bad = ISC_TRUE; 512135446Strhodes 513135446Strhodes } 514135446Strhodes 515135446Strhodes return (added); 516135446Strhodes} 517135446Strhodes 518135446Strhodes/* 519135446Strhodes * Extract some number of bytes from the random pool, decreasing the 520135446Strhodes * estimate of randomness as each byte is extracted. 521135446Strhodes * 522135446Strhodes * Do this by stiring the pool and returning a part of hash as randomness. 523135446Strhodes * Note that no secrets are given away here since parts of the hash are 524135446Strhodes * xored together before returned. 525135446Strhodes * 526135446Strhodes * Honor the request from the caller to only return good data, any data, 527135446Strhodes * etc. 528135446Strhodes */ 529135446Strhodesisc_result_t 530135446Strhodesisc_entropy_getdata(isc_entropy_t *ent, void *data, unsigned int length, 531135446Strhodes unsigned int *returned, unsigned int flags) 532135446Strhodes{ 533135446Strhodes unsigned int i; 534135446Strhodes isc_sha1_t hash; 535135446Strhodes unsigned char digest[ISC_SHA1_DIGESTLENGTH]; 536135446Strhodes isc_uint32_t remain, deltae, count, total; 537135446Strhodes isc_uint8_t *buf; 538135446Strhodes isc_boolean_t goodonly, partial, blocking; 539135446Strhodes 540135446Strhodes REQUIRE(VALID_ENTROPY(ent)); 541135446Strhodes REQUIRE(data != NULL); 542135446Strhodes REQUIRE(length > 0); 543135446Strhodes 544135446Strhodes goodonly = ISC_TF((flags & ISC_ENTROPY_GOODONLY) != 0); 545135446Strhodes partial = ISC_TF((flags & ISC_ENTROPY_PARTIAL) != 0); 546135446Strhodes blocking = ISC_TF((flags & ISC_ENTROPY_BLOCKING) != 0); 547135446Strhodes 548135446Strhodes REQUIRE(!partial || returned != NULL); 549135446Strhodes 550135446Strhodes LOCK(&ent->lock); 551135446Strhodes 552135446Strhodes remain = length; 553135446Strhodes buf = data; 554135446Strhodes total = 0; 555135446Strhodes while (remain != 0) { 556135446Strhodes count = ISC_MIN(remain, RND_ENTROPY_THRESHOLD); 557135446Strhodes 558135446Strhodes /* 559135446Strhodes * If we are extracting good data only, make certain we 560135446Strhodes * have enough data in our pool for this pass. If we don't, 561135446Strhodes * get some, and fail if we can't, and partial returns 562135446Strhodes * are not ok. 563135446Strhodes */ 564135446Strhodes if (goodonly) { 565135446Strhodes unsigned int fillcount; 566135446Strhodes 567135446Strhodes fillcount = ISC_MAX(remain * 8, count * 8); 568135446Strhodes 569135446Strhodes /* 570135446Strhodes * If, however, we have at least THRESHOLD_BITS 571135446Strhodes * of entropy in the pool, don't block here. It is 572135446Strhodes * better to drain the pool once in a while and 573135446Strhodes * then refill it than it is to constantly keep the 574135446Strhodes * pool full. 575135446Strhodes */ 576135446Strhodes if (ent->pool.entropy >= THRESHOLD_BITS) 577135446Strhodes fillpool(ent, fillcount, ISC_FALSE); 578135446Strhodes else 579135446Strhodes fillpool(ent, fillcount, blocking); 580135446Strhodes 581135446Strhodes /* 582135446Strhodes * Verify that we got enough entropy to do one 583135446Strhodes * extraction. If we didn't, bail. 584135446Strhodes */ 585135446Strhodes if (ent->pool.entropy < THRESHOLD_BITS) { 586135446Strhodes if (!partial) 587135446Strhodes goto zeroize; 588135446Strhodes else 589135446Strhodes goto partial_output; 590135446Strhodes } 591135446Strhodes } else { 592135446Strhodes /* 593135446Strhodes * If we've extracted half our pool size in bits 594135446Strhodes * since the last refresh, try to refresh here. 595135446Strhodes */ 596135446Strhodes if (ent->initialized < THRESHOLD_BITS) 597135446Strhodes fillpool(ent, THRESHOLD_BITS, blocking); 598135446Strhodes else 599135446Strhodes fillpool(ent, 0, ISC_FALSE); 600135446Strhodes 601135446Strhodes /* 602135446Strhodes * If we've not initialized with enough good random 603135446Strhodes * data, seed with our crappy code. 604135446Strhodes */ 605135446Strhodes if (ent->initialized < THRESHOLD_BITS) 606135446Strhodes reseed(ent); 607135446Strhodes } 608135446Strhodes 609135446Strhodes isc_sha1_init(&hash); 610135446Strhodes isc_sha1_update(&hash, (void *)(ent->pool.pool), 611135446Strhodes RND_POOLBYTES); 612135446Strhodes isc_sha1_final(&hash, digest); 613135446Strhodes 614135446Strhodes /* 615135446Strhodes * Stir the extracted data (all of it) back into the pool. 616135446Strhodes */ 617135446Strhodes entropypool_adddata(ent, digest, ISC_SHA1_DIGESTLENGTH, 0); 618135446Strhodes 619135446Strhodes for (i = 0; i < count; i++) 620135446Strhodes buf[i] = digest[i] ^ digest[i + RND_ENTROPY_THRESHOLD]; 621135446Strhodes 622135446Strhodes buf += count; 623135446Strhodes remain -= count; 624135446Strhodes 625135446Strhodes deltae = count * 8; 626135446Strhodes deltae = ISC_MIN(deltae, ent->pool.entropy); 627135446Strhodes total += deltae; 628135446Strhodes subtract_entropy(ent, deltae); 629135446Strhodes add_pseudo(ent, count * 8); 630135446Strhodes } 631135446Strhodes 632135446Strhodes partial_output: 633135446Strhodes memset(digest, 0, sizeof(digest)); 634135446Strhodes 635135446Strhodes if (returned != NULL) 636135446Strhodes *returned = (length - remain); 637135446Strhodes 638135446Strhodes UNLOCK(&ent->lock); 639135446Strhodes 640135446Strhodes return (ISC_R_SUCCESS); 641135446Strhodes 642135446Strhodes zeroize: 643135446Strhodes /* put the entropy we almost extracted back */ 644135446Strhodes add_entropy(ent, total); 645135446Strhodes memset(data, 0, length); 646135446Strhodes memset(digest, 0, sizeof(digest)); 647135446Strhodes if (returned != NULL) 648135446Strhodes *returned = 0; 649135446Strhodes 650135446Strhodes UNLOCK(&ent->lock); 651135446Strhodes 652135446Strhodes return (ISC_R_NOENTROPY); 653135446Strhodes} 654135446Strhodes 655135446Strhodesstatic void 656135446Strhodesisc_entropypool_init(isc_entropypool_t *pool) { 657135446Strhodes pool->cursor = RND_POOLWORDS - 1; 658135446Strhodes pool->entropy = 0; 659135446Strhodes pool->pseudo = 0; 660135446Strhodes pool->rotate = 0; 661135446Strhodes memset(pool->pool, 0, RND_POOLBYTES); 662135446Strhodes} 663135446Strhodes 664135446Strhodesstatic void 665135446Strhodesisc_entropypool_invalidate(isc_entropypool_t *pool) { 666135446Strhodes pool->cursor = 0; 667135446Strhodes pool->entropy = 0; 668135446Strhodes pool->pseudo = 0; 669135446Strhodes pool->rotate = 0; 670135446Strhodes memset(pool->pool, 0, RND_POOLBYTES); 671135446Strhodes} 672135446Strhodes 673135446Strhodesisc_result_t 674135446Strhodesisc_entropy_create(isc_mem_t *mctx, isc_entropy_t **entp) { 675170222Sdougb isc_result_t result; 676135446Strhodes isc_entropy_t *ent; 677135446Strhodes 678135446Strhodes REQUIRE(mctx != NULL); 679135446Strhodes REQUIRE(entp != NULL && *entp == NULL); 680135446Strhodes 681135446Strhodes ent = isc_mem_get(mctx, sizeof(isc_entropy_t)); 682135446Strhodes if (ent == NULL) 683135446Strhodes return (ISC_R_NOMEMORY); 684135446Strhodes 685135446Strhodes /* 686135446Strhodes * We need a lock. 687135446Strhodes */ 688170222Sdougb result = isc_mutex_init(&ent->lock); 689170222Sdougb if (result != ISC_R_SUCCESS) 690135446Strhodes goto errout; 691135446Strhodes 692135446Strhodes /* 693135446Strhodes * From here down, no failures will/can occur. 694135446Strhodes */ 695135446Strhodes ISC_LIST_INIT(ent->sources); 696135446Strhodes ent->nextsource = NULL; 697135446Strhodes ent->nsources = 0; 698135446Strhodes ent->mctx = NULL; 699135446Strhodes isc_mem_attach(mctx, &ent->mctx); 700135446Strhodes ent->refcnt = 1; 701135446Strhodes ent->initialized = 0; 702135446Strhodes ent->initcount = 0; 703135446Strhodes ent->magic = ENTROPY_MAGIC; 704135446Strhodes 705135446Strhodes isc_entropypool_init(&ent->pool); 706135446Strhodes 707135446Strhodes *entp = ent; 708135446Strhodes return (ISC_R_SUCCESS); 709135446Strhodes 710135446Strhodes errout: 711135446Strhodes isc_mem_put(mctx, ent, sizeof(isc_entropy_t)); 712135446Strhodes 713170222Sdougb return (result); 714135446Strhodes} 715135446Strhodes 716170222Sdougb/*! 717135446Strhodes * Requires "ent" be locked. 718135446Strhodes */ 719135446Strhodesstatic void 720135446Strhodesdestroysource(isc_entropysource_t **sourcep) { 721135446Strhodes isc_entropysource_t *source; 722135446Strhodes isc_entropy_t *ent; 723135446Strhodes isc_cbsource_t *cbs; 724135446Strhodes 725135446Strhodes source = *sourcep; 726135446Strhodes *sourcep = NULL; 727135446Strhodes ent = source->ent; 728135446Strhodes 729135446Strhodes ISC_LIST_UNLINK(ent->sources, source, link); 730135446Strhodes ent->nextsource = NULL; 731135446Strhodes REQUIRE(ent->nsources > 0); 732135446Strhodes ent->nsources--; 733135446Strhodes 734135446Strhodes switch (source->type) { 735135446Strhodes case ENTROPY_SOURCETYPE_FILE: 736135446Strhodes if (! source->bad) 737135446Strhodes destroyfilesource(&source->sources.file); 738135446Strhodes break; 739135446Strhodes case ENTROPY_SOURCETYPE_USOCKET: 740135446Strhodes if (! source->bad) 741135446Strhodes destroyusocketsource(&source->sources.usocket); 742135446Strhodes break; 743135446Strhodes case ENTROPY_SOURCETYPE_SAMPLE: 744135446Strhodes samplequeue_release(ent, &source->sources.sample.samplequeue); 745135446Strhodes break; 746135446Strhodes case ENTROPY_SOURCETYPE_CALLBACK: 747135446Strhodes cbs = &source->sources.callback; 748135446Strhodes if (cbs->start_called && cbs->stopfunc != NULL) { 749135446Strhodes cbs->stopfunc(source, cbs->arg); 750135446Strhodes cbs->start_called = ISC_FALSE; 751135446Strhodes } 752135446Strhodes samplequeue_release(ent, &cbs->samplequeue); 753135446Strhodes break; 754135446Strhodes } 755135446Strhodes 756135446Strhodes memset(source, 0, sizeof(isc_entropysource_t)); 757135446Strhodes 758135446Strhodes isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t)); 759135446Strhodes} 760135446Strhodes 761135446Strhodesstatic inline isc_boolean_t 762135446Strhodesdestroy_check(isc_entropy_t *ent) { 763135446Strhodes isc_entropysource_t *source; 764135446Strhodes 765135446Strhodes if (ent->refcnt > 0) 766135446Strhodes return (ISC_FALSE); 767135446Strhodes 768135446Strhodes source = ISC_LIST_HEAD(ent->sources); 769135446Strhodes while (source != NULL) { 770135446Strhodes switch (source->type) { 771135446Strhodes case ENTROPY_SOURCETYPE_FILE: 772135446Strhodes case ENTROPY_SOURCETYPE_USOCKET: 773135446Strhodes break; 774135446Strhodes default: 775135446Strhodes return (ISC_FALSE); 776135446Strhodes } 777135446Strhodes source = ISC_LIST_NEXT(source, link); 778135446Strhodes } 779135446Strhodes 780135446Strhodes return (ISC_TRUE); 781135446Strhodes} 782135446Strhodes 783135446Strhodesstatic void 784135446Strhodesdestroy(isc_entropy_t **entp) { 785135446Strhodes isc_entropy_t *ent; 786135446Strhodes isc_entropysource_t *source; 787135446Strhodes isc_mem_t *mctx; 788135446Strhodes 789135446Strhodes REQUIRE(entp != NULL && *entp != NULL); 790135446Strhodes ent = *entp; 791135446Strhodes *entp = NULL; 792135446Strhodes 793135446Strhodes LOCK(&ent->lock); 794135446Strhodes 795135446Strhodes REQUIRE(ent->refcnt == 0); 796135446Strhodes 797135446Strhodes /* 798135446Strhodes * Here, detach non-sample sources. 799135446Strhodes */ 800135446Strhodes source = ISC_LIST_HEAD(ent->sources); 801135446Strhodes while (source != NULL) { 802135446Strhodes switch(source->type) { 803135446Strhodes case ENTROPY_SOURCETYPE_FILE: 804135446Strhodes case ENTROPY_SOURCETYPE_USOCKET: 805135446Strhodes destroysource(&source); 806135446Strhodes break; 807135446Strhodes } 808135446Strhodes source = ISC_LIST_HEAD(ent->sources); 809135446Strhodes } 810135446Strhodes 811135446Strhodes /* 812135446Strhodes * If there are other types of sources, we've found a bug. 813135446Strhodes */ 814135446Strhodes REQUIRE(ISC_LIST_EMPTY(ent->sources)); 815135446Strhodes 816135446Strhodes mctx = ent->mctx; 817135446Strhodes 818135446Strhodes isc_entropypool_invalidate(&ent->pool); 819135446Strhodes 820135446Strhodes UNLOCK(&ent->lock); 821135446Strhodes 822135446Strhodes DESTROYLOCK(&ent->lock); 823135446Strhodes 824135446Strhodes memset(ent, 0, sizeof(isc_entropy_t)); 825135446Strhodes isc_mem_put(mctx, ent, sizeof(isc_entropy_t)); 826135446Strhodes isc_mem_detach(&mctx); 827135446Strhodes} 828135446Strhodes 829135446Strhodesvoid 830135446Strhodesisc_entropy_destroysource(isc_entropysource_t **sourcep) { 831135446Strhodes isc_entropysource_t *source; 832135446Strhodes isc_entropy_t *ent; 833135446Strhodes isc_boolean_t killit; 834135446Strhodes 835135446Strhodes REQUIRE(sourcep != NULL); 836135446Strhodes REQUIRE(VALID_SOURCE(*sourcep)); 837135446Strhodes 838135446Strhodes source = *sourcep; 839135446Strhodes *sourcep = NULL; 840135446Strhodes 841135446Strhodes ent = source->ent; 842135446Strhodes REQUIRE(VALID_ENTROPY(ent)); 843135446Strhodes 844135446Strhodes LOCK(&ent->lock); 845135446Strhodes 846135446Strhodes destroysource(&source); 847135446Strhodes 848135446Strhodes killit = destroy_check(ent); 849135446Strhodes 850135446Strhodes UNLOCK(&ent->lock); 851135446Strhodes 852135446Strhodes if (killit) 853135446Strhodes destroy(&ent); 854135446Strhodes} 855135446Strhodes 856135446Strhodesisc_result_t 857135446Strhodesisc_entropy_createcallbacksource(isc_entropy_t *ent, 858135446Strhodes isc_entropystart_t start, 859135446Strhodes isc_entropyget_t get, 860135446Strhodes isc_entropystop_t stop, 861135446Strhodes void *arg, 862135446Strhodes isc_entropysource_t **sourcep) 863135446Strhodes{ 864170222Sdougb isc_result_t result; 865135446Strhodes isc_entropysource_t *source; 866135446Strhodes isc_cbsource_t *cbs; 867135446Strhodes 868135446Strhodes REQUIRE(VALID_ENTROPY(ent)); 869135446Strhodes REQUIRE(get != NULL); 870135446Strhodes REQUIRE(sourcep != NULL && *sourcep == NULL); 871135446Strhodes 872135446Strhodes LOCK(&ent->lock); 873135446Strhodes 874135446Strhodes source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t)); 875135446Strhodes if (source == NULL) { 876170222Sdougb result = ISC_R_NOMEMORY; 877135446Strhodes goto errout; 878135446Strhodes } 879135446Strhodes source->bad = ISC_FALSE; 880135446Strhodes 881135446Strhodes cbs = &source->sources.callback; 882135446Strhodes 883170222Sdougb result = samplesource_allocate(ent, &cbs->samplequeue); 884170222Sdougb if (result != ISC_R_SUCCESS) 885135446Strhodes goto errout; 886135446Strhodes 887135446Strhodes cbs->start_called = ISC_FALSE; 888135446Strhodes cbs->startfunc = start; 889135446Strhodes cbs->getfunc = get; 890135446Strhodes cbs->stopfunc = stop; 891135446Strhodes cbs->arg = arg; 892135446Strhodes 893135446Strhodes /* 894135446Strhodes * From here down, no failures can occur. 895135446Strhodes */ 896135446Strhodes source->magic = SOURCE_MAGIC; 897135446Strhodes source->type = ENTROPY_SOURCETYPE_CALLBACK; 898135446Strhodes source->ent = ent; 899135446Strhodes source->total = 0; 900135446Strhodes memset(source->name, 0, sizeof(source->name)); 901135446Strhodes ISC_LINK_INIT(source, link); 902135446Strhodes 903135446Strhodes /* 904135446Strhodes * Hook it into the entropy system. 905135446Strhodes */ 906135446Strhodes ISC_LIST_APPEND(ent->sources, source, link); 907135446Strhodes ent->nsources++; 908135446Strhodes 909135446Strhodes *sourcep = source; 910135446Strhodes 911135446Strhodes UNLOCK(&ent->lock); 912135446Strhodes return (ISC_R_SUCCESS); 913135446Strhodes 914135446Strhodes errout: 915135446Strhodes if (source != NULL) 916135446Strhodes isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t)); 917135446Strhodes 918135446Strhodes UNLOCK(&ent->lock); 919135446Strhodes 920170222Sdougb return (result); 921135446Strhodes} 922135446Strhodes 923135446Strhodesvoid 924135446Strhodesisc_entropy_stopcallbacksources(isc_entropy_t *ent) { 925135446Strhodes isc_entropysource_t *source; 926135446Strhodes isc_cbsource_t *cbs; 927135446Strhodes 928135446Strhodes REQUIRE(VALID_ENTROPY(ent)); 929135446Strhodes 930135446Strhodes LOCK(&ent->lock); 931135446Strhodes 932135446Strhodes source = ISC_LIST_HEAD(ent->sources); 933135446Strhodes while (source != NULL) { 934135446Strhodes if (source->type == ENTROPY_SOURCETYPE_CALLBACK) { 935135446Strhodes cbs = &source->sources.callback; 936135446Strhodes if (cbs->start_called && cbs->stopfunc != NULL) { 937135446Strhodes cbs->stopfunc(source, cbs->arg); 938135446Strhodes cbs->start_called = ISC_FALSE; 939135446Strhodes } 940135446Strhodes } 941135446Strhodes 942135446Strhodes source = ISC_LIST_NEXT(source, link); 943135446Strhodes } 944135446Strhodes 945135446Strhodes UNLOCK(&ent->lock); 946135446Strhodes} 947135446Strhodes 948135446Strhodesisc_result_t 949135446Strhodesisc_entropy_createsamplesource(isc_entropy_t *ent, 950135446Strhodes isc_entropysource_t **sourcep) 951135446Strhodes{ 952170222Sdougb isc_result_t result; 953135446Strhodes isc_entropysource_t *source; 954135446Strhodes sample_queue_t *sq; 955135446Strhodes 956135446Strhodes REQUIRE(VALID_ENTROPY(ent)); 957135446Strhodes REQUIRE(sourcep != NULL && *sourcep == NULL); 958135446Strhodes 959135446Strhodes LOCK(&ent->lock); 960135446Strhodes 961135446Strhodes source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t)); 962135446Strhodes if (source == NULL) { 963170222Sdougb result = ISC_R_NOMEMORY; 964135446Strhodes goto errout; 965135446Strhodes } 966135446Strhodes 967135446Strhodes sq = &source->sources.sample.samplequeue; 968170222Sdougb result = samplesource_allocate(ent, sq); 969170222Sdougb if (result != ISC_R_SUCCESS) 970135446Strhodes goto errout; 971135446Strhodes 972135446Strhodes /* 973135446Strhodes * From here down, no failures can occur. 974135446Strhodes */ 975135446Strhodes source->magic = SOURCE_MAGIC; 976135446Strhodes source->type = ENTROPY_SOURCETYPE_SAMPLE; 977135446Strhodes source->ent = ent; 978135446Strhodes source->total = 0; 979135446Strhodes memset(source->name, 0, sizeof(source->name)); 980135446Strhodes ISC_LINK_INIT(source, link); 981135446Strhodes 982135446Strhodes /* 983135446Strhodes * Hook it into the entropy system. 984135446Strhodes */ 985135446Strhodes ISC_LIST_APPEND(ent->sources, source, link); 986135446Strhodes ent->nsources++; 987135446Strhodes 988135446Strhodes *sourcep = source; 989135446Strhodes 990135446Strhodes UNLOCK(&ent->lock); 991135446Strhodes return (ISC_R_SUCCESS); 992135446Strhodes 993135446Strhodes errout: 994135446Strhodes if (source != NULL) 995135446Strhodes isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t)); 996135446Strhodes 997135446Strhodes UNLOCK(&ent->lock); 998135446Strhodes 999170222Sdougb return (result); 1000135446Strhodes} 1001135446Strhodes 1002170222Sdougb/*! 1003135446Strhodes * Add a sample, and return ISC_R_SUCCESS if the queue has become full, 1004135446Strhodes * ISC_R_NOENTROPY if it has space remaining, and ISC_R_NOMORE if the 1005135446Strhodes * queue was full when this function was called. 1006135446Strhodes */ 1007135446Strhodesstatic isc_result_t 1008135446Strhodesaddsample(sample_queue_t *sq, isc_uint32_t sample, isc_uint32_t extra) { 1009135446Strhodes if (sq->nsamples >= RND_EVENTQSIZE) 1010135446Strhodes return (ISC_R_NOMORE); 1011135446Strhodes 1012135446Strhodes sq->samples[sq->nsamples] = sample; 1013135446Strhodes sq->extra[sq->nsamples] = extra; 1014135446Strhodes sq->nsamples++; 1015135446Strhodes 1016135446Strhodes if (sq->nsamples >= RND_EVENTQSIZE) 1017135446Strhodes return (ISC_R_QUEUEFULL); 1018135446Strhodes 1019135446Strhodes return (ISC_R_SUCCESS); 1020135446Strhodes} 1021135446Strhodes 1022135446Strhodesisc_result_t 1023135446Strhodesisc_entropy_addsample(isc_entropysource_t *source, isc_uint32_t sample, 1024135446Strhodes isc_uint32_t extra) 1025135446Strhodes{ 1026135446Strhodes isc_entropy_t *ent; 1027135446Strhodes sample_queue_t *sq; 1028135446Strhodes unsigned int entropy; 1029135446Strhodes isc_result_t result; 1030135446Strhodes 1031135446Strhodes REQUIRE(VALID_SOURCE(source)); 1032135446Strhodes 1033135446Strhodes ent = source->ent; 1034135446Strhodes 1035135446Strhodes LOCK(&ent->lock); 1036135446Strhodes 1037135446Strhodes sq = &source->sources.sample.samplequeue; 1038135446Strhodes result = addsample(sq, sample, extra); 1039135446Strhodes if (result == ISC_R_QUEUEFULL) { 1040135446Strhodes entropy = crunchsamples(ent, sq); 1041135446Strhodes add_entropy(ent, entropy); 1042135446Strhodes } 1043135446Strhodes 1044135446Strhodes UNLOCK(&ent->lock); 1045135446Strhodes 1046135446Strhodes return (result); 1047135446Strhodes} 1048135446Strhodes 1049135446Strhodesisc_result_t 1050135446Strhodesisc_entropy_addcallbacksample(isc_entropysource_t *source, isc_uint32_t sample, 1051135446Strhodes isc_uint32_t extra) 1052135446Strhodes{ 1053135446Strhodes sample_queue_t *sq; 1054135446Strhodes isc_result_t result; 1055135446Strhodes 1056135446Strhodes REQUIRE(VALID_SOURCE(source)); 1057135446Strhodes REQUIRE(source->type == ENTROPY_SOURCETYPE_CALLBACK); 1058135446Strhodes 1059135446Strhodes sq = &source->sources.callback.samplequeue; 1060135446Strhodes result = addsample(sq, sample, extra); 1061135446Strhodes 1062135446Strhodes return (result); 1063135446Strhodes} 1064135446Strhodes 1065135446Strhodesvoid 1066135446Strhodesisc_entropy_putdata(isc_entropy_t *ent, void *data, unsigned int length, 1067135446Strhodes isc_uint32_t entropy) 1068135446Strhodes{ 1069135446Strhodes REQUIRE(VALID_ENTROPY(ent)); 1070135446Strhodes 1071135446Strhodes LOCK(&ent->lock); 1072135446Strhodes 1073135446Strhodes entropypool_adddata(ent, data, length, entropy); 1074135446Strhodes 1075135446Strhodes if (ent->initialized < THRESHOLD_BITS) 1076135446Strhodes ent->initialized = THRESHOLD_BITS; 1077135446Strhodes 1078135446Strhodes UNLOCK(&ent->lock); 1079135446Strhodes} 1080135446Strhodes 1081135446Strhodesstatic void 1082135446Strhodesdumpstats(isc_entropy_t *ent, FILE *out) { 1083135446Strhodes fprintf(out, 1084135446Strhodes isc_msgcat_get(isc_msgcat, ISC_MSGSET_ENTROPY, 1085135446Strhodes ISC_MSG_ENTROPYSTATS, 1086135446Strhodes "Entropy pool %p: refcnt %u cursor %u," 1087135446Strhodes " rotate %u entropy %u pseudo %u nsources %u" 1088135446Strhodes " nextsource %p initialized %u initcount %u\n"), 1089135446Strhodes ent, ent->refcnt, 1090135446Strhodes ent->pool.cursor, ent->pool.rotate, 1091135446Strhodes ent->pool.entropy, ent->pool.pseudo, 1092135446Strhodes ent->nsources, ent->nextsource, ent->initialized, 1093135446Strhodes ent->initcount); 1094135446Strhodes} 1095135446Strhodes 1096135446Strhodes/* 1097135446Strhodes * This function ignores locking. Use at your own risk. 1098135446Strhodes */ 1099135446Strhodesvoid 1100135446Strhodesisc_entropy_stats(isc_entropy_t *ent, FILE *out) { 1101135446Strhodes REQUIRE(VALID_ENTROPY(ent)); 1102135446Strhodes 1103135446Strhodes LOCK(&ent->lock); 1104135446Strhodes dumpstats(ent, out); 1105135446Strhodes UNLOCK(&ent->lock); 1106135446Strhodes} 1107135446Strhodes 1108193149Sdougbunsigned int 1109193149Sdougbisc_entropy_status(isc_entropy_t *ent) { 1110193149Sdougb unsigned int estimate; 1111193149Sdougb 1112193149Sdougb LOCK(&ent->lock); 1113193149Sdougb estimate = ent->pool.entropy; 1114193149Sdougb UNLOCK(&ent->lock); 1115193149Sdougb 1116193149Sdougb return estimate; 1117193149Sdougb} 1118193149Sdougb 1119135446Strhodesvoid 1120135446Strhodesisc_entropy_attach(isc_entropy_t *ent, isc_entropy_t **entp) { 1121135446Strhodes REQUIRE(VALID_ENTROPY(ent)); 1122135446Strhodes REQUIRE(entp != NULL && *entp == NULL); 1123135446Strhodes 1124135446Strhodes LOCK(&ent->lock); 1125135446Strhodes 1126135446Strhodes ent->refcnt++; 1127135446Strhodes *entp = ent; 1128135446Strhodes 1129135446Strhodes UNLOCK(&ent->lock); 1130135446Strhodes} 1131135446Strhodes 1132135446Strhodesvoid 1133135446Strhodesisc_entropy_detach(isc_entropy_t **entp) { 1134135446Strhodes isc_entropy_t *ent; 1135135446Strhodes isc_boolean_t killit; 1136135446Strhodes 1137135446Strhodes REQUIRE(entp != NULL && VALID_ENTROPY(*entp)); 1138135446Strhodes ent = *entp; 1139135446Strhodes *entp = NULL; 1140135446Strhodes 1141135446Strhodes LOCK(&ent->lock); 1142135446Strhodes 1143135446Strhodes REQUIRE(ent->refcnt > 0); 1144135446Strhodes ent->refcnt--; 1145135446Strhodes 1146135446Strhodes killit = destroy_check(ent); 1147135446Strhodes 1148135446Strhodes UNLOCK(&ent->lock); 1149135446Strhodes 1150135446Strhodes if (killit) 1151135446Strhodes destroy(&ent); 1152135446Strhodes} 1153135446Strhodes 1154135446Strhodesstatic isc_result_t 1155135446Strhodeskbdstart(isc_entropysource_t *source, void *arg, isc_boolean_t blocking) { 1156135446Strhodes /* 1157135446Strhodes * The intent of "first" is to provide a warning message only once 1158135446Strhodes * during the run of a program that might try to gather keyboard 1159135446Strhodes * entropy multiple times. 1160135446Strhodes */ 1161135446Strhodes static isc_boolean_t first = ISC_TRUE; 1162135446Strhodes 1163135446Strhodes UNUSED(arg); 1164135446Strhodes 1165135446Strhodes if (! blocking) 1166135446Strhodes return (ISC_R_NOENTROPY); 1167135446Strhodes 1168135446Strhodes if (first) { 1169135446Strhodes if (source->warn_keyboard) 1170135446Strhodes fprintf(stderr, "You must use the keyboard to create " 1171135446Strhodes "entropy, since your system is lacking\n" 1172135446Strhodes "/dev/random (or equivalent)\n\n"); 1173135446Strhodes first = ISC_FALSE; 1174135446Strhodes } 1175135446Strhodes fprintf(stderr, "start typing:\n"); 1176135446Strhodes 1177135446Strhodes return (isc_keyboard_open(&source->kbd)); 1178135446Strhodes} 1179135446Strhodes 1180135446Strhodesstatic void 1181135446Strhodeskbdstop(isc_entropysource_t *source, void *arg) { 1182135446Strhodes 1183135446Strhodes UNUSED(arg); 1184135446Strhodes 1185135446Strhodes if (! isc_keyboard_canceled(&source->kbd)) 1186135446Strhodes fprintf(stderr, "stop typing.\r\n"); 1187135446Strhodes 1188135446Strhodes (void)isc_keyboard_close(&source->kbd, 3); 1189135446Strhodes} 1190135446Strhodes 1191135446Strhodesstatic isc_result_t 1192135446Strhodeskbdget(isc_entropysource_t *source, void *arg, isc_boolean_t blocking) { 1193135446Strhodes isc_result_t result; 1194135446Strhodes isc_time_t t; 1195135446Strhodes isc_uint32_t sample; 1196135446Strhodes isc_uint32_t extra; 1197135446Strhodes unsigned char c; 1198135446Strhodes 1199135446Strhodes UNUSED(arg); 1200135446Strhodes 1201135446Strhodes if (!blocking) 1202135446Strhodes return (ISC_R_NOTBLOCKING); 1203135446Strhodes 1204135446Strhodes result = isc_keyboard_getchar(&source->kbd, &c); 1205135446Strhodes if (result != ISC_R_SUCCESS) 1206135446Strhodes return (result); 1207135446Strhodes 1208135446Strhodes TIME_NOW(&t); 1209135446Strhodes 1210135446Strhodes sample = isc_time_nanoseconds(&t); 1211135446Strhodes extra = c; 1212135446Strhodes 1213135446Strhodes result = isc_entropy_addcallbacksample(source, sample, extra); 1214135446Strhodes if (result != ISC_R_SUCCESS) { 1215135446Strhodes fprintf(stderr, "\r\n"); 1216135446Strhodes return (result); 1217135446Strhodes } 1218135446Strhodes 1219135446Strhodes fprintf(stderr, "."); 1220135446Strhodes fflush(stderr); 1221135446Strhodes 1222135446Strhodes return (result); 1223135446Strhodes} 1224135446Strhodes 1225135446Strhodesisc_result_t 1226135446Strhodesisc_entropy_usebestsource(isc_entropy_t *ectx, isc_entropysource_t **source, 1227135446Strhodes const char *randomfile, int use_keyboard) 1228135446Strhodes{ 1229135446Strhodes isc_result_t result; 1230135446Strhodes isc_result_t final_result = ISC_R_NOENTROPY; 1231135446Strhodes isc_boolean_t userfile = ISC_TRUE; 1232135446Strhodes 1233135446Strhodes REQUIRE(VALID_ENTROPY(ectx)); 1234135446Strhodes REQUIRE(source != NULL && *source == NULL); 1235135446Strhodes REQUIRE(use_keyboard == ISC_ENTROPY_KEYBOARDYES || 1236135446Strhodes use_keyboard == ISC_ENTROPY_KEYBOARDNO || 1237135446Strhodes use_keyboard == ISC_ENTROPY_KEYBOARDMAYBE); 1238135446Strhodes 1239135446Strhodes#ifdef PATH_RANDOMDEV 1240135446Strhodes if (randomfile == NULL) { 1241135446Strhodes randomfile = PATH_RANDOMDEV; 1242135446Strhodes userfile = ISC_FALSE; 1243135446Strhodes } 1244135446Strhodes#endif 1245135446Strhodes 1246135446Strhodes if (randomfile != NULL && use_keyboard != ISC_ENTROPY_KEYBOARDYES) { 1247135446Strhodes result = isc_entropy_createfilesource(ectx, randomfile); 1248135446Strhodes if (result == ISC_R_SUCCESS && 1249135446Strhodes use_keyboard == ISC_ENTROPY_KEYBOARDMAYBE) 1250135446Strhodes use_keyboard = ISC_ENTROPY_KEYBOARDNO; 1251135446Strhodes if (result != ISC_R_SUCCESS && userfile) 1252135446Strhodes return (result); 1253135446Strhodes 1254135446Strhodes final_result = result; 1255135446Strhodes } 1256135446Strhodes 1257135446Strhodes if (use_keyboard != ISC_ENTROPY_KEYBOARDNO) { 1258135446Strhodes result = isc_entropy_createcallbacksource(ectx, kbdstart, 1259135446Strhodes kbdget, kbdstop, 1260135446Strhodes NULL, source); 1261135446Strhodes if (result == ISC_R_SUCCESS) 1262135446Strhodes (*source)->warn_keyboard = 1263135446Strhodes ISC_TF(use_keyboard == 1264135446Strhodes ISC_ENTROPY_KEYBOARDMAYBE); 1265135446Strhodes 1266135446Strhodes if (final_result != ISC_R_SUCCESS) 1267135446Strhodes final_result = result; 1268193149Sdougb } 1269135446Strhodes 1270135446Strhodes /* 1271135446Strhodes * final_result is ISC_R_SUCCESS if at least one source of entropy 1272135446Strhodes * could be started, otherwise it is the error from the most recently 1273135446Strhodes * failed operation (or ISC_R_NOENTROPY if PATH_RANDOMDEV is not 1274135446Strhodes * defined and use_keyboard is ISC_ENTROPY_KEYBOARDNO). 1275135446Strhodes */ 1276135446Strhodes return (final_result); 1277135446Strhodes} 1278