| /* 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 $ */ |