entropy.c revision f1b68725503ff3e46001eee5a1751e29a43a09d1
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt/*
990d0e893f5b70e735cdf990af66e9ec6e91fa78Tinderbox User * Copyright (C) 2000, 2001 Internet Software Consortium.
3e02c9e33656dcd9c364633d42dd785d3e6fdd66Automatic Updater *
3e02c9e33656dcd9c364633d42dd785d3e6fdd66Automatic Updater * Permission to use, copy, modify, and distribute this software for any
c6fb85f9500350e5ce58c9a24f5d264c8a8bd6f4Automatic Updater * purpose with or without fee is hereby granted, provided that the above
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * copyright notice and this permission notice appear in all copies.
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt *
3e02c9e33656dcd9c364633d42dd785d3e6fdd66Automatic Updater * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
3e02c9e33656dcd9c364633d42dd785d3e6fdd66Automatic Updater * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt */
ea94d370123a5892f6c47a97f21d1b28d44bb168Tinderbox User
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt/* $Id: entropy.c,v 1.4 2001/11/27 00:56:21 gson Exp $ */
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt/*
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * This is the system depenedent part of the ISC entropy API.
2cbb4ab75757fbb656997a82c14ca07db37d481aAutomatic Updater */
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt#include <config.h>
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt
0a7ed88633a680bb881868b75ded4d09a7bbbc50Automatic Updater#include <windows.h>
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt#include <wincrypt.h>
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt#include <process.h>
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt#include <io.h>
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt#include <share.h>
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt/*
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * There is only one variable in the entropy data structures that is not
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * system independent, but pulling the structure that uses it into this file
0a7ed88633a680bb881868b75ded4d09a7bbbc50Automatic Updater * ultimately means pulling several other independent structures here also to
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * resolve their interdependencies. Thus only the problem variable's type
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * is defined here.
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt */
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt#define FILESOURCE_HANDLE_TYPE HCRYPTPROV
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt#include "../entropy.c"
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Huntstatic unsigned int
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Huntget_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) {
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt isc_entropy_t *ent = source->ent;
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt unsigned char buf[128];
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt HCRYPTPROV hcryptprov = source->sources.file.handle;
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt ssize_t ndesired;
6f1205897504b8f50b1785975482c995888dd630Tinderbox User unsigned int added;
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt if (source->bad)
6f1205897504b8f50b1785975482c995888dd630Tinderbox User return (0);
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
fd0b768f4c23d22c89f8a156a632831583b7fb68Automatic Updater
fd0b768f4c23d22c89f8a156a632831583b7fb68Automatic Updater added = 0;
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt while (desired > 0) {
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt ndesired = ISC_MIN(desired, sizeof(buf));
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt if (!CryptGenRandom(hcryptprov, ndesired, buf)) {
6f1205897504b8f50b1785975482c995888dd630Tinderbox User CryptReleaseContext(hcryptprov, 0);
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt source->bad = ISC_TRUE;
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt goto out;
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt }
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt entropypool_adddata(ent, buf, ndesired, ndesired * 8);
0a7ed88633a680bb881868b75ded4d09a7bbbc50Automatic Updater added += ndesired * 8;
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt desired -= ndesired;
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt }
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt out:
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt return (added);
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt}
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt/*
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * Poll each source, trying to get data from it to stuff into the entropy
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * pool.
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt */
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Huntstatic void
6f1205897504b8f50b1785975482c995888dd630Tinderbox Userfillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) {
6f1205897504b8f50b1785975482c995888dd630Tinderbox User unsigned int added;
6f1205897504b8f50b1785975482c995888dd630Tinderbox User unsigned int remaining;
6f1205897504b8f50b1785975482c995888dd630Tinderbox User unsigned int needed;
8ec3c085233cedb22b05da36e2773c8f357a7e45Automatic Updater unsigned int nsource;
6ea2385360e9e2167e65f9286447da9eea189457Tinderbox User isc_entropysource_t *source;
6ea2385360e9e2167e65f9286447da9eea189457Tinderbox User isc_entropysource_t *firstsource;
6ea2385360e9e2167e65f9286447da9eea189457Tinderbox User
6ea2385360e9e2167e65f9286447da9eea189457Tinderbox User REQUIRE(VALID_ENTROPY(ent));
6ea2385360e9e2167e65f9286447da9eea189457Tinderbox User
6ea2385360e9e2167e65f9286447da9eea189457Tinderbox User needed = desired;
6ea2385360e9e2167e65f9286447da9eea189457Tinderbox User
6ea2385360e9e2167e65f9286447da9eea189457Tinderbox User /*
6ea2385360e9e2167e65f9286447da9eea189457Tinderbox User * This logic is a little strange, so an explanation is in order.
6ea2385360e9e2167e65f9286447da9eea189457Tinderbox User *
6ea2385360e9e2167e65f9286447da9eea189457Tinderbox User * If needed is 0, it means we are being asked to "fill to whatever
6ea2385360e9e2167e65f9286447da9eea189457Tinderbox User * we think is best." This means that if we have at least a
6ea2385360e9e2167e65f9286447da9eea189457Tinderbox User * partially full pool (say, > 1/4th of the pool) we probably don't
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * need to add anything.
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt *
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * Also, we will check to see if the "pseudo" count is too high.
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * If it is, try to mix in better data. Too high is currently
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * defined as 1/4th of the pool.
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt *
12bfbed87cfffa65ac300b72c5665ab38a355c2fAutomatic Updater * Next, if we are asked to add a specific bit of entropy, make
12bfbed87cfffa65ac300b72c5665ab38a355c2fAutomatic Updater * certain that we will do so. Clamp how much we try to add to
12bfbed87cfffa65ac300b72c5665ab38a355c2fAutomatic Updater * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy).
12bfbed87cfffa65ac300b72c5665ab38a355c2fAutomatic Updater *
12bfbed87cfffa65ac300b72c5665ab38a355c2fAutomatic Updater * Note that if we are in a blocking mode, we will only try to
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * get as much data as we need, not as much as we might want
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * to build up.
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt */
6f1205897504b8f50b1785975482c995888dd630Tinderbox User if (needed == 0) {
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt REQUIRE(!blocking);
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt if ((ent->pool.entropy >= RND_POOLBITS / 4)
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt && (ent->pool.pseudo <= RND_POOLBITS / 4))
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt return;
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt
6f1205897504b8f50b1785975482c995888dd630Tinderbox User needed = THRESHOLD_BITS * 4;
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt } else {
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt needed = ISC_MAX(needed, THRESHOLD_BITS);
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt needed = ISC_MIN(needed, RND_POOLBITS);
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt }
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt /*
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * In any case, clamp how much we need to how much we can add.
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt */
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt /*
0a7ed88633a680bb881868b75ded4d09a7bbbc50Automatic Updater * But wait! If we're not yet initialized, we need at least
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * THRESHOLD_BITS
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * of randomness.
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt */
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt if (ent->initialized < THRESHOLD_BITS)
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt
0a7ed88633a680bb881868b75ded4d09a7bbbc50Automatic Updater /*
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * Poll each file source to see if we can read anything useful from
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * it. XXXMLG When where are multiple sources, we should keep a
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * record of which one we last used so we can start from it (or the
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * next one) to avoid letting some sources build up entropy while
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt * others are always drained.
1bc5499c2a0fc5d2b11849e97cdd6305a64eb242Evan Hunt */
added = 0;
remaining = needed;
if (ent->nextsource == NULL) {
ent->nextsource = ISC_LIST_HEAD(ent->sources);
if (ent->nextsource == NULL)
return;
}
source = ent->nextsource;
/*
* Remember the first source so we can break if we have looped back to
* the beginning and still have nothing
*/
firstsource = source;
again_file:
for (nsource = 0; nsource < ent->nsources; nsource++) {
unsigned int got;
if (remaining == 0)
break;
got = 0;
if (source->type == ENTROPY_SOURCETYPE_FILE)
got = get_from_filesource(source, remaining);
added += got;
remaining -= ISC_MIN(remaining, got);
source = ISC_LIST_NEXT(source, link);
if (source == NULL)
source = ISC_LIST_HEAD(ent->sources);
}
ent->nextsource = source;
/*
* Go again only if there's been progress and we've not
* gone back to the beginning
*/
if (!(ent->nextsource == firstsource && added == 0)) {
if (blocking && remaining != 0) {
goto again_file;
}
}
/*
* Here, if there are bits remaining to be had and we can block,
* check to see if we have a callback source. If so, call them.
*/
source = ISC_LIST_HEAD(ent->sources);
while ((remaining != 0) && (source != NULL)) {
unsigned int got;
got = 0;
if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
got = get_from_callback(source, remaining, blocking);
added += got;
remaining -= ISC_MIN(remaining, got);
if (added >= needed)
break;
source = ISC_LIST_NEXT(source, link);
}
/*
* Mark as initialized if we've added enough data.
*/
if (ent->initialized < THRESHOLD_BITS)
ent->initialized += added;
}
/*
* Requires "ent" be locked.
*/
static void
destroyfilesource(isc_entropyfilesource_t *source) {
CryptReleaseContext(source->handle, 0);
}
isc_result_t
isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
isc_result_t ret;
isc_entropysource_t *source;
HCRYPTPROV hcryptprov;
DWORD errval;
BOOL err;
REQUIRE(VALID_ENTROPY(ent));
REQUIRE(fname != NULL);
LOCK(&ent->lock);
source = NULL;
/*
* The first time we just try to acquire the context
*/
err = CryptAcquireContext(&hcryptprov, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT);
if (!err){
errval = GetLastError();
ret = ISC_R_IOERROR;
goto errout;
}
source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
if (source == NULL) {
ret = ISC_R_NOMEMORY;
goto closecontext;
}
/*
* From here down, no failures can occur.
*/
source->magic = SOURCE_MAGIC;
source->type = ENTROPY_SOURCETYPE_FILE;
source->ent = ent;
source->total = 0;
source->bad = ISC_FALSE;
memset(source->name, 0, sizeof(source->name));
ISC_LINK_INIT(source, link);
source->sources.file.handle = hcryptprov;
/*
* Hook it into the entropy system.
*/
ISC_LIST_APPEND(ent->sources, source, link);
ent->nsources++;
UNLOCK(&ent->lock);
return (ISC_R_SUCCESS);
closecontext:
CryptReleaseContext(hcryptprov, 0);
errout:
if (source != NULL)
isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t));
UNLOCK(&ent->lock);
return (ret);
}