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