
/*
 *  ENCODE  key file file file ... file 	encrypt specified files
 *  DECODE  key file file file ... file 	decrypt specified files
 *
 *  Aztec Compilation please.  Use the +L option (32 bit ints).
 *
 *  Both are the same program.	The program looks at Arg0 to tell whether
 *  it is encrypting or decrypting.  If arg0 is one of "uUdD" it decrypts,
 *  else encrypts.  Files are a couple of bytes larger after encryption.
 *
 *  The method used is a variation of the DES algorithm.  Take your
 *  everyday DES, remove the 32->48->32 submaster permutation core, and
 *  add a dynamically changing permutation table... simple.
 *
 *  Feel free to modify the algorithm!
 *
 *				Matthew Dillon
 *
 *				...!ihnp4!ucbvax!dillon
 */

#include <stdio.h>
#include <fcntl.h>

#define FUNC(b48,b32)	(b48.b[0] ^ b32)

typedef unsigned long ulong;
typedef unsigned short uword;
typedef unsigned char ubyte;

typedef ulong	B32;
typedef struct { ulong b[2]; } B48;
typedef struct { ulong b[2]; } B56;
typedef struct { ulong b[2]; } B64;

extern ubyte *malloc();

/*
 *  Top level permutation (sets of 64 bits).  Mix it up as much as you
 *  want!
 */

static ubyte Perm[64] = {
     9,57,25,38,13,31,44,32,11,29, 8, 5,37,16,40,62,41, 0,53,47,48,22,24,
    54,45,39,52,34,43,12,63,36,10,55,51,33,28, 3,50,58,35,61,21, 6,26, 2,
    19,23,17, 1,27,42,49,14,56,60,18, 4, 7,20,59,46,15,30
};

static ubyte Iperm[64];
B48 SubMaster[16];	/*  The SUB-MASTER keys */
ubyte	Decrypt;

main(ac,av)
char *av[];
{
    register short i;
    register ubyte *buf;
    long len, elen, cnt;
    B64 key;
    ubyte padchar;

    if (av[0][0] == 'd' || av[0][0] == 'D' || av[0][0] == 'u' || av[0][0] == 'U')
	Decrypt = 1;
    if (ac == 1) {
	puts("DES key [files...]");
	exit(1);
    }
    bzero(&key,sizeof(key));
    bmov(av[1],&key,((strlen(av[1])>sizeof(key))?sizeof(key):strlen(av[1])));
    makeiperm();
    makesubmaster(&key);
    for (i = 2; i < ac; ++i) {
	int fd = open(av[i], O_RDWR);
	if (fd > 0) {
	    /*
	     *	We encrypt at least one more byte than the filesize, and
	     *	additionaly pad to 8 byte boundries.  This last byte is
	     *	the 'pad' byte and is different from the last byte in the
	     *	file... thus allowing the decrypt to restore the file
	     *	completely.
	     */

	    len = lseek(fd, 0L, 2);
	    elen = (len + 8) & ~7;
	    if (buf = malloc(elen)) {
		lseek(fd, 0L, 0);
		read(fd, buf, len);
		close(fd);
		padchar = buf[len-1] + 1;
		bset(buf+len,elen-len,padchar);
		for (cnt = 0; cnt < elen; cnt += 8)
		    cypher(buf+cnt);
		fd = open(av[i], O_WRONLY|O_CREAT|O_TRUNC);
		if (fd >= 0) {
		    if (Decrypt) {
			padchar = buf[(elen=len)-1];
			while (elen && buf[elen-1] == padchar)
			    --elen;
		    }
		    write(fd, buf, elen);
		} else {
		    printf("Unable to reopen for write: %s\n", av[i]);
		}
	    } else {
		printf("Unable to allocate memory for %s\n", av[i]);
	    }
	    close(fd);
	} else {
	    printf("Unable to open %s\n", av[i]);
	}
    }
}

makeiperm()
{
    register short i;
    for (i = 0; i < 64; ++i)
	Iperm[Perm[i]] = i;
}

cypher(b64)
B64 *b64;
{
    B64 Tmp;
    register B32 A,B,T;
    register short i;
    ulong dmod;

    for (i = 0; i < 64; ++i)
	setbit(&Tmp,i,getbit(b64,Perm[i]));
    A = Tmp.b[0];
    B = Tmp.b[1];

    if (Decrypt) {
	for (i = 15; i >= 0; --i) {
	    T = B;
	    B = A;
	    A = T ^ FUNC(SubMaster[i],A);
	}
    } else {
	for (i = 0; i < 16; ++i) {
	    T = A;
	    A = B;
	    B = T ^ FUNC(SubMaster[i],B);
	}
    }
    dmod = (Tmp.b[0] ^ A) >> 8;
    Tmp.b[0] = A;
    Tmp.b[1] = B;
    for (i = 0; i < 64; ++i)
	setbit(b64,i,getbit(&Tmp,Iperm[i]));

    /*
     *	Swap two entries in the Perm/IPerm array depending on dmod:
     */

    {
	short a = dmod & 63;
	short b = (dmod >> 6) & 63;
	short t;

	t = Perm[a]; Perm[a] = Perm[b]; Perm[b] = t;
	a = Perm[a]; b = Perm[b];
	t = Iperm[a]; Iperm[a] = Iperm[b]; Iperm[b] = t;
    }
}

/*
 *  Generate SubMaster keys
 */

makesubmaster(key)
B64 *key;
{
    static ubyte smiperm[64] = {    /*	initial sub-master permutation	*/
	 5,40,29,53, 2,39,25,42,46,35,23,16,49,62,34,63,17, 7,55,11,19,32,
	28, 4, 9, 3,22,21,38,41,20,58,45,37,24,36,57,15,26,54,10, 0,47,14,
	60,12,43, 6, 1,30,31,51,48, 8,44,13,50,61,59,52,18,33,27,56
    };
    static ubyte smselect[48] = {   /*	sub-master select bits		*/
	39,13,15, 4,45,32, 8,62,46,41,20,42,52,54,28, 5,25, 0,31, 2,
	30,36,23, 1,56,37,43,50,34,10, 9,55,51,58,47,26,24, 6,48,49,
	35,14,44, 3,38,60,63,17
    };
    static short shift[16] = { 2,2,1,1,1,1,1,1,2,1,1,1,1,1,1,2 };
    B64 icd;
    register short i, j;

    for (i = 0; i < 64; ++i)
	setbit(&icd,i,getbit(key,smiperm[i]));
    for (i = 0; i < 16; ++i) {
	for (j = shift[i]; j; --j) {
	    icd.b[0] = (icd.b[0] << 1) | (icd.b[0] >> 31);
	    icd.b[1] = (icd.b[1] << 1) | (icd.b[1] >> 31);
	}
	for (j = 0; j < 48; ++j)
	    setbit(&SubMaster[i],j,getbit(&icd,smselect[j]));
    }
}

/*
 *  Set/get bit in 64 bit quantities
 *
 *  63....0
 *
 */

setbit(ptr,bno,val)
ubyte *ptr;
short bno;
ubyte val;
{
    if (val)
	ptr[7-(bno>>3)] |= 1 << (bno&7);
    else
	ptr[7-(bno>>3)] &= ~(1 << (bno&7));
}

getbit(ptr,bno)
ubyte *ptr;
short bno;
{
    if (ptr[7-(bno>>3)] & (1 << (bno&7)))
	return(1);
    return(0);
}

