/**
 * A simulator of Linux Random Number Generator (LRNG).
 *
 * This code is based on the source code of LRNG,
 * written by Theodore Ts'o.
 *
 * For more details, see the README file.
 */

#include <stdio.h> //printf, scanf, fprintf, fopen, fclose, fgets, fread, NULL
#include <stdlib.h> //malloc, free, srttol, strtoul, strtoll, atoi
#include <string.h> //memset, memcpy (instead of linux/string.h), strcmp


/************************************************
 *********** DEFINITIONS ************************
 ************************************************/

//from: asm-generic/errno-base.h
//------------------------------
#define EPERM            1      /* Operation not permitted */
#define EAGAIN          11      /* Try again */
#define ENOMEM          12      /* Out of memory */
#define EFAULT          14      /* Bad address */
#define EINVAL          22      /* Invalid argument */

//from: linux/errno.h
//--------------------
#define ERESTARTSYS     512

//from: asm/types.h
//-----------------
typedef unsigned char __u8;
typedef unsigned short __u16;
typedef unsigned int __u32;

//from: asm/posix_types.h
//-----------------------
typedef unsigned int    __kernel_size_t;
typedef int             __kernel_ssize_t;

//from: linux/types.h
//-------------------
typedef __kernel_size_t         size_t;
typedef __kernel_ssize_t        ssize_t;


/************************************************
 *********** LRNG *******************************
 ************************************************/

/*
 * Configuration information
 */
#define DEFAULT_POOL_SIZE 512
#define SECONDARY_POOL_SIZE 128
#define BATCH_ENTROPY_SIZE 256
#define USE_SHA

/*
 * The minimum number of bits of entropy before we wake up a read on
 * /dev/random.  Should be enough to do a significant reseed.
 */
static int random_read_wakeup_thresh = 64;

/*
 * If the entropy count falls under this number of bits, then we
 * should wake up processes which are selecting or polling on write
 * access to /dev/random.
 */
static int random_write_wakeup_thresh = 128;

/*
 * A pool of size .poolwords is stirred with a primitive polynomial
 * of degree .poolwords over GF(2).  The taps for various sizes are
 * defined below.  They are chosen to be evenly spaced (minimum RMS
 * distance from evenly spaced; the numbers in the comments are a
 * scaled squared error sum) except for the last tap, which is 1 to
 * get the twisting happening as fast as possible.
 */
static struct poolinfo {
	int	poolwords;
	int	tap1, tap2, tap3, tap4, tap5;
} poolinfo_table[] = {
	/* x^2048 + x^1638 + x^1231 + x^819 + x^411 + x + 1  -- 115 */
	{ 2048,	1638,	1231,	819,	411,	1 },

	/* x^1024 + x^817 + x^615 + x^412 + x^204 + x + 1 -- 290 */
	{ 1024,	817,	615,	412,	204,	1 },
#if 0				/* Alternate polynomial */
	/* x^1024 + x^819 + x^616 + x^410 + x^207 + x^2 + 1 -- 115 */
	{ 1024,	819,	616,	410,	207,	2 },
#endif

	/* x^512 + x^411 + x^308 + x^208 + x^104 + x + 1 -- 225 */
	{ 512,	411,	308,	208,	104,	1 },
#if 0				/* Alternates */
	/* x^512 + x^409 + x^307 + x^206 + x^102 + x^2 + 1 -- 95 */
	{ 512,	409,	307,	206,	102,	2 },
	/* x^512 + x^409 + x^309 + x^205 + x^103 + x^2 + 1 -- 95 */
	{ 512,	409,	309,	205,	103,	2 },
#endif

	/* x^256 + x^205 + x^155 + x^101 + x^52 + x + 1 -- 125 */
	{ 256,	205,	155,	101,	52,	1 },

	/* x^128 + x^103 + x^76 + x^51 +x^25 + x + 1 -- 105 */
	{ 128,	103,	76,	51,	25,	1 },
#if 0	/* Alternate polynomial */
	/* x^128 + x^103 + x^78 + x^51 + x^27 + x^2 + 1 -- 70 */
	{ 128,	103,	78,	51,	27,	2 },
#endif

	/* x^64 + x^52 + x^39 + x^26 + x^14 + x + 1 -- 15 */
	{ 64,	52,	39,	26,	14,	1 },

	/* x^32 + x^26 + x^20 + x^14 + x^7 + x + 1 -- 15 */
	{ 32,	26,	20,	14,	7,	1 },

	{ 0,	0,	0,	0,	0,	0 },
};

