[net.sources] Net Mail Encryption - cypher.c

tracy (01/13/83)

/*
 * cypher - encrypt messages
 *
 * cypher implements a four rotor enigma machine with 94 element rotors.
 * It is intended to be used to encrypt network mail so that it will not
 * be routinely decrypted.
 *
 * Disclaimer:
 *
 *	No warranty of any kind is made concerning any use of this program.
 *	The author assumes no responsibility for it's use or for any problems
 *	arising from it's use by any recipient.
 *
 * Use:
 *
 *	cypher [-re[k key]] [file] . . .
 *	rcypher . . .
 *
 * Description:
 *
 *	Copy standard input to standard output, performing an encryption
 *	similar to crypt(1) with the following differences:
 *
 *		- only printing character including space and excluding 
 *		  newline
 *		  and tilde are encrypted (into the same set).
 *		- maximun of 512 byte lines (including \n) are allowed
 *		- if cypher produces a line beginning "From " while encrypting
 *		  it will prepend a tilde to the line.
 *		- there are four full rotors and one half rotor.
 *		- encryption occurs only on text on lines between (and not
 *		  including) lines beginning "[cypher]" and "[clear]".
 *
 *	If a filename is given, reads from the file instead of stdin.
 *
 * Options:
 *
 *	-r	causes cypher to accept a list of files and replace them
 *		with their encrypted form.  Any number of filenames may be
 *		used with this option.
 *
 *	-e	start processing the file in encryption mode.
 *
 *	-k key	specify a key  (otherwise you are asked for it)
 */

#include <stdio.h>

#define RS	94
#define RN	4
#define TEMPLATE	"#TC.XXXXXX"
#define RMASK	0x7fff	/* use only 15 bits */

#define LINESIZE	512
#define START	"[cypher]"
#define STOP	"[clear]"

char r[RS][RN];		/* rotors */
char ir[RS][RN];	/* inverse rotors */
char h[RS];		/* half rotor */
char s[RS];		/* shuffle vector */
int  p[RN];		/* rotor indices */

char tempfile[512];

int cypher, gotkey, gotfile, replacefile;

char *lock;

main( argc, argv)
char **argv;
{
	char *argp;
	char *mktemp(), *lastname(), *strcpy();
	int i;

	if ( *lastname( argv[0]) == 'r' )
		replacefile = 1;

	for ( i = 1; i < argc; i++ ) {
		argp = argv[i];
		if ( *argp == '-' ) {
			argp++;
			for ( ; *argp != '\0'; argp++ ) {
				switch ( *argp ) {
				case 'e':
					cypher = 1;
					break;
				case 'r':
					replacefile = 1;
					break;
				case 'k':
					if ( ++i < argc ) {
						gotkey = 1;
						argp = argv[i];
						makekey( argp);
						while ( *argp ) {
							*argp = '\0';
							argp++;
						}
						argp--;
					}
					else
						yell( -1, "Need a key following -k\n");
					break;
				default:
					yell( -1, "Bad flag:  -%c\n", *argp);
					break;

				}
			}
		}
		else {

			/* argument is a file.  process it */

			if ( NULL == freopen( argp, "r", stdin) ) {
				yell( -1, "Cannot open %s\n", argp);
			}
			else {
				if ( gotfile && !replacefile )
					yell( 4, "Only one file allowed\n");

				if ( replacefile ) {
					strcpy( tempfile, argp);
					strcpy( lastname( tempfile), mktemp( TEMPLATE));
					if ( NULL == freopen( tempfile, "w", stdout) )
						yell( 4, "Cannot open a tempfile!\n");
				}

				passfile();

				if ( replacefile ) {
					if ( unlink( argp) == -1 )
						yell( 8, "Strange unlink botch\n");
					if ( link( tempfile, argp) == -1 )
						yell( 8, "Can't link tempfile:  %s\n", tempfile);
					if ( unlink( tempfile) == -1 )
						yell( -1, "I didn't unlink the temp file\n");
				}
			}
			gotfile = 1;
		}
	}

	if ( !gotfile ) {
		if ( replacefile )
			yell( 4, "need filenames with -r flag\n");
		passfile();
	}

	exit ( 0 );
}

