/* random.c Random number generator. The random number generator collects data from the kernel and compressed that data into a seed for a psuedo random number generator. */ #include "../drivers.h" #include "../../kernel/const.h" #include "assert.h" #include "random.h" #include "sha2.h" #include "aes/rijndael.h" #define N_DERIV 16 #define NR_POOLS 32 #define MIN_SAMPLES 256 /* Number of samples needed in pool 0 for a * re-seed. */ PRIVATE unsigned long deriv[RANDOM_SOURCES][N_DERIV]; PRIVATE int pool_ind[RANDOM_SOURCES]; PRIVATE SHA256_CTX pool_ctx[NR_POOLS]; PRIVATE unsigned samples= 0; PRIVATE int got_seeded= 0; PRIVATE u8_t random_key[2*AES_BLOCKSIZE]; PRIVATE u32_t count_lo, count_hi; PRIVATE u32_t reseed_count; FORWARD _PROTOTYPE( void add_sample, (int source, unsigned long sample) ); FORWARD _PROTOTYPE( void data_block, (rd_keyinstance *keyp, void *data) ); FORWARD _PROTOTYPE( void reseed, (void) ); PUBLIC void random_init() { int i, j; assert(&deriv[RANDOM_SOURCES-1][N_DERIV-1] == &deriv[0][0] + RANDOM_SOURCES*N_DERIV -1); for (i= 0; i= RANDOM_SOURCES) panic("memory", "random_update: bad source", source); for (i= 0; i 0) { n= AES_BLOCKSIZE; if (n > size) { n= size; data_block(&key, output); memcpy(cp, output, n); } else data_block(&key, cp); cp += n; size -= n; } /* Generate new key */ assert(sizeof(random_key) == 2*AES_BLOCKSIZE); data_block(&key, random_key); data_block(&key, random_key+AES_BLOCKSIZE); } PUBLIC void random_putbytes(buf, size) void *buf; size_t size; { /* Add bits to pool zero */ SHA256_Update(&pool_ctx[0], buf, size); /* Assume that these bits are truely random. Increment samples * with the number of bits. */ samples += size*8; reseed(); } PRIVATE void add_sample(source, sample) int source; unsigned long sample; { int i, pool_nr; unsigned long d, v, di, min; /* Delete bad sample. Compute the Nth derivative. Delete the sample * if any derivative is too small. */ min= (unsigned long)-1; v= sample; for (i= 0; i= di) d= v-di; else d= di-v; deriv[source][i]= v; v= d; if (v = 0 && pool_nr < NR_POOLS); SHA256_Update(&pool_ctx[pool_nr], (unsigned char *)&sample, sizeof(sample)); if (pool_nr == 0) samples++; pool_nr++; if (pool_nr >= NR_POOLS) pool_nr= 0; pool_ind[source]= pool_nr; } PRIVATE void data_block(keyp, data) rd_keyinstance *keyp; void *data; { int r; u8_t input[AES_BLOCKSIZE]; memset(input, '\0', sizeof(input)); /* Do we want the output of the random numbers to be portable * across platforms (for example for RSA signatures)? At the moment * we don't do anything special. Encrypt the counter with the AES * key. */ assert(sizeof(count_lo)+sizeof(count_hi) <= AES_BLOCKSIZE); memcpy(input, &count_lo, sizeof(count_lo)); memcpy(input+sizeof(count_lo), &count_hi, sizeof(count_hi)); r= rijndael_ecb_encrypt(keyp, input, data, AES_BLOCKSIZE, NULL); assert(r == AES_BLOCKSIZE); count_lo++; if (count_lo == 0) count_hi++; } PRIVATE void reseed() { int i; SHA256_CTX ctx; u8_t digest[SHA256_DIGEST_LENGTH]; if (samples < MIN_SAMPLES) return; reseed_count++; SHA256_Init(&ctx); if (got_seeded) SHA256_Update(&ctx, random_key, sizeof(random_key)); SHA256_Final(digest, &pool_ctx[0]); SHA256_Update(&ctx, digest, sizeof(digest)); SHA256_Init(&pool_ctx[0]); for (i= 1; i