#define POOLBITS	poolwords*32
#define POOLBYTES	poolwords*4

/*
 * Static global variables
 */
static struct entropy_store *random_state; /* The default global store */
static struct entropy_store *sec_random_state; /* secondary store */

//2.6.10
static struct entropy_store *urandom_state; /* For urandom */

/*****************************************************************
 *
 * Utility functions, with some ASM defined functions for speed
 * purposes
 *
 *****************************************************************/

/*
 * Unfortunately, while the GCC optimizer for the i386 understands how
 * to optimize a static rotate left of x bits, it doesn't know how to
 * deal with a variable rotate of x bits.  So we use a bit of asm magic.
 */
#if (!defined (__i386__))
static __u32 rotate_left(int i, __u32 word)
{
	return (word << i) | (word >> (32 - i));

}
#else
static __u32 rotate_left(int i, __u32 word)
{
	__asm__("roll %%cl,%0"
		:"=r" (word)
		:"0" (word),"c" (i));
	return word;
}
#endif

/*
 * More asm magic....
 *
 * For entropy estimation, we need to do an integral base 2
 * logarithm.
 *
 * Note the "12bits" suffix - this is used for numbers between
 * 0 and 4095 only.  This allows a few shortcuts.
 */
#if 0	/* Slow but clear version */
static __u32 int_ln_12bits(__u32 word)
{
	__u32 nbits = 0;

	while (word >>= 1)
		nbits++;
	return nbits;
}
#else	/* Faster (more clever) version, courtesy Colin Plumb */
static __u32 int_ln_12bits(__u32 word)
{
	/* Smear msbit right to make an n-bit mask */
	word |= word >> 8;
	word |= word >> 4;
	word |= word >> 2;
	word |= word >> 1;
	/* Remove one bit to make this a logarithm */
	word >>= 1;
	/* Count the bits set in the word */
	word -= (word >> 1) & 0x555;
	word = (word & 0x333) + ((word >> 2) & 0x333);
	word += (word >> 4);
	word += (word >> 8);
	return word & 15;
}
#endif

/**********************************************************************
 *
 * OS independent entropy store.   Here are the functions which handle
 * storing entropy in an entropy pool.
 *
 **********************************************************************/

struct entropy_store {
	/* mostly-read data: */
	struct poolinfo poolinfo;
	__u32		*pool;

//2.6.10
	const char	*name;

	/* read-write data: */
  	unsigned	add_ptr;
	int		entropy_count;
	int		input_rotate;
};

/*
 * Initialize the entropy store.  The input argument is the size of
 * the random pool.
 *
 * Returns an negative error if there is a problem.
 */
static int create_entropy_store(int size,
				const char *name,//2.6.10
				struct entropy_store **ret_bucket)
{
	struct	entropy_store	*r;
	struct	poolinfo	*p;
	int	poolwords;

	poolwords = (size + 3) / 4; /* Convert bytes->words */
	/* The pool size must be a multiple of 16 32-bit words */
	poolwords = ((poolwords + 15) / 16) * 16;

	for (p = poolinfo_table; p->poolwords; p++) {
		if (poolwords == p->poolwords)
			break;
	}
	if (p->poolwords == 0)
		return -EINVAL;

	//r = kmalloc(sizeof(struct entropy_store), GFP_KERNEL);
	//kmaloc is the kernel memery allocation function. we moved to user-mode function
	r = (struct entropy_store*) malloc(sizeof(struct entropy_store));
	if (!r)
		return -ENOMEM;

	memset (r, 0, sizeof(struct entropy_store));
	r->poolinfo = *p;

	//r->pool = kmalloc(POOLBYTES, GFP_KERNEL);
	//kmaloc is the kernel memery allocation function. we moved to user-mode function
	r->pool = (__u32*) malloc(POOLBYTES);
	if (!r->pool) {
			free(r);//kfree(r);
			//kfree is the kernel memory free function. we moved to user-mode function
		return -ENOMEM;
	}
	memset(r->pool, 0, POOLBYTES);

//2.6.10
	r->name = name;

	*ret_bucket = r;
	return 0;
}

/* Clear the entropy pool and associated counters. */
static void clear_entropy_store(struct entropy_store *r)
{
	r->add_ptr = 0;
	r->entropy_count = 0;
	r->input_rotate = 0;
	memset(r->pool, 0, r->poolinfo.POOLBYTES);
}

