entropy.c revision 290001
1/* 2 * Copyright (C) 2004, 2007, 2009 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 2000-2002 Internet Software Consortium. 4 * 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. 8 * 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. 16 */ 17 18/* $Id: entropy.c,v 1.10 2009/01/18 23:48:14 tbox Exp $ */ 19 20/* 21 * This is the system dependent part of the ISC entropy API. 22 */ 23 24#include <config.h> 25 26#include <windows.h> 27#include <wincrypt.h> 28 29#include <process.h> 30#include <io.h> 31#include <share.h> 32 33/* 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 38 * is defined here. 39 */ 40#define FILESOURCE_HANDLE_TYPE HCRYPTPROV 41 42typedef struct { 43 int dummy; 44} isc_entropyusocketsource_t; 45 46#include "../entropy.c" 47 48static unsigned int 49get_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; 53 ssize_t ndesired; 54 unsigned int added; 55 56 if (source->bad) 57 return (0); 58 59 desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0); 60 61 added = 0; 62 while (desired > 0) { 63 ndesired = ISC_MIN(desired, sizeof(buf)); 64 if (!CryptGenRandom(hcryptprov, ndesired, buf)) { 65 CryptReleaseContext(hcryptprov, 0); 66 source->bad = ISC_TRUE; 67 goto out; 68 } 69 70 entropypool_adddata(ent, buf, ndesired, ndesired * 8); 71 added += ndesired * 8; 72 desired -= ndesired; 73 } 74 75 out: 76 return (added); 77} 78 79/* 80 * Poll each source, trying to get data from it to stuff into the entropy 81 * pool. 82 */ 83static void 84fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) { 85 unsigned int added; 86 unsigned int remaining; 87 unsigned int needed; 88 unsigned int nsource; 89 isc_entropysource_t *source; 90 isc_entropysource_t *firstsource; 91 92 REQUIRE(VALID_ENTROPY(ent)); 93 94 needed = desired; 95 96 /* 97 * This logic is a little strange, so an explanation is in order. 98 * 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. 103 * 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. 107 * 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). 111 * 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 114 * to build up. 115 */ 116 if (needed == 0) { 117 REQUIRE(!blocking); 118 119 if ((ent->pool.entropy >= RND_POOLBITS / 4) 120 && (ent->pool.pseudo <= RND_POOLBITS / 4)) 121 return; 122 123 needed = THRESHOLD_BITS * 4; 124 } else { 125 needed = ISC_MAX(needed, THRESHOLD_BITS); 126 needed = ISC_MIN(needed, RND_POOLBITS); 127 } 128 129 /* 130 * In any case, clamp how much we need to how much we can add. 131 */ 132 needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy); 133 134 /* 135 * But wait! If we're not yet initialized, we need at least 136 * THRESHOLD_BITS 137 * of randomness. 138 */ 139 if (ent->initialized < THRESHOLD_BITS) 140 needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized); 141 142 /* 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. 148 */ 149 150 added = 0; 151 remaining = needed; 152 if (ent->nextsource == NULL) { 153 ent->nextsource = ISC_LIST_HEAD(ent->sources); 154 if (ent->nextsource == NULL) 155 return; 156 } 157 source = ent->nextsource; 158 /* 159 * Remember the first source so we can break if we have looped back to 160 * the beginning and still have nothing 161 */ 162 firstsource = source; 163 again_file: 164 for (nsource = 0; nsource < ent->nsources; nsource++) { 165 unsigned int got; 166 167 if (remaining == 0) 168 break; 169 170 got = 0; 171 172 if (source->type == ENTROPY_SOURCETYPE_FILE) 173 got = get_from_filesource(source, remaining); 174 175 added += got; 176 177 remaining -= ISC_MIN(remaining, got); 178 179 source = ISC_LIST_NEXT(source, link); 180 if (source == NULL) 181 source = ISC_LIST_HEAD(ent->sources); 182 } 183 ent->nextsource = source; 184 185 /* 186 * Go again only if there's been progress and we've not 187 * gone back to the beginning 188 */ 189 if (!(ent->nextsource == firstsource && added == 0)) { 190 if (blocking && remaining != 0) { 191 goto again_file; 192 } 193 } 194 195 /* 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. 198 */ 199 source = ISC_LIST_HEAD(ent->sources); 200 while ((remaining != 0) && (source != NULL)) { 201 unsigned int got; 202 203 got = 0; 204 205 if (source->type == ENTROPY_SOURCETYPE_CALLBACK) 206 got = get_from_callback(source, remaining, blocking); 207 208 added += got; 209 remaining -= ISC_MIN(remaining, got); 210 211 if (added >= needed) 212 break; 213 214 source = ISC_LIST_NEXT(source, link); 215 } 216 217 /* 218 * Mark as initialized if we've added enough data. 219 */ 220 if (ent->initialized < THRESHOLD_BITS) 221 ent->initialized += added; 222} 223 224 225 226/* 227 * Requires "ent" be locked. 228 */ 229static void 230destroyfilesource(isc_entropyfilesource_t *source) { 231 CryptReleaseContext(source->handle, 0); 232} 233 234static void 235destroyusocketsource(isc_entropyusocketsource_t *source) { 236 UNUSED(source); 237} 238 239 240isc_result_t 241isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) { 242 isc_result_t ret; 243 isc_entropysource_t *source; 244 HCRYPTPROV hcryptprov; 245 DWORD errval; 246 BOOL err; 247 248 REQUIRE(VALID_ENTROPY(ent)); 249 REQUIRE(fname != NULL); 250 251 LOCK(&ent->lock); 252 253 source = NULL; 254 255 /* 256 * The first time we just try to acquire the context 257 */ 258 err = CryptAcquireContext(&hcryptprov, NULL, NULL, PROV_RSA_FULL, 259 CRYPT_VERIFYCONTEXT); 260 if (!err){ 261 errval = GetLastError(); 262 ret = ISC_R_IOERROR; 263 goto errout; 264 } 265 266 source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t)); 267 if (source == NULL) { 268 ret = ISC_R_NOMEMORY; 269 goto closecontext; 270 } 271 272 /* 273 * From here down, no failures can occur. 274 */ 275 source->magic = SOURCE_MAGIC; 276 source->type = ENTROPY_SOURCETYPE_FILE; 277 source->ent = ent; 278 source->total = 0; 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; 283 284 /* 285 * Hook it into the entropy system. 286 */ 287 ISC_LIST_APPEND(ent->sources, source, link); 288 ent->nsources++; 289 290 UNLOCK(&ent->lock); 291 return (ISC_R_SUCCESS); 292 293 closecontext: 294 CryptReleaseContext(hcryptprov, 0); 295 296 errout: 297 if (source != NULL) 298 isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t)); 299 300 UNLOCK(&ent->lock); 301 302 return (ret); 303} 304 305 306 307 308