/* $Id: randpool.c,v 1.8 1998/01/26 01:05:35 futo Exp $
 * Cryptographically strong Random Pool
 *
 * Author: Ariel Futoransky, Core-SDI
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include "blf.h"

#define RANDPOOL_SIZE (1024)
#define RANDPOOL_KEYLEN (16)
#define RANDPOOL_BUFSIZE (RANDPOOL_SIZE+RANDPOOL_KEYLEN)
#define RANDPOOL_SAVETIME (60*5)	/* 5 minutes */
#define RANDPOOL_ADDTIME (60*3)	/* 3 Minutes */

struct randpool
	{
		unsigned char *buf;
		int len, pos, addpos;
		int gets, puts;
		blf_ctx c;
		time_t at, st;

		int fd;
	}
r;

void
rand_hash ()
{
	int i, v1, v2;
	int *b1, *b2;

	b1 = (int *) (r.buf + r.len - 8);
	b2 = (int *) (r.buf + r.len - 4);
	v1 = *b1;
	v2 = *b2;

	for (i = 0; i < r.len + RANDPOOL_KEYLEN; i += 8)
		{
			b1 = (int *) (r.buf + i);
			b2 = (int *) (r.buf + i + 4);

			v1 ^= *b1;
			v2 ^= *b2;
			Blowfish_encipher (&r.c, (u_int32_t *) & v1, (u_int32_t *) & v2);
			*b1 ^= v1;
			*b2 ^= v2;
		}
	blf_key (&r.c, (u_int8_t *) r.buf, RANDPOOL_KEYLEN);
}

void
rand_add (buf, len)
		 char *buf;
		 int len;
{
	int i;

	time (&r.at);
	for (i = 0; i < len; i++)
		{
			if (r.addpos >= r.len + RANDPOOL_KEYLEN)
				{
					r.addpos = 0;
					rand_hash ();
				}
			r.buf[r.addpos++] ^= buf[i];
			r.puts++;
		}
	if (r.addpos != 0)
		rand_hash ();
	if (time (NULL) - r.st > RANDPOOL_SAVETIME)
		rand_save ();
}

int
rand_get (buf, len)
		 char *buf;
		 int len;
{
	while (len > 0)
		{
			*(buf++) = r.buf[r.pos++];
			len--;
			r.gets++;
			if (r.pos > r.len)
				{
					rand_hash ();
					r.pos = 0;
				}
		}
	if (time (NULL) - r.st > RANDPOOL_SAVETIME)
		rand_save ();
}

int
rand_need ()
{
	return (time (NULL) - r.at > RANDPOOL_ADDTIME);
}

int
rand_save ()
{
	lseek (r.fd, 0, SEEK_SET);
	write (r.fd, r.buf, r.len + RANDPOOL_KEYLEN);
	time (&r.st);
}

int
rand_open (filename)
		 char *filename;
{
	char buf[RANDPOOL_BUFSIZE];
	int len;

	if ((r.buf = malloc (RANDPOOL_SIZE + RANDPOOL_KEYLEN)) == NULL)
		return (-1);

	r.len = RANDPOOL_SIZE;
	r.pos = 0;
	r.addpos = 0;
	blf_key (&r.c, "DEFAULT", 7);
	if ((r.fd = open (filename, O_RDWR)) < 0)
		{
			if (errno == ENOENT)
				{
					if ((r.fd = open (filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR)) < 0)
						return (-1);
					printf ("Creating randpool, please enter some random characters.\n");
					printf ("enter to finish.\n");
					len = read (0, buf, RANDPOOL_BUFSIZE);
					rand_add ((char *) buf, len);
					rand_hash ();
					rand_save ();
				}
			else
				return (-1);
		}
	else
		{
			len = read (r.fd, buf, RANDPOOL_BUFSIZE);
			rand_add (buf, len);
			rand_hash ();
		}
	return (0);
}

void
rand_close ()
{
	rand_save ();
	close (r.fd);
	free (r.buf);
	r.len = 0;
	r.fd = -1;
}
