1 /* $NetBSD: entropy.c,v 1.6 2014/12/10 04:38:01 christos Exp $ */
4 * Copyright (C) 2004, 2007, 2009, 2013 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.10 2009/01/18 23:48:14 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
, (DWORD
)ndesired
, buf
)) {
67 CryptReleaseContext(hcryptprov
, 0);
68 source
->bad
= ISC_TRUE
;
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
;
84 * Poll each source, trying to get data from it to stuff into the entropy
88 fillpool(isc_entropy_t
*ent
, unsigned int desired
, isc_boolean_t blocking
) {
90 unsigned int remaining
;
93 isc_entropysource_t
*source
;
94 isc_entropysource_t
*firstsource
;
96 REQUIRE(VALID_ENTROPY(ent
));
101 * This logic is a little strange, so an explanation is in order.
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.
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.
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).
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
123 if ((ent
->pool
.entropy
>= RND_POOLBITS
/ 4)
124 && (ent
->pool
.pseudo
<= RND_POOLBITS
/ 4))
127 needed
= THRESHOLD_BITS
* 4;
129 needed
= ISC_MAX(needed
, THRESHOLD_BITS
);
130 needed
= ISC_MIN(needed
, RND_POOLBITS
);
134 * In any case, clamp how much we need to how much we can add.
136 needed
= ISC_MIN(needed
, RND_POOLBITS
- ent
->pool
.entropy
);
139 * But wait! If we're not yet initialized, we need at least
143 if (ent
->initialized
< THRESHOLD_BITS
)
144 needed
= ISC_MAX(needed
, THRESHOLD_BITS
- ent
->initialized
);
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.
156 if (ent
->nextsource
== NULL
) {
157 ent
->nextsource
= ISC_LIST_HEAD(ent
->sources
);
158 if (ent
->nextsource
== NULL
)
161 source
= ent
->nextsource
;
163 * Remember the first source so we can break if we have looped back to
164 * the beginning and still have nothing
166 firstsource
= source
;
168 for (nsource
= 0; nsource
< ent
->nsources
; nsource
++) {
176 if (source
->type
== ENTROPY_SOURCETYPE_FILE
)
177 got
= get_from_filesource(source
, remaining
);
181 remaining
-= ISC_MIN(remaining
, got
);
183 source
= ISC_LIST_NEXT(source
, link
);
185 source
= ISC_LIST_HEAD(ent
->sources
);
187 ent
->nextsource
= source
;
190 * Go again only if there's been progress and we've not
191 * gone back to the beginning
193 if (!(ent
->nextsource
== firstsource
&& added
== 0)) {
194 if (blocking
&& remaining
!= 0) {
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.
203 source
= ISC_LIST_HEAD(ent
->sources
);
204 while ((remaining
!= 0) && (source
!= NULL
)) {
209 if (source
->type
== ENTROPY_SOURCETYPE_CALLBACK
)
210 got
= get_from_callback(source
, remaining
, blocking
);
213 remaining
-= ISC_MIN(remaining
, got
);
218 source
= ISC_LIST_NEXT(source
, link
);
222 * Mark as initialized if we've added enough data.
224 if (ent
->initialized
< THRESHOLD_BITS
)
225 ent
->initialized
+= added
;
231 * Requires "ent" be locked.
234 destroyfilesource(isc_entropyfilesource_t
*source
) {
235 CryptReleaseContext(source
->handle
, 0);
239 destroyusocketsource(isc_entropyusocketsource_t
*source
) {
245 isc_entropy_createfilesource(isc_entropy_t
*ent
, const char *fname
) {
247 isc_entropysource_t
*source
;
248 HCRYPTPROV hcryptprov
;
251 REQUIRE(VALID_ENTROPY(ent
));
252 REQUIRE(fname
!= NULL
);
259 * The first time we just try to acquire the context
261 err
= CryptAcquireContext(&hcryptprov
, NULL
, NULL
, PROV_RSA_FULL
,
262 CRYPT_VERIFYCONTEXT
);
264 (void)GetLastError();
269 source
= isc_mem_get(ent
->mctx
, sizeof(isc_entropysource_t
));
270 if (source
== NULL
) {
271 ret
= ISC_R_NOMEMORY
;
276 * From here down, no failures can occur.
278 source
->magic
= SOURCE_MAGIC
;
279 source
->type
= ENTROPY_SOURCETYPE_FILE
;
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
;
288 * Hook it into the entropy system.
290 ISC_LIST_APPEND(ent
->sources
, source
, link
);
294 return (ISC_R_SUCCESS
);
297 CryptReleaseContext(hcryptprov
, 0);
301 isc_mem_put(ent
->mctx
, source
, sizeof(isc_entropysource_t
));