diff --git a/bin/tests/entropy_test.c b/bin/tests/entropy_test.c index ead74e5e2d..2c11d8e418 100644 --- a/bin/tests/entropy_test.c +++ b/bin/tests/entropy_test.c @@ -26,14 +26,16 @@ static void hex_dump(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", length, msg); + printf("DUMP of %d bytes: %s\n\t", length, msg); for (len = 0 ; len < length ; len++) { - if (len % 16 == 0) - printf("\n"); + if (len % 16 == 0 && !first) + printf("\n\t"); printf("%02x ", base[len]); + first = ISC_FALSE; } printf("\n"); } @@ -69,10 +71,20 @@ main(int argc, char **argv) { isc_entropy_stats(ent, stderr); +#if 1 devrandom = NULL; + flags = 0; + flags |= ISC_ENTROPYSOURCE_ISDEVICE; CHECK("isc_entropy_createfilesource()", isc_entropy_createfilesource(ent, "/dev/random", - 0, &devrandom)); + flags, &devrandom)); +#else + devrandom = NULL; + flags = 0; + CHECK("isc_entropy_createfilesource()", + isc_entropy_createfilesource(ent, "/tmp/foo", + flags, &devrandom)); +#endif fprintf(stderr, "Reading 32 bytes of GOOD random data only, partial OK\n"); @@ -83,18 +95,25 @@ main(int argc, char **argv) { result = isc_entropy_getdata(ent, buffer, 32, &returned, flags); if (result == ISC_R_NOENTROPY) { fprintf(stderr, "No entropy.\n"); - goto out; + goto any; } hex_dump("good data only:", buffer, returned); + any: isc_entropy_stats(ent, stderr); - CHECK("isc_entropy_getdata()", isc_entropy_getdata(ent, buffer, 128, NULL, 0)); + hex_dump("pseudorandom data", buffer, 128); - hex_dump("entropy data", buffer, 128); + isc_entropy_stats(ent, stderr); + flags = 0; + flags |= ISC_ENTROPY_GOODONLY; + flags |= ISC_ENTROPY_BLOCKING; + result = isc_entropy_getdata(ent, buffer, sizeof buffer, &returned, + flags); + CHECK("good data only, blocking mode", result); + hex_dump("blocking mode data", buffer, sizeof buffer); -out: { isc_entropy_t *entcopy1 = NULL; isc_entropy_t *entcopy2 = NULL; diff --git a/lib/isc/include/isc/entropy.h b/lib/isc/include/isc/entropy.h index fcc43bddba..83aef83280 100644 --- a/lib/isc/include/isc/entropy.h +++ b/lib/isc/include/isc/entropy.h @@ -96,15 +96,15 @@ ISC_LANG_BEGINDECLS * Estimate the amount of entropy contained in the sample pool. * If this is not set, the source will be gathered and perodically * mixed into the entropy pool, but no increment in contained entropy - * will be assumed. + * will be assumed. This flag only makes sense on sample sources. * - * _POLLABLE - * The entropy source is pollable for more data. This is most useful - * for things like files and devices. It should not be used for - * tty/keyboard data, device timings, etc. + * _ISDEVICE + * The file named is really a device file, and blocking is possible. + * Otherwise, the file is assumed to be a finite length file, and + * any I/O error (including blocking) terminates the source. */ #define ISC_ENTROPYSOURCE_ESTIMATE 0x00000001U -#define ISC_ENTROPYSOURCE_POLLABLE 0x00000002U +#define ISC_ENTROPYSOURCE_ISDEVICE 0x00000002U /*** *** Functions diff --git a/lib/isc/unix/entropy.c b/lib/isc/unix/entropy.c index 7a4ec091bb..749dd70076 100644 --- a/lib/isc/unix/entropy.c +++ b/lib/isc/unix/entropy.c @@ -52,8 +52,8 @@ /* * size of entropy pool in 32-bit words. This _MUST_ be a power of 2. */ -#define RND_POOLWORDS 128 -#define RND_POOLBITS (RND_POOLWORDS * 32) +#define RND_POOLWORDS 512 +#define RND_POOLBITS (RND_POOLWORDS * 32) /* * Number of bytes returned per hash. This must be true: @@ -130,6 +130,9 @@ entropypool_add_word(isc_entropypool_t *, isc_uint32_t); static void fillpool(isc_entropy_t *, unsigned int, isc_boolean_t); +static int +wait_for_sources(isc_entropy_t *); + /* * Add in entropy, even when the value we're adding in could be * very large. @@ -275,10 +278,13 @@ get_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) { int fd = source->sources.file.fd; ssize_t n, ndesired; isc_uint32_t added; + isc_boolean_t isdevice; if (fd == -1) return (0); + isdevice = ISC_TF((source->flags & ISC_ENTROPYSOURCE_ISDEVICE) != 0); + desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0); added = 0; @@ -286,13 +292,17 @@ get_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) { ndesired = ISC_MIN(desired, sizeof(buf)); n = read(fd, buf, ndesired); if (n < 0) { - if (errno == EAGAIN) + if (isdevice && (errno == EAGAIN)) goto out; close(fd); source->sources.file.fd = -1; - } - if (n == 0) goto out; + } + if (n == 0) { + close(fd); + source->sources.file.fd = -1; + goto out; + } entropypool_adddata(ent, buf, n, n * 8); added += n * 8; @@ -329,6 +339,10 @@ fillpool(isc_entropy_t *ent, unsigned int needed, isc_boolean_t blocking) { * Next, if we are asked to add a specific bit of entropy, make * certain that we will do so. Clamp how much we try to add to * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy). + * + * Note that if we are in a blocking mode, we will only try to + * get as much data as we need, not as much as we might want + * to build up. */ if (needed == 0) { isc_uint32_t needed_ent, needed_ps; @@ -350,6 +364,11 @@ fillpool(isc_entropy_t *ent, unsigned int needed, isc_boolean_t blocking) { needed = ISC_MIN(needed, RND_POOLBITS); } + /* + * In any case, clamp how much we need to how much we can add. + */ + needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy); + /* * Poll each file source to see if we can read anything useful from * it. XXXMLG When where are multiple sources, we should keep a @@ -358,25 +377,67 @@ fillpool(isc_entropy_t *ent, unsigned int needed, isc_boolean_t blocking) { * others are always drained. */ + again: source = ISC_LIST_HEAD(ent->sources); added = 0; - while (source != NULL) { - desired = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy); + while (source != NULL && needed > 0) { if (source->type == ENTROPY_SOURCETYPE_FILE) - added += get_from_filesource(source, desired); + added += get_from_filesource(source, needed); - if (added > needed) + if (added >= needed) break; + if (needed > added) + needed -= added; + else + needed = 0; + source = ISC_LIST_NEXT(source, link); } + if (blocking && added < needed) { + if (wait_for_sources(ent) > 0) + goto again; + } + /* * Adjust counts. */ subtract_pseudo(ent, added); } +static int +wait_for_sources(isc_entropy_t *ent) { + isc_entropysource_t *source; + int maxfd, fd; + int cc; + fd_set reads; + + maxfd = -1; + FD_ZERO(&reads); + + source = ISC_LIST_HEAD(ent->sources); + while (source != NULL) { + if (source->type == ENTROPY_SOURCETYPE_FILE) { + fd = source->sources.file.fd; + if (fd >= 0) { + maxfd = ISC_MAX(maxfd, fd); + FD_SET(fd, &reads); + } + } + source = ISC_LIST_NEXT(source, link); + } + + if (maxfd < 0) + return (-1); + + cc = select(maxfd + 1, &reads, NULL, NULL, NULL); + if (cc < 0) + return (-1); + + return (cc); +} + /* * Extract some number of bytes from the random pool, decreasing the * estimate of randomness as each byte is extracted. @@ -444,11 +505,16 @@ isc_entropy_getdata(isc_entropy_t *ent, void *data, unsigned int length, * are not ok. */ if (goodonly) { - fillpool(ent, (length - remain) * 8, blocking); - if (!partial && !blocking + fillpool(ent, remain * 8, blocking); + if (!blocking && !partial && ((ent->pool.entropy < count * 8) - || (ent->pool.entropy < RND_ENTROPY_THRESHOLD * 8))) + || (ent->pool.entropy + < RND_ENTROPY_THRESHOLD * 8))) goto zeroize; + if (blocking + && (ent->pool.entropy + <= RND_ENTROPY_THRESHOLD * 8)) + goto zeroize; } else { /* * If we've extracted half our pool size in bits @@ -477,10 +543,9 @@ isc_entropy_getdata(isc_entropy_t *ent, void *data, unsigned int length, deltae = ISC_MIN(deltae, ent->pool.entropy); total += deltae; subtract_entropy(ent, deltae); + add_pseudo(ent, count * 8); } - add_pseudo(ent, total); - memset(digest, 0, sizeof(digest)); if (returned != NULL)