| /* | 
 |  * fortuna.c | 
 |  *		Fortuna-like PRNG. | 
 |  * | 
 |  * Copyright (c) 2005 Marko Kreen | 
 |  * All rights reserved. | 
 |  * | 
 |  * Redistribution and use in source and binary forms, with or without | 
 |  * modification, are permitted provided that the following conditions | 
 |  * are met: | 
 |  * 1. Redistributions of source code must retain the above copyright | 
 |  *	  notice, this list of conditions and the following disclaimer. | 
 |  * 2. Redistributions in binary form must reproduce the above copyright | 
 |  *	  notice, this list of conditions and the following disclaimer in the | 
 |  *	  documentation and/or other materials provided with the distribution. | 
 |  * | 
 |  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | 
 |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
 |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
 |  * ARE DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | 
 |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
 |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 
 |  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 
 |  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
 |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 
 |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
 |  * SUCH DAMAGE. | 
 |  * | 
 |  * contrib/pgcrypto/fortuna.c | 
 |  */ | 
 |  | 
 | #include <arch/arch.h> | 
 | #include <time.h> | 
 |  | 
 | #include <random/fortuna.h> | 
 | #include <random/rijndael.h> | 
 | #include <random/sha2.h> | 
 |  | 
 | /* | 
 |  * Why Fortuna-like: There does not seem to be any definitive reference | 
 |  * on Fortuna in the net.  Instead this implementation is based on | 
 |  * following references: | 
 |  * | 
 |  * http://en.wikipedia.org/wiki/Fortuna_(PRNG) | 
 |  *	 - Wikipedia article | 
 |  * http://jlcooke.ca/random/ | 
 |  *	 - Jean-Luc Cooke Fortuna-based /dev/random driver for Linux. | 
 |  */ | 
 |  | 
 | /* | 
 |  * There is some confusion about whether and how to carry forward | 
 |  * the state of the pools.	Seems like original Fortuna does not | 
 |  * do it, resetting hash after each request.  I guess expecting | 
 |  * feeding to happen more often that requesting.   This is absolutely | 
 |  * unsuitable for pgcrypto, as nothing asynchronous happens here. | 
 |  * | 
 |  * J.L. Cooke fixed this by feeding previous hash to new re-initialized | 
 |  * hash context. | 
 |  * | 
 |  * Fortuna predecessor Yarrow requires ability to query intermediate | 
 |  * 'final result' from hash, without affecting it. | 
 |  * | 
 |  * This implementation uses the Yarrow method - asking intermediate | 
 |  * results, but continuing with old state. | 
 |  */ | 
 |  | 
 | /* | 
 |  * Algorithm parameters | 
 |  */ | 
 |  | 
 | /* | 
 |  * How many pools. | 
 |  * | 
 |  * Original Fortuna uses 32 pools, that means 32'th pool is | 
 |  * used not earlier than in 13th year.	This is a waste in | 
 |  * pgcrypto, as we have very low-frequancy seeding.  Here | 
 |  * is preferable to have all entropy usable in reasonable time. | 
 |  * | 
 |  * With 23 pools, 23th pool is used after 9 days which seems | 
 |  * more sane. | 
 |  * | 
 |  * In our case the minimal cycle time would be bit longer | 
 |  * than the system-randomness feeding frequency. | 
 |  */ | 
 | enum { numPools = 23, | 
 |  | 
 |        /* in microseconds */ | 
 |        reseedInterval = 100000, /* 0.1 sec */ | 
 |  | 
 |        /* for one big request, reseed after this many bytes */ | 
 |        reseedBytes = (1024 * 1024), | 
 |  | 
 |        /* | 
 | 	* Skip reseed if pool 0 has less than this many | 
 | 	* bytes added since last reseed. | 
 | 	*/ | 
 |        pool0Fill = (256 / 8), | 
 |  | 
 |        /* | 
 | 	* Algorithm constants | 
 | 	*/ | 
 |  | 
 |        /* Both cipher key size and hash result size */ | 
 |        block = 32, | 
 |  | 
 |        /* cipher block size */ | 
 |        ciphBlock = 16 }; | 
 |  | 
 | /* for internal wrappers */ | 
 | typedef SHA256Ctx mdCtx; | 
 | typedef rijndaelCtx ciphCtx; | 
 |  | 
 | struct FState { | 
 | 	uint8_t counter[ciphBlock]; | 
 | 	uint8_t result[ciphBlock]; | 
 | 	uint8_t key[block]; | 
 | 	mdCtx pool[numPools]; | 
 | 	ciphCtx ciph; | 
 | 	unsigned reseedCount; | 
 | 	int32_t lastReseedTime; | 
 | 	unsigned pool0Bytes; | 
 | 	unsigned rndPos; | 
 | 	int tricksDone; | 
 | }; | 
 | typedef struct FState FState; | 
 |  | 
 | /* | 
 |  * Use our own wrappers here. | 
 |  * - Need to get intermediate result from digest, without affecting it. | 
 |  * - Need re-set key on a cipher context. | 
 |  * - Algorithms are guaranteed to exist. | 
 |  * - No memory allocations. | 
 |  */ | 
 |  | 
 | static void ciph_init(ciphCtx *ctx, const uint8_t *key, int klen) | 
 | { | 
 | 	rijndael_set_key(ctx, (const uint32_t *)key, klen, 1); | 
 | } | 
 |  | 
 | static void ciph_encrypt(ciphCtx *ctx, const uint8_t *in, uint8_t *out) | 
 | { | 
 | 	rijndael_encrypt(ctx, (const uint32_t *)in, (uint32_t *)out); | 
 | } | 
 |  | 
 | static void md_init(mdCtx *ctx) | 
 | { | 
 | 	SHA256_Init(ctx); | 
 | } | 
 |  | 
 | static void md_update(mdCtx *ctx, const uint8_t *data, int len) | 
 | { | 
 | 	SHA256_Update(ctx, data, len); | 
 | } | 
 |  | 
 | static void md_result(mdCtx *ctx, uint8_t *dst) | 
 | { | 
 | 	SHA256Ctx tmp; | 
 |  | 
 | 	memmove(&tmp, ctx, sizeof(*ctx)); | 
 | 	SHA256_Final(dst, &tmp); | 
 | 	memset(&tmp, 0, sizeof(tmp)); | 
 | } | 
 |  | 
 | /* | 
 |  * initialize state | 
 |  */ | 
 | static void init_state(FState *st) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	memset(st, 0, sizeof(*st)); | 
 | 	for (i = 0; i < numPools; i++) | 
 | 		md_init(&st->pool[i]); | 
 | } | 
 |  | 
 | /* | 
 |  * Endianess does not matter. | 
 |  * It just needs to change without repeating. | 
 |  */ | 
 | static void inc_counter(FState *st) | 
 | { | 
 | 	uint32_t *val = (uint32_t *)st->counter; | 
 |  | 
 | 	if (++val[0]) | 
 | 		return; | 
 | 	if (++val[1]) | 
 | 		return; | 
 | 	if (++val[2]) | 
 | 		return; | 
 | 	++val[3]; | 
 | } | 
 |  | 
 | /* | 
 |  * This is called 'cipher in counter mode'. | 
 |  */ | 
 | static void encrypt_counter(FState *st, uint8_t *dst) | 
 | { | 
 | 	ciph_encrypt(&st->ciph, st->counter, dst); | 
 | 	inc_counter(st); | 
 | } | 
 |  | 
 | /* | 
 |  * The time between reseed must be at least reseedInterval | 
 |  * microseconds. | 
 |  */ | 
 | static int enough_time_passed(FState *st) | 
 | { | 
 | 	int ok; | 
 | 	int32_t now; | 
 | 	int32_t last = st->lastReseedTime; | 
 |  | 
 | 	now = tsc2sec(read_tsc()); | 
 |  | 
 | 	/* check how much time has passed */ | 
 | 	ok = 0; | 
 | 	if (now - last >= reseedInterval) | 
 | 		ok = 1; | 
 |  | 
 | 	/* reseed will happen, update lastReseedTime */ | 
 | 	if (ok) | 
 | 		st->lastReseedTime = now; | 
 |  | 
 | 	return ok; | 
 | } | 
 |  | 
 | /* | 
 |  * generate new key from all the pools | 
 |  */ | 
 | static void reseed(FState *st) | 
 | { | 
 | 	unsigned k; | 
 | 	unsigned n; | 
 | 	mdCtx key_md; | 
 | 	uint8_t buf[block]; | 
 |  | 
 | 	/* set pool as empty */ | 
 | 	st->pool0Bytes = 0; | 
 |  | 
 | 	/* | 
 | 	 * Both #0 and #1 reseed would use only pool 0. Just skip #0 then. | 
 | 	 */ | 
 | 	n = ++st->reseedCount; | 
 |  | 
 | 	/* | 
 | 	 * The goal: use k-th pool only 1/(2^k) of the time. | 
 | 	 */ | 
 | 	md_init(&key_md); | 
 | 	for (k = 0; k < numPools; k++) { | 
 | 		md_result(&st->pool[k], buf); | 
 | 		md_update(&key_md, buf, block); | 
 |  | 
 | 		if (n & 1 || !n) | 
 | 			break; | 
 | 		n >>= 1; | 
 | 	} | 
 |  | 
 | 	/* add old key into mix too */ | 
 | 	md_update(&key_md, st->key, block); | 
 |  | 
 | 	/* now we have new key */ | 
 | 	md_result(&key_md, st->key); | 
 |  | 
 | 	/* use new key */ | 
 | 	ciph_init(&st->ciph, st->key, block); | 
 |  | 
 | 	memset(&key_md, 0, sizeof(key_md)); | 
 | 	memset(buf, 0, block); | 
 | } | 
 |  | 
 | /* | 
 |  * Pick a random pool.	This uses key bytes as random source. | 
 |  */ | 
 | static unsigned get_rand_pool(FState *st) | 
 | { | 
 | 	unsigned rnd; | 
 |  | 
 | 	/* | 
 | 	 * This slightly prefers lower pools - thats OK. | 
 | 	 */ | 
 | 	rnd = st->key[st->rndPos] % numPools; | 
 |  | 
 | 	st->rndPos++; | 
 | 	if (st->rndPos >= block) | 
 | 		st->rndPos = 0; | 
 |  | 
 | 	return rnd; | 
 | } | 
 |  | 
 | /* | 
 |  * update pools | 
 |  */ | 
 | static void add_entropy(FState *st, const uint8_t *data, unsigned len) | 
 | { | 
 | 	unsigned pos; | 
 | 	uint8_t hash[block]; | 
 | 	mdCtx md; | 
 |  | 
 | 	/* hash given data */ | 
 | 	md_init(&md); | 
 | 	md_update(&md, data, len); | 
 | 	md_result(&md, hash); | 
 |  | 
 | 	/* | 
 | 	 * Make sure the pool 0 is initialized, then update randomly. | 
 | 	 */ | 
 | 	if (st->reseedCount == 0) | 
 | 		pos = 0; | 
 | 	else | 
 | 		pos = get_rand_pool(st); | 
 | 	md_update(&st->pool[pos], hash, block); | 
 |  | 
 | 	if (pos == 0) | 
 | 		st->pool0Bytes += len; | 
 |  | 
 | 	memset(hash, 0, block); | 
 | 	memset(&md, 0, sizeof(md)); | 
 | } | 
 |  | 
 | /* | 
 |  * Just take 2 next blocks as new key | 
 |  */ | 
 | static void rekey(FState *st) | 
 | { | 
 | 	encrypt_counter(st, st->key); | 
 | 	encrypt_counter(st, st->key + ciphBlock); | 
 | 	ciph_init(&st->ciph, st->key, block); | 
 | } | 
 |  | 
 | /* | 
 |  * Hide public constants. (counter, pools > 0) | 
 |  * | 
 |  * This can also be viewed as spreading the startup | 
 |  * entropy over all of the components. | 
 |  */ | 
 | static void startup_tricks(FState *st) | 
 | { | 
 | 	int i; | 
 | 	uint8_t buf[block]; | 
 |  | 
 | 	/* Use next block as counter. */ | 
 | 	encrypt_counter(st, st->counter); | 
 |  | 
 | 	/* Now shuffle pools, excluding #0 */ | 
 | 	for (i = 1; i < numPools; i++) { | 
 | 		encrypt_counter(st, buf); | 
 | 		encrypt_counter(st, buf + ciphBlock); | 
 | 		md_update(&st->pool[i], buf, block); | 
 | 	} | 
 | 	memset(buf, 0, block); | 
 |  | 
 | 	/* Hide the key. */ | 
 | 	rekey(st); | 
 |  | 
 | 	/* This can be done only once. */ | 
 | 	st->tricksDone = 1; | 
 | } | 
 |  | 
 | static void extract_data(FState *st, unsigned count, uint8_t *dst) | 
 | { | 
 | 	unsigned n; | 
 | 	unsigned block_nr = 0; | 
 |  | 
 | 	/* Should we reseed? */ | 
 | 	if (st->pool0Bytes >= pool0Fill || st->reseedCount == 0) | 
 | 		if (enough_time_passed(st)) | 
 | 			reseed(st); | 
 |  | 
 | 	/* Do some randomization on first call */ | 
 | 	if (!st->tricksDone) | 
 | 		startup_tricks(st); | 
 |  | 
 | 	while (count > 0) { | 
 | 		/* produce bytes */ | 
 | 		encrypt_counter(st, st->result); | 
 |  | 
 | 		/* copy result */ | 
 | 		if (count > ciphBlock) | 
 | 			n = ciphBlock; | 
 | 		else | 
 | 			n = count; | 
 | 		memmove(dst, st->result, n); | 
 | 		dst += n; | 
 | 		count -= n; | 
 |  | 
 | 		/* must not give out too many bytes with one key */ | 
 | 		block_nr++; | 
 | 		if (block_nr > (reseedBytes / ciphBlock)) { | 
 | 			rekey(st); | 
 | 			block_nr = 0; | 
 | 		} | 
 | 	} | 
 | 	/* Set new key for next request. */ | 
 | 	rekey(st); | 
 | } | 
 |  | 
 | static FState mainState; | 
 | static int initDone; | 
 |  | 
 | static void init() | 
 | { | 
 | 	init_state(&mainState); | 
 | 	initDone = 1; | 
 | } | 
 |  | 
 | /* | 
 |  * public interface | 
 |  */ | 
 |  | 
 | void fortuna_add_entropy(const uint8_t *data, unsigned len) | 
 | { | 
 | 	if (!initDone) | 
 | 		init(); | 
 |  | 
 | 	if (!data || !len) | 
 | 		return; | 
 | 	add_entropy(&mainState, data, len); | 
 | } | 
 |  | 
 | void fortuna_get_bytes(unsigned len, uint8_t *dst) | 
 | { | 
 | 	if (!initDone) | 
 | 		init(); | 
 |  | 
 | 	if (!dst || !len) | 
 | 		return; | 
 | 	extract_data(&mainState, len, dst); | 
 | } |