subr_cprng.c revision 1.1 1 /* $NetBSD: subr_cprng.c,v 1.1 2011/11/19 22:51:25 tls 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
42 #include <machine/cpu_counter.h>
43
44 #include <sys/cprng.h>
45
46 __KERNEL_RCSID(0, "$NetBSD: subr_cprng.c,v 1.1 2011/11/19 22:51:25 tls Exp $");
47
48 void
49 cprng_init(void)
50 {
51 nist_ctr_initialize();
52 }
53
54 static inline uint32_t
55 cprng_counter(void)
56 {
57 struct timeval tv;
58
59 #if defined(__HAVE_CPU_COUNTER)
60 if (cpu_hascounter())
61 return cpu_counter32();
62 #endif
63 if (__predict_false(cold)) {
64 /* microtime unsafe if clock not running yet */
65 return 0;
66 }
67 microtime(&tv);
68 return (tv.tv_sec * 1000000 + tv.tv_usec);
69 }
70
71 static void
72 cprng_strong_reseed(void *const arg)
73 {
74 cprng_strong_t *c = arg;
75 uint8_t key[NIST_BLOCK_KEYLEN_BYTES];
76 uint32_t cc = cprng_counter();
77
78 mutex_enter(&c->mtx);
79 if (c->reseed.len != sizeof(key)) {
80 panic("cprng_strong_reseed: bad entropy length %d "
81 " (expected %d)", (int)c->reseed.len, (int)sizeof(key));
82 }
83 if (nist_ctr_drbg_reseed(&c->drbg, c->reseed.data, c->reseed.len,
84 &cc, sizeof(cc))) {
85 panic("cprng %s: nist_ctr_drbg_reseed failed.", c->name);
86 }
87 c->reseed_pending = 0;
88 if (c->flags & CPRNG_USE_CV) {
89 cv_broadcast(&c->cv);
90 }
91 mutex_exit(&c->mtx);
92 }
93
94 cprng_strong_t *
95 cprng_strong_create(const char *const name, int ipl, int flags)
96 {
97 cprng_strong_t *c;
98 uint8_t key[NIST_BLOCK_KEYLEN_BYTES];
99 int r, getmore = 0;;
100 uint32_t cc;
101
102 c = kmem_alloc(sizeof(*c), KM_NOSLEEP);
103 if (c == NULL) {
104 return NULL;
105 }
106 c->flags = flags;
107 strlcpy(c->name, name, sizeof(c->name));
108 c->reseed_pending = 0;
109 c->reseed.cb = cprng_strong_reseed;
110 c->reseed.arg = c;
111 strlcpy(c->reseed.name, name, sizeof(c->reseed.name));
112
113 mutex_init(&c->mtx, MUTEX_DEFAULT, ipl);
114
115 if (c->flags & CPRNG_USE_CV) {
116 cv_init(&c->cv, name);
117 }
118
119 r = rnd_extract_data(key, sizeof(key), RND_EXTRACT_GOOD);
120 if (r != sizeof(key)) {
121 if (c->flags & CPRNG_INIT_ANY) {
122 printf("cprng %s: WARNING insufficient "
123 "entropy at creation.\n", name);
124 rnd_extract_data(key + r, sizeof(key - r),
125 RND_EXTRACT_ANY);
126 } else {
127 return NULL;
128 }
129 getmore++;
130 }
131
132 if (nist_ctr_drbg_instantiate(&c->drbg, key, sizeof(key),
133 &cc, sizeof(cc), name, strlen(name))) {
134 panic("cprng %s: instantiation failed.", name);
135 }
136
137 if (getmore) {
138 int wr = 0;
139
140 /* Ask for more. */
141 c->reseed_pending = 1;
142 c->reseed.len = sizeof(key);
143 rndsink_attach(&c->reseed);
144 if (c->flags & CPRNG_USE_CV) {
145 mutex_enter(&c->mtx);
146 do {
147 wr = cv_wait_sig(&c->cv, &c->mtx);
148 if (__predict_true(wr == 0)) {
149 break;
150 }
151 if (wr == ERESTART) {
152 continue;
153 } else {
154 cv_destroy(&c->cv);
155 mutex_exit(&c->mtx);
156 mutex_destroy(&c->mtx);
157 kmem_free(c, sizeof(c));
158 return NULL;
159 }
160 } while (1);
161 mutex_exit(&c->mtx);
162 }
163 }
164 return c;
165 }
166
167 size_t
168 cprng_strong(cprng_strong_t *const c, void *const p, size_t len)
169 {
170 uint32_t cc = cprng_counter();
171
172 if (len > CPRNG_MAX_LEN) { /* XXX should we loop? */
173 len = CPRNG_MAX_LEN; /* let the caller loop if desired */
174 }
175
176 mutex_enter(&c->mtx);
177 again:
178 if (nist_ctr_drbg_generate(&c->drbg, p, len, &cc, sizeof(cc))) {
179 /* A generator failure really means we hit the hard limit. */
180 if (c->flags & CPRNG_REKEY_ANY) {
181 uint8_t key[NIST_BLOCK_KEYLEN_BYTES];
182
183 printf("cprng %s: WARNING pseudorandom rekeying.\n",
184 c->name);
185 rnd_extract_data(key, sizeof(key), RND_EXTRACT_ANY);
186 cc = cprng_counter();
187 if (nist_ctr_drbg_reseed(&c->drbg, key, sizeof(key),
188 &cc, sizeof(cc))) {
189 panic("cprng %s: nist_ctr_drbg_reseed "
190 "failed.", c->name);
191 }
192 if (c->flags & CPRNG_USE_CV) {
193 cv_broadcast(&c->cv); /* XXX unnecessary? */
194 }
195 } else {
196 if (c->flags & CPRNG_USE_CV) {
197 int wr;
198
199 do {
200 wr = cv_wait_sig(&c->cv, &c->mtx);
201 if (wr == EINTR) {
202 mutex_exit(&c->mtx);
203 return 0;
204 }
205 } while (nist_ctr_drbg_generate(&c->drbg, p,
206 len, &cc,
207 sizeof(cc)));
208 } else {
209 mutex_exit(&c->mtx);
210 return 0;
211 }
212 }
213 }
214
215 #ifdef DIAGNOSTIC
216 /*
217 * If the generator has just been keyed, perform
218 * the statistical RNG test.
219 */
220 if (__predict_false(c->drbg.reseed_counter == 1)) {
221 rngtest_t rt;
222
223 strncpy(rt.rt_name, c->name, sizeof(rt.rt_name));
224
225 if (nist_ctr_drbg_generate(&c->drbg, rt.rt_b,
226 sizeof(rt.rt_b), NULL, 0)) {
227 panic("cprng %s: nist_ctr_drbg_generate failed!",
228 c->name);
229
230 }
231 if (rngtest(&rt)) {
232 printf("cprng %s: failed statistical RNG test.\n",
233 c->name);
234 c->drbg.reseed_counter =
235 NIST_CTR_DRBG_RESEED_INTERVAL + 1;
236 }
237
238 memset(&rt, 0, sizeof(rt));
239 }
240 #endif
241 if (__predict_false(c->drbg.reseed_counter >
242 (NIST_CTR_DRBG_RESEED_INTERVAL / 2))) {
243 if (!(c->reseed_pending)) {
244 c->reseed_pending = 1;
245 c->reseed.len = NIST_BLOCK_KEYLEN_BYTES;
246 rndsink_attach(&c->reseed);
247 }
248 if (__predict_false(c->drbg.reseed_counter >
249 NIST_CTR_DRBG_RESEED_INTERVAL)) {
250 goto again; /* statistical test failure */
251 }
252 }
253
254 mutex_exit(&c->mtx);
255 return len;
256 }
257
258 void
259 cprng_strong_destroy(cprng_strong_t *c)
260 {
261 KASSERT(!mutex_owned(&c->mtx));
262 if (c->flags & CPRNG_USE_CV) {
263 KASSERT(!cv_has_waiters(&c->cv));
264 cv_destroy(&c->cv);
265 }
266
267 mutex_destroy(&c->mtx);
268 if (c->reseed_pending) {
269 rndsink_detach(&c->reseed);
270 }
271 nist_ctr_drbg_destroy(&c->drbg);
272 memset(c, 0, sizeof(*c));
273 kmem_free(c, sizeof(*c));
274 }
275
276 int
277 cprng_strong_getflags(cprng_strong_t *const c)
278 {
279 KASSERT(mutex_owned(&c->mtx));
280 return c->flags;
281 }
282
283 void
284 cprng_strong_setflags(cprng_strong_t *const c, int flags)
285 {
286 KASSERT(mutex_owned(&c->mtx));
287 if (flags & CPRNG_USE_CV) {
288 if (!(c->flags & CPRNG_USE_CV)) {
289 cv_init(&c->cv, (const char *)c->name);
290 }
291 } else {
292 if (c->flags & CPRNG_USE_CV) {
293 KASSERT(!cv_has_waiters(&c->cv));
294 cv_destroy(&c->cv);
295 }
296 }
297 if (flags & CPRNG_REKEY_ANY) {
298 if (!(c->flags & CPRNG_REKEY_ANY)) {
299 if (c->flags & CPRNG_USE_CV) {
300 cv_broadcast(&c->cv);
301 }
302 }
303 }
304 c->flags = flags;
305 }
306