1258945Sroberto/*
2258945Sroberto * Copyright (C) 2004, 2007, 2009  Internet Systems Consortium, Inc. ("ISC")
3258945Sroberto * Copyright (C) 2000-2002  Internet Software Consortium.
4258945Sroberto *
5258945Sroberto * Permission to use, copy, modify, and/or distribute this software for any
6258945Sroberto * purpose with or without fee is hereby granted, provided that the above
7258945Sroberto * copyright notice and this permission notice appear in all copies.
8258945Sroberto *
9258945Sroberto * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10258945Sroberto * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11258945Sroberto * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12258945Sroberto * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13258945Sroberto * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14258945Sroberto * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15258945Sroberto * PERFORMANCE OF THIS SOFTWARE.
16258945Sroberto */
17258945Sroberto
18280849Scy/* $Id: entropy.c,v 1.10 2009/01/18 23:48:14 tbox Exp $ */
19258945Sroberto
20258945Sroberto/*
21258945Sroberto * This is the system dependent part of the ISC entropy API.
22258945Sroberto */
23258945Sroberto
24258945Sroberto#include <config.h>
25258945Sroberto
26258945Sroberto#include <windows.h>
27258945Sroberto#include <wincrypt.h>
28258945Sroberto
29258945Sroberto#include <process.h>
30258945Sroberto#include <io.h>
31258945Sroberto#include <share.h>
32258945Sroberto
33258945Sroberto/*
34258945Sroberto * There is only one variable in the entropy data structures that is not
35258945Sroberto * system independent, but pulling the structure that uses it into this file
36258945Sroberto * ultimately means pulling several other independent structures here also to
37258945Sroberto * resolve their interdependencies.  Thus only the problem variable's type
38258945Sroberto * is defined here.
39258945Sroberto */
40258945Sroberto#define FILESOURCE_HANDLE_TYPE	HCRYPTPROV
41258945Sroberto
42258945Srobertotypedef struct {
43258945Sroberto	int dummy;
44258945Sroberto} isc_entropyusocketsource_t;
45258945Sroberto
46258945Sroberto#include "../entropy.c"
47258945Sroberto
48258945Srobertostatic unsigned int
49258945Srobertoget_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) {
50258945Sroberto	isc_entropy_t *ent = source->ent;
51258945Sroberto	unsigned char buf[128];
52258945Sroberto	HCRYPTPROV hcryptprov = source->sources.file.handle;
53258945Sroberto	ssize_t ndesired;
54258945Sroberto	unsigned int added;
55258945Sroberto
56258945Sroberto	if (source->bad)
57258945Sroberto		return (0);
58258945Sroberto
59258945Sroberto	desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
60258945Sroberto
61258945Sroberto	added = 0;
62258945Sroberto	while (desired > 0) {
63258945Sroberto		ndesired = ISC_MIN(desired, sizeof(buf));
64258945Sroberto		if (!CryptGenRandom(hcryptprov, ndesired, buf)) {
65258945Sroberto			CryptReleaseContext(hcryptprov, 0);
66258945Sroberto			source->bad = ISC_TRUE;
67258945Sroberto			goto out;
68258945Sroberto		}
69258945Sroberto
70258945Sroberto		entropypool_adddata(ent, buf, ndesired, ndesired * 8);
71258945Sroberto		added += ndesired * 8;
72258945Sroberto		desired -= ndesired;
73258945Sroberto	}
74258945Sroberto
75258945Sroberto out:
76258945Sroberto	return (added);
77258945Sroberto}
78258945Sroberto
79258945Sroberto/*
80258945Sroberto * Poll each source, trying to get data from it to stuff into the entropy
81258945Sroberto * pool.
82258945Sroberto */
83258945Srobertostatic void
84258945Srobertofillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) {
85258945Sroberto	unsigned int added;
86258945Sroberto	unsigned int remaining;
87258945Sroberto	unsigned int needed;
88258945Sroberto	unsigned int nsource;
89258945Sroberto	isc_entropysource_t *source;
90258945Sroberto	isc_entropysource_t *firstsource;
91258945Sroberto
92258945Sroberto	REQUIRE(VALID_ENTROPY(ent));
93258945Sroberto
94258945Sroberto	needed = desired;
95258945Sroberto
96258945Sroberto	/*
97258945Sroberto	 * This logic is a little strange, so an explanation is in order.
98258945Sroberto	 *
99258945Sroberto	 * If needed is 0, it means we are being asked to "fill to whatever
100258945Sroberto	 * we think is best."  This means that if we have at least a
101258945Sroberto	 * partially full pool (say, > 1/4th of the pool) we probably don't
102258945Sroberto	 * need to add anything.
103258945Sroberto	 *
104258945Sroberto	 * Also, we will check to see if the "pseudo" count is too high.
105258945Sroberto	 * If it is, try to mix in better data.  Too high is currently
106258945Sroberto	 * defined as 1/4th of the pool.
107258945Sroberto	 *
108258945Sroberto	 * Next, if we are asked to add a specific bit of entropy, make
109258945Sroberto	 * certain that we will do so.  Clamp how much we try to add to
110258945Sroberto	 * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy).
111258945Sroberto	 *
112258945Sroberto	 * Note that if we are in a blocking mode, we will only try to
113258945Sroberto	 * get as much data as we need, not as much as we might want
114258945Sroberto	 * to build up.
115258945Sroberto	 */
116258945Sroberto	if (needed == 0) {
117258945Sroberto		REQUIRE(!blocking);
118258945Sroberto
119258945Sroberto		if ((ent->pool.entropy >= RND_POOLBITS / 4)
120258945Sroberto		    && (ent->pool.pseudo <= RND_POOLBITS / 4))
121258945Sroberto			return;
122258945Sroberto
123258945Sroberto		needed = THRESHOLD_BITS * 4;
124258945Sroberto	} else {
125258945Sroberto		needed = ISC_MAX(needed, THRESHOLD_BITS);
126258945Sroberto		needed = ISC_MIN(needed, RND_POOLBITS);
127258945Sroberto	}
128258945Sroberto
129258945Sroberto	/*
130258945Sroberto	 * In any case, clamp how much we need to how much we can add.
131258945Sroberto	 */
132258945Sroberto	needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
133258945Sroberto
134258945Sroberto	/*
135258945Sroberto	 * But wait!  If we're not yet initialized, we need at least
136258945Sroberto	 *	THRESHOLD_BITS
137258945Sroberto	 * of randomness.
138258945Sroberto	 */
139258945Sroberto	if (ent->initialized < THRESHOLD_BITS)
140258945Sroberto		needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
141258945Sroberto
142258945Sroberto	/*
143258945Sroberto	 * Poll each file source to see if we can read anything useful from
144258945Sroberto	 * it.  XXXMLG When where are multiple sources, we should keep a
145258945Sroberto	 * record of which one we last used so we can start from it (or the
146258945Sroberto	 * next one) to avoid letting some sources build up entropy while
147258945Sroberto	 * others are always drained.
148258945Sroberto	 */
149258945Sroberto
150258945Sroberto	added = 0;
151258945Sroberto	remaining = needed;
152258945Sroberto	if (ent->nextsource == NULL) {
153258945Sroberto		ent->nextsource = ISC_LIST_HEAD(ent->sources);
154258945Sroberto		if (ent->nextsource == NULL)
155258945Sroberto			return;
156258945Sroberto	}
157258945Sroberto	source = ent->nextsource;
158258945Sroberto	/*
159258945Sroberto	 * Remember the first source so we can break if we have looped back to
160258945Sroberto	 * the beginning and still have nothing
161258945Sroberto	 */
162258945Sroberto	firstsource = source;
163258945Sroberto again_file:
164258945Sroberto	for (nsource = 0; nsource < ent->nsources; nsource++) {
165258945Sroberto		unsigned int got;
166258945Sroberto
167258945Sroberto		if (remaining == 0)
168258945Sroberto			break;
169258945Sroberto
170258945Sroberto		got = 0;
171258945Sroberto
172258945Sroberto		if (source->type == ENTROPY_SOURCETYPE_FILE)
173258945Sroberto			got = get_from_filesource(source, remaining);
174258945Sroberto
175258945Sroberto		added += got;
176258945Sroberto
177258945Sroberto		remaining -= ISC_MIN(remaining, got);
178258945Sroberto
179258945Sroberto		source = ISC_LIST_NEXT(source, link);
180258945Sroberto		if (source == NULL)
181258945Sroberto			source = ISC_LIST_HEAD(ent->sources);
182258945Sroberto	}
183258945Sroberto	ent->nextsource = source;
184258945Sroberto
185258945Sroberto	/*
186258945Sroberto	 * Go again only if there's been progress and we've not
187258945Sroberto	 * gone back to the beginning
188258945Sroberto	 */
189258945Sroberto	if (!(ent->nextsource == firstsource && added == 0)) {
190258945Sroberto		if (blocking && remaining != 0) {
191258945Sroberto				goto again_file;
192258945Sroberto		}
193258945Sroberto	}
194258945Sroberto
195258945Sroberto	/*
196258945Sroberto	 * Here, if there are bits remaining to be had and we can block,
197258945Sroberto	 * check to see if we have a callback source.  If so, call them.
198258945Sroberto	 */
199258945Sroberto	source = ISC_LIST_HEAD(ent->sources);
200258945Sroberto	while ((remaining != 0) && (source != NULL)) {
201258945Sroberto		unsigned int got;
202258945Sroberto
203258945Sroberto		got = 0;
204258945Sroberto
205258945Sroberto		if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
206258945Sroberto			got = get_from_callback(source, remaining, blocking);
207258945Sroberto
208258945Sroberto		added += got;
209258945Sroberto		remaining -= ISC_MIN(remaining, got);
210258945Sroberto
211258945Sroberto		if (added >= needed)
212258945Sroberto			break;
213258945Sroberto
214258945Sroberto		source = ISC_LIST_NEXT(source, link);
215258945Sroberto	}
216258945Sroberto
217258945Sroberto	/*
218258945Sroberto	 * Mark as initialized if we've added enough data.
219258945Sroberto	 */
220258945Sroberto	if (ent->initialized < THRESHOLD_BITS)
221258945Sroberto		ent->initialized += added;
222258945Sroberto}
223258945Sroberto
224258945Sroberto
225258945Sroberto
226258945Sroberto/*
227258945Sroberto * Requires "ent" be locked.
228258945Sroberto */
229258945Srobertostatic void
230258945Srobertodestroyfilesource(isc_entropyfilesource_t *source) {
231258945Sroberto	CryptReleaseContext(source->handle, 0);
232258945Sroberto}
233258945Sroberto
234258945Srobertostatic void
235258945Srobertodestroyusocketsource(isc_entropyusocketsource_t *source) {
236258945Sroberto	UNUSED(source);
237258945Sroberto}
238258945Sroberto
239258945Sroberto
240258945Srobertoisc_result_t
241258945Srobertoisc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
242258945Sroberto	isc_result_t ret;
243258945Sroberto	isc_entropysource_t *source;
244258945Sroberto	HCRYPTPROV hcryptprov;
245258945Sroberto	DWORD errval;
246258945Sroberto	BOOL err;
247258945Sroberto
248258945Sroberto	REQUIRE(VALID_ENTROPY(ent));
249258945Sroberto	REQUIRE(fname != NULL);
250258945Sroberto
251258945Sroberto	LOCK(&ent->lock);
252258945Sroberto
253258945Sroberto	source = NULL;
254258945Sroberto
255258945Sroberto	/*
256258945Sroberto	 * The first time we just try to acquire the context
257258945Sroberto	 */
258258945Sroberto	err = CryptAcquireContext(&hcryptprov, NULL, NULL, PROV_RSA_FULL,
259258945Sroberto				  CRYPT_VERIFYCONTEXT);
260258945Sroberto	if (!err){
261258945Sroberto		errval = GetLastError();
262258945Sroberto		ret = ISC_R_IOERROR;
263258945Sroberto		goto errout;
264258945Sroberto	}
265258945Sroberto
266258945Sroberto	source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
267258945Sroberto	if (source == NULL) {
268258945Sroberto		ret = ISC_R_NOMEMORY;
269258945Sroberto		goto closecontext;
270258945Sroberto	}
271258945Sroberto
272258945Sroberto	/*
273258945Sroberto	 * From here down, no failures can occur.
274258945Sroberto	 */
275258945Sroberto	source->magic = SOURCE_MAGIC;
276258945Sroberto	source->type = ENTROPY_SOURCETYPE_FILE;
277258945Sroberto	source->ent = ent;
278258945Sroberto	source->total = 0;
279258945Sroberto	source->bad = ISC_FALSE;
280258945Sroberto	memset(source->name, 0, sizeof(source->name));
281258945Sroberto	ISC_LINK_INIT(source, link);
282258945Sroberto	source->sources.file.handle = hcryptprov;
283258945Sroberto
284258945Sroberto	/*
285258945Sroberto	 * Hook it into the entropy system.
286258945Sroberto	 */
287258945Sroberto	ISC_LIST_APPEND(ent->sources, source, link);
288258945Sroberto	ent->nsources++;
289258945Sroberto
290258945Sroberto	UNLOCK(&ent->lock);
291258945Sroberto	return (ISC_R_SUCCESS);
292258945Sroberto
293258945Sroberto closecontext:
294258945Sroberto	CryptReleaseContext(hcryptprov, 0);
295258945Sroberto
296258945Sroberto errout:
297258945Sroberto	if (source != NULL)
298258945Sroberto		isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t));
299258945Sroberto
300258945Sroberto	UNLOCK(&ent->lock);
301258945Sroberto
302258945Sroberto	return (ret);
303258945Sroberto}
304258945Sroberto
305258945Sroberto
306258945Sroberto
307258945Sroberto
308