passfile()
{
	register int i;
	register j, ph = 0;
	char *getpass(), sgetchar(), sputchar();
	static char keybuffer[9];

	while ( !gotkey ) {
		strcpy( keybuffer, getpass( "Enter key: "));
		if ( strcmp( keybuffer, getpass( "Once more: "))) {
			yell( -1, "Your keys were not the same\n");
			continue;
		}
		makekey( keybuffer);
		gotkey = 1;
	}

	setup();

	while( ( i = sgetchar()) != EOF ) {
		if ( cypher ) {
			if ( (i >= ' ') && (i < '~') ) {
				i -= ' ';

				for ( j = 0; j < RN; j++ )		/* rotor forwards */
					i = r[(i+p[j])%RS][j];

				i = ((h[(i+ph)%RS])-ph+RS)%RS;	/* half rotor */

				for ( j--  ; j >= 0; j-- )		/* rotor backwards */
					i = (ir[i][j]+RS-p[j])%RS;

				j = 0;							/* rotate rotors */
				p[0]++;
				while ( p[j] == RS ) {
					p[j] = 0;
					j++;
					if ( j == RN )
						break;
					p[j]++;
				}

				if ( ++ph == RS )
					ph = 0;

				i += ' ';
			}
		}
		sputchar(i);
	}

}

char		
sgetchar()
{
	static char buffer[LINESIZE], *bP;
	char nextchar;

	while ( bP == (char *) 0 ) {
		if ( NULL == fgets( buffer, LINESIZE, stdin))
			return ( EOF );
		if ( *buffer == '[' ) {
			if ( !strncmp( buffer, START, strlen( START))) {
				fputs( buffer, stdout);
				cypher = 1;
				continue;
			}
			if ( !strncmp( buffer, STOP, strlen( STOP))) {
				fputs( buffer, stdout);
				cypher = 0;
				continue;
			}
		}
		bP = buffer;
	}

	nextchar = *bP;
	if ( *(++bP) == '\0' ) {
		if ( *(bP - 1) != '\n' ) {
			yell( 1, "line in text is too long\n");
		}
		bP = (char *) 0;
	}
	return ( nextchar );

}

char
sputchar( out)
	char out;
{
	static char obuffer[LINESIZE], *obP = obuffer;

	*(obP++) = out;
	if ( out == '\n' ) {
		if ( cypher && !strncmp( obuffer, "From ", 5)) {
			if ( obP - obuffer < LINESIZE )
				putc( '~', stdout);	/* prepend a wiggle */
			else
				yell( 1, "line with \"From \" is too long\n");
		}
		*(obP++) = '\0';
		fputs( obuffer, stdout);
		obP = obuffer;
	}
}

makekey( rkey)
	char *rkey;
{
	char key[8], salt[2], *crypt();

	strncpy( key, rkey, 8);
	salt[0] = key[0];
	salt[1] = key[1];
	lock = crypt( key, salt);
}

/*
 * shuffle rotors.
 * shuffle each of the rotors indiscriminately.  shuffle the half-rotor
 * using a special obvious and not very tricky algorithm which is not as
 * sophisticated as the one in crypt(1) and Oh God, I'm so depressed.
 * After all this is done build the inverses of the rotors.
 */

setup()
{
	register long i, j, k, temp;
	long seed;

	for ( j = 0; j < RN; j++ ) {
		p[j] = 0;
		for ( i = 0; i < RS; i++ )
			r[i][j] = i;
	}

	seed = 123;
	for ( i = 0; i < 13; i++)			/* now personalize the seed */
		seed = (seed*lock[i] + i) & RMASK;

	for ( i = 0; i < RS; i++ )		/* initialize shuffle vector */
		h[i] = s[i] = i;

	for ( i = 0; i < RS; i++) {		/* shuffle the vector */
		seed = (5 * seed + lock[i%13]) & RMASK;;
		k = ((seed % 65521) & RMASK) % RS;
		temp = s[k];
		s[k] = s[i];
		s[i] = temp;
	}

	for ( i = 0; i < RS; i += 2 ) {	/* scramble the half-rotor */
		temp = h[s[i]];			/* swap rotor elements ONCE */
		h[s[i]] = h[s[i+1]];
		h[s[i+1]] = temp;
	}

	for ( j = 0; j < RN; j++) {			/* select a rotor */

		for ( i = 0; i < RS; i++) {		/* shuffle the vector */
			seed = (5 * seed + lock[i%13]) & RMASK;;
			k = ((seed % 65521) & RMASK) % RS;
			temp = r[i][j];
			r[i][j] = r[k][j];
			r[k][j] = temp;
		}

		for ( i = 0; i < RS; i++) {		/* create inverse rotors */
			ir[r[i][j]][j] = i;
		}
	}
}

char *
lastname( name)
	char *name;
{
	char *where, *rindex();
	where = rindex( name, '/');
	return ( where ? (where + 1) : name );
}

/*VARARGS2*/
yell( code, fmt, args)
	char *fmt;
	int code;
{
	fprintf( stderr, "cypher:  ");
	_doprnt( fmt, &args, stderr);

	if ( code != -1 )
		exit ( code );
}