static void free_entropy_store(struct entropy_store *r)
{
    if (r->pool)
	  free(r->pool);//kfree(r->pool);
    free(r);//kfree(r);
    //kfree is the kernel memory free function. we moved to user-mode function
}

/*
 * This function adds a byte into the entropy "pool".  It does not
 * update the entropy estimate.  The caller should call
 * credit_entropy_store if this is appropriate.
 *
 * The pool is stirred with a primitive polynomial of the appropriate
 * degree, and then twisted.  We twist by three bits at a time because
 * it's cheap to do so and helps slightly in the expected case where
 * the entropy is concentrated in the low-order bits.
 */

//2.6.10
static void __add_entropy_words(struct entropy_store *r, const __u32 *in,
				int nwords, __u32 out[16])
/*
static void add_entropy_words(struct entropy_store *r, const __u32 *in,
			      int nwords)
*/
{
  	static __u32 const twist_table[8] = {
		         0, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
		0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 };
	unsigned long i, add_ptr, tap1, tap2, tap3, tap4, tap5;
	int new_rotate, input_rotate;
	int wordmask = r->poolinfo.poolwords - 1;
	__u32 w, next_w;
	unsigned long flags;

	/* Taps are constant, so we can load them without holding r->lock.  */
	tap1 = r->poolinfo.tap1;
	tap2 = r->poolinfo.tap2;
	tap3 = r->poolinfo.tap3;
	tap4 = r->poolinfo.tap4;
	tap5 = r->poolinfo.tap5;
	next_w = *in++;

	input_rotate = r->input_rotate;
	add_ptr = r->add_ptr;

	while (nwords--) {

		w = rotate_left(input_rotate, next_w);

		if (nwords > 0)
			next_w = *in++;
		i = add_ptr = (add_ptr - 1) & wordmask;
		/*
		 * Normally, we add 7 bits of rotation to the pool.
		 * At the beginning of the pool, add an extra 7 bits
		 * rotation, so that successive passes spread the
		 * input bits across the pool evenly.
		 */
		new_rotate = input_rotate + 14;
		if (i)
			new_rotate = input_rotate + 7;
		input_rotate = new_rotate & 31;

		/* XOR in the various taps */
		w ^= r->pool[(i + tap1) & wordmask];
		w ^= r->pool[(i + tap2) & wordmask];
		w ^= r->pool[(i + tap3) & wordmask];
		w ^= r->pool[(i + tap4) & wordmask];
		w ^= r->pool[(i + tap5) & wordmask];
		w ^= r->pool[i];
		r->pool[i] = (w >> 3) ^ twist_table[w & 7];

	}

	r->input_rotate = input_rotate;
	r->add_ptr = add_ptr;

//2.6.10
	if (out) {
		for (i = 0; i < 16; i++) {
			out[i] = r->pool[add_ptr];
			add_ptr = (add_ptr - 1) & wordmask;
		}
	}
}


//2.6.10
static void add_entropy_words(struct entropy_store *r, const __u32 *in,
				     int nwords)
{
	__add_entropy_words(r, in, nwords, NULL);
}


/*
 * Credit (or debit) the entropy store with n bits of entropy
 */
static void credit_entropy_store(struct entropy_store *r, int nbits)
{
	unsigned long flags;

	if (r->entropy_count + nbits < 0) {
		r->entropy_count = 0;
	} else if (r->entropy_count + nbits > r->poolinfo.POOLBITS) {
		r->entropy_count = r->poolinfo.POOLBITS;
	} else {
		r->entropy_count += nbits;
	}
}

/******************************************************************
 *
 * Hash function definition
 *
 *******************************************************************/

/*
 * This chunk of code defines a function
 * void HASH_TRANSFORM(__u32 digest[HASH_BUFFER_SIZE + HASH_EXTRA_SIZE],
 * 		__u32 const data[16])
 *
 * The function hashes the input data to produce a digest in the first
 * HASH_BUFFER_SIZE words of the digest[] array, and uses HASH_EXTRA_SIZE
 * more words for internal purposes.  (This buffer is exported so the
 * caller can wipe it once rather than this code doing it each call,
 * and tacking it onto the end of the digest[] array is the quick and
 * dirty way of doing it.)
 *
 * It so happens that MD5 and SHA share most of the initial vector
 * used to initialize the digest[] array before the first call:
 * 1) 0x67452301
 * 2) 0xefcdab89
 * 3) 0x98badcfe
 * 4) 0x10325476
 * 5) 0xc3d2e1f0 (SHA only)
 *
 * For /dev/random purposes, the length of the data being hashed is
 * fixed in length, so appending a bit count in the usual way is not
 * cryptographically necessary.
 */

