From 60a7475dd868cb0bc4796378a17f0810b606653b Mon Sep 17 00:00:00 2001 From: Michael Graff Date: Thu, 8 Jun 2000 22:18:53 +0000 Subject: [PATCH] checkpoint --- lib/isc/include/isc/entropy.h | 22 ++- lib/isc/unix/entropy.c | 281 ++++++++++++++++++++++++++-------- 2 files changed, 236 insertions(+), 67 deletions(-) diff --git a/lib/isc/include/isc/entropy.h b/lib/isc/include/isc/entropy.h index 1f57bf93bd..a625ac0c5a 100644 --- a/lib/isc/include/isc/entropy.h +++ b/lib/isc/include/isc/entropy.h @@ -77,8 +77,23 @@ ISC_LANG_BEGINDECLS * Extract only "good" data; return failure if there is not enough * data available and there are no sources which we can poll to get * data, or those sources are empty. + * + * _PARTIAL + * Extract as much good data as possible, but if there isn't enough + * at hand, return what is available. This flag only makes sense + * when used with _GOODONLY. + * + * _BLOCKING + * Block the task until data is available. This is contrary to the + * ISC task system, where tasks should never block. However, if + * this is a special purpose application where blocking a task is + * acceptable (say, an offline zone signer) this flag may be set. + * This flag only makes sense when used with _GOODONLY, and will + * block regardless of the setting for _PARTIAL. */ #define ISC_ENTROPY_GOODONLY 0x00000001U +#define ISC_ENTROPY_PARTIAL 0x00000002U +#define ISC_ENTROPY_BLOCKING 0x00000004U /* * _ESTIMATE @@ -150,7 +165,7 @@ isc_entropy_createsamplesource(isc_entropy_t *ent, void isc_entropy_addsample(isc_entropysource_t *source, isc_uint32_t sample, - isc_uint32_t extra, isc_boolean_t has_entropy); + isc_uint32_t extra); /* * Add a sample to the sample source. The sample MUST be a timestamp * that increases over time, with the exception of wrap-around for @@ -159,10 +174,6 @@ isc_entropy_addsample(isc_entropysource_t *source, isc_uint32_t sample, * * The "extra" parameter is used only to add a bit more unpredictable * data. It is not used other than included in the hash of samples. - * - * If "has_entropy" is ISC_TRUE, entropy calculations are performed on - * this data, otherwise the sample is assumed to contain no entropy - * and is simply added to the sample list. */ isc_result_t @@ -175,5 +186,4 @@ isc_entropy_getdata(isc_entropy_t *ent, void *data, unsigned int length, ISC_LANG_ENDDECLS - #endif /* ISC_BUFFER_H */ diff --git a/lib/isc/unix/entropy.c b/lib/isc/unix/entropy.c index 315af3ae7f..25bf8d1928 100644 --- a/lib/isc/unix/entropy.c +++ b/lib/isc/unix/entropy.c @@ -17,7 +17,9 @@ #include +#include #include +#include #include #include @@ -34,6 +36,9 @@ * written by Michael Graff . */ +#define VALID_ENTROPY(e) ((e) != NULL) +#define VALID_SOURCE(s) ((s) != NULL) + /*** *** "constants." Do not change these unless you _really_ know what *** you are doing. @@ -86,17 +91,19 @@ typedef struct { struct isc_entropysource { unsigned int type; + isc_entropy_t *ent; unsigned int flags; /* flags */ isc_uint32_t total; /* entropy from this source */ + ISC_LINK(isc_entropysource_t) link; char name[32]; union { - isc_entropysamplesource_t samplesource; - isc_entropyfilesource_t filesource; + isc_entropysamplesource_t sample; + isc_entropyfilesource_t file; } sources; }; -#define RND_TYPE_SAMPLE 1 /* Type is a sample source */ -#define RND_TYPE_FILE 2 /* Type is a file source */ +#define ENTROPY_SOURCETYPE_SAMPLE 1 /* Type is a sample source */ +#define ENTROPY_SOURCETYPE_FILE 2 /* Type is a file source */ /* * The random pool "taps" @@ -110,6 +117,11 @@ struct isc_entropysource { static inline void entropypool_add_word(isc_entropypool_t *, isc_uint32_t); +static void +fillpool(isc_entropy_t *, unsigned int, isc_boolean_t); + +#define ENTROPY(ent) ((ent)->pool.entropy) + /* * Add one word to the pool, rotating the input as needed. */ @@ -147,9 +159,11 @@ entropypool_add_word(isc_entropypool_t *rp, isc_uint32_t val) } /* - * add a buffer's worth of data to the pool. + * Add a buffer's worth of data to the pool. + * + * Requires that the lock is held on the entropy pool. */ -void +static void entropypool_adddata(isc_entropypool_t *rp, void *p, unsigned int len, isc_uint32_t entropy) { @@ -204,6 +218,14 @@ entropypool_adddata(isc_entropypool_t *rp, void *p, unsigned int len, rp->entropy = RND_POOLBITS; } +/* + * Poll each source, trying to get data from it to stuff into the entropy + * pool. + */ +static void +fillpool(isc_entropy_t *ent, unsigned int needed, isc_boolean_t blocking) { +} + /* * Extract some number of bytes from the random pool, decreasing the * estimate of randomness as each byte is extracted. @@ -213,77 +235,103 @@ entropypool_adddata(isc_entropypool_t *rp, void *p, unsigned int len, * xored together before returned. * * Honor the request from the caller to only return good data, any data, - * etc. Note that we must have at least 80 bits of entropy in the pool - * before we return anything in the high-quality modes. + * etc. */ -int -entropypool_extract(isc_entropypool_t *rp, void *p, unsigned int len, - unsigned int mode) +isc_result_t +isc_entropy_getdata(isc_entropy_t *ent, void *data, unsigned int length, + unsigned int *returned, unsigned int flags) { unsigned int i; isc_sha1_t hash; unsigned char digest[ISC_SHA1_DIGESTLENGTH]; - isc_uint32_t remain, deltae, count; + isc_uint32_t remain, deltae, count, total; isc_uint8_t *buf; - int good; + isc_boolean_t goodonly, partial, blocking; - buf = p; - remain = len; + REQUIRE(VALID_ENTROPY(ent)); + REQUIRE(data != NULL); + REQUIRE(length > 0); - if ((mode & ISC_ENTROPY_GOODONLY) == 0) - good = 1; - else - good = (rp->entropy >= (8 * RND_ENTROPY_THRESHOLD)); + goodonly = ISC_TF((flags & ISC_ENTROPY_GOODONLY) != 0); + partial = ISC_TF((flags & ISC_ENTROPY_PARTIAL) != 0); + blocking = ISC_TF((flags & ISC_ENTROPY_BLOCKING) != 0); + + LOCK(&ent->lock); + + /* + * If we are blocking, we will block when actually extracting data. + * Otherwise, if we cannot block, there is a limit on how much data + * we can actually extract if good data is required. + * + * Here, clamp length to be the amount of data we can extract + * if goodonly and partial are both set, otherwise return an + * error. + */ + if (goodonly && !blocking) { + fillpool(ent, length * 8, ISC_FALSE); - while (good && (remain != 0)) { /* - * While bytes are requested, compute the hash of the pool, - * and then "fold" the hash in half with XOR, keeping the - * exact hash value secret, as it will be stirred back into - * the pool. - * - * XXX this approach needs examination by competant - * cryptographers! It's rather expensive per bit but - * also involves every bit of the pool in the - * computation of every output bit.. + * To extract good data, we need to have at least + * enough entropy to fill our digest. */ + if (ENTROPY(ent) < RND_ENTROPY_THRESHOLD * 8) { + UNLOCK(&ent->lock); + return (ISC_R_NOENTROPY); + } + } + + remain = length; + buf = data; + total = 0; + while (remain != 0) { + count = ISC_MIN(remain, RND_ENTROPY_THRESHOLD); + + /* Try to get more entropy if we need it. */ + if (goodonly) { + fillpool(ent, remain * 8, blocking); + if (!partial && (ENTROPY(ent) < count * 8)) + goto zeroize; + } + isc_sha1_init(&hash); - isc_sha1_update(&hash, (unsigned char *)rp->pool, + isc_sha1_update(&hash, (void *)(ent->pool.pool), RND_POOLWORDS * 4); isc_sha1_final(&hash, digest); /* - * Stir the hash back into the pool. This guarantees - * that the next hash will generate a different value - * if no new values were added to the pool. + * Stir the extracted data (all of it) back into the pool. */ - for (i = 0 ; i < 5 ; i++) { - isc_uint32_t word; - memcpy(&word, &digest[i * 4], 4); - entropypool_add_word(rp, word); - } - - count = ISC_MIN(remain, RND_ENTROPY_THRESHOLD); + entropypool_adddata(&ent->pool, digest, ISC_SHA1_DIGESTLENGTH, + 0); for (i = 0; i < count; i++) - buf[i] = digest[i] ^ digest[ i +RND_ENTROPY_THRESHOLD]; + buf[i] = digest[i] ^ digest[i + RND_ENTROPY_THRESHOLD]; buf += count; - deltae = count * 8; remain -= count; - deltae = ISC_MIN(deltae, rp->entropy); - - rp->entropy -= deltae; - - if ((mode & ISC_ENTROPY_GOODONLY) == 0) - good = (rp->entropy >= (8 * RND_ENTROPY_THRESHOLD)); + deltae = count * 8; + deltae = ISC_MIN(deltae, ENTROPY(ent)); + ent->pool.entropy -= deltae; + total += deltae; } - - memset(&hash, 0, sizeof(hash)); + memset(digest, 0, sizeof(digest)); - return (len - remain); + if (*returned != NULL) + *returned = length; + + return (ISC_R_SUCCESS); + + zeroize: + /* put the entropy we almost extracted back */ + entropypool_adddata(&ent->pool, data, length, total); + memset(data, 0, length); + memset(digest, 0, sizeof(digest)); + if (*returned != NULL) + *returned = 0; + + return (ISC_R_NOENTROPY); } static void @@ -360,17 +408,136 @@ isc_entropy_destroy(isc_entropy_t **entp) { memset(ent, 0, sizeof(isc_entropy_t)); } +/* + * Make a fd non-blocking + */ +static isc_result_t +make_nonblock(int fd) { + int ret; + int flags; + + flags = fcntl(fd, F_GETFL, 0); + flags |= O_NONBLOCK; + ret = fcntl(fd, F_SETFL, flags); + + if (ret == -1) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "fcntl(%d, F_SETFL, %d): %s", + fd, flags, strerror(errno)); + + return (ISC_R_UNEXPECTED); + } + + return (ISC_R_SUCCESS); +} + isc_result_t isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname, unsigned int flags, isc_entropysource_t **sourcep) { + int fd; + isc_result_t ret; + isc_entropysource_t *source; - return (ISC_R_NOTIMPLEMENTED); + REQUIRE(VALID_ENTROPY(ent)); + REQUIRE(fname != NULL); + REQUIRE(sourcep != NULL && *sourcep == NULL); + + LOCK(&ent->lock); + + source = NULL; + fd = -1; + + fd = open(fname, O_RDONLY | O_NONBLOCK, 0); + if (fd < 0) { + ret = ISC_R_IOERROR; + goto errout; + } + + ret = make_nonblock(fd); + if (ret != ISC_R_SUCCESS) + goto errout; + + source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t)); + if (source == NULL) { + ret = ISC_R_NOMEMORY; + goto errout; + } + + /* + * From here down, no failures can occur. + */ + source->type = ENTROPY_SOURCETYPE_FILE; + source->ent = ent; + source->flags = flags; + source->total = 0; + memset(source->name, 0, sizeof(source->name)); + ISC_LINK_INIT(source, link); + source->sources.file.fd = fd; + + /* + * Hook it into the entropy system. + */ + ISC_LIST_APPEND(ent->sources, source, link); + + *sourcep = source; + + UNLOCK(&ent->lock); + return (ISC_R_SUCCESS); + + errout: + if (source != NULL) + isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t)); + if (fd >= 0) + close(fd); + + UNLOCK(&ent->lock); + + return (ret); } void isc_entropy_destroysource(isc_entropysource_t **sourcep) { + isc_entropysource_t *source; + isc_entropy_t *ent; + void *ptr; + int fd; + + REQUIRE(sourcep != NULL); + REQUIRE(VALID_SOURCE(*sourcep)); + + source = *sourcep; + *sourcep = NULL; + + ent = source->ent; + REQUIRE(VALID_ENTROPY(ent)); + + LOCK(&ent->lock); + + ISC_LIST_UNLINK(ent->sources, source, link); + + switch (source->type) { + case ENTROPY_SOURCETYPE_FILE: + fd = source->sources.file.fd; + if (fd >= 0) + close(fd); + break; + case ENTROPY_SOURCETYPE_SAMPLE: + ptr = source->sources.sample.samples; + if (ptr != NULL) + isc_mem_put(ent->mctx, ptr, RND_EVENTQSIZE * 4); + ptr = source->sources.sample.extra; + if (ptr != NULL) + isc_mem_put(ent->mctx, ptr, RND_EVENTQSIZE * 4); + break; + } + + memset(source, 0, sizeof(isc_entropysource_t)); + + isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t)); + + UNLOCK(&ent->lock); } isc_result_t @@ -383,14 +550,6 @@ isc_entropy_createsamplesource(isc_entropy_t *ent, void isc_entropy_addsample(isc_entropysource_t *source, isc_uint32_t sample, - isc_uint32_t extra, isc_boolean_t has_entropy) + isc_uint32_t extra) { } - -isc_result_t -isc_entropy_getdata(isc_entropy_t *ent, void *data, unsigned int length, - unsigned int *returned, unsigned int flags) -{ - - return (ISC_R_NOTIMPLEMENTED); -}