/* LibTomCrypt, modular cryptographic library -- Tom St Denis
*
* LibTomCrypt is a library that provides various cryptographic
* algorithms in a highly modular and flexible manner.
*
* The library is free for all purposes without any express
* guarantee it works.
*
* Tom St Denis, tomstdenis@gmail.com, http://libtomcrypt.com
*/
#include "tomcrypt.h"
/**
@file fortuna.c
Fortuna PRNG, Tom St Denis
*/
/* Implementation of Fortuna by Tom St Denis
We deviate slightly here for reasons of simplicity [and to fit in the API]. First all "sources"
in the AddEntropy function are fixed to 0. Second since no reliable timer is provided
we reseed automatically when len(pool0) >= 64 or every FORTUNA_WD calls to the read function */
#ifdef FORTUNA
/* requries SHA256 and AES */
#if !(defined(RIJNDAEL) && defined(SHA256))
#error FORTUNA requires SHA256 and RIJNDAEL (AES)
#endif
#ifndef FORTUNA_POOLS
#warning FORTUNA_POOLS was not previously defined (old headers?)
#define FORTUNA_POOLS 32
#endif
#if FORTUNA_POOLS < 4 || FORTUNA_POOLS > 32
#error FORTUNA_POOLS must be in [4..32]
#endif
const struct ltc_prng_descriptor fortuna_desc = {
"fortuna", 1024,
&fortuna_start,
&fortuna_add_entropy,
&fortuna_ready,
&fortuna_read,
&fortuna_done,
&fortuna_export,
&fortuna_import,
&fortuna_test
};
/* update the IV */
static void fortuna_update_iv(prng_state *prng)
{
int x;
unsigned char *IV;
/* update IV */
IV = prng->fortuna.IV;
for (x = 0; x < 16; x++) {
IV[x] = (IV[x] + 1) & 255;
if (IV[x] != 0) break;
}
}
/* reseed the PRNG */
static int fortuna_reseed(prng_state *prng)
{
unsigned char tmp[MAXBLOCKSIZE];
hash_state md;
int err, x;
++prng->fortuna.reset_cnt;
/* new K == SHA256(K || s) where s == SHA256(P0) || SHA256(P1) ... */
sha256_init(&md);
if ((err = sha256_process(&md, prng->fortuna.K, 32)) != CRYPT_OK) {
sha256_done(&md, tmp);
return err;
}
for (x = 0; x < FORTUNA_POOLS; x++) {
if (x == 0 || ((prng->fortuna.reset_cnt >> (x-1)) & 1) == 0) {
/* terminate this hash */
if ((err = sha256_done(&prng->fortuna.pool[x], tmp)) != CRYPT_OK) {
sha256_done(&md, tmp);
return err;
}
/* add it to the string */
if ((err = sha256_process(&md, tmp, 32)) != CRYPT_OK) {
sha256_done(&md, tmp);
return err;
}
/* reset this pool */
if ((err = sha256_init(&prng->fortuna.pool[x])) != CRYPT_OK) {
sha256_done(&md, tmp);
return err;
}
} else {
break;
}
}
/* finish key */
if ((err = sha256_done(&md, prng->fortuna.K)) != CRYPT_OK) {
return err;
}
if ((err = rijndael_setup(prng->fortuna.K, 32, 0, &prng->fortuna.skey)) != CRYPT_OK) {
return err;
}
fortuna_update_iv(prng);
/* reset pool len */
prng->fortuna.pool0_len = 0;
prng->fortuna.wd = 0;
#ifdef LTC_CLEAN_STACK
zeromem(&md, sizeof(md));
zeromem(tmp, sizeof(tmp));
#endif
return CRYPT_OK;
}
/**
Start the PRNG
@param prng [out] The PRNG state to initialize
@return CRYPT_OK if successful
*/
int fortuna_start(prng_state *prng)
{
int err, x, y;
unsigned char tmp[MAXBLOCKSIZE];
LTC_ARGCHK(prng != NULL);
/* initialize the pools */
for (x = 0; x < FORTUNA_POOLS; x++) {
if ((err = sha256_init(&prng->fortuna.pool[x])) != CRYPT_OK) {
for (y = 0; y < x; y++) {
sha256_done(&prng->fortuna.pool[y], tmp);
}
return err;
}
}
prng->fortuna.pool_idx = prng->fortuna.pool0_len = prng->fortuna.wd = 0;
prng->fortuna.reset_cnt = 0;
/* reset bufs */
zeromem(prng->fortuna.K, 32);
if ((err = rijndael_setup(prng->fortuna.K, 32, 0, &prng->fortuna.skey)) != CRYPT_OK) {
for (x = 0; x < FORTUNA_POOLS; x++) {
sha256_done(&prng->fortuna.pool[x], tmp);
}
return err;
}
zeromem(prng->fortuna.IV, 16);
LTC_MUTEX_INIT(&prng->fortuna.prng_lock)
return CRYPT_OK;
}
/**
Add entropy to the PRNG state
@param in The data to add
@param inlen Length of the data to add
@param prng PRNG state to update
@return CRYPT_OK if successful
*/
int fortuna_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng)
{
unsigned char tmp[2];
int err;
LTC_ARGCHK(in != NULL);
LTC_ARGCHK(prng != NULL);
LTC_MUTEX_LOCK(&prng->fortuna.prng_lock);
/* ensure inlen <= 32 */
if (inlen > 32) {
LTC_MUTEX_UNLOCK(&prng->fortuna.prng_lock);
return CRYPT_INVALID_ARG;
}
/* add s || length(in) || in to pool[pool_idx] */
tmp[0] = 0;
tmp[1] = (unsigned char)inlen;
if ((err = sha256_process(&prng->fortuna.pool[prng->fortuna.pool_idx], tmp, 2)) != CRYPT_OK) {
LTC_MUTEX_UNLOCK(&prng->fortuna.prng_lock);
return err;
}
if ((err = sha256_process(&prng->fortuna.pool[prng->fortuna.pool_idx], in, inlen)) != CRYPT_OK) {
LTC_MUTEX_UNLOCK(&prng->fortuna.prng_lock);
return err;
}
if (prng->fortuna.pool_idx == 0) {
prng->fortuna.pool0_len += inlen;
}
if (++(prng->fortuna.pool_idx) == FORTUNA_POOLS) {
prng->fortuna.pool_idx = 0;
}
LTC_MUTEX_UNLOCK(&prng->fortuna.prng_lock);
return CRYPT_OK;
}
/**
Make the PRNG ready to read from
@param prng The PRNG to make active
@return CRYPT_OK if successful
*/
int fortuna_ready(prng_state *prng)
{
return fortuna_reseed(prng);
}
/**
Read from the PRNG
@param out Destination
@param outlen Length of output
@param prng The active PRNG to read from
@return Number of octets read
*/
unsigned long fortuna_read(unsigned char *out, unsigned long outlen, prng_state *prng)
{
unsigned char tmp[16];
int err;
unsigned long tlen;
LTC_ARGCHK(out != NULL);
LTC_ARGCHK(prng != NULL);
LTC_MUTEX_LOCK(&prng->fortuna.prng_lock);
/* do we have to reseed? */
if (++prng->fortuna.wd == FORTUNA_WD || prng->fortuna.pool0_len >= 64) {
if ((err = fortuna_reseed(prng)) != CRYPT_OK) {
LTC_MUTEX_UNLOCK(&prng->fortuna.prng_lock);
return 0;
}
}
/* now generate the blocks required */
tlen = outlen;
/* handle whole blocks without the extra XMEMCPY */
while (outlen >= 16) {
/* encrypt the IV and store it */
rijndael_ecb_encrypt(prng->fortuna.IV, out, &prng->fortuna.skey);
out += 16;
outlen -= 16;
fortuna_update_iv(prng);
}
/* left over bytes? */
if (outlen > 0) {
rijndael_ecb_encrypt(prng->fortuna.IV, tmp, &prng->fortuna.skey);
XMEMCPY(out, tmp, outlen);
fortuna_update_iv(prng);
}
/* generate new key */
rijndael_ecb_encrypt(prng->fortuna.IV, prng->fortuna.K , &prng->fortuna.skey); fortuna_update_iv(prng);
rijndael_ecb_encrypt(prng->fortuna.IV, prng->fortuna.K+16, &prng->fortuna.skey); fortuna_update_iv(prng);
if ((err = rijndael_setup(prng->fortuna.K, 32, 0, &prng->fortuna.skey)) != CRYPT_OK) {
LTC_MUTEX_UNLOCK(&prng->fortuna.prng_lock);
return 0;
}
#ifdef LTC_CLEAN_STACK
zeromem(tmp, sizeof(tmp));
#endif
LTC_MUTEX_UNLOCK(&prng->fortuna.prng_lock);
return tlen;
}
/**
Terminate the PRNG
@param prng The PRNG to terminate
@return CRYPT_OK if successful
*/
int fortuna_done(prng_state *prng)
{
int err, x;
unsigned char tmp[32];
LTC_ARGCHK(prng != NULL);
LTC_MUTEX_LOCK(&prng->fortuna.prng_lock);
/* terminate all the hashes */
for (x = 0; x < FORTUNA_POOLS; x++) {
if ((err = sha256_done(&(prng->fortuna.pool[x]), tmp)) != CRYPT_OK) {
LTC_MUTEX_UNLOCK(&prng->fortuna.prng_lock);
return err;
}
}
/* call cipher done when we invent one ;-) */
#ifdef LTC_CLEAN_STACK
zeromem(tmp, sizeof(tmp));
#endif
LTC_MUTEX_UNLOCK(&prng->fortuna.prng_lock);
return CRYPT_OK;
}
/**
Export the PRNG state
@param out [out] Destination
@param outlen [in/out] Max size and resulting size of the state
@param prng The PRNG to export
@return CRYPT_OK if successful
*/
int fortuna_export(unsigned char *out, unsigned long *outlen, prng_state *prng)
{
int x, err;
hash_state *md;
LTC_ARGCHK(out != NULL);
LTC_ARGCHK(outlen != NULL);
LTC_ARGCHK(prng != NULL);
LTC_MUTEX_LOCK(&prng->fortuna.prng_lock);
/* we'll write bytes for s&g's */
if (*outlen < 32*FORTUNA_POOLS) {
LTC_MUTEX_UNLOCK(&prng->fortuna.prng_lock);
*outlen = 32*FORTUNA_POOLS;
return CRYPT_BUFFER_OVERFLOW;
}
md = XMALLOC(sizeof(hash_state));
if (md == NULL) {
LTC_MUTEX_UNLOCK(&prng->fortuna.prng_lock);
return CRYPT_MEM;
}
/* to emit the state we copy each pool, terminate it then hash it again so
* an attacker who sees the state can't determine the current state of the PRNG
*/
for (x = 0; x < FORTUNA_POOLS; x++) {
/* copy the PRNG */
XMEMCPY(md, &(prng->fortuna.pool[x]), sizeof(*md));
/* terminate it */
if ((err = sha256_done(md, out+x*32)) != CRYPT_OK) {
goto LBL_ERR;
}
/* now hash it */
if ((err = sha256_init(md)) != CRYPT_OK) {
goto LBL_ERR;
}
if ((err = sha256_process(md, out+x*32, 32)) != CRYPT_OK) {
goto LBL_ERR;
}
if ((err = sha256_done(md, out+x*32)) != CRYPT_OK) {
goto LBL_ERR;
}
}
*outlen = 32*FORTUNA_POOLS;
err = CRYPT_OK;
LBL_ERR:
#ifdef LTC_CLEAN_STACK
zeromem(md, sizeof(*md));
#endif
XFREE(md);
LTC_MUTEX_UNLOCK(&prng->fortuna.prng_lock);
return err;
}
/**
Import a PRNG state
@param in The PRNG state
@param inlen Size of the state
@param prng The PRNG to import
@return CRYPT_OK if successful
*/
int fortuna_import(const unsigned char *in, unsigned long inlen, prng_state *prng)
{
int err, x;
LTC_ARGCHK(in != NULL);
LTC_ARGCHK(prng != NULL);
if (inlen != 32*FORTUNA_POOLS) {
return CRYPT_INVALID_ARG;
}
if ((err = fortuna_start(prng)) != CRYPT_OK) {
return err;
}
for (x = 0; x < FORTUNA_POOLS; x++) {
if ((err = fortuna_add_entropy(in+x*32, 32, prng)) != CRYPT_OK) {
return err;
}
}
return err;
}
/**
PRNG self-test
@return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled
*/
int fortuna_test(void)
{
#ifndef LTC_TEST
return CRYPT_NOP;
#else
int err;
if ((err = sha256_test()) != CRYPT_OK) {
return err;
}
return rijndael_test();
#endif
}
#endif
/* $Source: /cvs/libtom/libtomcrypt/src/prngs/fortuna.c,v $ */
/* $Revision: 1.12 $ */
/* $Date: 2006/12/04 21:34:03 $ */