#ifdef USE_SHA

#define HASH_BUFFER_SIZE 5
#define HASH_EXTRA_SIZE 80
#define HASH_TRANSFORM SHATransform

/* Various size/speed tradeoffs are available.  Choose 0..3. */
#define SHA_CODE_SIZE 0

/*
 * SHA transform algorithm, taken from code written by Peter Gutmann,
 * and placed in the public domain.
 */

/* The SHA f()-functions.  */

#define f1(x,y,z)   ( z ^ (x & (y^z)) )		/* Rounds  0-19: x ? y : z */
#define f2(x,y,z)   (x ^ y ^ z)			/* Rounds 20-39: XOR */
#define f3(x,y,z)   ( (x & y) + (z & (x ^ y)) )	/* Rounds 40-59: majority */
#define f4(x,y,z)   (x ^ y ^ z)			/* Rounds 60-79: XOR */

/* The SHA Mysterious Constants */

#define K1  0x5A827999L			/* Rounds  0-19: sqrt(2) * 2^30 */
#define K2  0x6ED9EBA1L			/* Rounds 20-39: sqrt(3) * 2^30 */
#define K3  0x8F1BBCDCL			/* Rounds 40-59: sqrt(5) * 2^30 */
#define K4  0xCA62C1D6L			/* Rounds 60-79: sqrt(10) * 2^30 */

#define ROTL(n,X)  ( ( ( X ) << n ) | ( ( X ) >> ( 32 - n ) ) )

static void SHATransform(__u32 digest[85], __u32 const data[16])
{
    __u32 A, B, C, D, E;     /* Local vars */
    __u32 TEMP;
    int	i;
#define W (digest + HASH_BUFFER_SIZE)	/* Expanded data array */

    /*
     * Do the preliminary expansion of 16 to 80 words.  Doing it
     * out-of-line line this is faster than doing it in-line on
     * register-starved machines like the x86, and not really any
     * slower on real processors.
     */
    memcpy(W, data, 16*sizeof(__u32));
    for (i = 0; i < 64; i++) {
	    TEMP = W[i] ^ W[i+2] ^ W[i+8] ^ W[i+13];
	    W[i+16] = ROTL(1, TEMP);
    }

    /* Set up first buffer and local data buffer */
    A = digest[ 0 ];
    B = digest[ 1 ];
    C = digest[ 2 ];
    D = digest[ 3 ];
    E = digest[ 4 ];

    /* Heavy mangling, in 4 sub-rounds of 20 iterations each. */
#if SHA_CODE_SIZE == 0
    /*
     * Approximately 50% of the speed of the largest version, but
     * takes up 1/16 the space.  Saves about 6k on an i386 kernel.
     */
    for (i = 0; i < 80; i++) {
	if (i < 40) {
	    if (i < 20)
		TEMP = f1(B, C, D) + K1;
	    else
		TEMP = f2(B, C, D) + K2;
	} else {
	    if (i < 60)
		TEMP = f3(B, C, D) + K3;
	    else
		TEMP = f4(B, C, D) + K4;
	}
	TEMP += ROTL(5, A) + E + W[i];
	E = D; D = C; C = ROTL(30, B); B = A; A = TEMP;
    }

#else
#error Illegal SHA_CODE_SIZE
#endif

    /* Build message digest */
    digest[ 0 ] += A;
    digest[ 1 ] += B;
    digest[ 2 ] += C;
    digest[ 3 ] += D;
    digest[ 4 ] += E;

	/* W is wiped by the caller */
#undef W
}

#undef ROTL
#undef f1
#undef f2
#undef f3
#undef f4
#undef K1
#undef K2
#undef K3
#undef K4

#endif /* !USE_SHA */

/*********************************************************************
 *
 * Entropy extraction routines
 *
 *********************************************************************/

#define EXTRACT_ENTROPY_USER		1
#define EXTRACT_ENTROPY_SECONDARY	2
#define EXTRACT_ENTROPY_LIMIT		4
#define TMP_BUF_SIZE			(HASH_BUFFER_SIZE + HASH_EXTRA_SIZE)
#define SEC_XFER_SIZE			(TMP_BUF_SIZE*4)

