xref: /minix/external/bsd/bind/dist/lib/isc/win32/entropy.c (revision 00b67f09)
1 /*	$NetBSD: entropy.c,v 1.6 2014/12/10 04:38:01 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2004, 2007, 2009, 2013  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 
44 typedef struct {
45 	int dummy;
46 } isc_entropyusocketsource_t;
47 
48 #include "../entropy.c"
49 
50 static unsigned int
get_from_filesource(isc_entropysource_t * source,isc_uint32_t desired)51 get_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, (DWORD)ndesired, buf)) {
67 			CryptReleaseContext(hcryptprov, 0);
68 			source->bad = ISC_TRUE;
69 			goto out;
70 		}
71 
72 		entropypool_adddata(ent, buf,
73 				    (unsigned int)ndesired,
74 				    (unsigned int)ndesired * 8);
75 		added += (unsigned int)ndesired * 8;
76 		desired -= (isc_uint32_t)ndesired;
77 	}
78 
79  out:
80 	return (added);
81 }
82 
83 /*
84  * Poll each source, trying to get data from it to stuff into the entropy
85  * pool.
86  */
87 static void
fillpool(isc_entropy_t * ent,unsigned int desired,isc_boolean_t blocking)88 fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) {
89 	unsigned int added;
90 	unsigned int remaining;
91 	unsigned int needed;
92 	unsigned int nsource;
93 	isc_entropysource_t *source;
94 	isc_entropysource_t *firstsource;
95 
96 	REQUIRE(VALID_ENTROPY(ent));
97 
98 	needed = desired;
99 
100 	/*
101 	 * This logic is a little strange, so an explanation is in order.
102 	 *
103 	 * If needed is 0, it means we are being asked to "fill to whatever
104 	 * we think is best."  This means that if we have at least a
105 	 * partially full pool (say, > 1/4th of the pool) we probably don't
106 	 * need to add anything.
107 	 *
108 	 * Also, we will check to see if the "pseudo" count is too high.
109 	 * If it is, try to mix in better data.  Too high is currently
110 	 * defined as 1/4th of the pool.
111 	 *
112 	 * Next, if we are asked to add a specific bit of entropy, make
113 	 * certain that we will do so.  Clamp how much we try to add to
114 	 * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy).
115 	 *
116 	 * Note that if we are in a blocking mode, we will only try to
117 	 * get as much data as we need, not as much as we might want
118 	 * to build up.
119 	 */
120 	if (needed == 0) {
121 		REQUIRE(!blocking);
122 
123 		if ((ent->pool.entropy >= RND_POOLBITS / 4)
124 		    && (ent->pool.pseudo <= RND_POOLBITS / 4))
125 			return;
126 
127 		needed = THRESHOLD_BITS * 4;
128 	} else {
129 		needed = ISC_MAX(needed, THRESHOLD_BITS);
130 		needed = ISC_MIN(needed, RND_POOLBITS);
131 	}
132 
133 	/*
134 	 * In any case, clamp how much we need to how much we can add.
135 	 */
136 	needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
137 
138 	/*
139 	 * But wait!  If we're not yet initialized, we need at least
140 	 *	THRESHOLD_BITS
141 	 * of randomness.
142 	 */
143 	if (ent->initialized < THRESHOLD_BITS)
144 		needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
145 
146 	/*
147 	 * Poll each file source to see if we can read anything useful from
148 	 * it.  XXXMLG When where are multiple sources, we should keep a
149 	 * record of which one we last used so we can start from it (or the
150 	 * next one) to avoid letting some sources build up entropy while
151 	 * others are always drained.
152 	 */
153 
154 	added = 0;
155 	remaining = needed;
156 	if (ent->nextsource == NULL) {
157 		ent->nextsource = ISC_LIST_HEAD(ent->sources);
158 		if (ent->nextsource == NULL)
159 			return;
160 	}
161 	source = ent->nextsource;
162 	/*
163 	 * Remember the first source so we can break if we have looped back to
164 	 * the beginning and still have nothing
165 	 */
166 	firstsource = source;
167  again_file:
168 	for (nsource = 0; nsource < ent->nsources; nsource++) {
169 		unsigned int got;
170 
171 		if (remaining == 0)
172 			break;
173 
174 		got = 0;
175 
176 		if (source->type == ENTROPY_SOURCETYPE_FILE)
177 			got = get_from_filesource(source, remaining);
178 
179 		added += got;
180 
181 		remaining -= ISC_MIN(remaining, got);
182 
183 		source = ISC_LIST_NEXT(source, link);
184 		if (source == NULL)
185 			source = ISC_LIST_HEAD(ent->sources);
186 	}
187 	ent->nextsource = source;
188 
189 	/*
190 	 * Go again only if there's been progress and we've not
191 	 * gone back to the beginning
192 	 */
193 	if (!(ent->nextsource == firstsource && added == 0)) {
194 		if (blocking && remaining != 0) {
195 				goto again_file;
196 		}
197 	}
198 
199 	/*
200 	 * Here, if there are bits remaining to be had and we can block,
201 	 * check to see if we have a callback source.  If so, call them.
202 	 */
203 	source = ISC_LIST_HEAD(ent->sources);
204 	while ((remaining != 0) && (source != NULL)) {
205 		unsigned int got;
206 
207 		got = 0;
208 
209 		if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
210 			got = get_from_callback(source, remaining, blocking);
211 
212 		added += got;
213 		remaining -= ISC_MIN(remaining, got);
214 
215 		if (added >= needed)
216 			break;
217 
218 		source = ISC_LIST_NEXT(source, link);
219 	}
220 
221 	/*
222 	 * Mark as initialized if we've added enough data.
223 	 */
224 	if (ent->initialized < THRESHOLD_BITS)
225 		ent->initialized += added;
226 }
227 
228 
229 
230 /*
231  * Requires "ent" be locked.
232  */
233 static void
destroyfilesource(isc_entropyfilesource_t * source)234 destroyfilesource(isc_entropyfilesource_t *source) {
235 	CryptReleaseContext(source->handle, 0);
236 }
237 
238 static void
destroyusocketsource(isc_entropyusocketsource_t * source)239 destroyusocketsource(isc_entropyusocketsource_t *source) {
240 	UNUSED(source);
241 }
242 
243 
244 isc_result_t
isc_entropy_createfilesource(isc_entropy_t * ent,const char * fname)245 isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
246 	isc_result_t ret;
247 	isc_entropysource_t *source;
248 	HCRYPTPROV hcryptprov;
249 	BOOL err;
250 
251 	REQUIRE(VALID_ENTROPY(ent));
252 	REQUIRE(fname != NULL);
253 
254 	LOCK(&ent->lock);
255 
256 	source = NULL;
257 
258 	/*
259 	 * The first time we just try to acquire the context
260 	 */
261 	err = CryptAcquireContext(&hcryptprov, NULL, NULL, PROV_RSA_FULL,
262 				  CRYPT_VERIFYCONTEXT);
263 	if (!err){
264 		(void)GetLastError();
265 		ret = ISC_R_IOERROR;
266 		goto errout;
267 	}
268 
269 	source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
270 	if (source == NULL) {
271 		ret = ISC_R_NOMEMORY;
272 		goto closecontext;
273 	}
274 
275 	/*
276 	 * From here down, no failures can occur.
277 	 */
278 	source->magic = SOURCE_MAGIC;
279 	source->type = ENTROPY_SOURCETYPE_FILE;
280 	source->ent = ent;
281 	source->total = 0;
282 	source->bad = ISC_FALSE;
283 	memset(source->name, 0, sizeof(source->name));
284 	ISC_LINK_INIT(source, link);
285 	source->sources.file.handle = hcryptprov;
286 
287 	/*
288 	 * Hook it into the entropy system.
289 	 */
290 	ISC_LIST_APPEND(ent->sources, source, link);
291 	ent->nsources++;
292 
293 	UNLOCK(&ent->lock);
294 	return (ISC_R_SUCCESS);
295 
296  closecontext:
297 	CryptReleaseContext(hcryptprov, 0);
298 
299  errout:
300 	if (source != NULL)
301 		isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t));
302 
303 	UNLOCK(&ent->lock);
304 
305 	return (ret);
306 }
307 
308 
309 
310 
311