1/*	$NetBSD: entropy.c,v 1.2.6.1 2012/06/05 21:15:31 bouyer Exp $	*/
2
3/*
4 * Copyright (C) 2004, 2007, 2009  Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 2000-2002  Internet Software Consortium.
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20/* Id: entropy.c,v 1.10 2009/01/18 23:48:14 tbox Exp  */
21
22/*
23 * This is the system dependent part of the ISC entropy API.
24 */
25
26#include <config.h>
27
28#include <windows.h>
29#include <wincrypt.h>
30
31#include <process.h>
32#include <io.h>
33#include <share.h>
34
35/*
36 * There is only one variable in the entropy data structures that is not
37 * system independent, but pulling the structure that uses it into this file
38 * ultimately means pulling several other independent structures here also to
39 * resolve their interdependencies.  Thus only the problem variable's type
40 * is defined here.
41 */
42#define FILESOURCE_HANDLE_TYPE	HCRYPTPROV
43
44typedef struct {
45	int dummy;
46} isc_entropyusocketsource_t;
47
48#include "../entropy.c"
49
50static unsigned int
51get_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) {
52	isc_entropy_t *ent = source->ent;
53	unsigned char buf[128];
54	HCRYPTPROV hcryptprov = source->sources.file.handle;
55	ssize_t ndesired;
56	unsigned int added;
57
58	if (source->bad)
59		return (0);
60
61	desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
62
63	added = 0;
64	while (desired > 0) {
65		ndesired = ISC_MIN(desired, sizeof(buf));
66		if (!CryptGenRandom(hcryptprov, ndesired, buf)) {
67			CryptReleaseContext(hcryptprov, 0);
68			source->bad = ISC_TRUE;
69			goto out;
70		}
71
72		entropypool_adddata(ent, buf, ndesired, ndesired * 8);
73		added += ndesired * 8;
74		desired -= ndesired;
75	}
76
77 out:
78	return (added);
79}
80
81/*
82 * Poll each source, trying to get data from it to stuff into the entropy
83 * pool.
84 */
85static void
86fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) {
87	unsigned int added;
88	unsigned int remaining;
89	unsigned int needed;
90	unsigned int nsource;
91	isc_entropysource_t *source;
92	isc_entropysource_t *firstsource;
93
94	REQUIRE(VALID_ENTROPY(ent));
95
96	needed = desired;
97
98	/*
99	 * This logic is a little strange, so an explanation is in order.
100	 *
101	 * If needed is 0, it means we are being asked to "fill to whatever
102	 * we think is best."  This means that if we have at least a
103	 * partially full pool (say, > 1/4th of the pool) we probably don't
104	 * need to add anything.
105	 *
106	 * Also, we will check to see if the "pseudo" count is too high.
107	 * If it is, try to mix in better data.  Too high is currently
108	 * defined as 1/4th of the pool.
109	 *
110	 * Next, if we are asked to add a specific bit of entropy, make
111	 * certain that we will do so.  Clamp how much we try to add to
112	 * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy).
113	 *
114	 * Note that if we are in a blocking mode, we will only try to
115	 * get as much data as we need, not as much as we might want
116	 * to build up.
117	 */
118	if (needed == 0) {
119		REQUIRE(!blocking);
120
121		if ((ent->pool.entropy >= RND_POOLBITS / 4)
122		    && (ent->pool.pseudo <= RND_POOLBITS / 4))
123			return;
124
125		needed = THRESHOLD_BITS * 4;
126	} else {
127		needed = ISC_MAX(needed, THRESHOLD_BITS);
128		needed = ISC_MIN(needed, RND_POOLBITS);
129	}
130
131	/*
132	 * In any case, clamp how much we need to how much we can add.
133	 */
134	needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
135
136	/*
137	 * But wait!  If we're not yet initialized, we need at least
138	 *	THRESHOLD_BITS
139	 * of randomness.
140	 */
141	if (ent->initialized < THRESHOLD_BITS)
142		needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
143
144	/*
145	 * Poll each file source to see if we can read anything useful from
146	 * it.  XXXMLG When where are multiple sources, we should keep a
147	 * record of which one we last used so we can start from it (or the
148	 * next one) to avoid letting some sources build up entropy while
149	 * others are always drained.
150	 */
151
152	added = 0;
153	remaining = needed;
154	if (ent->nextsource == NULL) {
155		ent->nextsource = ISC_LIST_HEAD(ent->sources);
156		if (ent->nextsource == NULL)
157			return;
158	}
159	source = ent->nextsource;
160	/*
161	 * Remember the first source so we can break if we have looped back to
162	 * the beginning and still have nothing
163	 */
164	firstsource = source;
165 again_file:
166	for (nsource = 0; nsource < ent->nsources; nsource++) {
167		unsigned int got;
168
169		if (remaining == 0)
170			break;
171
172		got = 0;
173
174		if (source->type == ENTROPY_SOURCETYPE_FILE)
175			got = get_from_filesource(source, remaining);
176
177		added += got;
178
179		remaining -= ISC_MIN(remaining, got);
180
181		source = ISC_LIST_NEXT(source, link);
182		if (source == NULL)
183			source = ISC_LIST_HEAD(ent->sources);
184	}
185	ent->nextsource = source;
186
187	/*
188	 * Go again only if there's been progress and we've not
189	 * gone back to the beginning
190	 */
191	if (!(ent->nextsource == firstsource && added == 0)) {
192		if (blocking && remaining != 0) {
193				goto again_file;
194		}
195	}
196
197	/*
198	 * Here, if there are bits remaining to be had and we can block,
199	 * check to see if we have a callback source.  If so, call them.
200	 */
201	source = ISC_LIST_HEAD(ent->sources);
202	while ((remaining != 0) && (source != NULL)) {
203		unsigned int got;
204
205		got = 0;
206
207		if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
208			got = get_from_callback(source, remaining, blocking);
209
210		added += got;
211		remaining -= ISC_MIN(remaining, got);
212
213		if (added >= needed)
214			break;
215
216		source = ISC_LIST_NEXT(source, link);
217	}
218
219	/*
220	 * Mark as initialized if we've added enough data.
221	 */
222	if (ent->initialized < THRESHOLD_BITS)
223		ent->initialized += added;
224}
225
226
227
228/*
229 * Requires "ent" be locked.
230 */
231static void
232destroyfilesource(isc_entropyfilesource_t *source) {
233	CryptReleaseContext(source->handle, 0);
234}
235
236static void
237destroyusocketsource(isc_entropyusocketsource_t *source) {
238	UNUSED(source);
239}
240
241
242isc_result_t
243isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
244	isc_result_t ret;
245	isc_entropysource_t *source;
246	HCRYPTPROV hcryptprov;
247	DWORD errval;
248	BOOL err;
249
250	REQUIRE(VALID_ENTROPY(ent));
251	REQUIRE(fname != NULL);
252
253	LOCK(&ent->lock);
254
255	source = NULL;
256
257	/*
258	 * The first time we just try to acquire the context
259	 */
260	err = CryptAcquireContext(&hcryptprov, NULL, NULL, PROV_RSA_FULL,
261				  CRYPT_VERIFYCONTEXT);
262	if (!err){
263		errval = GetLastError();
264		ret = ISC_R_IOERROR;
265		goto errout;
266	}
267
268	source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
269	if (source == NULL) {
270		ret = ISC_R_NOMEMORY;
271		goto closecontext;
272	}
273
274	/*
275	 * From here down, no failures can occur.
276	 */
277	source->magic = SOURCE_MAGIC;
278	source->type = ENTROPY_SOURCETYPE_FILE;
279	source->ent = ent;
280	source->total = 0;
281	source->bad = ISC_FALSE;
282	memset(source->name, 0, sizeof(source->name));
283	ISC_LINK_INIT(source, link);
284	source->sources.file.handle = hcryptprov;
285
286	/*
287	 * Hook it into the entropy system.
288	 */
289	ISC_LIST_APPEND(ent->sources, source, link);
290	ent->nsources++;
291
292	UNLOCK(&ent->lock);
293	return (ISC_R_SUCCESS);
294
295 closecontext:
296	CryptReleaseContext(hcryptprov, 0);
297
298 errout:
299	if (source != NULL)
300		isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t));
301
302	UNLOCK(&ent->lock);
303
304	return (ret);
305}
306
307
308
309
310