static ssize_t extract_entropy(struct entropy_store *r, void * buf,
			       size_t nbytes, int flags);

/*
 * This utility inline function is responsible for transfering entropy
 * from the primary pool to the secondary extraction pool. We make
 * sure we pull enough for a 'catastrophic reseed'.
 */
static void xfer_secondary_pool(struct entropy_store *r,
				       size_t nbytes, __u32 *tmp)
{
 	if (r->entropy_count < nbytes * 8 &&
	    r->entropy_count < r->poolinfo.POOLBITS) {

		//min(nbytes, TMP_BUF_SIZE)
		int __temp_min = nbytes < TMP_BUF_SIZE  ?   nbytes: TMP_BUF_SIZE;
		//max(random_read_wakeup_thresh / 8, __temp_min)
		int bytes =	random_read_wakeup_thresh / 8 > __temp_min ? random_read_wakeup_thresh/8 : __temp_min;

		bytes=extract_entropy(random_state, tmp, bytes,
				      EXTRACT_ENTROPY_LIMIT);
		//here we fix the "uninitialized memory read" bug (see Section 6.3 in the thesis)
		add_entropy_words(r, tmp, bytes/4);//bytes);
		credit_entropy_store(r, bytes*8);
	}
}

/*
 * This function extracts randomness from the "entropy pool", and
 * returns it in a buffer.  This function computes how many remaining
 * bits of entropy are left in the pool, but it does not restrict the
 * number of bytes that are actually obtained.  If the EXTRACT_ENTROPY_USER
 * flag is given, then the buf pointer is assumed to be in user space.
 *
 * If the EXTRACT_ENTROPY_SECONDARY flag is given, then we are actually
 * extracting entropy from the secondary pool, and can refill from the
 * primary pool if needed.
 *
 * Note: extract_entropy() assumes that .poolwords is a multiple of 16 words.
 */
static ssize_t extract_entropy(struct entropy_store *r, void * buf,
			       size_t nbytes, int flags)
{
	ssize_t ret, i;

//2.6.10
	__u32 tmp[TMP_BUF_SIZE], data[16];//__u32 tmp[TMP_BUF_SIZE];

	__u32 x;
	unsigned long cpuflags;


	/* Redundant, but just in case... */
	if (r->entropy_count > r->poolinfo.POOLBITS)
		r->entropy_count = r->poolinfo.POOLBITS;

	if (flags & EXTRACT_ENTROPY_SECONDARY)
		xfer_secondary_pool(r, nbytes, tmp);

	if (flags & EXTRACT_ENTROPY_LIMIT && nbytes >= r->entropy_count / 8)
		nbytes = r->entropy_count / 8;

	if (r->entropy_count / 8 >= nbytes)
		r->entropy_count -= nbytes*8;
	else
		r->entropy_count = 0;

	ret = 0;
	while (nbytes) {
		/* Hash the pool to get the output */
		tmp[0] = 0x67452301;
		tmp[1] = 0xefcdab89;
		tmp[2] = 0x98badcfe;
		tmp[3] = 0x10325476;
#ifdef USE_SHA
		tmp[4] = 0xc3d2e1f0;
#endif
		/*
		 * As we hash the pool, we mix intermediate values of
		 * the hash back into the pool.  This eliminates
		 * backtracking attacks (where the attacker knows
		 * the state of the pool plus the current outputs, and
		 * attempts to find previous ouputs), unless the hash
		 * function can be inverted.
		 */
		for (i = 0, x = 0; i < r->poolinfo.poolwords; i += 16, x+=2) {
			HASH_TRANSFORM(tmp, r->pool+i);
			add_entropy_words(r, &tmp[x%HASH_BUFFER_SIZE], 1);
		}

//2.6.10
		/*
		 * To avoid duplicates, we atomically extract a
		 * portion of the pool while mixing, and hash one
		 * final time.
		 */
		__add_entropy_words(r, &tmp[x%HASH_BUFFER_SIZE], 1, data);
		HASH_TRANSFORM(tmp, data);

		/*
		 * In case the hash function has some recognizable
		 * output pattern, we fold it in half.
		 */
		for (i = 0; i <  HASH_BUFFER_SIZE/2; i++)
			tmp[i] ^= tmp[i + (HASH_BUFFER_SIZE+1)/2];
#if HASH_BUFFER_SIZE & 1	/* There's a middle word to deal with */
		x = tmp[HASH_BUFFER_SIZE/2];
		x ^= (x >> 16);		/* Fold it in half */
		((__u16 *)tmp)[HASH_BUFFER_SIZE-1] = (__u16)x;
#endif

		/* Copy data to destination buffer */

		//i = min(nbytes, HASH_BUFFER_SIZE*sizeof(__u32)/2)
		i = (nbytes < HASH_BUFFER_SIZE*sizeof(__u32)/2 ? nbytes : HASH_BUFFER_SIZE*sizeof(__u32)/2);

		//we work only in user space
		/*
		if (flags & EXTRACT_ENTROPY_USER) {
			i -= copy_to_user(buf, (__u8 const *)tmp, i);
			if (!i) {
				ret = -EFAULT;
				break;
			}
		} else
		*/

		memcpy(buf, (__u8 const *)tmp, i);

		nbytes -= i;
		(char*)buf += i;
		ret += i;
	}

	/* Wipe data just returned from memory */
	memset(tmp, 0, sizeof(tmp));

	return ret;
}

