fortuna.c revision 274340
1/*- 2 * Copyright (c) 2013-2014 Mark R V Murray 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer 10 * in this position and unchanged. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * 26 */ 27 28/* This implementation of Fortuna is based on the descriptions found in 29 * ISBN 0-471-22357-3 "Practical Cryptography" by Ferguson and Schneier 30 * ("F&S"). 31 * 32 * The above book is superseded by ISBN 978-0-470-47424-2 "Cryptography 33 * Engineering" by Ferguson, Schneier and Kohno ("FS&K"). The code has 34 * not yet fully caught up with FS&K. 35 */ 36 37#include <sys/cdefs.h> 38__FBSDID("$FreeBSD: head/sys/dev/random/fortuna.c 274340 2014-11-10 09:44:38Z des $"); 39 40#ifdef _KERNEL 41#include "opt_random.h" 42 43#include <sys/param.h> 44#include <sys/kernel.h> 45#include <sys/lock.h> 46#include <sys/malloc.h> 47#include <sys/mutex.h> 48#include <sys/random.h> 49#include <sys/sysctl.h> 50#include <sys/systm.h> 51 52#include <machine/cpu.h> 53 54#include <crypto/rijndael/rijndael-api-fst.h> 55#include <crypto/sha2/sha2.h> 56 57#include <dev/random/hash.h> 58#include <dev/random/randomdev.h> 59#include <dev/random/random_adaptors.h> 60#include <dev/random/random_harvestq.h> 61#include <dev/random/uint128.h> 62#include <dev/random/fortuna.h> 63#else /* !_KERNEL */ 64#include <sys/param.h> 65#include <sys/types.h> 66#include <inttypes.h> 67#include <stdio.h> 68#include <stdlib.h> 69#include <string.h> 70#include <threads.h> 71 72#include "unit_test.h" 73 74#include <crypto/rijndael/rijndael-api-fst.h> 75#include <crypto/sha2/sha2.h> 76 77#include <dev/random/hash.h> 78#include <dev/random/uint128.h> 79#include <dev/random/fortuna.h> 80#endif /* _KERNEL */ 81 82#if !defined(RANDOM_YARROW) && !defined(RANDOM_FORTUNA) 83#define RANDOM_YARROW 84#elif defined(RANDOM_YARROW) && defined(RANDOM_FORTUNA) 85#error "Must define either RANDOM_YARROW or RANDOM_FORTUNA" 86#endif 87 88#if defined(RANDOM_FORTUNA) 89 90#define NPOOLS 32 91#define MINPOOLSIZE 64 92#define DEFPOOLSIZE 256 93#define MAXPOOLSIZE 65536 94 95/* This algorithm (and code) presumes that KEYSIZE is twice as large as BLOCKSIZE */ 96CTASSERT(BLOCKSIZE == sizeof(uint128_t)); 97CTASSERT(KEYSIZE == 2*BLOCKSIZE); 98 99/* This is the beastie that needs protecting. It contains all of the 100 * state that we are excited about. 101 * Exactly one is instantiated. 102 */ 103static struct fortuna_state { 104 /* P_i */ 105 struct pool { 106 u_int length; 107 struct randomdev_hash hash; 108 } pool[NPOOLS]; 109 110 /* ReseedCnt */ 111 u_int reseedcount; 112 113 /* C - 128 bits */ 114 union { 115 uint8_t byte[BLOCKSIZE]; 116 uint128_t whole; 117 } counter; 118 119 /* K */ 120 struct randomdev_key key; 121 122 /* Extras */ 123 u_int minpoolsize; 124 125 /* Extras for the OS */ 126 127#ifdef _KERNEL 128 /* For use when 'pacing' the reseeds */ 129 sbintime_t lasttime; 130#endif 131} fortuna_state; 132 133/* The random_reseed_mtx mutex protects seeding and polling/blocking. */ 134static mtx_t random_reseed_mtx; 135 136static struct fortuna_start_cache { 137 uint8_t junk[PAGE_SIZE]; 138 size_t length; 139 struct randomdev_hash hash; 140} fortuna_start_cache; 141 142#ifdef _KERNEL 143static struct sysctl_ctx_list random_clist; 144RANDOM_CHECK_UINT(minpoolsize, MINPOOLSIZE, MAXPOOLSIZE); 145#endif 146 147void 148random_fortuna_init_alg(void) 149{ 150 int i; 151#ifdef _KERNEL 152 struct sysctl_oid *random_fortuna_o; 153#endif 154 155 memset(fortuna_start_cache.junk, 0, sizeof(fortuna_start_cache.junk)); 156 fortuna_start_cache.length = 0U; 157 randomdev_hash_init(&fortuna_start_cache.hash); 158 159 /* Set up a lock for the reseed process */ 160#ifdef _KERNEL 161 mtx_init(&random_reseed_mtx, "reseed mutex", NULL, MTX_DEF); 162#else /* !_KERNEL */ 163 mtx_init(&random_reseed_mtx, mtx_plain); 164#endif /* _KERNEL */ 165 166#ifdef _KERNEL 167 /* Fortuna parameters. Do not adjust these unless you have 168 * have a very good clue about what they do! 169 */ 170 random_fortuna_o = SYSCTL_ADD_NODE(&random_clist, 171 SYSCTL_STATIC_CHILDREN(_kern_random), 172 OID_AUTO, "fortuna", CTLFLAG_RW, 0, 173 "Fortuna Parameters"); 174 175 SYSCTL_ADD_PROC(&random_clist, 176 SYSCTL_CHILDREN(random_fortuna_o), OID_AUTO, 177 "minpoolsize", CTLTYPE_UINT|CTLFLAG_RW, 178 &fortuna_state.minpoolsize, DEFPOOLSIZE, 179 random_check_uint_minpoolsize, "IU", 180 "Minimum pool size necessary to cause a reseed automatically"); 181 182 fortuna_state.lasttime = 0U; 183#endif 184 185 fortuna_state.minpoolsize = DEFPOOLSIZE; 186 187 /* F&S - InitializePRNG() */ 188 189 /* F&S - P_i = \epsilon */ 190 for (i = 0; i < NPOOLS; i++) { 191 randomdev_hash_init(&fortuna_state.pool[i].hash); 192 fortuna_state.pool[i].length = 0U; 193 } 194 195 /* F&S - ReseedCNT = 0 */ 196 fortuna_state.reseedcount = 0U; 197 198 /* F&S - InitializeGenerator() */ 199 200 /* F&S - C = 0 */ 201 uint128_clear(&fortuna_state.counter.whole); 202 203 /* F&S - K = 0 */ 204 memset(&fortuna_state.key, 0, sizeof(fortuna_state.key)); 205} 206 207void 208random_fortuna_deinit_alg(void) 209{ 210 211 mtx_destroy(&random_reseed_mtx); 212 memset(&fortuna_state, 0, sizeof(fortuna_state)); 213} 214 215/* F&S - AddRandomEvent() */ 216/* Process a single stochastic event off the harvest queue */ 217void 218random_fortuna_process_event(struct harvest_event *event) 219{ 220 u_int pl; 221 222 /* We must be locked for all this as plenty of state gets messed with */ 223 mtx_lock(&random_reseed_mtx); 224 225 /* Accumulate the event into the appropriate pool 226 * where each event carries the destination information 227 */ 228 /* F&S - P_i = P_i|<harvested stuff> */ 229 /* The hash_init and hash_finish are done in random_fortuna_read() below */ 230 pl = event->he_destination % NPOOLS; 231 randomdev_hash_iterate(&fortuna_state.pool[pl].hash, event, sizeof(*event)); 232 /* No point in counting above the outside maximum */ 233 fortuna_state.pool[pl].length += event->he_size; 234 fortuna_state.pool[pl].length = MIN(fortuna_state.pool[pl].length, MAXPOOLSIZE); 235 236 /* Done with state-messing */ 237 mtx_unlock(&random_reseed_mtx); 238} 239 240/* F&S - Reseed() */ 241/* Reseed Mutex is held */ 242static void 243reseed(uint8_t *junk, u_int length) 244{ 245 struct randomdev_hash context; 246 uint8_t hash[KEYSIZE]; 247 248 KASSERT(fortuna_state.minpoolsize > 0, ("random: Fortuna threshold = 0")); 249#ifdef _KERNEL 250 mtx_assert(&random_reseed_mtx, MA_OWNED); 251#endif 252 253 /* FS&K - K = Hd(K|s) where Hd(m) is H(H(0^512|m)) */ 254 randomdev_hash_init(&context); 255 randomdev_hash_iterate(&context, zero_region, 512/8); 256 randomdev_hash_iterate(&context, &fortuna_state.key, sizeof(fortuna_state.key)); 257 randomdev_hash_iterate(&context, junk, length); 258 randomdev_hash_finish(&context, hash); 259 randomdev_hash_init(&context); 260 randomdev_hash_iterate(&context, hash, KEYSIZE); 261 randomdev_hash_finish(&context, hash); 262 randomdev_encrypt_init(&fortuna_state.key, hash); 263 memset(hash, 0, sizeof(hash)); 264 265 /* Unblock the device if it was blocked due to being unseeded */ 266 if (uint128_is_zero(fortuna_state.counter.whole)) 267 random_adaptor_unblock(); 268 /* FS&K - C = C + 1 */ 269 uint128_increment(&fortuna_state.counter.whole); 270} 271 272/* F&S - GenerateBlocks() */ 273/* Reseed Mutex is held, and buf points to a whole number of blocks. */ 274static __inline void 275random_fortuna_genblocks(uint8_t *buf, u_int blockcount) 276{ 277 u_int i; 278 279 for (i = 0u; i < blockcount; i++) { 280 /* F&S - r = r|E(K,C) */ 281 randomdev_encrypt(&fortuna_state.key, fortuna_state.counter.byte, buf, BLOCKSIZE); 282 buf += BLOCKSIZE; 283 284 /* F&S - C = C + 1 */ 285 uint128_increment(&fortuna_state.counter.whole); 286 } 287} 288 289/* F&S - PseudoRandomData() */ 290/* Reseed Mutex is held, and buf points to a whole number of blocks. */ 291static __inline void 292random_fortuna_genrandom(uint8_t *buf, u_int bytecount) 293{ 294 static uint8_t temp[BLOCKSIZE*(KEYSIZE/BLOCKSIZE)]; 295 u_int blockcount; 296 297 /* F&S - assert(n < 2^20) */ 298 KASSERT((bytecount <= (1 << 20)), ("invalid single read request to fortuna of %d bytes", bytecount)); 299 300 /* F&S - r = first-n-bytes(GenerateBlocks(ceil(n/16))) */ 301 blockcount = (bytecount + BLOCKSIZE - 1)/BLOCKSIZE; 302 random_fortuna_genblocks(buf, blockcount); 303 304 /* F&S - K = GenerateBlocks(2) */ 305 random_fortuna_genblocks(temp, KEYSIZE/BLOCKSIZE); 306 randomdev_encrypt_init(&fortuna_state.key, temp); 307 memset(temp, 0, sizeof(temp)); 308} 309 310/* F&S - RandomData() */ 311/* Used to return processed entropy from the PRNG */ 312/* The argument buf points to a whole number of blocks. */ 313void 314random_fortuna_read(uint8_t *buf, u_int bytecount) 315{ 316#ifdef _KERNEL 317 sbintime_t thistime; 318#endif 319 struct randomdev_hash context; 320 uint8_t s[NPOOLS*KEYSIZE], temp[KEYSIZE]; 321 int i; 322 u_int seedlength; 323 324 /* We must be locked for all this as plenty of state gets messed with */ 325 mtx_lock(&random_reseed_mtx); 326 327 /* if buf == NULL and bytecount == 0 then this is the pre-read. */ 328 /* if buf == NULL and bytecount != 0 then this is the post-read; ignore. */ 329 if (buf == NULL) { 330 if (bytecount == 0) { 331 if (fortuna_state.pool[0].length >= fortuna_state.minpoolsize 332#ifdef _KERNEL 333 /* F&S - Use 'getsbinuptime()' to prevent reseed-spamming. */ 334 && ((thistime = getsbinuptime()) - fortuna_state.lasttime > hz/10) 335#endif 336 ) { 337#ifdef _KERNEL 338 fortuna_state.lasttime = thistime; 339#endif 340 341 seedlength = 0U; 342 /* F&S - ReseedCNT = ReseedCNT + 1 */ 343 fortuna_state.reseedcount++; 344 /* s = \epsilon by default */ 345 for (i = 0; i < NPOOLS; i++) { 346 /* F&S - if Divides(ReseedCnt, 2^i) ... */ 347 if ((fortuna_state.reseedcount % (1 << i)) == 0U) { 348 seedlength += KEYSIZE; 349 /* F&S - temp = (P_i) */ 350 randomdev_hash_finish(&fortuna_state.pool[i].hash, temp); 351 /* F&S - P_i = \epsilon */ 352 randomdev_hash_init(&fortuna_state.pool[i].hash); 353 fortuna_state.pool[i].length = 0U; 354 /* F&S - s = s|H(temp) */ 355 randomdev_hash_init(&context); 356 randomdev_hash_iterate(&context, temp, KEYSIZE); 357 randomdev_hash_finish(&context, s + i*KEYSIZE); 358 } 359 else 360 break; 361 } 362#ifdef RANDOM_DEBUG 363 printf("random: active reseed: reseedcount [%d] ", fortuna_state.reseedcount); 364 for (i = 0; i < NPOOLS; i++) 365 printf(" %d", fortuna_state.pool[i].length); 366 printf("\n"); 367#endif 368 /* F&S */ 369 reseed(s, seedlength); 370 371 /* Clean up */ 372 memset(s, 0, seedlength); 373 seedlength = 0U; 374 memset(temp, 0, sizeof(temp)); 375 memset(&context, 0, sizeof(context)); 376 } 377 } 378 } 379 /* if buf != NULL do a regular read. */ 380 else 381 random_fortuna_genrandom(buf, bytecount); 382 383 mtx_unlock(&random_reseed_mtx); 384} 385 386/* Internal function to hand external entropy to the PRNG */ 387void 388random_fortuna_write(uint8_t *buf, u_int count) 389{ 390 uint8_t temp[KEYSIZE]; 391 int i; 392 uintmax_t timestamp; 393 394 timestamp = get_cyclecount(); 395 randomdev_hash_iterate(&fortuna_start_cache.hash, ×tamp, sizeof(timestamp)); 396 randomdev_hash_iterate(&fortuna_start_cache.hash, buf, count); 397 timestamp = get_cyclecount(); 398 randomdev_hash_iterate(&fortuna_start_cache.hash, ×tamp, sizeof(timestamp)); 399 randomdev_hash_finish(&fortuna_start_cache.hash, temp); 400 for (i = 0; i < KEYSIZE; i++) 401 fortuna_start_cache.junk[(fortuna_start_cache.length + i)%PAGE_SIZE] ^= temp[i]; 402 fortuna_start_cache.length += KEYSIZE; 403 404#ifdef RANDOM_DEBUG 405 printf("random: %s - ", __func__); 406 for (i = 0; i < KEYSIZE; i++) 407 printf("%02X", temp[i]); 408 printf("\n"); 409#endif 410 411 memset(temp, 0, KEYSIZE); 412 413 /* We must be locked for all this as plenty of state gets messed with */ 414 mtx_lock(&random_reseed_mtx); 415 416 randomdev_hash_init(&fortuna_start_cache.hash); 417 418 reseed(fortuna_start_cache.junk, MIN(PAGE_SIZE, fortuna_start_cache.length)); 419 memset(fortuna_start_cache.junk, 0, sizeof(fortuna_start_cache.junk)); 420 421 mtx_unlock(&random_reseed_mtx); 422} 423 424void 425random_fortuna_reseed(void) 426{ 427 428 /* CWOT */ 429} 430 431int 432random_fortuna_seeded(void) 433{ 434 435 return (!uint128_is_zero(fortuna_state.counter.whole)); 436} 437 438#endif /* RANDOM_FORTUNA */ 439