[comp.sources.amiga] v89i062: cbc - cipher block chaining encryption

page@swan.ulowell.edu (Bob Page) (03/16/89)

Submitted-by: uunet!mcvax!unido!altger!vyrus (David Vincenzetti)
Posting-number: Volume 89, Issue 62
Archive-name: applications/cbc.1

[File encoding routines.  Binary was not supplied, but a makefile was.
..Bob]

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	read_me
#	makefile
#	cbc.h
#	cbc.c
#	dofile.c
#	func.c
# This archive created: Wed Mar 15 15:15:44 1989
cat << \SHAR_EOF > read_me
/* :ts=8 */

ProgramName:	Cbc (Cipher Block Chaining with DES)

Purpose:	To protect files in a real secure manner
		using cryptography.

Programmer:	David Vincenzetti [vyrus@altger.UUCP]

PlagiarPolicy:	Don't use commercially!


Cbc is simple to use: you have to provide at least one file to be
encoded (if you supply the -e option or no option) or decoded (-d option).
If you do not use the -i option the Ignore Mode is not enabled so the
program will write the encryption key in an encoded format to ciphertext
while encoding and it will check for key correctness while decoding.
For istance if you crypt a file (no -i option!) with key = "hello" and try
to decrypt with key = "hallo" you will get an error message telling that
you are using the wrong key.
I found the -i option somewhat dangerous at late night.

Cbc was written for the great Amiga PC but is surely portable everywhere
(ie: in your trashcan if you print it), with a minimal work.
You can copy || modify it but you *Cannot* sell or use it whithin commercial
products - You must keep copyright notices too (I wanna be famous you see).

Feel free to send me comments and suggestions (please do it!!): I would
REALLY appreciate if someone very experienced about cryptology would seriously
test this program and send me a short note describing its weakness, bugs etc.
Also, you you have written something of your own, or you got something neat
send it to me. Top three topics: viruses, computer security & cryptography.

Cuse me for the shortness of this docfile but I am not good at writing in free
english and I never use the dictionary...I hope that all this is intellegible
to the common human..if not discard everything and send flames to /dev/null.

State [Zzzz] Mail: (Italian mail is the slower in the universe)
	David Vincenzetti,
	8th Street #39,
	20090 Segrate (MI)
		Italy

