2 * Copyright (C) 2004, 2007, 2009 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000-2002 Internet Software Consortium.
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
18 /* $Id: entropy.c,v 1.10 2009/01/18 23:48:14 tbox Exp $ */
21 * This is the system dependent part of the ISC entropy API.
34 * There is only one variable in the entropy data structures that is not
35 * system independent, but pulling the structure that uses it into this file
36 * ultimately means pulling several other independent structures here also to
37 * resolve their interdependencies. Thus only the problem variable's type
40 #define FILESOURCE_HANDLE_TYPE HCRYPTPROV
44 } isc_entropyusocketsource_t;
46 #include "../entropy.c"
49 get_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) {
50 isc_entropy_t *ent = source->ent;
51 unsigned char buf[128];
52 HCRYPTPROV hcryptprov = source->sources.file.handle;
59 desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
63 ndesired = ISC_MIN(desired, sizeof(buf));
64 if (!CryptGenRandom(hcryptprov, ndesired, buf)) {
65 CryptReleaseContext(hcryptprov, 0);
66 source->bad = ISC_TRUE;
70 entropypool_adddata(ent, buf, ndesired, ndesired * 8);
71 added += ndesired * 8;
80 * Poll each source, trying to get data from it to stuff into the entropy
84 fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) {
86 unsigned int remaining;
89 isc_entropysource_t *source;
90 isc_entropysource_t *firstsource;
92 REQUIRE(VALID_ENTROPY(ent));
97 * This logic is a little strange, so an explanation is in order.
99 * If needed is 0, it means we are being asked to "fill to whatever
100 * we think is best." This means that if we have at least a
101 * partially full pool (say, > 1/4th of the pool) we probably don't
102 * need to add anything.
104 * Also, we will check to see if the "pseudo" count is too high.
105 * If it is, try to mix in better data. Too high is currently
106 * defined as 1/4th of the pool.
108 * Next, if we are asked to add a specific bit of entropy, make
109 * certain that we will do so. Clamp how much we try to add to
110 * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy).
112 * Note that if we are in a blocking mode, we will only try to
113 * get as much data as we need, not as much as we might want
119 if ((ent->pool.entropy >= RND_POOLBITS / 4)
120 && (ent->pool.pseudo <= RND_POOLBITS / 4))
123 needed = THRESHOLD_BITS * 4;
125 needed = ISC_MAX(needed, THRESHOLD_BITS);
126 needed = ISC_MIN(needed, RND_POOLBITS);
130 * In any case, clamp how much we need to how much we can add.
132 needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
135 * But wait! If we're not yet initialized, we need at least
139 if (ent->initialized < THRESHOLD_BITS)
140 needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
143 * Poll each file source to see if we can read anything useful from
144 * it. XXXMLG When where are multiple sources, we should keep a
145 * record of which one we last used so we can start from it (or the
146 * next one) to avoid letting some sources build up entropy while
147 * others are always drained.
152 if (ent->nextsource == NULL) {
153 ent->nextsource = ISC_LIST_HEAD(ent->sources);
154 if (ent->nextsource == NULL)
157 source = ent->nextsource;
159 * Remember the first source so we can break if we have looped back to
160 * the beginning and still have nothing
162 firstsource = source;
164 for (nsource = 0; nsource < ent->nsources; nsource++) {
172 if (source->type == ENTROPY_SOURCETYPE_FILE)
173 got = get_from_filesource(source, remaining);
177 remaining -= ISC_MIN(remaining, got);
179 source = ISC_LIST_NEXT(source, link);
181 source = ISC_LIST_HEAD(ent->sources);
183 ent->nextsource = source;
186 * Go again only if there's been progress and we've not
187 * gone back to the beginning
189 if (!(ent->nextsource == firstsource && added == 0)) {
190 if (blocking && remaining != 0) {
196 * Here, if there are bits remaining to be had and we can block,
197 * check to see if we have a callback source. If so, call them.
199 source = ISC_LIST_HEAD(ent->sources);
200 while ((remaining != 0) && (source != NULL)) {
205 if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
206 got = get_from_callback(source, remaining, blocking);
209 remaining -= ISC_MIN(remaining, got);
214 source = ISC_LIST_NEXT(source, link);
218 * Mark as initialized if we've added enough data.
220 if (ent->initialized < THRESHOLD_BITS)
221 ent->initialized += added;
227 * Requires "ent" be locked.
230 destroyfilesource(isc_entropyfilesource_t *source) {
231 CryptReleaseContext(source->handle, 0);
235 destroyusocketsource(isc_entropyusocketsource_t *source) {
241 isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
243 isc_entropysource_t *source;
244 HCRYPTPROV hcryptprov;
248 REQUIRE(VALID_ENTROPY(ent));
249 REQUIRE(fname != NULL);
256 * The first time we just try to acquire the context
258 err = CryptAcquireContext(&hcryptprov, NULL, NULL, PROV_RSA_FULL,
259 CRYPT_VERIFYCONTEXT);
261 errval = GetLastError();
266 source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
267 if (source == NULL) {
268 ret = ISC_R_NOMEMORY;
273 * From here down, no failures can occur.
275 source->magic = SOURCE_MAGIC;
276 source->type = ENTROPY_SOURCETYPE_FILE;
279 source->bad = ISC_FALSE;
280 memset(source->name, 0, sizeof(source->name));
281 ISC_LINK_INIT(source, link);
282 source->sources.file.handle = hcryptprov;
285 * Hook it into the entropy system.
287 ISC_LIST_APPEND(ent->sources, source, link);
291 return (ISC_R_SUCCESS);
294 CryptReleaseContext(hcryptprov, 0);
298 isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t));