239 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			239 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
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; i++)
 | 
						|
	{
 | 
						|
		for (j= 0; j<N_DERIV; j++)
 | 
						|
			deriv[i][j]= 0;
 | 
						|
		pool_ind[i]= 0;
 | 
						|
	}
 | 
						|
	for (i= 0; i<NR_POOLS; i++)
 | 
						|
		SHA256_Init(&pool_ctx[i]);
 | 
						|
	count_lo= 0;
 | 
						|
	count_hi= 0;
 | 
						|
	reseed_count= 0;
 | 
						|
}
 | 
						|
 | 
						|
PUBLIC int random_isseeded()
 | 
						|
{
 | 
						|
	if (got_seeded)
 | 
						|
		return 1;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
PUBLIC void random_update(source, buf, count)
 | 
						|
int source;
 | 
						|
unsigned short *buf;
 | 
						|
int count;
 | 
						|
{
 | 
						|
	int i;
 | 
						|
 | 
						|
#if 0
 | 
						|
	printf("random_update: got %d samples for source %d\n", count, source);
 | 
						|
#endif
 | 
						|
	if (source < 0 || source >= RANDOM_SOURCES)
 | 
						|
		panic("memory", "random_update: bad source", source);
 | 
						|
	for (i= 0; i<count; i++)
 | 
						|
		add_sample(source, buf[i]);
 | 
						|
	reseed();
 | 
						|
}
 | 
						|
 | 
						|
PUBLIC void random_getbytes(buf, size)
 | 
						|
void *buf;
 | 
						|
size_t size;
 | 
						|
{
 | 
						|
	int n, r;
 | 
						|
	u8_t *cp;
 | 
						|
	rd_keyinstance key;
 | 
						|
	u8_t output[AES_BLOCKSIZE];
 | 
						|
 | 
						|
	r= rijndael_makekey(&key, sizeof(random_key), random_key);
 | 
						|
	assert(r == 0);
 | 
						|
 | 
						|
	cp= buf;
 | 
						|
	while (size > 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<N_DERIV; i++)
 | 
						|
	{
 | 
						|
		di= deriv[source][i];
 | 
						|
 | 
						|
		/* Compute the difference */
 | 
						|
		if (v >= di)
 | 
						|
			d= v-di;
 | 
						|
		else
 | 
						|
			d= di-v;
 | 
						|
		deriv[source][i]= v;
 | 
						|
		v= d;
 | 
						|
		if (v <min)
 | 
						|
			min= v;
 | 
						|
	}
 | 
						|
	if (min < 2)
 | 
						|
	{
 | 
						|
#if 0
 | 
						|
		printf("ignoring sample '%u' from source %d\n",
 | 
						|
			sample, source);
 | 
						|
#endif
 | 
						|
		return;
 | 
						|
	}
 | 
						|
#if 0
 | 
						|
	printf("accepting sample '%u' from source %d\n", sample, source);
 | 
						|
#endif
 | 
						|
 | 
						|
	pool_nr= pool_ind[source];
 | 
						|
	assert(pool_nr >= 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<NR_POOLS; i++)
 | 
						|
	{
 | 
						|
		if ((reseed_count & (1UL << (i-1))) != 0)
 | 
						|
			break;
 | 
						|
		SHA256_Final(digest, &pool_ctx[i]);
 | 
						|
		SHA256_Update(&ctx, digest, sizeof(digest));
 | 
						|
		SHA256_Init(&pool_ctx[i]);
 | 
						|
	}
 | 
						|
	SHA256_Final(digest, &ctx);
 | 
						|
	assert(sizeof(random_key) == sizeof(digest));
 | 
						|
	memcpy(random_key, &digest, sizeof(random_key));
 | 
						|
	samples= 0;
 | 
						|
 | 
						|
	got_seeded= 1;
 | 
						|
}
 | 
						|
 |