/*
 * This function is the exported kernel interface.  It returns some
 * number of good random numbers, suitable for seeding TCP sequence
 * numbers, etc.
 */
void get_random_bytes(void *buf, int nbytes)
{
//2.6.10
/*
	if (sec_random_state)
		extract_entropy(sec_random_state, (char *) buf, nbytes,
				EXTRACT_ENTROPY_SECONDARY);
	else if (random_state)
		extract_entropy(random_state, (char *) buf, nbytes, 0);
	else
	    printf("<KERN_NOTICE>: " "get_random_bytes called before "
				   "random driver initialization\n");
*/
	struct entropy_store *r = urandom_state;
	int flags = EXTRACT_ENTROPY_SECONDARY;

	if (!r)
		r = sec_random_state;
	if (!r) {
		r = random_state;
		flags = 0;
	}
	if (!r) {
		printf("<KERN_NOTICE>:" "get_random_bytes called before "
				   "random driver initialization\n");
		return;
	}
	extract_entropy(r, (char *) buf, nbytes, flags);
}



/************************************************
 *********** SIMULATION FUNCTIONS ***************
 ************************************************/


//---------PRINTING A POOL CONTENT-------------

void print_pool(struct entropy_store *r)
{
  __u32 *temp = r->pool;
  ssize_t i = 0;
  printf("printing %s pool (8 words in a line):\n%d-%d\t:",r->name,0,7);
  for (; i<r->poolinfo.poolwords; i++) {
    printf("%lx, ", *temp);
    temp++;
    if ( (i % 8 == 7) &&  (i+1<r->poolinfo.poolwords) )
    	printf("\n%d-%d\t:",i+1, (i+8 < r->poolinfo.poolwords-1 ? i+8 : r->poolinfo.poolwords-1));//min(i+8, r->poolinfo.poolwords-1));
  }
  printf("\n");
}

//---------PRINTING A POOL PARAMETERS-------
void print_pool_params(struct entropy_store *r)
{
  printf("printing the parameters of %s pool:\n",r->name);
  printf("add_ptr = %u, entropy_count = %d, input_rotate = %d\n\n",
	 r->add_ptr, r->entropy_count, r->input_rotate );
}

//--------RESET THE POOLS CONTENT AND PARAMETERS------------
void reset_pools()
{
  clear_entropy_store(random_state);
  clear_entropy_store(sec_random_state);
  clear_entropy_store(urandom_state);
}

//---------MAIN-------------------

