/* Software interface to DES for RPC */
/* DESSofter.c */

/* Dan Swinehart, November 11, 1982 9:59 am */
/* Adapted from DESSoft.mesa, by Schroeder and Birrell, 1981-2 */
/* L. Stewart, December 27, 1982  2:56 PM
     flush nested declarations */

#include <Env.h>
#include <Encrypt.h>
#include <RPC.h>
	/* For slXXX encryption values */

extern Block();
extern Encrypt();
extern MoveBlock();
extern DoubleDifference();

char parityTable[16];
struct Words4 { int words[4]; };

EncryptBlock(key, from, to) int *key; struct Words4 *from, *to; {
	CryptData(key, 1, from, to, dirEncrypt, slECB, 0); };

DecryptBlock(key, from, to) int *key; struct Words4 *from, *to; {
	CryptData(key, 1, from, to, dirDecrypt, slECB, 0); };

CBCCheckDecrypt(key, nBlks, from, to, seed) int *key, nBlks;
		struct Words4 *from, *to, *seed; {
	CryptData(key, nBlks, from, to, dirDecrypt, slCBCCheck, seed); };

XOR64(a,b,out) int a[], b[], out[]; {
	int i;
	for (i=0; i<4; ++i) out[i] = a[i] ↑ b[i]; };

OKToContinue(flag) int *flag; { *flag = true; };

CryptData(keyP, nBlks, from, to, direction, mode, firstSeed)
	int *keyP;
	int nBlks;
	struct Words4  from[], to[];
	int direction, mode;
	struct Words4 *firstSeed; {

	struct ECB ecb;
	struct Words4 newSeed;
	struct Words4 seed;
	struct Words4 *seedP;
	int okToContinue, blk;

	seedP = &seed;
	MoveBlock(seedP, firstSeed, 4);
	if (to==0) to = from;
	ecb.kp = keyP;
	ecb.encrypt = direction;
	ecb.item = (int) &okToContinue;
	ecb.proc = (int) &OKToContinue;

	if (mode == slCBCCheck && direction == dirEncrypt && nBlks > 0) {
		for (blk=0; blk<nBlks-1; ++blk)
			XOR64(&from[nBlks-1], &from[blk], &from[nBlks-1]);
		Block(); };

	if (mode == slECB) {
		ecb.count = nBlks*8; /* Why beat around the bush? */
		ecb.srcp = from;
		ecb.dstp = to;
		Docrypt(&ecb); return; };
	
	ecb.count = 8;
	for (blk=0; blk<nBlks; ++blk) {
		if (direction==dirEncrypt) XOR64(&from[blk], seedP, &from[blk]);
		else MoveBlock(&newSeed, &from[blk], 4);
		ecb.srcp = &from[blk];
		ecb.dstp = &to[blk];
		Docrypt(&ecb);
		if (direction == dirEncrypt) seedP = &to[blk];
		else {XOR64(&to[blk], seedP, &to[blk]); MoveBlock(seedP, &newSeed, 4); }; };
	Block();

	if (mode == slCBCCheck && direction == dirDecrypt && nBlks > 0) {
		for (blk=0; blk<nBlks-1; ++blk)
			XOR64(&to[nBlks-1], &to[blk], &to[nBlks-1]);
		Block(); }; }; /* CryptData */

Docrypt(ecb) struct ECB *ecb; {
	int *okToContinue;
	int i;
	char *from, *to, *key;
	okToContinue = (int *) ecb->item;
	*okToContinue = false;
	/* Encrypt(ecb); */
	/* Dummy encryption/decryption: XOR key with data, ho ho. */
	{
		from = ecb->srcp;
		to = ecb->dstp;
		key = ecb->kp;
		for (i=0; i<ecb->count; ++i) { to[i] = from[i] ↑ key[i&7]; };
		*okToContinue = true;  Block(); };
	/* End of Dummy encryption/decryption */
	while (!(*okToContinue)) Block(); };

MakeKey(source, key) struct ShortSTRING *source; char key[]; {

/* User interface routine to construct a 64 bit key from an ASCII string.
    "key" is an array that holds the 64 bit key.
    Note: every eighth bit of "key" is an odd parity bit for the preceeding seven bits. */

	int i;
	int j; char c;
	Zero(key, 4);
	for (i=0; i<source->length; ++i) {
		j = i&7;
		c=source->text[i];
		if ('A' <= c && c <= 'Z') c += ('a' - 'A');
		key[j] ↑= c<<1; };
	CorrectParity(key); };

CorrectParity(keyP) char keyP[]; {
	int i;
	char c;
	int pWord, pBitNo;
	if (parityTable[0] != 0x96) InitParityTable();
	for (i=0; i<8; ++i) {
		c=keyP[i];
		pWord = parityTable[c>>4];
		c=c>>1;
		pBitNo = 7-(c&7);
		pWord = (pWord>>pBitNo)&1;
		keyP[i] = (c<<1) | pWord; }; };

int DESBlocks(n) int n; { return (n+3)/4; };

struct Words4 randomSeed;

GetRandomIV(iv) struct Words4 *iv; {
	struct Words4 prevSeed, temp;
	MoveBlock(&prevSeed, &randomSeed, 4);
	Timer(&temp);
     ReadCalendar(&temp.words[2]);
	DoubleDifference(&randomSeed, &temp);
	Zero(&temp, 4);
	CorrectParity(&randomSeed);
	EncryptBlock(&randomSeed, &temp, &randomSeed);
	CorrectParity(&randomSeed);
	EncryptBlock(&randomSeed, &prevSeed, iv); };

InitParityTable() {
	parityTable[0] = 0x96;
	parityTable[1] = 0x69;
	parityTable[2] = 0x69;
	parityTable[3] = 0x96;
	parityTable[4] = 0x69;
	parityTable[5] = 0x96;
	parityTable[6] = 0x96;
	parityTable[7] = 0x69;
	parityTable[8] = 0x69;
	parityTable[9] = 0x96;
	parityTable[10] = 0x96;
	parityTable[11] = 0x69;
	parityTable[12] = 0x96;
	parityTable[13] = 0x69;
	parityTable[14] = 0x69;
	parityTable[15] = 0x96; };