1 /* $NetBSD: subr_cprng.c,v 1.12 2012/09/08 02:58:13 msaitoh 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.12 2012/09/08 02:58:13 msaitoh 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_doreseed(cprng_strong_t *const c) 76 { 77 uint32_t cc = cprng_counter(); 78 79 KASSERT(mutex_owned(&c->mtx)); 80 KASSERT(mutex_owned(&c->reseed.mtx)); 81 KASSERT(c->reseed.len == NIST_BLOCK_KEYLEN_BYTES); 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 #ifdef RND_VERBOSE 88 printf("cprng %s: reseeded with rnd_filled = %d\n", c->name, 89 rnd_filled); 90 #endif 91 c->entropy_serial = rnd_filled; 92 c->reseed.state = RSTATE_IDLE; 93 if (c->flags & CPRNG_USE_CV) { 94 cv_broadcast(&c->cv); 95 } 96 selnotify(&c->selq, 0, 0); 97 } 98 99 static void 100 cprng_strong_sched_reseed(cprng_strong_t *const c) 101 { 102 KASSERT(mutex_owned(&c->mtx)); 103 if (mutex_tryenter(&c->reseed.mtx)) { 104 switch (c->reseed.state) { 105 case RSTATE_IDLE: 106 c->reseed.state = RSTATE_PENDING; 107 c->reseed.len = NIST_BLOCK_KEYLEN_BYTES; 108 rndsink_attach(&c->reseed); 109 break; 110 case RSTATE_HASBITS: 111 /* Just rekey the underlying generator now. */ 112 cprng_strong_doreseed(c); 113 break; 114 case RSTATE_PENDING: 115 if (c->entropy_serial != rnd_filled) { 116 rndsink_detach(&c->reseed); 117 rndsink_attach(&c->reseed); 118 } 119 break; 120 default: 121 panic("cprng %s: bad reseed state %d", 122 c->name, c->reseed.state); 123 break; 124 } 125 mutex_spin_exit(&c->reseed.mtx); 126 } 127 #ifdef RND_VERBOSE 128 else { 129 printf("cprng %s: skipping sched_reseed, sink busy\n", 130 c->name); 131 } 132 #endif 133 } 134 135 static void 136 cprng_strong_reseed(void *const arg) 137 { 138 cprng_strong_t *c = arg; 139 140 KASSERT(mutex_owned(&c->reseed.mtx)); 141 KASSERT(RSTATE_HASBITS == c->reseed.state); 142 143 if (!mutex_tryenter(&c->mtx)) { 144 #ifdef RND_VERBOSE 145 printf("cprng: sink %s cprng busy, no reseed\n", c->reseed.name); 146 #endif 147 if (c->flags & CPRNG_USE_CV) { /* XXX if flags change? */ 148 cv_broadcast(&c->cv); 149 } 150 return; 151 } 152 153 cprng_strong_doreseed(c); 154 mutex_exit(&c->mtx); 155 } 156 157 cprng_strong_t * 158 cprng_strong_create(const char *const name, int ipl, int flags) 159 { 160 cprng_strong_t *c; 161 uint8_t key[NIST_BLOCK_KEYLEN_BYTES]; 162 int r, getmore = 0, hard = 0; 163 uint32_t cc; 164 165 c = kmem_alloc(sizeof(*c), KM_NOSLEEP); 166 if (c == NULL) { 167 return NULL; 168 } 169 c->flags = flags; 170 strlcpy(c->name, name, sizeof(c->name)); 171 c->reseed.state = RSTATE_IDLE; 172 c->reseed.cb = cprng_strong_reseed; 173 c->reseed.arg = c; 174 c->entropy_serial = rnd_initial_entropy ? rnd_filled : -1; 175 mutex_init(&c->reseed.mtx, MUTEX_DEFAULT, IPL_VM); 176 strlcpy(c->reseed.name, name, sizeof(c->reseed.name)); 177 178 mutex_init(&c->mtx, MUTEX_DEFAULT, ipl); 179 180 if (c->flags & CPRNG_USE_CV) { 181 cv_init(&c->cv, name); 182 } 183 184 selinit(&c->selq); 185 186 r = rnd_extract_data(key, sizeof(key), RND_EXTRACT_GOOD); 187 if (r != sizeof(key)) { 188 if (c->flags & CPRNG_INIT_ANY) { 189 #ifdef DEBUG 190 printf("cprng %s: WARNING insufficient " 191 "entropy at creation.\n", name); 192 #endif 193 rnd_extract_data(key + r, sizeof(key - r), 194 RND_EXTRACT_ANY); 195 } else { 196 hard++; 197 } 198 getmore++; 199 } 200 201 if (nist_ctr_drbg_instantiate(&c->drbg, key, sizeof(key), 202 &cc, sizeof(cc), name, strlen(name))) { 203 panic("cprng %s: instantiation failed.", name); 204 } 205 206 if (getmore) { 207 /* Cause readers to wait for rekeying. */ 208 if (hard) { 209 c->drbg.reseed_counter = 210 NIST_CTR_DRBG_RESEED_INTERVAL + 1; 211 } else { 212 c->drbg.reseed_counter = 213 (NIST_CTR_DRBG_RESEED_INTERVAL / 2) + 1; 214 } 215 } 216 return c; 217 } 218 219 size_t 220 cprng_strong(cprng_strong_t *const c, void *const p, size_t len, int flags) 221 { 222 uint32_t cc = cprng_counter(); 223 #ifdef DEBUG 224 int testfail = 0; 225 #endif 226 if (len > CPRNG_MAX_LEN) { /* XXX should we loop? */ 227 len = CPRNG_MAX_LEN; /* let the caller loop if desired */ 228 } 229 mutex_enter(&c->mtx); 230 231 /* If we were initialized with the pool empty, rekey ASAP */ 232 if (__predict_false(c->entropy_serial == -1) && rnd_initial_entropy) { 233 c->entropy_serial = 0; 234 goto rekeyany; /* We have _some_ entropy, use it. */ 235 } 236 237 if (nist_ctr_drbg_generate(&c->drbg, p, len, &cc, sizeof(cc))) { 238 /* A generator failure really means we hit the hard limit. */ 239 rekeyany: 240 if (c->flags & CPRNG_REKEY_ANY) { 241 uint8_t key[NIST_BLOCK_KEYLEN_BYTES]; 242 243 printf("cprng %s: WARNING pseudorandom rekeying.\n", 244 c->name); 245 rnd_extract_data(key, sizeof(key), RND_EXTRACT_ANY); 246 cc = cprng_counter(); 247 if (nist_ctr_drbg_reseed(&c->drbg, key, sizeof(key), 248 &cc, sizeof(cc))) { 249 panic("cprng %s: nist_ctr_drbg_reseed " 250 "failed.", c->name); 251 } 252 } else { 253 int wr; 254 255 do { 256 cprng_strong_sched_reseed(c); 257 if ((flags & FNONBLOCK) || 258 !(c->flags & CPRNG_USE_CV)) { 259 len = 0; 260 break; 261 } 262 /* 263 * XXX There's a race with the cv_broadcast 264 * XXX in cprng_strong_sched_reseed, because 265 * XXX of the use of tryenter in that function. 266 * XXX This "timedwait" hack works around it, 267 * XXX at the expense of occasionaly polling 268 * XXX for success on a /dev/random rekey. 269 */ 270 wr = cv_timedwait_sig(&c->cv, &c->mtx, 271 mstohz(100)); 272 if (wr == ERESTART) { 273 mutex_exit(&c->mtx); 274 return 0; 275 } 276 } while (nist_ctr_drbg_generate(&c->drbg, p, 277 len, &cc, 278 sizeof(cc))); 279 } 280 } 281 282 #ifdef DEBUG 283 /* 284 * If the generator has just been keyed, perform 285 * the statistical RNG test. 286 */ 287 if (__predict_false(c->drbg.reseed_counter == 1) && 288 (flags & FASYNC) == 0) { 289 rngtest_t *rt = kmem_alloc(sizeof(*rt), KM_NOSLEEP); 290 291 if (rt) { 292 293 strncpy(rt->rt_name, c->name, sizeof(rt->rt_name)); 294 295 if (nist_ctr_drbg_generate(&c->drbg, rt->rt_b, 296 sizeof(rt->rt_b), NULL, 0)) { 297 panic("cprng %s: nist_ctr_drbg_generate " 298 "failed!", c->name); 299 300 } 301 testfail = rngtest(rt); 302 303 if (testfail) { 304 printf("cprng %s: failed statistical RNG " 305 "test.\n", c->name); 306 c->drbg.reseed_counter = 307 NIST_CTR_DRBG_RESEED_INTERVAL + 1; 308 len = 0; 309 } 310 memset(rt, 0, sizeof(*rt)); 311 kmem_free(rt, sizeof(*rt)); 312 } 313 } 314 #endif 315 if (__predict_false(c->drbg.reseed_counter > 316 (NIST_CTR_DRBG_RESEED_INTERVAL / 2))) { 317 cprng_strong_sched_reseed(c); 318 } else if (rnd_full) { 319 if (c->entropy_serial != rnd_filled) { 320 #ifdef RND_VERBOSE 321 printf("cprng %s: reseeding from full pool " 322 "(serial %d vs pool %d)\n", c->name, 323 c->entropy_serial, rnd_filled); 324 #endif 325 cprng_strong_sched_reseed(c); 326 } 327 } 328 329 mutex_exit(&c->mtx); 330 return len; 331 } 332 333 void 334 cprng_strong_destroy(cprng_strong_t *c) 335 { 336 mutex_enter(&c->mtx); 337 mutex_spin_enter(&c->reseed.mtx); 338 339 if (c->flags & CPRNG_USE_CV) { 340 KASSERT(!cv_has_waiters(&c->cv)); 341 cv_destroy(&c->cv); 342 } 343 seldestroy(&c->selq); 344 345 if (RSTATE_PENDING == c->reseed.state) { 346 rndsink_detach(&c->reseed); 347 } 348 mutex_spin_exit(&c->reseed.mtx); 349 mutex_destroy(&c->reseed.mtx); 350 351 nist_ctr_drbg_destroy(&c->drbg); 352 353 mutex_exit(&c->mtx); 354 mutex_destroy(&c->mtx); 355 356 memset(c, 0, sizeof(*c)); 357 kmem_free(c, sizeof(*c)); 358 } 359 360 int 361 cprng_strong_getflags(cprng_strong_t *const c) 362 { 363 KASSERT(mutex_owned(&c->mtx)); 364 return c->flags; 365 } 366 367 void 368 cprng_strong_setflags(cprng_strong_t *const c, int flags) 369 { 370 KASSERT(mutex_owned(&c->mtx)); 371 if (flags & CPRNG_USE_CV) { 372 if (!(c->flags & CPRNG_USE_CV)) { 373 cv_init(&c->cv, (const char *)c->name); 374 } 375 } else { 376 if (c->flags & CPRNG_USE_CV) { 377 KASSERT(!cv_has_waiters(&c->cv)); 378 cv_destroy(&c->cv); 379 } 380 } 381 if (flags & CPRNG_REKEY_ANY) { 382 if (!(c->flags & CPRNG_REKEY_ANY)) { 383 if (c->flags & CPRNG_USE_CV) { 384 cv_broadcast(&c->cv); 385 } 386 selnotify(&c->selq, 0, 0); 387 } 388 } 389 c->flags = flags; 390 } 391