UUCP: (I hope korn (the sysop) won't close my account! Greetings Korn!!)
	{elsewhere}...!uunet!mcvax!unido!altger!vyrus
SHAR_EOF
cat << \SHAR_EOF > makefile
# :ts=8 bk=0
# makefile for cbc

OBJECTS = cbc.o dofile.o func.o
CFLAGS = -n

xxcbc: $(OBJECTS)
	ln -o cbc -g +q $(OBJECTS) -lc

$(OBJECTS):	cbc.h
SHAR_EOF
cat << \SHAR_EOF > cbc.h
/* cbc.h */

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

#define ENCRYPT		0x0001	/* encode */
#define DECRYPT		0x0002	/* decode */
#define IGNORE		0x0004	/* don't put & check header */

/* returns */
#define OK		0
#define R_ERR		1	/* read error */
#define W_ERR		2	/* write error */
#define BAD		3	/* dangerous failure */
#define MAT_ERR		4	/* encoded key wrong/missing */
#define COR_ERR		5	/* file not in proper form */

/* booleans */
#define TRUE		1
#define FALSE		0
SHAR_EOF
cat << \SHAR_EOF > cbc.c
/* :ts=8 bk=0 */
/* xxcbc.c */

#include	"cbc.h"

char sccsid[] = "@(#) cbc.c 1.0 dec-88 =C= by David Vincenzetti";

void main(argc, argv)
int argc;
char **argv;
{
	void bad();

	char key[8];
	int optindex;

	short mode;

	if(argc < 2) bad("use: cbc [-{e|d}i] key files..");

	mode = 0;
	mode |= ENCRYPT; /* encrypt by default */

	for(optindex = 1; argv[optindex][0] == '-'; optindex++) {
		int option;

		option = toupper(argv[optindex][1]);
		if(option == '\0' || option == '-') break;
		switch(option) {
			case 'E':
				mode |= ENCRYPT;
				break;
			case 'D':
				mode |= DECRYPT;
				break;
			case 'I':
				mode |= IGNORE;
				break;
			default:
				printf("option -%c ignored\n", option);
				break;
		}
	}

	argv += optindex; argc -= optindex;

	/* key and/or filename is missing */
	if(argc < 2) bad("too few arguments");

	/*
	 * copy key to buffer 
	 */
	setmem(key, 8, 0);
	strncpy(key, argv[0], 8);

	/*
	 * get rid of ps -f or similar
	 */
	while(*argv[0]) *argv[0]++ = 0;
	++argv; --argc;

	/*
	 * set cipherment keys
	 */
	setup(key);

	/*
	 * Create IV Initializing Variable
	 * Its secrecy and key dependency is very important
	 * for CBC security. I think crypt() could be used.
	 */
	dcrypt(key, 0); /* lets scramble twice */
	dcrypt(key, 0);

	/*
	 * main loop
	 */
	for(optindex = 0; optindex < argc; optindex++) {
		int tim;
		int size;
		struct stat fbuf;

		tim = time(NULL);
		stat(argv[optindex], &fbuf);
		size = fbuf.st_size;

		switch(dofile(argv[optindex], key, mode)){
			case OK:
				printf("%s: %d bytes; elapsed: %d\n",
					argv[optindex],
					size,
					time(NULL) - tim);
				break;
			case R_ERR:
			case W_ERR:
				printf("R/W error; %s unencoded\n\n",
					argv[optindex]);
				break;
			/*
			 * something shouldn't had happened HAS happened!
			 * We probabily lost some tmp file or..
			 */
			case BAD:
				printf("BAD error for file %s!!\n\n",
					argv[optindex]);
				break;
			/*
			 * we checked the file header and
			 * we found it mismatching
			 */
			case MAT_ERR:
				printf("key incorrect; %s ignored\n",
					argv[optindex]);
				break;
			default:
				bad("_dofile return value unknown");
				break;
		}
	}
} /* main */

void bad(message)

char *message;
{
	puts(message);
	exit(1);
}
SHAR_EOF
cat << \SHAR_EOF > dofile.c
/* dofile.c */

#include "cbc.h"

/*
 * CBC en/decrypts a file given the IV
 */

int dofile();
char *mktemp();
char *crypt();

int dofile(source, IV, mode)
char *source, *IV;
short mode;
{	
	char *target;		/* tmp file */
	char buffer[8];		/* data buffer */
	char previous[8];	/* previous data buffer */
	char tmp[8];		/* yet another data buffer */
	char header[11];	/* encoded key for sanity */

	int in, out;

	short first_block;	/* first block flag */
	short count;		/* byte count */

	/* create tmp file */
	target = mktemp("RAM:cipherXXX.XXX");
	if(!*target) {
		fprintf(stderr, "_mktemp failed\n");
		return(BAD);
	}

	if((in = open(source, O_RDONLY)) < 0){
		perror(source);
		return(R_ERR);
	}

	if((out = open(target, O_WRONLY | O_TRUNC | O_CREAT)) < 0) {
		close(out);
		perror(target);
		return(W_ERR);
	}

	if(mode & DECRYPT && !(mode & IGNORE)) {
		/*
		 * decryption is on, so we check for
		 * encoded header to match with key
		 */
		count = read(in, header, 11);
		if(count != 11 ||
			strncmp(header, crypt(IV, IV) + 2, 11)) {
				close(in);
				close(out);

				if(count < 0) {
					perror(source);
					return(R_ERR);
				}
				else return(MAT_ERR);
		}
	}
	else if(!(mode & IGNORE)) {
		/*
		 * write encoded key to target file
		 */
		count = write(out, crypt(IV, IV) + 2, 11);
		if(count != 11) {
			close(in);
			close(out);
			perror(target);
			return(W_ERR);
		}
	}

	first_block = TRUE;

	do{
		/*** read block ***/
		if((count = read(in, buffer, 8)) < 0) {
			close(in);
			close(out);
			perror("_read");
			return(R_ERR);
		}


		/*** decode ***/
		if(mode & DECRYPT) {
			if(first_block) strcopy(previous, IV, 8);
			else strcopy(previous, tmp, 8);
			
			if(count == 8){
				strcopy(tmp, buffer, 8);
				dcrypt(buffer, 1);
				strsum(buffer, previous);
			}
		}
		/*** encode ***/
		else {
			if(first_block) strcopy(previous, IV, 8);
			
			if(count == 8){
				strsum(buffer, previous);
				dcrypt(buffer, 0);
				strcopy(previous, buffer, 8);
			}
		}

		/*** cipher last short block ***/
		if(count != 8){
			dcrypt(previous, 0);
			strsum(buffer, previous);
		}	

		/*** write block ***/
		if((write(out, buffer, count)) < 0) {
			close(in);
			close(out);
			perror("_write");
			return(R_ERR);
		}

		first_block = FALSE;
		
	} while(count == 8);

	/* close & move */
	if(close(in) || close(out) || mv(target, source)) {
		perror("_close");
		return(BAD);
	}

	return(OK);
}
SHAR_EOF
cat << \SHAR_EOF > func.c
/* :ts=8 bk=0 */
/* func.h */

#include "cbc.h"

void setup();	/* init cipher */
void expand();	/* from bytes to bits */
void compress();/* from bits to bytes */
void dcrypt();	/* perform DES encryption */
void strsum();	/* binary sum */
int mv();	/* move files */
void strcopy();	/* binary version of _strncpy */

void setup(key)
char *key;
{
	char bits[64];
	char keybuf[8];

	strcopy(keybuf, key, 8);
	/****
	while(*key) *key++ = 0;
	****/

	expand(keybuf, bits);
	setkey(bits);
}

void expand(in, out)
unsigned char *in;
char *out;
{
	int divide;
	int i,j;
	
	for(i = 0; i < 8; i++){
		divide = 1;
		for(j = 0; j < 8; j++){
#ifdef XENIX
			*out++=(in[i] / divide) &1;
#else
			*out++ = (in[i] >> j) & 1;
#endif
			divide *=2;
		}
	}
}

void compress(in,out)
char *in;
unsigned char *out;
{
	int temp;
	int i, j;
	
	for(i=0; i < 8; i++){
		out[i] = 0;
		temp = 1;
		for(j = 0; j < 8; j++){
#ifdef XENIX
			out[i] = out[i] + (*in++ * temp);
#else
			out[i] = out[i] + (*in++ << j);
#endif
			temp*=2;
		}
	}
}

void dcrypt(data,decrypt)
char *data;
short decrypt;
{
	char input[8], output[8], bits[64];
	
	strcopy(input, data, 8);
	
	expand(input, bits);
	encrypt(bits, decrypt);
	compress(bits, output);

	strcopy(data, output, 8);
}

/*
 * This program implements the
 * Proposed Federal Information Processing
 *  Data Encryption Standard.
*/
 
static char IP[] = {
	58,50,42,34,26,18,10, 2,
	60,52,44,36,28,20,12, 4,
	62,54,46,38,30,22,14, 6,
	64,56,48,40,32,24,16, 8,
	57,49,41,33,25,17, 9, 1,
	59,51,43,35,27,19,11, 3,
	61,53,45,37,29,21,13, 5,
	63,55,47,39,31,23,15, 7,
};

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

/*
 * Permuted-choice 1 from the key bits
 * to yield C and D.
 * Note that bits 8,16... are left out:
 * They are intended for a parity check.
 */
static	char	PC1_C[] = {
	57,49,41,33,25,17, 9,
	 1,58,50,42,34,26,18,
	10, 2,59,51,43,35,27,
	19,11, 3,60,52,44,36,
};

static	char	PC1_D[] = {
	63,55,47,39,31,23,15,
	 7,62,54,46,38,30,22,
	14, 6,61,53,45,37,29,
	21,13, 5,28,20,12, 4,
};

/*
 * Sequence of shifts used for the key schedule.
*/
static	char	shifts[] = {
	1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1,
};

/*
 * Permuted-choice 2, to pick out the bits from
 * the CD array that generate the key schedule.
 */
static	char	PC2_C[] = {
	14,17,11,24, 1, 5,
	 3,28,15, 6,21,10,
	23,19,12, 4,26, 8,
	16, 7,27,20,13, 2,
};

static	char	PC2_D[] = {
	41,52,31,37,47,55,
	30,40,51,45,33,48,
	44,49,39,56,34,53,
	46,42,50,36,29,32,
};

/*
 * The C and D arrays used to calculate the key schedule.
 */

static char	C[28];
static	char	D[28];
/*
 * The key schedule.
 * Generated from the key.
 */
static	char	KS[16][48];

/*
 * The E bit-selection table.
 */
static	char	E[48];
static	char	e[] = {
	32, 1, 2, 3, 4, 5,
	 4, 5, 6, 7, 8, 9,
	 8, 9,10,11,12,13,
	12,13,14,15,16,17,
	16,17,18,19,20,21,
	20,21,22,23,24,25,
	24,25,26,27,28,29,
	28,29,30,31,32, 1,
};

/*
 * Set up the key schedule from the key.
 */

setkey(key)
char *key;
{
	register i, j, k;
	int t;

	/*
	 * First, generate C and D by permuting
	 * the key.  The low order bit of each
	 * 8-bit char is not used, so C and D are only 28
	 * bits apiece.
	 */
	for (i=0; i<28; i++) {
		C[i] = key[PC1_C[i]-1];
		D[i] = key[PC1_D[i]-1];
	}
	/*
	 * To generate Ki, rotate C and D according
	 * to schedule and pick up a permutation
	 * using PC2.
	 */
	for (i=0; i<16; i++) {
		/*
		 * rotate.
		 */
		for (k=0; k<shifts[i]; k++) {
			t = C[0];
			for (j=0; j<28-1; j++)
				C[j] = C[j+1];
			C[27] = t;
			t = D[0];
			for (j=0; j<28-1; j++)
				D[j] = D[j+1];
			D[27] = t;
		}
		/*
		 * get Ki. Note C and D are concatenated.
		 */
		for (j=0; j<24; j++) {
			KS[i][j] = C[PC2_C[j]-1];
			KS[i][j+24] = D[PC2_D[j]-28-1];
		}
	}

	for(i=0;i<48;i++)
		E[i] = e[i];
}

/*
 * The 8 selection functions.
 * For some reason, they give a 0-origin
 * index, unlike everything else.
 */
static	char	S[8][64] = {
	14, 4,13, 1, 2,15,11, 8, 3,10, 6,12, 5, 9, 0, 7,
	 0,15, 7, 4,14, 2,13, 1,10, 6,12,11, 9, 5, 3, 8,
	 4, 1,14, 8,13, 6, 2,11,15,12, 9, 7, 3,10, 5, 0,
	15,12, 8, 2, 4, 9, 1, 7, 5,11, 3,14,10, 0, 6,13,

	15, 1, 8,14, 6,11, 3, 4, 9, 7, 2,13,12, 0, 5,10,
	 3,13, 4, 7,15, 2, 8,14,12, 0, 1,10, 6, 9,11, 5,
	 0,14, 7,11,10, 4,13, 1, 5, 8,12, 6, 9, 3, 2,15,
	13, 8,10, 1, 3,15, 4, 2,11, 6, 7,12, 0, 5,14, 9,

	10, 0, 9,14, 6, 3,15, 5, 1,13,12, 7,11, 4, 2, 8,
	13, 7, 0, 9, 3, 4, 6,10, 2, 8, 5,14,12,11,15, 1,
	13, 6, 4, 9, 8,15, 3, 0,11, 1, 2,12, 5,10,14, 7,
	 1,10,13, 0, 6, 9, 8, 7, 4,15,14, 3,11, 5, 2,12,

	 7,13,14, 3, 0, 6, 9,10, 1, 2, 8, 5,11,12, 4,15,
	13, 8,11, 5, 6,15, 0, 3, 4, 7, 2,12, 1,10,14, 9,
	10, 6, 9, 0,12,11, 7,13,15, 1, 3,14, 5, 2, 8, 4,
	 3,15, 0, 6,10, 1,13, 8, 9, 4, 5,11,12, 7, 2,14,

	 2,12, 4, 1, 7,10,11, 6, 8, 5, 3,15,13, 0,14, 9,
	14,11, 2,12, 4, 7,13, 1, 5, 0,15,10, 3, 9, 8, 6,
	 4, 2, 1,11,10,13, 7, 8,15, 9,12, 5, 6, 3, 0,14,
	11, 8,12, 7, 1,14, 2,13, 6,15, 0, 9,10, 4, 5, 3,

	12, 1,10,15, 9, 2, 6, 8, 0,13, 3, 4,14, 7, 5,11,
	10,15, 4, 2, 7,12, 9, 5, 6, 1,13,14, 0,11, 3, 8,
	 9,14,15, 5, 2, 8,12, 3, 7, 0, 4,10, 1,13,11, 6,
	 4, 3, 2,12, 9, 5,15,10,11,14, 1, 7, 6, 0, 8,13,

	 4,11, 2,14,15, 0, 8,13, 3,12, 9, 7, 5,10, 6, 1,
	13, 0,11, 7, 4, 9, 1,10,14, 3, 5,12, 2,15, 8, 6,
	 1, 4,11,13,12, 3, 7,14,10,15, 6, 8, 0, 5, 9, 2,
	 6,11,13, 8, 1, 4,10, 7, 9, 5, 0,15,14, 2, 3,12,

	13, 2, 8, 4, 6,15,11, 1,10, 9, 3,14, 5, 0,12, 7,
	 1,15,13, 8,10, 3, 7, 4,12, 5, 6,11, 0,14, 9, 2,
	 7,11, 4, 1, 9,12,14, 2, 0, 6,10,13,15, 3, 5, 8,
	 2, 1,14, 7, 4,10, 8,13,15,12, 9, 0, 3, 5, 6,11,
};

/*
 * P is a permutation on the selected combination
 * of the current L and key.
 */
static	char	P[] = {
	16, 7,20,21,
	29,12,28,17,
	 1,15,23,26,
	 5,18,31,10,
	 2, 8,24,14,
	32,27, 3, 9,
	19,13,30, 6,
	22,11, 4,25,
};

/*
 * The current block, divided into 2 halves.
 */
static	char	L[32], R[32];
static	char	tempL[32];
static	char	f[32];

/*
 * The combination of the key and the input, before selection.
 */
static	char	preS[48];

/*
 * The payoff: encrypt a block.
 */

encrypt(block, edflag)
char *block;
int edflag;
{
	int i, ii;
	register t, j, k;

	/*
	 * First, permute the bits in the input
	 */
	for (j=0; j<64; j++)
		L[j] = block[IP[j]-1];
	/*
	 * Perform an encryption operation 16 times.
	 */
	for (ii=0; ii<16; ii++) {
		/*
		 * Encrypts if edflag is null,
		 * decrypts otherwise.. by David Vincenzetti 4-Sep-88 :-)
		 * Paint paint...it was an hard word to get these sources!
		 */
		if(!edflag)
			i = ii;		 /* encrypts */
		else
			i=15-ii;		/* decrypts */
		/*
		 * Save the R array,
		 * which will be the new L.
		 */
		for (j=0; j<32; j++)
			tempL[j] = R[j];
		/*
		 * Expand R to 48 bits using the E selector;
		 * exclusive-or with the current key bits.
		 */
		for (j=0; j<48; j++)
			preS[j] = R[E[j]-1] ^ KS[i][j];
		/*
		 * The pre-select bits are now considered
		 * in 8 groups of 6 bits each.
		 * The 8 selection functions map these
		 * 6-bit quantities into 4-bit quantities
		 * and the results permuted
		 * to make an f(R, K).
		 * The indexing into the selection functions
		 * is peculiar; it could be simplified by
		 * rewriting the tables.
		 */
		for (j=0; j<8; j++) {
			t = 6*j;
			k = S[j][(preS[t+0]<<5)+
				(preS[t+1]<<3)+
				(preS[t+2]<<2)+
				(preS[t+3]<<1)+
				(preS[t+4]<<0)+
				(preS[t+5]<<4)];
			t = 4*j;
			f[t+0] = (k>>3)&01;
			f[t+1] = (k>>2)&01;
			f[t+2] = (k>>1)&01;
			f[t+3] = (k>>0)&01;
		}
		/*
		 * The new R is L ^ f(R, K).
		 * The f here has to be permuted first, though.
		 */
		for (j=0; j<32; j++)
			R[j] = L[j] ^ f[P[j]-1];
		/*
		 * Finally, the new L (the original R)
		 * is copied back.
		 */
		for (j=0; j<32; j++)
			L[j] = tempL[j];
	}
	/*
	 * The output L and R are reversed.
	 */
	for (j=0; j<32; j++) {
		t = L[j];
		L[j] = R[j];
		R[j] = t;
	}
	/*
	 * The final output
	 * gets the inverse permutation of the very original.
	 */
	for (j=0; j<64; j++)
		block[j] = L[FP[j]-1];
}

char *
crypt(pw,salt)
char *pw;
char *salt;
{
	register i, j, c;
	int temp;
	static char block[66], iobuf[16];

	for(i=0; i<66; i++)
		block[i] = 0;
	for(i=0; (c= *pw) && i<64; pw++){
		for(j=0; j<7; j++, i++)
			block[i] = (c>>(6-j)) & 01;
		i++;
	}
	
	setkey(block);
	
	for(i=0; i<66; i++)
		block[i] = 0;

	for(i=0;i<2;i++){
		c = *salt++;
		iobuf[i] = c;
		if(c>'Z') c -= 6;
		if(c>'9') c -= 7;
		c -= '.';
		for(j=0;j<6;j++){
			if((c>>j) & 01){
				temp = E[6*i+j];
				E[6*i+j] = E[6*i+j+24];
				E[6*i+j+24] = temp;
				}
			}
		}
	
	for(i=0; i<25; i++)
		encrypt(block,0);
	
	for(i=0; i<11; i++){
		c = 0;
		for(j=0; j<6; j++){
			c <<= 1;
			c |= block[6*i+j];
			}
		c += '.';
		if(c>'9') c += 7;
		if(c>'Z') c += 6;
		iobuf[i+2] = c;
	}
	iobuf[i+2] = 0;
	if(iobuf[1]==0)
		iobuf[1] = iobuf[0];
	return(iobuf);
}

void strsum(one, two)
char *one, *two;
{
	short i;
	
	for(i=0; i < 8; i++)
		one[i] = one[i] ^ two[i];
}

int mv(from,to)
char *from, *to;
{
	int in, out, len;
	char block[512];
	
	if((in = open(from, O_RDONLY)) < 0) {
		perror(from);
		return(R_ERR);
	}
	if((out = open(to, O_WRONLY | O_CREAT | O_TRUNC)) < 0) {
		close(in);
		perror(to);
		return(W_ERR);
	}
	
	do{
		len = read(in, block, 512);
		write(out, block, len);
	} while (len == 512);
	
	if(close(in) || close(out) ||
		unlink(from)) {
			perror("_unlink");
			return(BAD);
	}

	return(OK);
}

void strcopy(to, from, len)
unsigned char *from, *to;
int len;
{
	while(len > 0) {
		*to++ = *from++;
		--len;
	}
}
SHAR_EOF
#	End of shell archive
exit 0
-- 
Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
Have five nice days.