1 /* $NetBSD: subr_cprng.c,v 1.7 2012/04/10 15:12:40 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 #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.7 2012/04/10 15:12:40 tls 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) && mutex_tryenter(&c->reseed.mtx)) { 79 c->reseed_pending = 1; 80 c->reseed.len = NIST_BLOCK_KEYLEN_BYTES; 81 rndsink_attach(&c->reseed); 82 mutex_spin_exit(&c->reseed.mtx); 83 } 84 } 85 86 static void 87 cprng_strong_reseed(void *const arg) 88 { 89 cprng_strong_t *c = arg; 90 uint8_t key[NIST_BLOCK_KEYLEN_BYTES]; 91 uint32_t cc = cprng_counter(); 92 93 if (!mutex_tryenter(&c->mtx)) { 94 return; 95 } 96 97 if (c->reseed.len != sizeof(key)) { 98 panic("cprng_strong_reseed: bad entropy length %d " 99 " (expected %d)", (int)c->reseed.len, (int)sizeof(key)); 100 } 101 if (nist_ctr_drbg_reseed(&c->drbg, c->reseed.data, c->reseed.len, 102 &cc, sizeof(cc))) { 103 panic("cprng %s: nist_ctr_drbg_reseed failed.", c->name); 104 } 105 c->reseed_pending = 0; 106 if (c->flags & CPRNG_USE_CV) { 107 cv_broadcast(&c->cv); 108 } 109 selnotify(&c->selq, 0, 0); 110 mutex_exit(&c->mtx); 111 } 112 113 cprng_strong_t * 114 cprng_strong_create(const char *const name, int ipl, int flags) 115 { 116 cprng_strong_t *c; 117 uint8_t key[NIST_BLOCK_KEYLEN_BYTES]; 118 int r, getmore = 0, hard = 0; 119 uint32_t cc; 120 121 c = kmem_alloc(sizeof(*c), KM_NOSLEEP); 122 if (c == NULL) { 123 return NULL; 124 } 125 c->flags = flags; 126 strlcpy(c->name, name, sizeof(c->name)); 127 c->reseed_pending = 0; 128 c->reseed.cb = cprng_strong_reseed; 129 c->reseed.arg = c; 130 mutex_init(&c->reseed.mtx, MUTEX_DEFAULT, IPL_VM); 131 strlcpy(c->reseed.name, name, sizeof(c->reseed.name)); 132 133 mutex_init(&c->mtx, MUTEX_DEFAULT, ipl); 134 135 if (c->flags & CPRNG_USE_CV) { 136 cv_init(&c->cv, name); 137 } 138 139 selinit(&c->selq); 140 141 r = rnd_extract_data(key, sizeof(key), RND_EXTRACT_GOOD); 142 if (r != sizeof(key)) { 143 if (c->flags & CPRNG_INIT_ANY) { 144 #ifdef DEBUG 145 printf("cprng %s: WARNING insufficient " 146 "entropy at creation.\n", name); 147 #endif 148 rnd_extract_data(key + r, sizeof(key - r), 149 RND_EXTRACT_ANY); 150 } else { 151 hard++; 152 } 153 getmore++; 154 } 155 156 if (nist_ctr_drbg_instantiate(&c->drbg, key, sizeof(key), 157 &cc, sizeof(cc), name, strlen(name))) { 158 panic("cprng %s: instantiation failed.", name); 159 } 160 161 if (getmore) { 162 /* Cause readers to wait for rekeying. */ 163 if (hard) { 164 c->drbg.reseed_counter = 165 NIST_CTR_DRBG_RESEED_INTERVAL + 1; 166 } else { 167 c->drbg.reseed_counter = 168 (NIST_CTR_DRBG_RESEED_INTERVAL / 2) + 1; 169 } 170 } 171 return c; 172 } 173 174 size_t 175 cprng_strong(cprng_strong_t *const c, void *const p, size_t len, int flags) 176 { 177 uint32_t cc = cprng_counter(); 178 #ifdef DEBUG 179 int testfail = 0; 180 #endif 181 if (len > CPRNG_MAX_LEN) { /* XXX should we loop? */ 182 len = CPRNG_MAX_LEN; /* let the caller loop if desired */ 183 } 184 mutex_enter(&c->mtx); 185 186 if (nist_ctr_drbg_generate(&c->drbg, p, len, &cc, sizeof(cc))) { 187 /* A generator failure really means we hit the hard limit. */ 188 if (c->flags & CPRNG_REKEY_ANY) { 189 uint8_t key[NIST_BLOCK_KEYLEN_BYTES]; 190 191 printf("cprng %s: WARNING pseudorandom rekeying.\n", 192 c->name); 193 rnd_extract_data(key, sizeof(key), RND_EXTRACT_ANY); 194 cc = cprng_counter(); 195 if (nist_ctr_drbg_reseed(&c->drbg, key, sizeof(key), 196 &cc, sizeof(cc))) { 197 panic("cprng %s: nist_ctr_drbg_reseed " 198 "failed.", c->name); 199 } 200 } else { 201 if (!(flags & FNONBLOCK) && 202 (c->flags & CPRNG_USE_CV)) { 203 int wr; 204 205 cprng_strong_sched_reseed(c); 206 do { 207 wr = cv_wait_sig(&c->cv, &c->mtx); 208 if (wr == ERESTART) { 209 mutex_exit(&c->mtx); 210 return 0; 211 } 212 } while (nist_ctr_drbg_generate(&c->drbg, p, 213 len, &cc, 214 sizeof(cc))); 215 } else { 216 len = 0; 217 } 218 } 219 } 220 221 #ifdef DEBUG 222 /* 223 * If the generator has just been keyed, perform 224 * the statistical RNG test. 225 */ 226 if (__predict_false(c->drbg.reseed_counter == 1)) { 227 rngtest_t *rt = kmem_alloc(sizeof(*rt), KM_NOSLEEP); 228 229 if (rt) { 230 231 strncpy(rt->rt_name, c->name, sizeof(rt->rt_name)); 232 233 if (nist_ctr_drbg_generate(&c->drbg, rt->rt_b, 234 sizeof(rt->rt_b), NULL, 0)) { 235 panic("cprng %s: nist_ctr_drbg_generate " 236 "failed!", c->name); 237 238 } 239 testfail = rngtest(rt); 240 241 if (testfail) { 242 printf("cprng %s: failed statistical RNG " 243 "test.\n", c->name); 244 c->drbg.reseed_counter = 245 NIST_CTR_DRBG_RESEED_INTERVAL + 1; 246 len = 0; 247 } 248 memset(rt, 0, sizeof(*rt)); 249 kmem_free(rt, sizeof(*rt)); 250 } 251 } 252 #endif 253 if (__predict_false(c->drbg.reseed_counter > 254 (NIST_CTR_DRBG_RESEED_INTERVAL / 2))) { 255 cprng_strong_sched_reseed(c); 256 } 257 258 if (rnd_full) { 259 if (!c->rekeyed_on_full) { 260 c->rekeyed_on_full++; 261 cprng_strong_sched_reseed(c); 262 } 263 } else { 264 c->rekeyed_on_full = 0; 265 } 266 267 mutex_exit(&c->mtx); 268 return len; 269 } 270 271 void 272 cprng_strong_destroy(cprng_strong_t *c) 273 { 274 mutex_enter(&c->mtx); 275 mutex_spin_enter(&c->reseed.mtx); 276 277 if (c->flags & CPRNG_USE_CV) { 278 KASSERT(!cv_has_waiters(&c->cv)); 279 cv_destroy(&c->cv); 280 } 281 seldestroy(&c->selq); 282 283 if (c->reseed_pending) { 284 rndsink_detach(&c->reseed); 285 } 286 mutex_spin_exit(&c->reseed.mtx); 287 mutex_destroy(&c->reseed.mtx); 288 289 nist_ctr_drbg_destroy(&c->drbg); 290 291 mutex_exit(&c->mtx); 292 mutex_destroy(&c->mtx); 293 294 memset(c, 0, sizeof(*c)); 295 kmem_free(c, sizeof(*c)); 296 } 297 298 int 299 cprng_strong_getflags(cprng_strong_t *const c) 300 { 301 KASSERT(mutex_owned(&c->mtx)); 302 return c->flags; 303 } 304 305 void 306 cprng_strong_setflags(cprng_strong_t *const c, int flags) 307 { 308 KASSERT(mutex_owned(&c->mtx)); 309 if (flags & CPRNG_USE_CV) { 310 if (!(c->flags & CPRNG_USE_CV)) { 311 cv_init(&c->cv, (const char *)c->name); 312 } 313 } else { 314 if (c->flags & CPRNG_USE_CV) { 315 KASSERT(!cv_has_waiters(&c->cv)); 316 cv_destroy(&c->cv); 317 } 318 } 319 if (flags & CPRNG_REKEY_ANY) { 320 if (!(c->flags & CPRNG_REKEY_ANY)) { 321 if (c->flags & CPRNG_USE_CV) { 322 cv_broadcast(&c->cv); 323 } 324 selnotify(&c->selq, 0, 0); 325 } 326 } 327 c->flags = flags; 328 } 329