From 96e79f7ede9fd09c79ac6452ab09e4e48b288e4d Mon Sep 17 00:00:00 2001 From: Michael Graff Date: Wed, 21 Jun 2000 00:04:57 +0000 Subject: [PATCH] Fully implement callback sources, and add a test program to test it. --- bin/tests/.cvsignore | 1 + bin/tests/Makefile.in | 6 ++ bin/tests/entropy2_test.c | 152 ++++++++++++++++++++++++++++++ lib/isc/include/isc/entropy.h | 28 ++++-- lib/isc/include/isc/result.h | 4 +- lib/isc/result.c | 3 +- lib/isc/unix/entropy.c | 168 ++++++++++++++++++---------------- 7 files changed, 270 insertions(+), 92 deletions(-) create mode 100644 bin/tests/entropy2_test.c diff --git a/bin/tests/.cvsignore b/bin/tests/.cvsignore index 7397c534bd..cf2e86e384 100644 --- a/bin/tests/.cvsignore +++ b/bin/tests/.cvsignore @@ -10,6 +10,7 @@ db_test dispatch_tcp_test dispatch_test entropy_test +entropy2_test fsaccess_test gxba_test gxbn_test diff --git a/bin/tests/Makefile.in b/bin/tests/Makefile.in index f3f3d9f76f..27d3f8e37b 100644 --- a/bin/tests/Makefile.in +++ b/bin/tests/Makefile.in @@ -50,6 +50,7 @@ XTARGETS = adb_test \ dispatch_tcp_test \ dispatch_test \ entropy_test \ + entropy2_test \ gxba_test \ gxbn_test \ hash_test \ @@ -90,6 +91,7 @@ SRCS = adb_test.c \ dispatch_tcp_test.c \ dispatch_test.c \ entropy_test.c \ + entropy2_test.c \ gxba_test.c \ gxbn_test.c \ hash_test.c \ @@ -171,6 +173,10 @@ entropy_test: entropy_test.@O@ ${ISCDEPLIBS} ${LIBTOOL} ${CC} ${CFLAGS} -o $@ entropy_test.@O@ \ ${ISCLIBS} ${LIBS} +entropy2_test: entropy2_test.@O@ ${ISCDEPLIBS} + ${LIBTOOL} ${CC} ${CFLAGS} -o $@ entropy2_test.@O@ \ + ${ISCLIBS} ${LIBS} + sock_test: sock_test.@O@ ${ISCDEPLIBS} ${LIBTOOL} ${CC} ${CFLAGS} -o $@ sock_test.@O@ \ ${ISCLIBS} ${LIBS} diff --git a/bin/tests/entropy2_test.c b/bin/tests/entropy2_test.c new file mode 100644 index 0000000000..99f22e2aff --- /dev/null +++ b/bin/tests/entropy2_test.c @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2000 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include +#include +#include +#include + +#include + +static void +hex_dump(const char *msg, void *data, unsigned int length) { + unsigned int len; + unsigned char *base; + isc_boolean_t first = ISC_TRUE; + + base = data; + + printf("DUMP of %d bytes: %s\n\t", length, msg); + for (len = 0 ; len < length ; len++) { + if (len % 16 == 0 && !first) + printf("\n\t"); + printf("%02x ", base[len]); + first = ISC_FALSE; + } + printf("\n"); +} + +static void +CHECK(const char *msg, isc_result_t result) { + if (result != ISC_R_SUCCESS) { + printf("FAILURE: %s: %s\n", msg, isc_result_totext(result)); + exit(1); + } +} + +static isc_result_t +start(isc_entropysource_t *source, void *arg, isc_boolean_t blocking) +{ + printf("start called, non-blocking mode.\n"); + + return (ISC_R_SUCCESS); +} + +static void +stop(isc_entropysource_t *source, void *arg) { + printf("stop called\n"); +} + +/* + * This function is by no way a good one to actually add entropy into + * the system. It is intended to fool the entropy system into beliving + * there are actual bits from us. + */ +static isc_result_t +get(isc_entropysource_t *source, void *arg, isc_boolean_t blocking) { + isc_result_t result; + static isc_uint32_t val = 1; + static int count = 0; + + /* + * Here, we should check to see if we are in blocking mode or not. + * If we will block and the application asked us not to, + * we should return an error instead, rather than block. + */ + if (!blocking) { + count++; + if (count > 6) + return (ISC_R_NOENTROPY); + } + + do { + if (val == 0) + val = 0x12345678; + val <<= 3; + val %= 100000; + + result = isc_entropy_addcallbacksample(source, val, 0); + } while (result == ISC_R_SUCCESS); + + return (result); +} + +int +main(int argc, char **argv) { + isc_mem_t *mctx; + unsigned char buffer[512]; + isc_entropy_t *ent; + isc_entropysource_t *source; + unsigned int returned; + unsigned int flags; + isc_result_t result; + + UNUSED(argc); + UNUSED(argv); + + mctx = NULL; + CHECK("isc_mem_create()", + isc_mem_create(0, 0, &mctx)); + + ent = NULL; + CHECK("isc_entropy_create()", + isc_entropy_create(mctx, &ent)); + + isc_entropy_stats(ent, stderr); + + source = NULL; + result = isc_entropy_createcallbacksource(ent, start, get, stop, NULL, + &source); + CHECK("isc_entropy_createcallbacksource()", result); + + fprintf(stderr, + "Reading 32 bytes of GOOD random data only, partial OK\n"); + + flags = 0; + flags |= ISC_ENTROPY_GOODONLY; + flags |= ISC_ENTROPY_PARTIAL; +#if 0 + flags |= ISC_ENTROPY_BLOCKING; +#endif + returned = 0; + result = isc_entropy_getdata(ent, buffer, 32, &returned, flags); + if (result == ISC_R_NOENTROPY) { + fprintf(stderr, "No entropy.\n"); + } + hex_dump("good data only:", buffer, returned); + + isc_entropy_stats(ent, stderr); + + isc_entropy_destroysource(&source); + isc_entropy_detach(&ent); + + isc_mem_stats(mctx, stderr); + isc_mem_destroy(&mctx); + + return (0); +} + diff --git a/lib/isc/include/isc/entropy.h b/lib/isc/include/isc/entropy.h index e997a52529..32b308c56d 100644 --- a/lib/isc/include/isc/entropy.h +++ b/lib/isc/include/isc/entropy.h @@ -67,15 +67,11 @@ ISC_LANG_BEGINDECLS /* * Entropy callback function. */ -typedef isc_result_t (*isc_entropystart_t)(isc_entropy_t *ent, - isc_entropysource_t *source, - void *arg); -typedef isc_result_t (*isc_entropyget_t)(isc_entropy_t *ent, - isc_entropysource_t *source, - void *arg); -typedef void (*isc_entropystop_t)(isc_entropy_t *ent, - isc_entropysource_t *source, - void *arg); +typedef isc_result_t (*isc_entropystart_t)(isc_entropysource_t *source, + void *arg, isc_boolean_t blocking); +typedef isc_result_t (*isc_entropyget_t)(isc_entropysource_t *source, + void *arg, isc_boolean_t blocking); +typedef void (*isc_entropystop_t)(isc_entropysource_t *source, void *arg); /*** *** Flags. @@ -179,12 +175,23 @@ isc_entropy_createcallbacksource(isc_entropy_t *ent, * Create an entropy source that is polled via a callback. This would * be used when keyboard input is used, or a GUI input method. It can * also be used to hook in any external entropy source. + * + * Samples are added via isc_entropy_addcallbacksample(), below. + * _addcallbacksample() is the only function which may be called from + * within an entropy API callback function. */ void isc_entropy_stopcallbacksources(isc_entropy_t *ent); +/* + * Call the stop functions for callback sources that have had their + * start functions called. + */ isc_result_t +isc_entropy_addcallbacksample(isc_entropysource_t *source, isc_uint32_t sample, + isc_uint32_t extra); +isc_result_t isc_entropy_addsample(isc_entropysource_t *source, isc_uint32_t sample, isc_uint32_t extra); /* @@ -195,6 +202,9 @@ 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. + * + * When in an entropy API callback function, _addcallbacksource() must be + * used. At all other times, _addsample() must be used. */ isc_result_t diff --git a/lib/isc/include/isc/result.h b/lib/isc/include/isc/result.h index 3eeadc5a3a..fb199580cf 100644 --- a/lib/isc/include/isc/result.h +++ b/lib/isc/include/isc/result.h @@ -67,8 +67,8 @@ #define ISC_R_MULTICAST 43 /* invalid use of multicast */ #define ISC_R_NOTFILE 44 /* not a file */ #define ISC_R_NOTDIRECTORY 45 /* not a directory */ - -#define ISC_R_NRESULTS 46 /* Number of results */ +#define ISC_R_QUEUEFULL 46 /* queue is full */ +#define ISC_R_NRESULTS 47 /* Number of results */ ISC_LANG_BEGINDECLS diff --git a/lib/isc/result.c b/lib/isc/result.c index de478cf24a..bc6fa5c1dd 100644 --- a/lib/isc/result.c +++ b/lib/isc/result.c @@ -81,7 +81,8 @@ static const char *text[ISC_R_NRESULTS] = { "out of entropy", /* 42 */ "invalid use of multicast address", /* 43 */ "not a file", /* 44 */ - "not a directory" /* 45 */ + "not a directory", /* 45 */ + "queue is full" /* 46 */ }; #define ISC_RESULT_RESULTSET 2 diff --git a/lib/isc/unix/entropy.c b/lib/isc/unix/entropy.c index ffc84762dd..ae3ecd2ead 100644 --- a/lib/isc/unix/entropy.c +++ b/lib/isc/unix/entropy.c @@ -177,6 +177,8 @@ samplesource_allocate(isc_entropy_t *ent, sample_queue_t *sq) { return (ISC_R_NOMEMORY); } + sq->nsamples = 0; + return (ISC_R_SUCCESS); } @@ -359,20 +361,35 @@ get_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) { } static unsigned int -get_from_callback(isc_entropysource_t *source, unsigned int desired) { +get_from_callback(isc_entropysource_t *source, unsigned int desired, + isc_boolean_t blocking) +{ isc_entropy_t *ent = source->ent; isc_cbsource_t *cbs = &source->sources.callback; unsigned int added; + unsigned int got; isc_result_t result; - added = 0; - while (desired > 0) { - if (!cbs->start_called && cbs->startfunc != NULL) - cbs->startfunc(ent, source, cbs->arg); - result = cbs->getfunc(ent, source, cbs->arg); - added += crunchsamples(ent, &cbs->samplequeue); + if (desired == 0) + return (0); - desired -= added; + if (!cbs->start_called && cbs->startfunc != NULL) { + result = cbs->startfunc(source, cbs->arg, blocking); + if (result != ISC_R_SUCCESS) + return (0); + cbs->start_called = ISC_TRUE; + } + + added = 0; + result = ISC_R_SUCCESS; + while (desired > 0 && result == ISC_R_SUCCESS) { + result = cbs->getfunc(source, cbs->arg, blocking); + if (result == ISC_R_QUEUEFULL) { + got = crunchsamples(ent, &cbs->samplequeue); + added += got; + desired -= ISC_MIN(got, desired); + result = ISC_R_SUCCESS; + } } return (added); @@ -415,22 +432,13 @@ fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) { * to build up. */ if (needed == 0) { - isc_uint32_t needed_ent, needed_ps; - REQUIRE(!blocking); if ((ent->pool.entropy >= RND_POOLBITS / 4) && (ent->pool.pseudo <= RND_POOLBITS / 4)) return; - needed_ent = 0; - needed_ps = 0; - if (ent->pool.entropy < THRESHOLD_BITS) - needed_ent = THRESHOLD_BITS; - if (ent->pool.pseudo > RND_POOLBITS / 4) - needed_ps = THRESHOLD_BITS; - needed = ISC_MAX(needed_ent, needed_ps); - if (needed == 0 && ent->initialized >= THRESHOLD_BITS) - return; + + needed = THRESHOLD_BITS * 4; } else { needed = ISC_MAX(needed, THRESHOLD_BITS); needed = ISC_MIN(needed, RND_POOLBITS); @@ -479,10 +487,7 @@ fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) { added += got; - if (remaining > got) - remaining -= got; - else - remaining = 0; + remaining -= ISC_MIN(remaining, got); source = ISC_LIST_NEXT(source, link); if (source == NULL) @@ -507,25 +512,19 @@ fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) { * check to see if we have a callback source. If so, call them. */ source = ISC_LIST_HEAD(ent->sources); - while (remaining != 0 && source != NULL) { + while ((remaining != 0) && (source != NULL)) { unsigned int got; got = 0; if (source->type == ENTROPY_SOURCETYPE_CALLBACK) - got = get_from_callback(source, remaining); + got = get_from_callback(source, remaining, blocking); added += got; + remaining -= ISC_MIN(remaining, got); - if (remaining > got) - remaining -= got; - else - remaining = 0; - -#if 0 if (added >= needed) break; -#endif source = ISC_LIST_NEXT(source, link); } @@ -601,28 +600,6 @@ isc_entropy_getdata(isc_entropy_t *ent, void *data, unsigned int length, 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); - - /* - * To extract good data, we need to have at least - * enough entropy to fill our digest. - */ - if (ent->pool.entropy < THRESHOLD_BITS) { - UNLOCK(&ent->lock); - return (ISC_R_NOENTROPY); - } - } - remain = length; buf = data; total = 0; @@ -652,9 +629,12 @@ isc_entropy_getdata(isc_entropy_t *ent, void *data, unsigned int length, else fillpool(ent, fillcount, blocking); - if (!partial) - if (ent->pool.entropy < THRESHOLD_BITS) + if (ent->pool.entropy < THRESHOLD_BITS) { + if (!blocking && !partial) goto zeroize; + else if (partial) + goto partial_output; + } } else { /* * If we've extracted half our pool size in bits @@ -696,6 +676,8 @@ isc_entropy_getdata(isc_entropy_t *ent, void *data, unsigned int length, add_pseudo(ent, count * 8); } + partial_output: + memset(digest, 0, sizeof(digest)); if (returned != NULL) @@ -809,8 +791,10 @@ destroysource(isc_entropysource_t **sourcep) { break; case ENTROPY_SOURCETYPE_CALLBACK: cbs = &source->sources.callback; - if (cbs->start_called && cbs->stopfunc != NULL) - cbs->stopfunc(ent, source, cbs->arg); + if (cbs->start_called && cbs->stopfunc != NULL) { + cbs->stopfunc(source, cbs->arg); + cbs->start_called = ISC_FALSE; + } samplequeue_release(ent, &cbs->samplequeue); break; } @@ -1049,6 +1033,8 @@ isc_entropy_createcallbacksource(isc_entropy_t *ent, ISC_LIST_APPEND(ent->sources, source, link); ent->nsources++; + *sourcep = source; + UNLOCK(&ent->lock); return (ISC_R_SUCCESS); @@ -1074,8 +1060,10 @@ isc_entropy_stopcallbacksources(isc_entropy_t *ent) { while (source != NULL) { if (source->type == ENTROPY_SOURCETYPE_CALLBACK) { cbs = &source->sources.callback; - if (cbs->start_called && cbs->stopfunc != NULL) - cbs->stopfunc(ent, source, cbs->arg); + if (cbs->start_called && cbs->stopfunc != NULL) { + cbs->stopfunc(source, cbs->arg); + cbs->start_called = ISC_FALSE; + } } source = ISC_LIST_NEXT(source, link); @@ -1124,6 +1112,8 @@ isc_entropy_createsamplesource(isc_entropy_t *ent, ISC_LIST_APPEND(ent->sources, source, link); ent->nsources++; + *sourcep = source; + UNLOCK(&ent->lock); return (ISC_R_SUCCESS); @@ -1189,7 +1179,7 @@ crunchsamples(isc_entropy_t *ent, sample_queue_t *sq) { unsigned int ns; unsigned int added; - if (sq->nsamples < 5) + if (sq->nsamples < 6) return (0); added = 0; @@ -1210,6 +1200,17 @@ crunchsamples(isc_entropy_t *ent, sample_queue_t *sq) { entropypool_adddata(ent, sq->samples, sq->nsamples * 4, added); entropypool_adddata(ent, sq->extra, sq->nsamples * 4, 0); + /* + * Move the last 4 samples into the first 4 positions, and start + * adding new samples from that point. + */ + for (ns = 0 ; ns < 4 ; ns++) { + sq->samples[ns] = sq->samples[sq->nsamples - 4 + ns]; + sq->extra[ns] = sq->extra[sq->nsamples - 4 + ns]; + } + + sq->nsamples = 0; + return (added); } @@ -1228,9 +1229,9 @@ addsample(sample_queue_t *sq, isc_uint32_t sample, isc_uint32_t extra) { sq->nsamples++; if (sq->nsamples >= RND_EVENTQSIZE) - return (ISC_R_SUCCESS); + return (ISC_R_QUEUEFULL); - return (ISC_R_NOENTROPY); + return (ISC_R_SUCCESS); } isc_result_t @@ -1248,23 +1249,11 @@ isc_entropy_addsample(isc_entropysource_t *source, isc_uint32_t sample, LOCK(&ent->lock); - switch (source->type) { - case ENTROPY_SOURCETYPE_SAMPLE: - sq = &source->sources.sample.samplequeue; - result = addsample(sq, sample, extra); - if (result == ISC_R_SUCCESS) { - entropy = crunchsamples(ent, sq); - add_entropy(ent, entropy); - } - break; - case ENTROPY_SOURCETYPE_CALLBACK: - sq = &source->sources.sample.samplequeue; - result = addsample(sq, sample, extra); - break; - default: - result = ISC_R_UNEXPECTED; - REQUIRE(source->type == ENTROPY_SOURCETYPE_SAMPLE - || source->type == ENTROPY_SOURCETYPE_CALLBACK); + sq = &source->sources.sample.samplequeue; + result = addsample(sq, sample, extra); + if (result == ISC_R_QUEUEFULL) { + entropy = crunchsamples(ent, sq); + add_entropy(ent, entropy); } UNLOCK(&ent->lock); @@ -1272,6 +1261,25 @@ isc_entropy_addsample(isc_entropysource_t *source, isc_uint32_t sample, return (result); } +isc_result_t +isc_entropy_addcallbacksample(isc_entropysource_t *source, isc_uint32_t sample, + isc_uint32_t extra) +{ + isc_entropy_t *ent; + sample_queue_t *sq; + isc_result_t result; + + REQUIRE(VALID_SOURCE(source)); + REQUIRE(source->type == ENTROPY_SOURCETYPE_CALLBACK); + + ent = source->ent; + + sq = &source->sources.callback.samplequeue; + result = addsample(sq, sample, extra); + + return (result); +} + void isc_entropy_putdata(isc_entropy_t *ent, void *data, unsigned int length, isc_uint32_t entropy)