#include <config.h>
#include <windows.h>
#include <wincrypt.h>
#include <process.h>
#include <io.h>
#include <share.h>
#define FILESOURCE_HANDLE_TYPE HCRYPTPROV
typedef struct {
int dummy;
} isc_entropyusocketsource_t;
#include "../entropy.c"
static unsigned int
get_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) {
isc_entropy_t *ent = source->ent;
unsigned char buf[128];
HCRYPTPROV hcryptprov = source->sources.file.handle;
ssize_t ndesired;
unsigned int added;
if (source->bad)
return (0);
desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
added = 0;
while (desired > 0) {
ndesired = ISC_MIN(desired, sizeof(buf));
if (!CryptGenRandom(hcryptprov, ndesired, buf)) {
CryptReleaseContext(hcryptprov, 0);
source->bad = ISC_TRUE;
goto out;
}
entropypool_adddata(ent, buf, ndesired, ndesired * 8);
added += ndesired * 8;
desired -= ndesired;
}
out:
return (added);
}
static void
fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) {
unsigned int added;
unsigned int remaining;
unsigned int needed;
unsigned int nsource;
isc_entropysource_t *source;
isc_entropysource_t *firstsource;
REQUIRE(VALID_ENTROPY(ent));
needed = desired;
if (needed == 0) {
REQUIRE(!blocking);
if ((ent->pool.entropy >= RND_POOLBITS / 4)
&& (ent->pool.pseudo <= RND_POOLBITS / 4))
return;
needed = THRESHOLD_BITS * 4;
} else {
needed = ISC_MAX(needed, THRESHOLD_BITS);
needed = ISC_MIN(needed, RND_POOLBITS);
}
needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
if (ent->initialized < THRESHOLD_BITS)
needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
added = 0;
remaining = needed;
if (ent->nextsource == NULL) {
ent->nextsource = ISC_LIST_HEAD(ent->sources);
if (ent->nextsource == NULL)
return;
}
source = ent->nextsource;
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;
if (!(ent->nextsource == firstsource && added == 0)) {
if (blocking && remaining != 0) {
goto again_file;
}
}
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);
}
if (ent->initialized < THRESHOLD_BITS)
ent->initialized += added;
}
static void
destroyfilesource(isc_entropyfilesource_t *source) {
CryptReleaseContext(source->handle, 0);
}
static void
destroyusocketsource(isc_entropyusocketsource_t *source) {
UNUSED(source);
}
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;
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;
}
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;
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);
}