Header And Logo

PostgreSQL
| The world's most advanced open source database.

fortuna.c

Go to the documentation of this file.
00001 /*
00002  * fortuna.c
00003  *      Fortuna-like PRNG.
00004  *
00005  * Copyright (c) 2005 Marko Kreen
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions
00010  * are met:
00011  * 1. Redistributions of source code must retain the above copyright
00012  *    notice, this list of conditions and the following disclaimer.
00013  * 2. Redistributions in binary form must reproduce the above copyright
00014  *    notice, this list of conditions and the following disclaimer in the
00015  *    documentation and/or other materials provided with the distribution.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
00018  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
00021  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00022  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00023  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00024  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00025  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00026  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00027  * SUCH DAMAGE.
00028  *
00029  * contrib/pgcrypto/fortuna.c
00030  */
00031 
00032 #include "postgres.h"
00033 
00034 #include <sys/time.h>
00035 #include <time.h>
00036 
00037 #include "rijndael.h"
00038 #include "sha2.h"
00039 #include "fortuna.h"
00040 
00041 
00042 /*
00043  * Why Fortuna-like: There does not seem to be any definitive reference
00044  * on Fortuna in the net.  Instead this implementation is based on
00045  * following references:
00046  *
00047  * http://en.wikipedia.org/wiki/Fortuna_(PRNG)
00048  *   - Wikipedia article
00049  * http://jlcooke.ca/random/
00050  *   - Jean-Luc Cooke Fortuna-based /dev/random driver for Linux.
00051  */
00052 
00053 /*
00054  * There is some confusion about whether and how to carry forward
00055  * the state of the pools.  Seems like original Fortuna does not
00056  * do it, resetting hash after each request.  I guess expecting
00057  * feeding to happen more often that requesting.   This is absolutely
00058  * unsuitable for pgcrypto, as nothing asynchronous happens here.
00059  *
00060  * J.L. Cooke fixed this by feeding previous hash to new re-initialized
00061  * hash context.
00062  *
00063  * Fortuna predecessor Yarrow requires ability to query intermediate
00064  * 'final result' from hash, without affecting it.
00065  *
00066  * This implementation uses the Yarrow method - asking intermediate
00067  * results, but continuing with old state.
00068  */
00069 
00070 
00071 /*
00072  * Algorithm parameters
00073  */
00074 
00075 /*
00076  * How many pools.
00077  *
00078  * Original Fortuna uses 32 pools, that means 32'th pool is
00079  * used not earlier than in 13th year.  This is a waste in
00080  * pgcrypto, as we have very low-frequancy seeding.  Here
00081  * is preferable to have all entropy usable in reasonable time.
00082  *
00083  * With 23 pools, 23th pool is used after 9 days which seems
00084  * more sane.
00085  *
00086  * In our case the minimal cycle time would be bit longer
00087  * than the system-randomness feeding frequency.
00088  */
00089 #define NUM_POOLS       23
00090 
00091 /* in microseconds */
00092 #define RESEED_INTERVAL 100000  /* 0.1 sec */
00093 
00094 /* for one big request, reseed after this many bytes */
00095 #define RESEED_BYTES    (1024*1024)
00096 
00097 /*
00098  * Skip reseed if pool 0 has less than this many
00099  * bytes added since last reseed.
00100  */
00101 #define POOL0_FILL      (256/8)
00102 
00103 /*
00104  * Algorithm constants
00105  */
00106 
00107 /* Both cipher key size and hash result size */
00108 #define BLOCK           32
00109 
00110 /* cipher block size */
00111 #define CIPH_BLOCK      16
00112 
00113 /* for internal wrappers */
00114 #define MD_CTX          SHA256_CTX
00115 #define CIPH_CTX        rijndael_ctx
00116 
00117 struct fortuna_state
00118 {
00119     uint8       counter[CIPH_BLOCK];
00120     uint8       result[CIPH_BLOCK];
00121     uint8       key[BLOCK];
00122     MD_CTX      pool[NUM_POOLS];
00123     CIPH_CTX    ciph;
00124     unsigned    reseed_count;
00125     struct timeval last_reseed_time;
00126     unsigned    pool0_bytes;
00127     unsigned    rnd_pos;
00128     int         tricks_done;
00129 };
00130 typedef struct fortuna_state FState;
00131 
00132 
00133 /*
00134  * Use our own wrappers here.
00135  * - Need to get intermediate result from digest, without affecting it.
00136  * - Need re-set key on a cipher context.
00137  * - Algorithms are guaranteed to exist.
00138  * - No memory allocations.
00139  */
00140 
00141 static void
00142 ciph_init(CIPH_CTX * ctx, const uint8 *key, int klen)
00143 {
00144     rijndael_set_key(ctx, (const uint32 *) key, klen, 1);
00145 }
00146 
00147 static void
00148 ciph_encrypt(CIPH_CTX * ctx, const uint8 *in, uint8 *out)
00149 {
00150     rijndael_encrypt(ctx, (const uint32 *) in, (uint32 *) out);
00151 }
00152 
00153 static void
00154 md_init(MD_CTX * ctx)
00155 {
00156     SHA256_Init(ctx);
00157 }
00158 
00159 static void
00160 md_update(MD_CTX * ctx, const uint8 *data, int len)
00161 {
00162     SHA256_Update(ctx, data, len);
00163 }
00164 
00165 static void
00166 md_result(MD_CTX * ctx, uint8 *dst)
00167 {
00168     SHA256_CTX  tmp;
00169 
00170     memcpy(&tmp, ctx, sizeof(*ctx));
00171     SHA256_Final(dst, &tmp);
00172     memset(&tmp, 0, sizeof(tmp));
00173 }
00174 
00175 /*
00176  * initialize state
00177  */
00178 static void
00179 init_state(FState *st)
00180 {
00181     int         i;
00182 
00183     memset(st, 0, sizeof(*st));
00184     for (i = 0; i < NUM_POOLS; i++)
00185         md_init(&st->pool[i]);
00186 }
00187 
00188 /*
00189  * Endianess does not matter.
00190  * It just needs to change without repeating.
00191  */
00192 static void
00193 inc_counter(FState *st)
00194 {
00195     uint32     *val = (uint32 *) st->counter;
00196 
00197     if (++val[0])
00198         return;
00199     if (++val[1])
00200         return;
00201     if (++val[2])
00202         return;
00203     ++val[3];
00204 }
00205 
00206 /*
00207  * This is called 'cipher in counter mode'.
00208  */
00209 static void
00210 encrypt_counter(FState *st, uint8 *dst)
00211 {
00212     ciph_encrypt(&st->ciph, st->counter, dst);
00213     inc_counter(st);
00214 }
00215 
00216 
00217 /*
00218  * The time between reseed must be at least RESEED_INTERVAL
00219  * microseconds.
00220  */
00221 static int
00222 enough_time_passed(FState *st)
00223 {
00224     int         ok;
00225     struct timeval tv;
00226     struct timeval *last = &st->last_reseed_time;
00227 
00228     gettimeofday(&tv, NULL);
00229 
00230     /* check how much time has passed */
00231     ok = 0;
00232     if (tv.tv_sec > last->tv_sec + 1)
00233         ok = 1;
00234     else if (tv.tv_sec == last->tv_sec + 1)
00235     {
00236         if (1000000 + tv.tv_usec - last->tv_usec >= RESEED_INTERVAL)
00237             ok = 1;
00238     }
00239     else if (tv.tv_usec - last->tv_usec >= RESEED_INTERVAL)
00240         ok = 1;
00241 
00242     /* reseed will happen, update last_reseed_time */
00243     if (ok)
00244         memcpy(last, &tv, sizeof(tv));
00245 
00246     memset(&tv, 0, sizeof(tv));
00247 
00248     return ok;
00249 }
00250 
00251 /*
00252  * generate new key from all the pools
00253  */
00254 static void
00255 reseed(FState *st)
00256 {
00257     unsigned    k;
00258     unsigned    n;
00259     MD_CTX      key_md;
00260     uint8       buf[BLOCK];
00261 
00262     /* set pool as empty */
00263     st->pool0_bytes = 0;
00264 
00265     /*
00266      * Both #0 and #1 reseed would use only pool 0. Just skip #0 then.
00267      */
00268     n = ++st->reseed_count;
00269 
00270     /*
00271      * The goal: use k-th pool only 1/(2^k) of the time.
00272      */
00273     md_init(&key_md);
00274     for (k = 0; k < NUM_POOLS; k++)
00275     {
00276         md_result(&st->pool[k], buf);
00277         md_update(&key_md, buf, BLOCK);
00278 
00279         if (n & 1 || !n)
00280             break;
00281         n >>= 1;
00282     }
00283 
00284     /* add old key into mix too */
00285     md_update(&key_md, st->key, BLOCK);
00286 
00287     /* now we have new key */
00288     md_result(&key_md, st->key);
00289 
00290     /* use new key */
00291     ciph_init(&st->ciph, st->key, BLOCK);
00292 
00293     memset(&key_md, 0, sizeof(key_md));
00294     memset(buf, 0, BLOCK);
00295 }
00296 
00297 /*
00298  * Pick a random pool.  This uses key bytes as random source.
00299  */
00300 static unsigned
00301 get_rand_pool(FState *st)
00302 {
00303     unsigned    rnd;
00304 
00305     /*
00306      * This slightly prefers lower pools - thats OK.
00307      */
00308     rnd = st->key[st->rnd_pos] % NUM_POOLS;
00309 
00310     st->rnd_pos++;
00311     if (st->rnd_pos >= BLOCK)
00312         st->rnd_pos = 0;
00313 
00314     return rnd;
00315 }
00316 
00317 /*
00318  * update pools
00319  */
00320 static void
00321 add_entropy(FState *st, const uint8 *data, unsigned len)
00322 {
00323     unsigned    pos;
00324     uint8       hash[BLOCK];
00325     MD_CTX      md;
00326 
00327     /* hash given data */
00328     md_init(&md);
00329     md_update(&md, data, len);
00330     md_result(&md, hash);
00331 
00332     /*
00333      * Make sure the pool 0 is initialized, then update randomly.
00334      */
00335     if (st->reseed_count == 0)
00336         pos = 0;
00337     else
00338         pos = get_rand_pool(st);
00339     md_update(&st->pool[pos], hash, BLOCK);
00340 
00341     if (pos == 0)
00342         st->pool0_bytes += len;
00343 
00344     memset(hash, 0, BLOCK);
00345     memset(&md, 0, sizeof(md));
00346 }
00347 
00348 /*
00349  * Just take 2 next blocks as new key
00350  */
00351 static void
00352 rekey(FState *st)
00353 {
00354     encrypt_counter(st, st->key);
00355     encrypt_counter(st, st->key + CIPH_BLOCK);
00356     ciph_init(&st->ciph, st->key, BLOCK);
00357 }
00358 
00359 /*
00360  * Hide public constants. (counter, pools > 0)
00361  *
00362  * This can also be viewed as spreading the startup
00363  * entropy over all of the components.
00364  */
00365 static void
00366 startup_tricks(FState *st)
00367 {
00368     int         i;
00369     uint8       buf[BLOCK];
00370 
00371     /* Use next block as counter. */
00372     encrypt_counter(st, st->counter);
00373 
00374     /* Now shuffle pools, excluding #0 */
00375     for (i = 1; i < NUM_POOLS; i++)
00376     {
00377         encrypt_counter(st, buf);
00378         encrypt_counter(st, buf + CIPH_BLOCK);
00379         md_update(&st->pool[i], buf, BLOCK);
00380     }
00381     memset(buf, 0, BLOCK);
00382 
00383     /* Hide the key. */
00384     rekey(st);
00385 
00386     /* This can be done only once. */
00387     st->tricks_done = 1;
00388 }
00389 
00390 static void
00391 extract_data(FState *st, unsigned count, uint8 *dst)
00392 {
00393     unsigned    n;
00394     unsigned    block_nr = 0;
00395 
00396     /* Should we reseed? */
00397     if (st->pool0_bytes >= POOL0_FILL || st->reseed_count == 0)
00398         if (enough_time_passed(st))
00399             reseed(st);
00400 
00401     /* Do some randomization on first call */
00402     if (!st->tricks_done)
00403         startup_tricks(st);
00404 
00405     while (count > 0)
00406     {
00407         /* produce bytes */
00408         encrypt_counter(st, st->result);
00409 
00410         /* copy result */
00411         if (count > CIPH_BLOCK)
00412             n = CIPH_BLOCK;
00413         else
00414             n = count;
00415         memcpy(dst, st->result, n);
00416         dst += n;
00417         count -= n;
00418 
00419         /* must not give out too many bytes with one key */
00420         block_nr++;
00421         if (block_nr > (RESEED_BYTES / CIPH_BLOCK))
00422         {
00423             rekey(st);
00424             block_nr = 0;
00425         }
00426     }
00427     /* Set new key for next request. */
00428     rekey(st);
00429 }
00430 
00431 /*
00432  * public interface
00433  */
00434 
00435 static FState main_state;
00436 static int  init_done = 0;
00437 
00438 void
00439 fortuna_add_entropy(const uint8 *data, unsigned len)
00440 {
00441     if (!init_done)
00442     {
00443         init_state(&main_state);
00444         init_done = 1;
00445     }
00446     if (!data || !len)
00447         return;
00448     add_entropy(&main_state, data, len);
00449 }
00450 
00451 void
00452 fortuna_get_bytes(unsigned len, uint8 *dst)
00453 {
00454     if (!init_done)
00455     {
00456         init_state(&main_state);
00457         init_done = 1;
00458     }
00459     if (!dst || !len)
00460         return;
00461     extract_data(&main_state, len, dst);
00462 }