int main (int argc, char** argv){

  __u32 in[1];		//the input
  int nwords = 1;	//the input size in words

  char buf[512];	//the user buffer
  size_t nbytes = 0,//number of bytes to extract
  n = 0;			//number of actually extracted bytes

  //local variables, used for the switch-case
  int choice;
  enum options { RESET_POOLS=1,
  				 PRINT_POOLS,
  				 PRINT_POOLS_PARAMS,
  				 ADD_ENTROPY,
  				 EXTRACT_ENTROPY_RANDOM,
  				 EXTRACT_ENTROPY_URANDOM,
  				 PRINT_USER_BUFFER,
  				 QUIT};

  char *b;
  int k;

  if (argc!=1){
	printf("Usage:\n");
	exit(1);
  }

//creating the pools
  if (create_entropy_store(DEFAULT_POOL_SIZE, "primary", &random_state))
    goto err;
  if (create_entropy_store(SECONDARY_POOL_SIZE, "secondary", &sec_random_state))
    goto err;
  if (create_entropy_store(SECONDARY_POOL_SIZE, "urandom", &urandom_state))
    goto err;
  reset_pools();

  //infinite loop
  while (1) {

	printf("LRNG user-mode simulator. Please choose one of the following options\n");

    printf(
	   "(%d) reset the pools' contents and parameters\n"
	   "(%d) print the pools\n"
	   "(%d) print the pools' parameters\n"
	   "(%d) add entropy (one word (32 bits) at a time)\n"
	   "(%d) extract entropy using /dev/random\n"
	   "(%d) extract entropy using /dev/urandom\n"
	   "(%d) print user buffer\n"
	   "(%d) quit\n",
	   RESET_POOLS, PRINT_POOLS, PRINT_POOLS_PARAMS,
  	   ADD_ENTROPY, EXTRACT_ENTROPY_RANDOM,
  	   EXTRACT_ENTROPY_URANDOM, PRINT_USER_BUFFER,
  	   QUIT);

    scanf("%d", &choice);

	//the options in the switch are mostly self-explained
	//one exception is the user-buffer printing:
	//after extracting entropy, the extracted entropy is copied to a user-buffer,
	//PRINT_USER_BUFFER enables observing its content
    switch(choice){

		case RESET_POOLS:
			reset_pools();
			break;

	    case PRINT_POOLS:
			print_pool(random_state);
			print_pool(sec_random_state);
			print_pool(urandom_state);
			break;

		case PRINT_POOLS_PARAMS:
			print_pool_params(random_state);
			print_pool_params(sec_random_state);
			print_pool_params(urandom_state);
			break;

		case ADD_ENTROPY:
			printf("Adding one word (32 bits) to the pools.\n"
			"Please enter an integer number\n");
			scanf("%d", in);
			add_entropy_words(random_state, in, nwords);
			//we credit the entropy_store in the exact ammount that I add to it
			//(we assume that the data we add is truly random)
			credit_entropy_store(random_state, nwords*32);
			break;

		case EXTRACT_ENTROPY_RANDOM:
			printf("Please enter an integer number (number of bytes to extract using /dev/random)\n");
			scanf("%u", &nbytes);
			n = nbytes;
			if (n > SEC_XFER_SIZE){
			  n = SEC_XFER_SIZE;
			}
			n = extract_entropy(sec_random_state, buf, n,
					EXTRACT_ENTROPY_USER |
					EXTRACT_ENTROPY_LIMIT |
					EXTRACT_ENTROPY_SECONDARY);
			printf("/dev/random extracted %d bytes,\n "
				   "to see the user buffer after extracting choose %d in the next iteration\n",
				   n, PRINT_USER_BUFFER);
			break;

		case EXTRACT_ENTROPY_URANDOM:
			printf("Please enter an integer number (number of bytes to extract using /dev/urandom)\n");
			scanf("%u", &nbytes);
			n = nbytes;
			n = extract_entropy(urandom_state, buf, n,
					 EXTRACT_ENTROPY_USER |
					 EXTRACT_ENTROPY_SECONDARY);
			printf("/dev/urandom extracted %d bytes,\n "
				   "to see the user buffer after extracting choose %d in the next iteration\n",
				   n, PRINT_USER_BUFFER);
			break;

		case PRINT_USER_BUFFER:
			if (n > 512)
				n = 512;
			printf("user buffer has %d bytes: \n", n);
			b = buf;
			k = 0;
			if (n > 0)
				printf("%d-%d\t:",0,(k+7 < n-1 ? k+7 : n-1));//min(k+7, n-1));
			for (; k<n; k++) {
				printf("%lx, ", *b);
				b++;
				if ( (k % 8 == 7) &&  (k+1<n) )
    				printf("\n%d-%d\t:",k+1, (k+8 < n-1 ? k+8 : n-1));//min(k+8, n-1));
			}
			printf("\n");
			break;

		case QUIT:
		default:
			goto cont;
    }
  }

 cont:
  //freeing the memory resources
  free_entropy_store(random_state);
  free_entropy_store(sec_random_state);
  free_entropy_store(urandom_state);

  printf("done\n");
  return 0;

 err:
  return -1;
}

