1 /* $NetBSD: rndpool.c,v 1.19 2005/12/11 12:20:53 christos Exp $ */
4 * Copyright (c) 1997 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Michael Graff <explorer@flame.org>. This code uses ideas and
9 * algorithms from the Linux driver written by Ted Ts'o.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: rndpool.c,v 1.19 2005/12/11 12:20:53 christos Exp $");
36 #include <sys/param.h>
37 #include <sys/systm.h>
43 * The random pool "taps"
51 static inline void rndpool_add_one_word(rndpool_t
*, u_int32_t
);
54 rndpool_init(rndpool_t
*rp
)
60 memset(&rp
->stats
, 0, sizeof(rp
->stats
));
62 rp
->stats
.curentropy
= 0;
63 rp
->stats
.poolsize
= RND_POOLWORDS
;
64 rp
->stats
.threshold
= RND_ENTROPY_THRESHOLD
;
65 rp
->stats
.maxentropy
= RND_POOLBITS
;
67 KASSERT(RND_ENTROPY_THRESHOLD
*2 <= 20); /* XXX sha knowledge */
71 rndpool_get_entropy_count(rndpool_t
*rp
)
74 return (rp
->stats
.curentropy
);
77 void rndpool_get_stats(rndpool_t
*rp
, void *rsp
, int size
)
80 memcpy(rsp
, &rp
->stats
, size
);
84 rndpool_increment_entropy_count(rndpool_t
*rp
, u_int32_t entropy
)
87 rp
->stats
.curentropy
+= entropy
;
88 rp
->stats
.added
+= entropy
;
89 if (rp
->stats
.curentropy
> RND_POOLBITS
) {
90 rp
->stats
.discarded
+= (rp
->stats
.curentropy
- RND_POOLBITS
);
91 rp
->stats
.curentropy
= RND_POOLBITS
;
96 rndpool_get_pool(rndpool_t
*rp
)
103 rndpool_get_poolsize(void)
106 return (RND_POOLWORDS
);
110 * The input function treats the contents of the pool as an array of
111 * 32 LFSR's of length RND_POOLWORDS, one per bit-plane. The LFSR's
112 * are clocked once in parallel, using 32-bit xor operations, for each
115 * Each word to be added is xor'd with the output word of the LFSR
116 * array (one tap at a time).
118 * In order to facilitate distribution of entropy between the
119 * bit-planes, a 32-bit rotate of this result is performed prior to
120 * feedback. The rotation distance is incremented every RND_POOLWORDS
121 * clocks, by a value that is relativly prime to the word size to try
122 * to spread the bits throughout the pool quickly when the pool is
125 * Each LFSR thus takes its feedback from another LFSR, and is
126 * effectively re-keyed by both that LFSR and the new data. Feedback
127 * occurs with another XOR into the new LFSR, rather than assignment,
128 * to avoid destroying any entropy in the destination.
130 * Even with zeros as input, the LFSR output data are never visible;
131 * the contents of the pool are never divulged except via a hash of
132 * the entire pool, so there is no information for correlation
133 * attacks. With rotation-based rekeying, each LFSR runs at most a few
134 * cycles before being permuted. However, beware of initial
135 * conditions when no entropy has been added.
137 * The output function also stirs the generated hash back into the
138 * pool, further permuting the LFSRs and spreading entropy through the
139 * pool. Any unknown bits anywhere in the pool are thus reflected
140 * across all the LFSRs after output.
142 * (The final XOR assignment into the pool for feedback is equivalent
143 * to an additional LFSR tap of the MSB before shifting, in the case
144 * where no rotation is done, once every 32 cycles. This LFSR runs for
145 * at most one length.)
148 rndpool_add_one_word(rndpool_t
*rp
, u_int32_t val
)
151 * Shifting is implemented using a cursor and taps as offsets,
152 * added mod the size of the pool. For this reason,
153 * RND_POOLWORDS must be a power of two.
155 val
^= rp
->pool
[(rp
->cursor
+ TAP1
) & (RND_POOLWORDS
- 1)];
156 val
^= rp
->pool
[(rp
->cursor
+ TAP2
) & (RND_POOLWORDS
- 1)];
157 val
^= rp
->pool
[(rp
->cursor
+ TAP3
) & (RND_POOLWORDS
- 1)];
158 val
^= rp
->pool
[(rp
->cursor
+ TAP4
) & (RND_POOLWORDS
- 1)];
159 val
^= rp
->pool
[(rp
->cursor
+ TAP5
) & (RND_POOLWORDS
- 1)];
161 val
= ((val
<< rp
->rotate
) | (val
>> (32 - rp
->rotate
)));
162 rp
->pool
[rp
->cursor
++] ^= val
;
165 * If we have looped around the pool, increment the rotate
166 * variable so the next value will get xored in rotated to
167 * a different position.
169 if (rp
->cursor
== RND_POOLWORDS
) {
171 rp
->rotate
= (rp
->rotate
+ 7) & 31;
177 * Stir a 32-bit value (with possibly less entropy than that) into the pool.
178 * Update entropy estimate.
181 rndpool_add_uint32(rndpool_t
*rp
, u_int32_t val
, u_int32_t entropy
)
183 rndpool_add_one_word(rp
, val
);
185 rp
->entropy
+= entropy
;
186 rp
->stats
.added
+= entropy
;
187 if (rp
->entropy
> RND_POOLBITS
) {
188 rp
->stats
.discarded
+= (rp
->entropy
- RND_POOLBITS
);
189 rp
->entropy
= RND_POOLBITS
;
195 * Add a buffer's worth of data to the pool.
198 rndpool_add_data(rndpool_t
*rp
, void *p
, u_int32_t len
, u_int32_t entropy
)
205 for (; len
> 3; len
-= 4) {
206 val
= *((u_int32_t
*)buf
);
208 rndpool_add_one_word(rp
, val
);
218 val
= val
<< 8 | *buf
++;
220 val
= val
<< 8 | *buf
++;
223 rndpool_add_one_word(rp
, val
);
226 rp
->stats
.curentropy
+= entropy
;
227 rp
->stats
.added
+= entropy
;
229 if (rp
->stats
.curentropy
> RND_POOLBITS
) {
230 rp
->stats
.discarded
+= (rp
->stats
.curentropy
- RND_POOLBITS
);
231 rp
->stats
.curentropy
= RND_POOLBITS
;
236 * Extract some number of bytes from the random pool, decreasing the
237 * estimate of randomness as each byte is extracted.
239 * Do this by hashing the pool and returning a part of the hash as
240 * randomness. Stir the hash back into the pool. Note that no
241 * secrets going back into the pool are given away here since parts of
242 * the hash are xored together before being returned.
244 * Honor the request from the caller to only return good data, any data,
245 * etc. Note that we must have at least 64 bits of entropy in the pool
246 * before we return anything in the high-quality modes.
249 rndpool_extract_data(rndpool_t
*rp
, void *p
, u_int32_t len
, u_int32_t mode
)
253 u_char digest
[20]; /* XXX SHA knowledge */
254 u_int32_t remain
, deltae
, count
;
261 if (mode
== RND_EXTRACT_ANY
)
264 good
= (rp
->stats
.curentropy
>= (8 * RND_ENTROPY_THRESHOLD
));
266 KASSERT(RND_ENTROPY_THRESHOLD
*2 <= 20); /* XXX SHA knowledge */
268 while (good
&& (remain
!= 0)) {
270 * While bytes are requested, compute the hash of the pool,
271 * and then "fold" the hash in half with XOR, keeping the
272 * exact hash value secret, as it will be stirred back into
275 * XXX this approach needs examination by competant
276 * cryptographers! It's rather expensive per bit but
277 * also involves every bit of the pool in the
278 * computation of every output bit..
281 SHA1Update(&hash
, (u_int8_t
*)rp
->pool
, RND_POOLWORDS
* 4);
282 SHA1Final(digest
, &hash
);
285 * Stir the hash back into the pool. This guarantees
286 * that the next hash will generate a different value
287 * if no new values were added to the pool.
289 for (i
= 0; i
< 5; i
++) {
291 memcpy(&word
, &digest
[i
* 4], 4);
292 rndpool_add_one_word(rp
, word
);
295 count
= min(remain
, RND_ENTROPY_THRESHOLD
);
297 for (i
= 0; i
< count
; i
++)
298 buf
[i
] = digest
[i
] ^ digest
[i
+ RND_ENTROPY_THRESHOLD
];
304 deltae
= min(deltae
, rp
->stats
.curentropy
);
306 rp
->stats
.removed
+= deltae
;
307 rp
->stats
.curentropy
-= deltae
;
309 if (rp
->stats
.curentropy
== 0)
310 rp
->stats
.generated
+= (count
* 8) - deltae
;
312 if (mode
== RND_EXTRACT_GOOD
)
313 good
= (rp
->stats
.curentropy
>=
314 (8 * RND_ENTROPY_THRESHOLD
));
317 memset(&hash
, 0, sizeof(hash
));
318 memset(digest
, 0, sizeof(digest
));
320 return (len
- remain
);