root/source4/heimdal/lib/hcrypto/rand-fortuna.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ciph_init
  2. ciph_encrypt
  3. md_init
  4. md_update
  5. md_result
  6. init_state
  7. inc_counter
  8. encrypt_counter
  9. enough_time_passed
  10. reseed
  11. get_rand_pool
  12. add_entropy
  13. rekey
  14. startup_tricks
  15. extract_data
  16. fortuna_reseed
  17. fortuna_init
  18. fortuna_seed
  19. fortuna_bytes
  20. fortuna_cleanup
  21. fortuna_add
  22. fortuna_pseudorand
  23. fortuna_status
  24. RAND_fortuna_method

   1 /*
   2  * fortuna.c
   3  *              Fortuna-like PRNG.
   4  *
   5  * Copyright (c) 2005 Marko Kreen
   6  * All rights reserved.
   7  *
   8  * Redistribution and use in source and binary forms, with or without
   9  * modification, are permitted provided that the following conditions
  10  * are met:
  11  * 1. Redistributions of source code must retain the above copyright
  12  *        notice, this list of conditions and the following disclaimer.
  13  * 2. Redistributions in binary form must reproduce the above copyright
  14  *        notice, this list of conditions and the following disclaimer in the
  15  *        documentation and/or other materials provided with the distribution.
  16  *
  17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20  * ARE DISCLAIMED.      IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27  * SUCH DAMAGE.
  28  *
  29  * $PostgreSQL: pgsql/contrib/pgcrypto/fortuna.c,v 1.8 2006/10/04 00:29:46 momjian Exp $
  30  */
  31 
  32 #ifdef HAVE_CONFIG_H
  33 #include <config.h>
  34 #endif
  35 
  36 RCSID("$Id$");
  37 
  38 #include <stdio.h>
  39 #include <stdlib.h>
  40 #include <rand.h>
  41 
  42 #include <roken.h>
  43 
  44 #include "randi.h"
  45 #include "aes.h"
  46 #include "sha.h"
  47 
  48 /*
  49  * Why Fortuna-like: There does not seem to be any definitive reference
  50  * on Fortuna in the net.  Instead this implementation is based on
  51  * following references:
  52  *
  53  * http://en.wikipedia.org/wiki/Fortuna_(PRNG)
  54  *       - Wikipedia article
  55  * http://jlcooke.ca/random/
  56  *       - Jean-Luc Cooke Fortuna-based /dev/random driver for Linux.
  57  */
  58 
  59 /*
  60  * There is some confusion about whether and how to carry forward
  61  * the state of the pools.      Seems like original Fortuna does not
  62  * do it, resetting hash after each request.  I guess expecting
  63  * feeding to happen more often that requesting.   This is absolutely
  64  * unsuitable for pgcrypto, as nothing asynchronous happens here.
  65  *
  66  * J.L. Cooke fixed this by feeding previous hash to new re-initialized
  67  * hash context.
  68  *
  69  * Fortuna predecessor Yarrow requires ability to query intermediate
  70  * 'final result' from hash, without affecting it.
  71  *
  72  * This implementation uses the Yarrow method - asking intermediate
  73  * results, but continuing with old state.
  74  */
  75 
  76 
  77 /*
  78  * Algorithm parameters
  79  */
  80 
  81 #define NUM_POOLS               32
  82 
  83 /* in microseconds */
  84 #define RESEED_INTERVAL 100000  /* 0.1 sec */
  85 
  86 /* for one big request, reseed after this many bytes */
  87 #define RESEED_BYTES    (1024*1024)
  88 
  89 /*
  90  * Skip reseed if pool 0 has less than this many
  91  * bytes added since last reseed.
  92  */
  93 #define POOL0_FILL              (256/8)
  94 
  95 /*
  96  * Algorithm constants
  97  */
  98 
  99 /* Both cipher key size and hash result size */
 100 #define BLOCK                   32
 101 
 102 /* cipher block size */
 103 #define CIPH_BLOCK              16
 104 
 105 /* for internal wrappers */
 106 #define MD_CTX                  SHA256_CTX
 107 #define CIPH_CTX                AES_KEY
 108 
 109 struct fortuna_state
 110 {
 111     unsigned char       counter[CIPH_BLOCK];
 112     unsigned char       result[CIPH_BLOCK];
 113     unsigned char       key[BLOCK];
 114     MD_CTX              pool[NUM_POOLS];
 115     CIPH_CTX            ciph;
 116     unsigned            reseed_count;
 117     struct timeval      last_reseed_time;
 118     unsigned            pool0_bytes;
 119     unsigned            rnd_pos;
 120     int                 tricks_done;
 121     pid_t               pid;
 122 };
 123 typedef struct fortuna_state FState;
 124 
 125 
 126 /*
 127  * Use our own wrappers here.
 128  * - Need to get intermediate result from digest, without affecting it.
 129  * - Need re-set key on a cipher context.
 130  * - Algorithms are guaranteed to exist.
 131  * - No memory allocations.
 132  */
 133 
 134 static void
 135 ciph_init(CIPH_CTX * ctx, const unsigned char *key, int klen)
     /* [<][>][^][v][top][bottom][index][help] */
 136 {
 137     AES_set_encrypt_key(key, klen * 8, ctx);
 138 }
 139 
 140 static void
 141 ciph_encrypt(CIPH_CTX * ctx, const unsigned char *in, unsigned char *out)
     /* [<][>][^][v][top][bottom][index][help] */
 142 {
 143     AES_encrypt(in, out, ctx);
 144 }
 145 
 146 static void
 147 md_init(MD_CTX * ctx)
     /* [<][>][^][v][top][bottom][index][help] */
 148 {
 149     SHA256_Init(ctx);
 150 }
 151 
 152 static void
 153 md_update(MD_CTX * ctx, const unsigned char *data, int len)
     /* [<][>][^][v][top][bottom][index][help] */
 154 {
 155     SHA256_Update(ctx, data, len);
 156 }
 157 
 158 static void
 159 md_result(MD_CTX * ctx, unsigned char *dst)
     /* [<][>][^][v][top][bottom][index][help] */
 160 {
 161     SHA256_CTX  tmp;
 162 
 163     memcpy(&tmp, ctx, sizeof(*ctx));
 164     SHA256_Final(dst, &tmp);
 165     memset(&tmp, 0, sizeof(tmp));
 166 }
 167 
 168 /*
 169  * initialize state
 170  */
 171 static void
 172 init_state(FState * st)
     /* [<][>][^][v][top][bottom][index][help] */
 173 {
 174     int                 i;
 175 
 176     memset(st, 0, sizeof(*st));
 177     for (i = 0; i < NUM_POOLS; i++)
 178         md_init(&st->pool[i]);
 179     st->pid = getpid();
 180 }
 181 
 182 /*
 183  * Endianess does not matter.
 184  * It just needs to change without repeating.
 185  */
 186 static void
 187 inc_counter(FState * st)
     /* [<][>][^][v][top][bottom][index][help] */
 188 {
 189     uint32_t   *val = (uint32_t *) st->counter;
 190 
 191     if (++val[0])
 192         return;
 193     if (++val[1])
 194         return;
 195     if (++val[2])
 196         return;
 197     ++val[3];
 198 }
 199 
 200 /*
 201  * This is called 'cipher in counter mode'.
 202  */
 203 static void
 204 encrypt_counter(FState * st, unsigned char *dst)
     /* [<][>][^][v][top][bottom][index][help] */
 205 {
 206     ciph_encrypt(&st->ciph, st->counter, dst);
 207     inc_counter(st);
 208 }
 209 
 210 
 211 /*
 212  * The time between reseed must be at least RESEED_INTERVAL
 213  * microseconds.
 214  */
 215 static int
 216 enough_time_passed(FState * st)
     /* [<][>][^][v][top][bottom][index][help] */
 217 {
 218     int                 ok;
 219     struct timeval tv;
 220     struct timeval *last = &st->last_reseed_time;
 221 
 222     gettimeofday(&tv, NULL);
 223 
 224     /* check how much time has passed */
 225     ok = 0;
 226     if (tv.tv_sec > last->tv_sec + 1)
 227         ok = 1;
 228     else if (tv.tv_sec == last->tv_sec + 1)
 229     {
 230         if (1000000 + tv.tv_usec - last->tv_usec >= RESEED_INTERVAL)
 231             ok = 1;
 232     }
 233     else if (tv.tv_usec - last->tv_usec >= RESEED_INTERVAL)
 234         ok = 1;
 235 
 236     /* reseed will happen, update last_reseed_time */
 237     if (ok)
 238         memcpy(last, &tv, sizeof(tv));
 239 
 240     memset(&tv, 0, sizeof(tv));
 241 
 242     return ok;
 243 }
 244 
 245 /*
 246  * generate new key from all the pools
 247  */
 248 static void
 249 reseed(FState * st)
     /* [<][>][^][v][top][bottom][index][help] */
 250 {
 251     unsigned    k;
 252     unsigned    n;
 253     MD_CTX              key_md;
 254     unsigned char       buf[BLOCK];
 255 
 256     /* set pool as empty */
 257     st->pool0_bytes = 0;
 258 
 259     /*
 260      * Both #0 and #1 reseed would use only pool 0. Just skip #0 then.
 261      */
 262     n = ++st->reseed_count;
 263 
 264     /*
 265      * The goal: use k-th pool only 1/(2^k) of the time.
 266      */
 267     md_init(&key_md);
 268     for (k = 0; k < NUM_POOLS; k++)
 269     {
 270         md_result(&st->pool[k], buf);
 271         md_update(&key_md, buf, BLOCK);
 272 
 273         if (n & 1 || !n)
 274             break;
 275         n >>= 1;
 276     }
 277 
 278     /* add old key into mix too */
 279     md_update(&key_md, st->key, BLOCK);
 280 
 281     /* add pid to make output diverse after fork() */
 282     md_update(&key_md, (const unsigned char *)&st->pid, sizeof(st->pid));
 283 
 284     /* now we have new key */
 285     md_result(&key_md, st->key);
 286 
 287     /* use new key */
 288     ciph_init(&st->ciph, st->key, BLOCK);
 289 
 290     memset(&key_md, 0, sizeof(key_md));
 291     memset(buf, 0, BLOCK);
 292 }
 293 
 294 /*
 295  * Pick a random pool.  This uses key bytes as random source.
 296  */
 297 static unsigned
 298 get_rand_pool(FState * st)
     /* [<][>][^][v][top][bottom][index][help] */
 299 {
 300     unsigned    rnd;
 301 
 302     /*
 303      * This slightly prefers lower pools - thats OK.
 304      */
 305     rnd = st->key[st->rnd_pos] % NUM_POOLS;
 306 
 307     st->rnd_pos++;
 308     if (st->rnd_pos >= BLOCK)
 309         st->rnd_pos = 0;
 310 
 311     return rnd;
 312 }
 313 
 314 /*
 315  * update pools
 316  */
 317 static void
 318 add_entropy(FState * st, const unsigned char *data, unsigned len)
     /* [<][>][^][v][top][bottom][index][help] */
 319 {
 320     unsigned            pos;
 321     unsigned char       hash[BLOCK];
 322     MD_CTX              md;
 323 
 324     /* hash given data */
 325     md_init(&md);
 326     md_update(&md, data, len);
 327     md_result(&md, hash);
 328 
 329     /*
 330      * Make sure the pool 0 is initialized, then update randomly.
 331      */
 332     if (st->reseed_count == 0)
 333         pos = 0;
 334     else
 335         pos = get_rand_pool(st);
 336     md_update(&st->pool[pos], hash, BLOCK);
 337 
 338     if (pos == 0)
 339         st->pool0_bytes += len;
 340 
 341     memset(hash, 0, BLOCK);
 342     memset(&md, 0, sizeof(md));
 343 }
 344 
 345 /*
 346  * Just take 2 next blocks as new key
 347  */
 348 static void
 349 rekey(FState * st)
     /* [<][>][^][v][top][bottom][index][help] */
 350 {
 351     encrypt_counter(st, st->key);
 352     encrypt_counter(st, st->key + CIPH_BLOCK);
 353     ciph_init(&st->ciph, st->key, BLOCK);
 354 }
 355 
 356 /*
 357  * Hide public constants. (counter, pools > 0)
 358  *
 359  * This can also be viewed as spreading the startup
 360  * entropy over all of the components.
 361  */
 362 static void
 363 startup_tricks(FState * st)
     /* [<][>][^][v][top][bottom][index][help] */
 364 {
 365     int                 i;
 366     unsigned char       buf[BLOCK];
 367 
 368     /* Use next block as counter. */
 369     encrypt_counter(st, st->counter);
 370 
 371     /* Now shuffle pools, excluding #0 */
 372     for (i = 1; i < NUM_POOLS; i++)
 373     {
 374         encrypt_counter(st, buf);
 375         encrypt_counter(st, buf + CIPH_BLOCK);
 376         md_update(&st->pool[i], buf, BLOCK);
 377     }
 378     memset(buf, 0, BLOCK);
 379 
 380     /* Hide the key. */
 381     rekey(st);
 382 
 383     /* This can be done only once. */
 384     st->tricks_done = 1;
 385 }
 386 
 387 static void
 388 extract_data(FState * st, unsigned count, unsigned char *dst)
     /* [<][>][^][v][top][bottom][index][help] */
 389 {
 390     unsigned    n;
 391     unsigned    block_nr = 0;
 392     pid_t       pid = getpid();
 393 
 394     /* Should we reseed? */
 395     if (st->pool0_bytes >= POOL0_FILL || st->reseed_count == 0)
 396         if (enough_time_passed(st))
 397             reseed(st);
 398 
 399     /* Do some randomization on first call */
 400     if (!st->tricks_done)
 401         startup_tricks(st);
 402 
 403     /* If we forked, force a reseed again */
 404     if (pid != st->pid) {
 405         st->pid = pid;
 406         reseed(st);
 407     }
 408 
 409     while (count > 0)
 410     {
 411         /* produce bytes */
 412         encrypt_counter(st, st->result);
 413 
 414         /* copy result */
 415         if (count > CIPH_BLOCK)
 416             n = CIPH_BLOCK;
 417         else
 418             n = count;
 419         memcpy(dst, st->result, n);
 420         dst += n;
 421         count -= n;
 422 
 423         /* must not give out too many bytes with one key */
 424         block_nr++;
 425         if (block_nr > (RESEED_BYTES / CIPH_BLOCK))
 426         {
 427             rekey(st);
 428             block_nr = 0;
 429         }
 430     }
 431     /* Set new key for next request. */
 432     rekey(st);
 433 }
 434 
 435 /*
 436  * public interface
 437  */
 438 
 439 static FState   main_state;
 440 static int      init_done;
 441 static int      have_entropy;
 442 #define FORTUNA_RESEED_BYTE     10000
 443 static unsigned resend_bytes;
 444 
 445 /*
 446  * Try our best to do an inital seed
 447  */
 448 #define INIT_BYTES      128
 449 
 450 static int
 451 fortuna_reseed(void)
     /* [<][>][^][v][top][bottom][index][help] */
 452 {
 453     int entropy_p = 0;
 454 
 455     if (!init_done)
 456         abort();
 457 
 458     {
 459         unsigned char buf[INIT_BYTES];
 460         if ((*hc_rand_unix_method.bytes)(buf, sizeof(buf)) == 1) {
 461             add_entropy(&main_state, buf, sizeof(buf));
 462             entropy_p = 1;
 463             memset(buf, 0, sizeof(buf));
 464         }
 465     }
 466 #ifdef HAVE_ARC4RANDOM
 467     {
 468         uint32_t buf[INIT_BYTES / sizeof(uint32_t)];
 469         int i;
 470 
 471         for (i = 0; i < sizeof(buf)/sizeof(buf[0]); i++)
 472             buf[i] = arc4random();
 473         add_entropy(&main_state, (void *)buf, sizeof(buf));
 474         entropy_p = 1;
 475     }
 476 #endif
 477     /*
 478      * Only to get egd entropy if /dev/random or arc4rand failed since
 479      * it can be horribly slow to generate new bits.
 480      */
 481     if (!entropy_p) {
 482         unsigned char buf[INIT_BYTES];
 483         if ((*hc_rand_egd_method.bytes)(buf, sizeof(buf)) == 1) {
 484             add_entropy(&main_state, buf, sizeof(buf));
 485             entropy_p = 1;
 486             memset(buf, 0, sizeof(buf));
 487         }
 488     }
 489     /*
 490      * Fall back to gattering data from timer and secret files, this
 491      * is really the last resort.
 492      */
 493     if (!entropy_p) {
 494         /* to save stackspace */
 495         union {
 496             unsigned char buf[INIT_BYTES];
 497             unsigned char shad[1001];
 498         } u;
 499         int fd;
 500 
 501         /* add timer info */
 502         if ((*hc_rand_timer_method.bytes)(u.buf, sizeof(u.buf)) == 1)
 503             add_entropy(&main_state, u.buf, sizeof(u.buf));
 504         /* add /etc/shadow */
 505         fd = open("/etc/shadow", O_RDONLY, 0);
 506         if (fd >= 0) {
 507             ssize_t n;
 508             rk_cloexec(fd);
 509             /* add_entropy will hash the buf */
 510             while ((n = read(fd, (char *)u.shad, sizeof(u.shad))) > 0)
 511                 add_entropy(&main_state, u.shad, sizeof(u.shad));
 512             close(fd);
 513         }
 514 
 515         memset(&u, 0, sizeof(u));
 516 
 517         entropy_p = 1; /* sure about this ? */
 518     }
 519     {
 520         pid_t pid = getpid();
 521         add_entropy(&main_state, (void *)&pid, sizeof(pid));
 522     }
 523     {
 524         struct timeval tv;
 525         gettimeofday(&tv, NULL);
 526         add_entropy(&main_state, (void *)&tv, sizeof(tv));
 527     }
 528     {
 529         uid_t u = getuid();
 530         add_entropy(&main_state, (void *)&u, sizeof(u));
 531     }
 532     return entropy_p;
 533 }
 534 
 535 static int
 536 fortuna_init(void)
     /* [<][>][^][v][top][bottom][index][help] */
 537 {
 538     if (!init_done)
 539     {
 540         init_state(&main_state);
 541         init_done = 1;
 542     }
 543     if (!have_entropy)
 544         have_entropy = fortuna_reseed();
 545     return (init_done && have_entropy);
 546 }
 547 
 548 
 549 
 550 static void
 551 fortuna_seed(const void *indata, int size)
     /* [<][>][^][v][top][bottom][index][help] */
 552 {
 553     fortuna_init();
 554     add_entropy(&main_state, indata, size);
 555     if (size >= INIT_BYTES)
 556         have_entropy = 1;
 557 }
 558 
 559 static int
 560 fortuna_bytes(unsigned char *outdata, int size)
     /* [<][>][^][v][top][bottom][index][help] */
 561 {
 562     if (!fortuna_init())
 563         return 0;
 564     resend_bytes += size;
 565     if (resend_bytes > FORTUNA_RESEED_BYTE || resend_bytes < size) {
 566         resend_bytes = 0;
 567         fortuna_reseed();
 568     }
 569     extract_data(&main_state, size, outdata);
 570     return 1;
 571 }
 572 
 573 static void
 574 fortuna_cleanup(void)
     /* [<][>][^][v][top][bottom][index][help] */
 575 {
 576     init_done = 0;
 577     have_entropy = 0;
 578     memset(&main_state, 0, sizeof(main_state));
 579 }
 580 
 581 static void
 582 fortuna_add(const void *indata, int size, double entropi)
     /* [<][>][^][v][top][bottom][index][help] */
 583 {
 584     fortuna_seed(indata, size);
 585 }
 586 
 587 static int
 588 fortuna_pseudorand(unsigned char *outdata, int size)
     /* [<][>][^][v][top][bottom][index][help] */
 589 {
 590     return fortuna_bytes(outdata, size);
 591 }
 592 
 593 static int
 594 fortuna_status(void)
     /* [<][>][^][v][top][bottom][index][help] */
 595 {
 596     return fortuna_init() ? 1 : 0;
 597 }
 598 
 599 const RAND_METHOD hc_rand_fortuna_method = {
 600     fortuna_seed,
 601     fortuna_bytes,
 602     fortuna_cleanup,
 603     fortuna_add,
 604     fortuna_pseudorand,
 605     fortuna_status
 606 };
 607 
 608 const RAND_METHOD *
 609 RAND_fortuna_method(void)
     /* [<][>][^][v][top][bottom][index][help] */
 610 {
 611     return &hc_rand_fortuna_method;
 612 }

/* [<][>][^][v][top][bottom][index][help] */