/* Encrypt/decrypt command compatible with Sun's "des" command */
#include <stdio.h>

char iv[8];	/* Initial vector for CBC mode */
int block;

main(argc,argv)
int argc;

char *argv[];

{
	int c,cnt,encrypt,decrypt,hexflag,help,outfile, infile;
	register int i;
	char key[8],tkey1[20],tkey2[20],*akey,*getpass(), *inname, *outname;
	extern char *optarg;
	FILE	*instreme = stdin;		
	FILE	*outstreme = stdout;	
	FILE 	*fp, *fp2, *fopen();

	hexflag = infile = help = outfile = block = encrypt = decrypt = 0;
	akey = NULL;
	while((c = getopt(argc,argv,"hedk:bi:o:?")) != EOF){
		switch(c){
		case 'h':
			hexflag++;
			break;
		case 'e':
			encrypt++;
			break;
		case 'd':
			decrypt++;
			break;
		case 'k':
			akey = optarg;
			break;
		case 'b':
			block++;
			break;
		case 'i':
			infile++;
			inname = optarg;
			break;
		case 'o':
			outfile++;
			outname = optarg;
			break;
		case '?':
			help++;
			break;
		}
	}
	if((encrypt == 0 && decrypt == 0) || (akey == NULL)||(help)){
		fprintf(stderr,"Usage: des -e|-d [-b] [-h] -k key [-i infile] [-o outfile] [-?]\n");
		if(help){
			printf("\nArgs:\n-e To encrypt\t-d To decrypt\n");
			printf("-b Electronic Code Book -- uses 8 byte chunks to encrypt\n");
			printf("\tSlight weaker than default CBC but useful for data with errors.\n");
			printf("\n-h Hex input\t-k 8 byte key\n");
			printf("-i Inputfile\t-o Outputfile\n");
			printf("\t optional -- stdin and/or stdout fine.\n");
			printf("-? This help message\n\n");
		}
		exit(2);
	}
	
	if(hexflag){
		for(i=0;i<16;i++){
			if(htoa(akey[i]) == -1){
				fprintf(stderr,"Non-hex character in key\n");
				exit(1);
			}
		}
		gethex(key,akey,8);
	} else {
		strncpy(key,akey,8);
		/* Set up key, determine parity bit */
		for(cnt = 0; cnt < 8; cnt++){
			c = 0;
			for(i=0;i<7;i++)
				if(key[cnt] & (1 << i))
					c++;
			if((c & 1) == 0)
				key[cnt] |= 0x80;
			else
				key[cnt] &= ~0x80;
		}
	}
	/*tacked on file junk*/
	if(infile){
		if((fp=fopen(inname, "r")) != NULL)
			instreme = fp;
		
		else {
			fprintf(stderr,"Input file not opened.\n");
			exit(1);
		}
	}
	if(outfile) {
		if((fp2=fopen(outname,"w")) !=NULL)
			outstreme = fp2;
		else {
			fprintf(stderr,"Output file not opened.\n");
			exit(1);
		}
	}
	/* Blot out original key */
	i = strlen(akey);
	i = (i < 8) ? i : 8;
	memset(akey,0,i);
	
	desinit(0);
	setkey(key);

	/* Initialize IV to all zeros */
	memset(iv,0,8);

	if(encrypt){
		doencrypt(instreme,outstreme);
	}
	else {
		dodecrypt(instreme,outstreme);
	}
	if(infile)
		fclose(fp);
	if(outfile)
		fclose(fp2);
}
/* Encrypt standard input to standard output */
doencrypt(instreme,outstreme)
FILE *instreme, *outstreme;

{
	char work[8],*cp,*cp1;
	int cnt,i;

	for(;;){
		if((cnt = fread(work,1,8,instreme)) != 8){
			/* Put residual byte count in the last block.
			 * Note that garbage is left in the other bytes,
			 * if any; this is a feature, not a bug, since it'll
			 * be stripped out at decrypt time.
			 */
			work[7] = cnt;
		}
		if(!block){
			/* CBC mode; chain in last cipher word */
			cp = work;
			cp1 = iv;
			for(i=8; i!=0; i--)
				*cp++ ^= *cp1++;
		}
		endes(work);	/* Encrypt block */
		if(!block){	/* Save outgoing ciphertext for chain */
			memcpy(iv,work,8);
		}
		fwrite(work,1,8,outstreme);
		if(cnt != 8)
			break;
	}
}
dodecrypt(instreme,outstreme)
FILE *instreme, *outstreme;

{
	char work[8],nwork[8],ivtmp[8],*cp,*cp1;
	int cnt,i;


	cnt = fread(work,1,8,instreme);	/* Prime the pump */
	for(;;){
		if(!block){	/* Save incoming ciphertext for chain */
			memcpy(ivtmp,work,8);
		}
		dedes(work);
		if(!block){	/* Unchain block, save ciphertext for next */
			cp = work;
			cp1 = iv;
			for(i=8; i!=0; i--){
				*cp++ ^= *cp1++;
			}
			memcpy(iv,ivtmp,8);
		}
		/* Save buffer pending next read */
		memcpy(nwork,work,8);
		/* Try to read next block */
		cnt = fread(work,1,8,instreme);
		if(cnt != 8){	/* Can "only" be 0 if not 8 */
			/* Prev block was last one, write appropriate number
			 * of bytes
			 */
			cnt = nwork[7];
			if(cnt < 0 || cnt > 7){
				fprintf(stderr,"Corrupted file or wrong key\n");
			} else if(cnt != 0)
				fwrite(nwork,1,cnt,outstreme);
			exit(0);
		} else {
			/* Now okay to write previous buffer */
			fwrite(nwork,1,8,outstreme);
		}

	}
}
/* Convert hex/ascii nybble to binary */
int
htoa(c)
char c;
{
	if(c >= '0' && c <= '9')
		return c - '0';
	if(c >= 'a' && c <= 'f')
		return 10 + c - 'a';
	if(c >= 'A' && c <= 'F')
		return 10 + c - 'A';
	return -1;
}
/* Convert bytes from hex/ascii to binary */
gethex(result,cp,cnt)
register char *result;
register char *cp;
register int cnt;
{
	while(cnt-- != 0){
		*result = htoa(*cp++) << 4;
		*result++ |= htoa(*cp++);
	}
}
#ifdef	DEBUG
put8(cp)
register char *cp;
{
	int i;

	for(i=0;i<8;i++){
		fprintf(stderr,"%02x ",*cp++ & 0xff);
	}
}
#endif








