subr_cprng.c revision 1.4.2.1 1 /* $NetBSD: subr_cprng.c,v 1.4.2.1 2012/02/18 07:35:32 mrg Exp $ */
2
3 /*-
4 * Copyright (c) 2011 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Thor Lancelot Simon.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/types.h>
33 #include <sys/time.h>
34 #include <sys/param.h>
35 #include <sys/kernel.h>
36 #include <sys/systm.h>
37 #include <sys/kmem.h>
38 #include <sys/mutex.h>
39 #include <sys/rngtest.h>
40 #include <sys/rnd.h>
41 #include <dev/rnd_private.h>
42
43 #if defined(__HAVE_CPU_COUNTER)
44 #include <machine/cpu_counter.h>
45 #endif
46
47 #include <sys/cprng.h>
48
49 __KERNEL_RCSID(0, "$NetBSD: subr_cprng.c,v 1.4.2.1 2012/02/18 07:35:32 mrg Exp $");
50
51 void
52 cprng_init(void)
53 {
54 nist_ctr_initialize();
55 }
56
57 static inline uint32_t
58 cprng_counter(void)
59 {
60 struct timeval tv;
61
62 #if defined(__HAVE_CPU_COUNTER)
63 if (cpu_hascounter())
64 return cpu_counter32();
65 #endif
66 if (__predict_false(cold)) {
67 /* microtime unsafe if clock not running yet */
68 return 0;
69 }
70 microtime(&tv);
71 return (tv.tv_sec * 1000000 + tv.tv_usec);
72 }
73
74 static void
75 cprng_strong_sched_reseed(cprng_strong_t *const c)
76 {
77 KASSERT(mutex_owned(&c->mtx));
78 if (!(c->reseed_pending)) {
79 c->reseed_pending = 1;
80 c->reseed.len = NIST_BLOCK_KEYLEN_BYTES;
81 rndsink_attach(&c->reseed);
82 }
83 }
84
85 static void
86 cprng_strong_reseed(void *const arg)
87 {
88 cprng_strong_t *c = arg;
89 uint8_t key[NIST_BLOCK_KEYLEN_BYTES];
90 uint32_t cc = cprng_counter();
91
92 mutex_enter(&c->mtx);
93 if (c->reseed.len != sizeof(key)) {
94 panic("cprng_strong_reseed: bad entropy length %d "
95 " (expected %d)", (int)c->reseed.len, (int)sizeof(key));
96 }
97 if (nist_ctr_drbg_reseed(&c->drbg, c->reseed.data, c->reseed.len,
98 &cc, sizeof(cc))) {
99 panic("cprng %s: nist_ctr_drbg_reseed failed.", c->name);
100 }
101 c->reseed_pending = 0;
102 if (c->flags & CPRNG_USE_CV) {
103 cv_broadcast(&c->cv);
104 }
105 selnotify(&c->selq, 0, 0);
106 mutex_exit(&c->mtx);
107 }
108
109 cprng_strong_t *
110 cprng_strong_create(const char *const name, int ipl, int flags)
111 {
112 cprng_strong_t *c;
113 uint8_t key[NIST_BLOCK_KEYLEN_BYTES];
114 int r, getmore = 0, hard = 0;
115 uint32_t cc;
116
117 c = kmem_alloc(sizeof(*c), KM_NOSLEEP);
118 if (c == NULL) {
119 return NULL;
120 }
121 c->flags = flags;
122 strlcpy(c->name, name, sizeof(c->name));
123 c->reseed_pending = 0;
124 c->reseed.cb = cprng_strong_reseed;
125 c->reseed.arg = c;
126 strlcpy(c->reseed.name, name, sizeof(c->reseed.name));
127
128 mutex_init(&c->mtx, MUTEX_DEFAULT, ipl);
129
130 if (c->flags & CPRNG_USE_CV) {
131 cv_init(&c->cv, name);
132 }
133
134 selinit(&c->selq);
135
136 r = rnd_extract_data(key, sizeof(key), RND_EXTRACT_GOOD);
137 if (r != sizeof(key)) {
138 if (c->flags & CPRNG_INIT_ANY) {
139 #ifdef DEBUG
140 printf("cprng %s: WARNING insufficient "
141 "entropy at creation.\n", name);
142 #endif
143 rnd_extract_data(key + r, sizeof(key - r),
144 RND_EXTRACT_ANY);
145 } else {
146 hard++;
147 }
148 getmore++;
149 }
150
151 if (nist_ctr_drbg_instantiate(&c->drbg, key, sizeof(key),
152 &cc, sizeof(cc), name, strlen(name))) {
153 panic("cprng %s: instantiation failed.", name);
154 }
155
156 if (getmore) {
157 /* Cause readers to wait for rekeying. */
158 if (hard) {
159 c->drbg.reseed_counter =
160 NIST_CTR_DRBG_RESEED_INTERVAL + 1;
161 } else {
162 c->drbg.reseed_counter =
163 (NIST_CTR_DRBG_RESEED_INTERVAL / 2) + 1;
164 }
165 }
166 return c;
167 }
168
169 size_t
170 cprng_strong(cprng_strong_t *const c, void *const p, size_t len, int flags)
171 {
172 uint32_t cc = cprng_counter();
173 #ifdef DEBUG
174 int testfail = 0;
175 #endif
176 if (len > CPRNG_MAX_LEN) { /* XXX should we loop? */
177 len = CPRNG_MAX_LEN; /* let the caller loop if desired */
178 }
179 mutex_enter(&c->mtx);
180
181 if (nist_ctr_drbg_generate(&c->drbg, p, len, &cc, sizeof(cc))) {
182 /* A generator failure really means we hit the hard limit. */
183 if (c->flags & CPRNG_REKEY_ANY) {
184 uint8_t key[NIST_BLOCK_KEYLEN_BYTES];
185
186 printf("cprng %s: WARNING pseudorandom rekeying.\n",
187 c->name);
188 rnd_extract_data(key, sizeof(key), RND_EXTRACT_ANY);
189 cc = cprng_counter();
190 if (nist_ctr_drbg_reseed(&c->drbg, key, sizeof(key),
191 &cc, sizeof(cc))) {
192 panic("cprng %s: nist_ctr_drbg_reseed "
193 "failed.", c->name);
194 }
195 } else {
196 if (!(flags & FNONBLOCK) &&
197 (c->flags & CPRNG_USE_CV)) {
198 int wr;
199
200 cprng_strong_sched_reseed(c);
201 do {
202 wr = cv_wait_sig(&c->cv, &c->mtx);
203 if (wr == ERESTART) {
204 mutex_exit(&c->mtx);
205 return 0;
206 }
207 } while (nist_ctr_drbg_generate(&c->drbg, p,
208 len, &cc,
209 sizeof(cc)));
210 } else {
211 len = 0;
212 }
213 }
214 }
215
216 #ifdef DEBUG
217 /*
218 * If the generator has just been keyed, perform
219 * the statistical RNG test.
220 */
221 if (__predict_false(c->drbg.reseed_counter == 1)) {
222 rngtest_t *rt = kmem_alloc(sizeof(*rt), KM_NOSLEEP);
223
224 if (rt) {
225
226 strncpy(rt->rt_name, c->name, sizeof(rt->rt_name));
227
228 if (nist_ctr_drbg_generate(&c->drbg, rt->rt_b,
229 sizeof(rt->rt_b), NULL, 0)) {
230 panic("cprng %s: nist_ctr_drbg_generate "
231 "failed!", c->name);
232
233 }
234 testfail = rngtest(rt);
235
236 if (testfail) {
237 printf("cprng %s: failed statistical RNG "
238 "test.\n", c->name);
239 c->drbg.reseed_counter =
240 NIST_CTR_DRBG_RESEED_INTERVAL + 1;
241 len = 0;
242 }
243 memset(rt, 0, sizeof(*rt));
244 kmem_free(rt, sizeof(*rt));
245 }
246 }
247 #endif
248 if (__predict_false(c->drbg.reseed_counter >
249 (NIST_CTR_DRBG_RESEED_INTERVAL / 2))) {
250 cprng_strong_sched_reseed(c);
251 }
252
253 if (rnd_full) {
254 if (!c->rekeyed_on_full) {
255 c->rekeyed_on_full++;
256 cprng_strong_sched_reseed(c);
257 }
258 } else {
259 c->rekeyed_on_full = 0;
260 }
261
262 mutex_exit(&c->mtx);
263 return len;
264 }
265
266 void
267 cprng_strong_destroy(cprng_strong_t *c)
268 {
269 mutex_enter(&c->mtx);
270
271 if (c->flags & CPRNG_USE_CV) {
272 KASSERT(!cv_has_waiters(&c->cv));
273 cv_destroy(&c->cv);
274 }
275 seldestroy(&c->selq);
276
277 if (c->reseed_pending) {
278 rndsink_detach(&c->reseed);
279 }
280 nist_ctr_drbg_destroy(&c->drbg);
281
282 mutex_exit(&c->mtx);
283 mutex_destroy(&c->mtx);
284
285 memset(c, 0, sizeof(*c));
286 kmem_free(c, sizeof(*c));
287 }
288
289 int
290 cprng_strong_getflags(cprng_strong_t *const c)
291 {
292 KASSERT(mutex_owned(&c->mtx));
293 return c->flags;
294 }
295
296 void
297 cprng_strong_setflags(cprng_strong_t *const c, int flags)
298 {
299 KASSERT(mutex_owned(&c->mtx));
300 if (flags & CPRNG_USE_CV) {
301 if (!(c->flags & CPRNG_USE_CV)) {
302 cv_init(&c->cv, (const char *)c->name);
303 }
304 } else {
305 if (c->flags & CPRNG_USE_CV) {
306 KASSERT(!cv_has_waiters(&c->cv));
307 cv_destroy(&c->cv);
308 }
309 }
310 if (flags & CPRNG_REKEY_ANY) {
311 if (!(c->flags & CPRNG_REKEY_ANY)) {
312 if (c->flags & CPRNG_USE_CV) {
313 cv_broadcast(&c->cv);
314 }
315 selnotify(&c->selq, 0, 0);
316 }
317 }
318 c->flags = flags;
319 }
320