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 ); }