4 * Copyright (C) 2004, 2007, 2009 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 2000-2002 Internet Software Consortium.
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.
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.
20 /* Id: entropy.c,v 1.8.332.2 2009/01/18 23:47:41 tbox Exp */
23 * This is the system dependent part of the ISC entropy API.
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
42 #define FILESOURCE_HANDLE_TYPE HCRYPTPROV
46 } isc_entropyusocketsource_t
;
48 #include "../entropy.c"
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
;
61 desired
= desired
/ 8 + (((desired
& 0x07) > 0) ? 1 : 0);
65 ndesired
= ISC_MIN(desired
, sizeof(buf
));
66 if (!CryptGenRandom(hcryptprov
, ndesired
, buf
)) {
67 CryptReleaseContext(hcryptprov
, 0);
68 source
->bad
= ISC_TRUE
;
72 entropypool_adddata(ent
, buf
, ndesired
, ndesired
* 8);
73 added
+= ndesired
* 8;
82 * Poll each source, trying to get data from it to stuff into the entropy
86 fillpool(isc_entropy_t
*ent
, unsigned int desired
, isc_boolean_t blocking
) {
88 unsigned int remaining
;
91 isc_entropysource_t
*source
;
92 isc_entropysource_t
*firstsource
;
94 REQUIRE(VALID_ENTROPY(ent
));
99 * This logic is a little strange, so an explanation is in order.
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.
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.
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).
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
121 if ((ent
->pool
.entropy
>= RND_POOLBITS
/ 4)
122 && (ent
->pool
.pseudo
<= RND_POOLBITS
/ 4))
125 needed
= THRESHOLD_BITS
* 4;
127 needed
= ISC_MAX(needed
, THRESHOLD_BITS
);
128 needed
= ISC_MIN(needed
, RND_POOLBITS
);
132 * In any case, clamp how much we need to how much we can add.
134 needed
= ISC_MIN(needed
, RND_POOLBITS
- ent
->pool
.entropy
);
137 * But wait! If we're not yet initialized, we need at least
141 if (ent
->initialized
< THRESHOLD_BITS
)
142 needed
= ISC_MAX(needed
, THRESHOLD_BITS
- ent
->initialized
);
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.
154 if (ent
->nextsource
== NULL
) {
155 ent
->nextsource
= ISC_LIST_HEAD(ent
->sources
);
156 if (ent
->nextsource
== NULL
)
159 source
= ent
->nextsource
;
161 * Remember the first source so we can break if we have looped back to
162 * the beginning and still have nothing
164 firstsource
= source
;
166 for (nsource
= 0; nsource
< ent
->nsources
; nsource
++) {
174 if (source
->type
== ENTROPY_SOURCETYPE_FILE
)
175 got
= get_from_filesource(source
, remaining
);
179 remaining
-= ISC_MIN(remaining
, got
);
181 source
= ISC_LIST_NEXT(source
, link
);
183 source
= ISC_LIST_HEAD(ent
->sources
);
185 ent
->nextsource
= source
;
188 * Go again only if there's been progress and we've not
189 * gone back to the beginning
191 if (!(ent
->nextsource
== firstsource
&& added
== 0)) {
192 if (blocking
&& remaining
!= 0) {
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.
201 source
= ISC_LIST_HEAD(ent
->sources
);
202 while ((remaining
!= 0) && (source
!= NULL
)) {
207 if (source
->type
== ENTROPY_SOURCETYPE_CALLBACK
)
208 got
= get_from_callback(source
, remaining
, blocking
);
211 remaining
-= ISC_MIN(remaining
, got
);
216 source
= ISC_LIST_NEXT(source
, link
);
220 * Mark as initialized if we've added enough data.
222 if (ent
->initialized
< THRESHOLD_BITS
)
223 ent
->initialized
+= added
;
229 * Requires "ent" be locked.
232 destroyfilesource(isc_entropyfilesource_t
*source
) {
233 CryptReleaseContext(source
->handle
, 0);
237 destroyusocketsource(isc_entropyusocketsource_t
*source
) {
243 isc_entropy_createfilesource(isc_entropy_t
*ent
, const char *fname
) {
245 isc_entropysource_t
*source
;
246 HCRYPTPROV hcryptprov
;
250 REQUIRE(VALID_ENTROPY(ent
));
251 REQUIRE(fname
!= NULL
);
258 * The first time we just try to acquire the context
260 err
= CryptAcquireContext(&hcryptprov
, NULL
, NULL
, PROV_RSA_FULL
,
261 CRYPT_VERIFYCONTEXT
);
263 errval
= GetLastError();
268 source
= isc_mem_get(ent
->mctx
, sizeof(isc_entropysource_t
));
269 if (source
== NULL
) {
270 ret
= ISC_R_NOMEMORY
;
275 * From here down, no failures can occur.
277 source
->magic
= SOURCE_MAGIC
;
278 source
->type
= ENTROPY_SOURCETYPE_FILE
;
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
;
287 * Hook it into the entropy system.
289 ISC_LIST_APPEND(ent
->sources
, source
, link
);
293 return (ISC_R_SUCCESS
);
296 CryptReleaseContext(hcryptprov
, 0);
300 isc_mem_put(ent
->mctx
, source
, sizeof(isc_entropysource_t
));