random.c

Go to the documentation of this file.
00001 /*
00002 random.c
00003 
00004 Random number generator.
00005 
00006 The random number generator collects data from the kernel and compressed
00007 that data into a seed for a psuedo random number generator.
00008 */
00009 
00010 #include "../drivers.h"
00011 #include "../../kernel/const.h"
00012 #include "assert.h"
00013 
00014 #include "random.h"
00015 #include "sha2.h"
00016 #include "aes/rijndael.h"
00017 
00018 #define N_DERIV 16
00019 #define NR_POOLS 32
00020 #define MIN_SAMPLES     256     /* Number of samples needed in pool 0 for a
00021                                  * re-seed.
00022                                  */
00023 
00024 PRIVATE unsigned long deriv[RANDOM_SOURCES][N_DERIV];
00025 PRIVATE int pool_ind[RANDOM_SOURCES];
00026 PRIVATE SHA256_CTX pool_ctx[NR_POOLS];
00027 PRIVATE unsigned samples= 0;
00028 PRIVATE int got_seeded= 0;
00029 PRIVATE u8_t random_key[2*AES_BLOCKSIZE];
00030 PRIVATE u32_t count_lo, count_hi;
00031 PRIVATE u32_t reseed_count;
00032 
00033 FORWARD _PROTOTYPE( void add_sample, (int source, unsigned long sample) );
00034 FORWARD _PROTOTYPE( void data_block, (rd_keyinstance *keyp,
00035                                                         void *data)     );
00036 FORWARD _PROTOTYPE( void reseed, (void)                                 );
00037 
00038 PUBLIC void random_init()
00039 {
00040         int i, j;
00041 
00042         assert(&deriv[RANDOM_SOURCES-1][N_DERIV-1] ==
00043                 &deriv[0][0] + RANDOM_SOURCES*N_DERIV -1);
00044 
00045         for (i= 0; i<RANDOM_SOURCES; i++)
00046         {
00047                 for (j= 0; j<N_DERIV; j++)
00048                         deriv[i][j]= 0;
00049                 pool_ind[i]= 0;
00050         }
00051         for (i= 0; i<NR_POOLS; i++)
00052                 SHA256_Init(&pool_ctx[i]);
00053         count_lo= 0;
00054         count_hi= 0;
00055         reseed_count= 0;
00056 }
00057 
00058 PUBLIC int random_isseeded()
00059 {
00060         if (got_seeded)
00061                 return 1;
00062         return 0;
00063 }
00064 
00065 PUBLIC void random_update(source, buf, count)
00066 int source;
00067 unsigned short *buf;
00068 int count;
00069 {
00070         int i;
00071 
00072 #if 0
00073         printf("random_update: got %d samples for source %d\n", count, source);
00074 #endif
00075         if (source < 0 || source >= RANDOM_SOURCES)
00076                 panic("memory", "random_update: bad source", source);
00077         for (i= 0; i<count; i++)
00078                 add_sample(source, buf[i]);
00079         reseed();
00080 }
00081 
00082 PUBLIC void random_getbytes(buf, size)
00083 void *buf;
00084 size_t size;
00085 {
00086         int n, r;
00087         u8_t *cp;
00088         rd_keyinstance key;
00089         u8_t output[AES_BLOCKSIZE];
00090 
00091         r= rijndael_makekey(&key, sizeof(random_key), random_key);
00092         assert(r == 0);
00093 
00094         cp= buf;
00095         while (size > 0)
00096         {
00097                 n= AES_BLOCKSIZE;
00098                 if (n > size)
00099                 {
00100                         n= size;
00101                         data_block(&key, output);
00102                         memcpy(cp, output, n);
00103                 }
00104                 else
00105                         data_block(&key, cp);
00106                 cp += n;
00107                 size -= n;
00108         }
00109 
00110         /* Generate new key */
00111         assert(sizeof(random_key) == 2*AES_BLOCKSIZE);
00112         data_block(&key, random_key);
00113         data_block(&key, random_key+AES_BLOCKSIZE);
00114 }
00115 
00116 PUBLIC void random_putbytes(buf, size)
00117 void *buf;
00118 size_t size;
00119 {
00120         /* Add bits to pool zero */
00121         SHA256_Update(&pool_ctx[0], buf, size);
00122 
00123         /* Assume that these bits are truely random. Increment samples
00124          * with the number of bits.
00125          */
00126         samples += size*8;
00127 
00128         reseed();
00129 }
00130 
00131 PRIVATE void add_sample(source, sample)
00132 int source;
00133 unsigned long sample;
00134 {
00135         int i, pool_nr;
00136         unsigned long d, v, di, min;
00137 
00138         /* Delete bad sample. Compute the Nth derivative. Delete the sample
00139          * if any derivative is too small.
00140          */
00141         min= (unsigned long)-1;
00142         v= sample;
00143         for (i= 0; i<N_DERIV; i++)
00144         {
00145                 di= deriv[source][i];
00146 
00147                 /* Compute the difference */
00148                 if (v >= di)
00149                         d= v-di;
00150                 else
00151                         d= di-v;
00152                 deriv[source][i]= v;
00153                 v= d;
00154                 if (v <min)
00155                         min= v;
00156         }
00157         if (min < 2)
00158         {
00159 #if 0
00160                 printf("ignoring sample '%u' from source %d\n",
00161                         sample, source);
00162 #endif
00163                 return;
00164         }
00165 #if 0
00166         printf("accepting sample '%u' from source %d\n", sample, source);
00167 #endif
00168 
00169         pool_nr= pool_ind[source];
00170         assert(pool_nr >= 0 && pool_nr < NR_POOLS);
00171 
00172         SHA256_Update(&pool_ctx[pool_nr], (unsigned char *)&sample,
00173                 sizeof(sample));
00174         if (pool_nr == 0)
00175                 samples++;
00176         pool_nr++;
00177         if (pool_nr >= NR_POOLS)
00178                 pool_nr= 0;
00179         pool_ind[source]= pool_nr;
00180 }
00181 
00182 PRIVATE void data_block(keyp, data)
00183 rd_keyinstance *keyp;
00184 void *data;
00185 {
00186         int r;
00187         u8_t input[AES_BLOCKSIZE];
00188 
00189         memset(input, '\0', sizeof(input));
00190 
00191         /* Do we want the output of the random numbers to be portable 
00192          * across platforms (for example for RSA signatures)? At the moment
00193          * we don't do anything special. Encrypt the counter with the AES
00194          * key.
00195          */
00196         assert(sizeof(count_lo)+sizeof(count_hi) <= AES_BLOCKSIZE);
00197         memcpy(input, &count_lo, sizeof(count_lo));
00198         memcpy(input+sizeof(count_lo), &count_hi, sizeof(count_hi));
00199         r= rijndael_ecb_encrypt(keyp, input, data, AES_BLOCKSIZE, NULL);
00200         assert(r == AES_BLOCKSIZE);
00201 
00202         count_lo++;
00203         if (count_lo == 0)
00204                 count_hi++;
00205 }
00206 
00207 PRIVATE void reseed()
00208 {
00209         int i;
00210         SHA256_CTX ctx;
00211         u8_t digest[SHA256_DIGEST_LENGTH];
00212 
00213         if (samples < MIN_SAMPLES)
00214                 return;
00215 
00216         reseed_count++;
00217         SHA256_Init(&ctx);
00218         if (got_seeded)
00219                 SHA256_Update(&ctx, random_key, sizeof(random_key));
00220         SHA256_Final(digest, &pool_ctx[0]);
00221         SHA256_Update(&ctx, digest, sizeof(digest));
00222         SHA256_Init(&pool_ctx[0]);
00223         for (i= 1; i<NR_POOLS; i++)
00224         {
00225                 if ((reseed_count & (1UL << (i-1))) != 0)
00226                         break;
00227                 SHA256_Final(digest, &pool_ctx[i]);
00228                 SHA256_Update(&ctx, digest, sizeof(digest));
00229                 SHA256_Init(&pool_ctx[i]);
00230         }
00231         SHA256_Final(digest, &ctx);
00232         assert(sizeof(random_key) == sizeof(digest));
00233         memcpy(random_key, &digest, sizeof(random_key));
00234         samples= 0;
00235 
00236         got_seeded= 1;
00237 }
00238 

Generated on Fri Apr 14 22:57:18 2006 for minix by  doxygen 1.4.6