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