/*
 * random number daemon (randomd)
 *
 * cryptographic stuff (c) 1999 Andreas Steinmetz, astmail@yahoo.com
 * other code (c) 1999 D.O.M. Datenverarbeitung GmbH, author Andreas Steinmetz
 *
 * License:
 * This code is in the public domain under the GNU public license.
 * The copyright holders will however retain their copyright.
 * There is no guarantee for the fitness and usability of this code
 * for any purpose. The author and the copyright holders take no
 * responsibility for any damages caused by the use of this code.
 * Distribution and use of this code is explicitly granted provided
 * that the above header is not modified and the above conditions
 * are met.
 *
 * Important note: This code is by no means designed to be a perfect
 * random number generator. It lacks entropy, etc. It is just a fast
 * hack to supply at least some randomness to vpnd on linux 1.x
 * systems which miss the /dev/random device. If you have a better
 * solution: use it!!!
 *
 */

/* header files */

#include <stdio.h>
#include <sys/time.h>
#include <sys/times.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/ioctl.h>
#define BLOWFISH_NO_DECRYPT
#include "common.h"
#include "blowfish.h"

/* global variables */

int p=-1;			/* handle to named pipe			*/
int x=-1;			/* dummy handle to named pipe		*/
WORD08 intinfo[2048];		/* buffer for /proc/interrupts contents	*/
int infolen;			/* length of contents of above buffer	*/
BLOWFISH_SCHEDULE(s);		/* blowfish key schedule		*/
WORD08 iv[BLOWFISH_BLOCKSIZE];	/* iv for cfb mode encryption		*/

/*
 * setsig
 *
 * input: signal - the signal for which action is to be taken
 *taction - the action to be taken for the signal
 *
 * This procedure sets up a signal handler for the
 * desired signal which is sequentialized and
 * multiple signal aware.
 */

void setsig(int signal,void (*action)(int))
{
	struct sigaction act;   /* signal setup structure */


	/* preset signal setup structure */

	act.sa_handler=action;
	sigemptyset(&act.sa_mask);
	act.sa_flags=0;

	/* set signal handler */

	sigaction(signal,&act,NULL);
}

/*
 * die
 *
 * This procedure closes open handles and then just terminates the process.
 */

void die(int dummy)
{
	/* close all open handles */

	if(p!=-1)close(p);
	if(x!=-1)close(x);

	/* terminate */

	exit(1);
}

void readinfo(void)
{
	int f;	/* file handle	*/


	/* open /proc/interrupts, terminate in case of failure */

	if((f=open("/proc/interrupts",O_RDONLY))==-1)die(0);

	/* read contents of /proc/interrupts */

	infolen=read(f,intinfo,sizeof(intinfo));

	/* close /proc/interrupts */

	close(f);

	/* terminate in case of read error */

	if(infolen==-1)die(0);
}

/*
 * encrypt_cfb_8
 *
 * input: total - the amount of data to be encrypted
 *
 * inout: data - the date to be encrypted
 *
 * This procedure encrypts the given amount of data in cfb8 mode
 */

void encrypt_cfb_8(WORD08 *data,WORD32 total)
{
	register WORD32 i;		/* general purpose usage	*/
	register WORD08 *ptr;		/* buffer fast access		*/
	register WORD08 mem;		/* general purpose memory	*/
	WORD08 bfr[BLOWFISH_BLOCKSIZE];	/* encryption buffer		*/


	/* copy iv to buffer */

	for(i=0,ptr=bfr;i<BLOWFISH_BLOCKSIZE;i++)*ptr++=iv[i];

	/* increase iv */

	for(mem=1,i=0,ptr=iv;mem&&i<BLOWFISH_BLOCKSIZE;i++)
	{
		*ptr+=mem;
		mem=*ptr++?0:1;
	}

	/* encrypt using CFB8 */

	while(total--)
	{
		/* encrypt buffer */

		blowfish_encrypt(bfr,s);

		/* XOR data byte with buffer */

		*data^=*bfr;

		/* shift iv left and append cipher byte */

		for(i=0,ptr=bfr+1;i<BLOWFISH_BLOCKSIZE-1;i++)bfr[i]=*ptr++;
		bfr[i]=*data++;
	}
}

/*
 * main
 *
 * This procedure it the random number generator.
 */

void main(void)
{
	int i;			/* general purpose		*/
	time_t t;		/* current time			*/
	clock_t u;		/* clock ticks since boot	*/
	struct tms dummy;	/* unused			*/


	/* make sure only root starts the daemon */

	if(getuid()||geteuid()||getgid()||getegid())
	{
		printf("must be root:root to start randomd\n");
		die(0);
	}

	/* set up signal handling */

	for(i=1;i<_NSIG;i++)setsig(i,SIG_IGN);
	setsig(SIGINT,die);
	setsig(SIGTERM,die);

	/* create named pipe, ignore errors */

	mknod("/dev/randomd",S_IFIFO|0644,0);

	/* dummy open of named pipe to allow WRONLY access */

	if((x=open("/dev/randomd",O_RDWR|O_NONBLOCK))==-1)
	{
		printf("can't open random pipe\n");
		die(0);
	}

	/* actual open of named pipe */

	if((p=open("/dev/randomd",O_WRONLY))==-1)
	{
		printf("can't open random pipe\n");
		die(0);
	}

	/* get some entropy from /proc/interrupts */

	readinfo();

	/* terminate if insufficient entropy */

	if(infolen<BLOWFISH_MAXKEY)
	{
		printf("insufficient data from /proc/interrupts\n");
		die(0);
	}

	/* merge data read */

	for(i=BLOWFISH_MAXKEY;i<infolen;i++)
		intinfo[i%BLOWFISH_MAXKEY]^=
			(intinfo[i]<<(8-(i&7)))|(intinfo[i]>>(i&7));

	/* create key schedule from entropy */

	blowfish_key_schedule(intinfo,BLOWFISH_MAXKEY,s);

	/* create initial iv */

	t=time(NULL);
	u=times(&dummy);

	iv[0]=(WORD08)(t);
	iv[1]=(WORD08)(t>>8);
	iv[2]=(WORD08)(t>>16);
	iv[3]=(WORD08)(t>>24);
	iv[4]=(WORD08)(u);
	iv[5]=(WORD08)(u>>8);
	iv[6]=(WORD08)(u>>16);
	iv[7]=(WORD08)(u>>24);

	/* just wait a bit */

	usleep(987654);

	/* detach from console */

	switch(fork())
	{
		case 0: break;
		case -1:printf("can't fork\n");
			die(0);
		default:die(0);
	}

	switch(fork())
	{
		case 0: break;
		case -1:printf("can't fork\n");
			die(0);
		default:die(0);
	}

	/* remove any controlling terminal */

	i=1;
	ioctl(0,TIOCNOTTY,&i);

	/* close possibly open handles */

	for(i=0;i<256;i++)if(i!=p&&i!=x)close(i);

	/* start up processing */

	while(1)
	{
		/* read next status from /proc/interrupts */

		readinfo();

		/* encrypt result in cfb8 mode */

		encrypt_cfb_8(intinfo,infolen);

		/* write data to /dev/randomd, handle errors */

		if(write(p,intinfo,infolen)==-1)die(0);
	}
}
