/* This code is released into the public domain under the GNU Public
	License. The MD5 and SHS routines retain the rights of their
	respective authors.

	This code was written by Michael Graffam (mgraffam@mhv.net).
	It is pretty dirty, but shows the basics of implementing a
	deniable encryption system based on Ron Rivest's
	chaffing/winnowing concept.
*/

#include <stdio.h>
#include <stdlib.h>
#include "shs.h"
#include "md5.h"

void help(void);
int chaf(char hash, char *passphrase, int i, char *filename, char *rand_path, int ratio);

int main(int argc, char **argv)
{
	int i;
	int ratio = 1;
	char hash_function = 's';
	char rand_path[256] = "/dev/urandom";
	char filename[256] = "";
	char passphrase[1024];
	FILE *fp;
	for (i=1; i<=argc-1; i++)
	{
		if ( argv[i][0]=='-' )
		{
			switch (argv[i][1])
			{
				case 'h':
					if ( (argv[i][2]=='s') || (argv[i][2]=='5') )
						hash_function=argv[i][2];
					break;
				case 'r':
					strcpy(rand_path,argv[i+1]);
					break;
				case 'a':
					strcpy(passphrase,argv[i+1]);
					break;
				case 'f':
					strcpy(filename,argv[i+1]);
					break;
				case 'm':
					ratio=atoi(argv[i+1]);
					break;
				default:
					fprintf(stderr,"Bad option: %s\n", argv[i]);
					help();
					return -1;
			}
		}
	} /* end parsing of option */
	if (passphrase[0]==0)
	{
		if ( (fp=fopen(rand_path,"r")) == 0)
		{
			fprintf(stderr,"Unable to open random numbers.\n");
			help();
			return 0;
		}
		i=fread(passphrase, 1, 256, fp);
		passphrase[256]=0;
		fclose(fp);
	}
	else
		i = strlen(passphrase);

	chaf(hash_function, passphrase, i, filename, rand_path, ratio);
} /* end of main */
void help(void)
{
	fprintf(stderr,"Valid options:\n");
	fprintf(stderr,"     -hN             selects hash to use (s = SHA, 5 = MD5). Default: SHA\n");
	fprintf(stderr,"     -r rand_path    defines the path to a file of random number used for chaf.\n");
        fprintf(stderr,"                     Default: /dev/urandom\n");
	fprintf(stderr,"     -a phrase       Defines passphrase when using valid MACs\n");
	fprintf(stderr,"     -f filename     Defines the fieldfile to use.\n");
	fprintf(stderr,"                     Default: none.\n");
	fprintf(stderr,"     -m ratio        Define the modulus for chaf to wheat ratio\n");
	fprintf(stderr,"\n");
	fprintf(stderr,"     Input is always from stdin, output is always stdout.\n\n");
	fprintf(stderr,"Example: chaf -hs -a password -f secret.field < secret.doc > new.field\n");
	fprintf(stderr,"This will use SHA with 'password' as the MAC phrase.\n");
	fprintf(stderr,"secret.doc will get encoded into new.field with secret.field acting as chaf.\n");
	fprintf(stderr,"Documents in secret.field can still be retrieved from new.field.\n\n");
 }
int chaf(char hash, char *passphrase, int passlen, char *filename, char *randpath, int ratio)
{
	FILE *fp, *randnums;
	int i,j;
	unsigned long seq = 1;
	char *buffer;
	char *buffer1;
#define blocksize 1
	char r;
	int hashsize;
	SHS_INFO shsInfo; /* data for SHA */
	MD5_CTX  md5Info; /* data for MD5 */

	if ( hash=='s' )
		hashsize=20; /* SHA hash size = 160 bits */
	if ( hash=='5' )
		hashsize=16; /* MD5 hash size = 128 bits */

	if ( filename[0] != 0)
		if ( (fp=fopen(filename,"r")) == 0)
		{
			fprintf(stderr, "Unable to open file %s\n", filename);
			help();
			return 0;
		}
	if ( (randnums=fopen(randpath,"r")) == 0)
	{
		fprintf(stderr,"Unable to open random numbers.\n");
		help();
		return 0;
	}
	buffer=(char *)malloc(blocksize+hashsize+sizeof(long));
	buffer1=(char *)malloc(blocksize+hashsize+sizeof(long)+passlen);
	while ( (!feof(stdin)) || (!feof(fp)) )
	{
		if ( (i=fread(buffer, 1, blocksize, stdin)) )
		{
			fwrite(buffer,1,i,stdout);
			if (hash=='s') {
				shsInit(&shsInfo);
				shsUpdate(&shsInfo, buffer, blocksize);
				shsUpdate(&shsInfo, passphrase, passlen);
				shsUpdate(&shsInfo, &seq, sizeof(long));
				shsFinal(&shsInfo);
				fwrite(shsInfo.digest, 1, hashsize, stdout);
			} else {
				MD5Init(&md5Info);
				MD5Update(&md5Info, buffer, blocksize);
				MD5Update(&md5Info, passphrase, passlen);
				MD5Update(&md5Info, &seq, sizeof(long));
				MD5Final(&md5Info);
				fwrite(md5Info.digest, 1, hashsize, stdout);
			}
			seq++;
			fread(&r,1, 1, randnums);
			r=((r%ratio)+2);
			for (i=0; i<=r; i++) 
			{
				j=fread(buffer,1,blocksize+hashsize,randnums);
				fwrite(buffer,1,j,stdout);
			}
		}
		if ( (j=fread(buffer,1,blocksize+hashsize,fp)) )
			fwrite(buffer,1,j,stdout);
			
	}
	if (fp)
		fclose(fp);
	free(buffer);	
}
