/*-----------------------------------------------------------------------
Tcl Scripting Language Components (Tslc)
Copyright (C) 1996-2002 William Byrne

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

WilliamB@ByrneLitho.com
------------------------------------------------------------------------*/
// compiled in BC++ v5.02 without underscores
#ifdef REAL_DES
// place real DES code here. I'm not interested in
// dealing with  NSA issues.

// implement the following 3 functions with the real stuff...
void desinit(char *k);
void endes(char *in, char *out);
void dedes(char *in, char *out);

#else
// bogus nibble confusion<g>
#define nibswap( c ) (((c << 4) & 0xF0) | ((c >> 4) & 0x0F))


char deskey[8];
void desinit(char *k){
	int x;
	for(x = 0; x < 8; x++){
   	deskey[x] = *k++;
   }
}

void endes(char *in, char *out) {
	int x;
   char c;
	for(x = 0; x < 8; x++){
   	c = nibswap(*in);
		*out = c ^ deskey[x];
//		*out = *in ^ deskey[x];
		out++;
      in++;
   }
}

void dedes(char *in, char *out) {
	int x;
   char c;
	for(x = 0; x < 8; x++){
      c = *in ^ deskey[x];
		*out = nibswap(c);
//		*out = *in ^ deskey[x];
      out++;
      in++;
   }
}

#endif
/* End of DES algorithm (except for calling desinit below)	*/

#define CYPHER_HEX_LEN		49
#define MAX_PASSWORD_LEN	8
#define PASSWORD_TOO_LONG	-1
#define INVALID_ARGUMENTS	-2
#define READ_ERROR			-3
#define WRITE_ERROR			-4
#define INVALID_CYPHER_HEX	-5
#define INVALID_HASH			-6
#define STATIC_LOCK			-7
#define INVALID_KEY			-8


char *DES_About(){
	return 	"Fake DES - "
   			"\n\tEncrypts using nibble swap then xor on key."
            "\n\tDecrypts using xor on key then nibble swap."
            "\n\tMaximum key of 8 characters, zero padding short keys.";
}

char *DES_Version() {
	return "0.0";
}

typedef int (ReadProc)(char *buf, int cbLen, void *clientData);
typedef int (WriteProc)(char *buf, int cbLen, void *clientData);
typedef char (Garbage)();

int DES_MaxPasswordLen(){
	return MAX_PASSWORD_LEN;
}

int DES_Encrypt(char *key, ReadProc *fnRead, void *readData, WriteProc *fnWrite, void *writeData, Garbage *fnGarbage) {
	int x, r, j, w;
   char buf[512], *b;

	if ( key == 0 || fnRead == 0 || fnWrite == 0 || fnGarbage == 0)
      return INVALID_ARGUMENTS;

   for( x = 0; key[x]; x++);
   if (x > 8) return PASSWORD_TOO_LONG;

	for(x=0;key[x];x++) buf[x] = key[x];
   for(;x<8;x++) buf[x] = 0;
   desinit(buf);

	while ((w = r = (*fnRead)(buf, 512, readData)) != 0) {
		if (r < 0) return READ_ERROR;
		for (b=buf; r>0; r -= 8, b += 8){
         if (r<8)	{
            for (j=0; j<8-r; j++)
					b[r+j]= (*fnGarbage)(); /* fill block with trash  */
				w += 8-r;   /* complete the block		*/
			} else
           	 j=0;	/* number of nulls in last block*/
			endes(b,b);	/* don't need diff input, output*/
		}
		if (w != (*fnWrite)(buf, w, writeData)) return WRITE_ERROR;
   }

	buf[0] = 8 - j;
	if ((*fnWrite)(buf, 1, writeData) != 1) return WRITE_ERROR;
   else return 0;
}

int DES_Decrypt(char *key, ReadProc *fnRead, void *readData, WriteProc *fnWrite, void *writeData) {
	int x, r, j, w, doLast = 0;
   char buf[512], last[512], *b;

	if ( key == 0 || fnRead == 0 || fnWrite == 0)
      return INVALID_ARGUMENTS;

//   for( x = 0; key[x]; x++);
//   if (x > 8) return PASSWORD_TOO_LONG;

	for(x=0;key[x];x++) buf[x] = key[x];
   for(;x<8;x++) buf[x] = 0;
   desinit(buf);

	while ((w = r = (*fnRead)(buf, 512, readData)) != 0) {
		if (r < 0) return READ_ERROR;
		for (b=buf; r>0; r -= 8, b += 8){
         if (r<8)	{
      		w -= 9 - *b;
			}
			dedes(b,b);	/* don't need diff input, output*/
		}
      if (doLast) {
			if (doLast != (*fnWrite)(last, doLast, writeData)) return WRITE_ERROR;
      }
      doLast = w;
      for (x=0; x < w; x++) last[x] = buf[x];
   }
	if ((*fnWrite)(last, doLast, writeData) != doLast) return WRITE_ERROR;
   else return 0;
}

const HexArray[256] = {
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1,
-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
};

Hex2Bytes (char *dest, int destLen, char *src, int srcLen) {
	int x;
   char L, H;
   if (srcLen % 2 != 0 || srcLen / 2 > destLen - 1)
   	return INVALID_ARGUMENTS;
  	for(x = 0; x < srcLen;) {
      H = HexArray[(unsigned char)src[x++]];
      if (H < 0) return INVALID_ARGUMENTS;
      L = HexArray[(unsigned char)src[x++]];
      if (L < 0) return INVALID_ARGUMENTS;
      *dest++ = (H << 4) | L;
   }
	*dest = '\0';
   return 0;
}

Bytes2Hex(char *dest, int destLen, char *src, int srcLen) {
	const char Hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
   int x;
   if ( destLen < srcLen * 2 + 1) return srcLen * 2 + 1;
 	for(x = 0; x < srcLen; x++ ) {
		*dest++ = Hex[(*src >> 4) & 0xF];
      *dest++ = Hex[*src++ & 0xF];
   }
	*dest = '\0';
   return 0;
}

// crunch Preprocessor
#ifndef CHAR_BIT
#	define CHAR_BIT 8
#endif
#define BITS_IN_int		( sizeof( int ) * CHAR_BIT )
#define THREE_QUARTERS	((int) ((BITS_IN_int * 3) / 4))
#define ONE_EIGHTH		((int) (BITS_IN_int / 8 ))
#define HIGH_BITS		( ~((unsigned int)(~0) >> ONE_EIGHTH ))

// crunch
//
unsigned long crunch( const char *data, unsigned int cbLen ) {
	unsigned int h = 0, i, x;
    for ( x = cbLen; x;  x-- ) {
    	h = ( h << ONE_EIGHTH ) + *data++;
        if ( ( i = h & HIGH_BITS ) != 0 )
        	h = ( h ^ ( i >> THREE_QUARTERS ) ) & ~HIGH_BITS;
    }
    return h;
}

union TLongStr {
	char str[5];
   long l;
};

const char *ClientKey = 0;
char ClientKeyBuf[CYPHER_HEX_LEN];
const char *__StaticKey = "69A44E0814C1750597332159";
#ifdef REAL_DES
const char *StaticKey = "7C7DF9E320516F19C3E1C831D6B046E3B8EE42DC5B838C78";
#else
const char *StaticKey = "237B731B03BB23DB644B133BB3AB33EB237B130B93AB63FB";
#endif
const char *_StaticKey = "681F3FA8FA716A9AF92AD280D74D862E879AFC733802775D";

int StaticOff = 0;
int Locked = 0;

void StaticLock(void){
   if (!ClientKey) return;
	Locked = 1;
}

#define FINAL_EXP ((x << 2) * (x << 1))

int StaticEncrypt(char *ClearKey, char *CypherHexBuf, int BufLen) {
   char cypher[MAX_PASSWORD_LEN + 1], key[MAX_PASSWORD_LEN + 1],
   	PriHex[CYPHER_HEX_LEN];
   int x;
   union TLongStr u;
   int staticOff = StaticOff;
   StaticOff = 0;
   for ( x = 0; ClearKey[x]; x++ );
   if (x > MAX_PASSWORD_LEN)
   	return (PASSWORD_TOO_LONG);
	if (CypherHexBuf == 0 || BufLen < CYPHER_HEX_LEN)
   	return (CYPHER_HEX_LEN);
	for ( x = 0; x <= MAX_PASSWORD_LEN; x++)
   	key[x] = cypher[x] = '\0';

	if (!staticOff){
		desinit(key);
	   Hex2Bytes(key, MAX_PASSWORD_LEN + 1, (ClientKey == 0 ? StaticKey:ClientKey) + 8, 16);
		dedes(key, key);
   }

   desinit(key);
	for (x = 0; x < MAX_PASSWORD_LEN && ClearKey[x]; x++)
   	key[x] = ClearKey[x];
   while ( x < MAX_PASSWORD_LEN )
   	key[x++] = '\0';

	endes(key, cypher);
	u.l = crunch(cypher, 8);
   Bytes2Hex(PriHex, CYPHER_HEX_LEN, u.str, 4);
   Bytes2Hex(PriHex + 8, CYPHER_HEX_LEN - 8, cypher, 8);
//   Bytes2Hex(CypherHexBuf, 25, u.str, 4);
//   Bytes2Hex(CypherHexBuf + 8, 17, cypher, 8);
	for (x = 0; x < MAX_PASSWORD_LEN; x++)
    	key[x] = FINAL_EXP;
	desinit(key);
    endes(PriHex, PriHex);
    endes(PriHex + 8, PriHex + 8);
    endes(PriHex + 16, PriHex + 16);
    Bytes2Hex(CypherHexBuf, BufLen, PriHex, 24);
   return 0;
}

StaticDecrypt(char *CypherHex, char *ClearKeyBuf, int BufLen) {
   char cypher[MAX_PASSWORD_LEN + 1], key[MAX_PASSWORD_LEN + 1],
   	PriHex[CYPHER_HEX_LEN];
   int x;
   union TLongStr u;
   int staticOff = StaticOff;
   StaticOff = 0;
   if ( Locked ) return STATIC_LOCK;
   for ( x = 0; CypherHex[x]; x++ );
   if (x != CYPHER_HEX_LEN - 1)
   	return INVALID_CYPHER_HEX - 100;
	if (ClearKeyBuf == 0 || BufLen < MAX_PASSWORD_LEN + 1)
   	return MAX_PASSWORD_LEN + 1;

	for( x = 0; x <= MAX_PASSWORD_LEN; x++)
    	key[x] = FINAL_EXP;

	if ( (x = Hex2Bytes(PriHex, CYPHER_HEX_LEN, CypherHex, CYPHER_HEX_LEN - 1)) != 0)
    	return x < 0 ? x - 200: x + 200;
    desinit(key);
    dedes(PriHex, PriHex);
    dedes(PriHex + 8, PriHex + 8);
    dedes(PriHex + 16, PriHex + 16);


	for ( x = 0; x <= MAX_PASSWORD_LEN; x++)
   	key[x] = ClearKeyBuf[x] = '\0';

	if (!staticOff) {
		desinit(key);
	   Hex2Bytes(key, MAX_PASSWORD_LEN + 1, (ClientKey == 0 ? StaticKey:ClientKey) + 8, 16);
		dedes(key, key);
   }

   Hex2Bytes(u.str, 5, PriHex, 8);
   Hex2Bytes(cypher, MAX_PASSWORD_LEN + 1, PriHex + 8, 16);
   desinit(key);

	dedes(cypher, ClearKeyBuf);
	return u.l == crunch(cypher, 8) ? 0:INVALID_HASH;
}

int GenStaticKey(char *ClearKey, char *CypherHexBuf, int BufLen) {
	StaticOff = 1;
   return StaticEncrypt(ClearKey, CypherHexBuf, BufLen);
}

extern void _stdcall Sleep(int milli);

int StaticUnlock(char *key) {
	char hexBuf[CYPHER_HEX_LEN];
   int x, r;
   if (!Locked) return 0;
   r = GenStaticKey(key, hexBuf, CYPHER_HEX_LEN);
   if (r < 0) return r;
	Sleep(1000);
	for (x = 0; x < CYPHER_HEX_LEN; x++)
   	if (hexBuf[x] != ClientKeyBuf[x])
      	return INVALID_KEY;
	return 0;
}

int SetStaticKey(char *CypherHex, int lock){
	char buf[MAX_PASSWORD_LEN + 1];
	int r, prev = Locked;
   Locked = 0;
   if (!CypherHex){
   	ClientKey = 0;
      return 0;
   }
   StaticOff = 1;
   r = StaticDecrypt(CypherHex, buf, MAX_PASSWORD_LEN + 1);
	if (r != 0){
   	Locked = prev;
   	return r;
   }
   Locked = lock;
   for (r = 0; r < CYPHER_HEX_LEN; r++)
   	ClientKeyBuf[r] = CypherHex[r];
   ClientKey = ClientKeyBuf;
   return 0;
}


//#define TEST
#ifdef TEST
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

FILE *in, *out;

#ifndef UNDERSCORE
WriteIt(char *buf, int cbLen, void *clientData){
	return fwrite(buf, 1, cbLen, out);
}

ReadIt(char *buf, int cbLen, void *clientData){
	return fread(buf, 1, cbLen, in);
}
#else
WriteIt(char *buf, int cbLen, void *clientData){
	return _fwrite(buf, 1, cbLen, out);
}

ReadIt(char *buf, int cbLen, void *clientData){
	return _fread(buf, 1, cbLen, in);
}
#endif
char Junk(){
 	return '#';
}

#ifndef UNDERSCORE

void main() {
	int x;
   char buf[100], buf2[100], buf3[100];
	FILE *f;

if (0) {
	if (GenStaticKey("ezattack", buf, 50) < 0){
   	puts("No Gen");
      getch();
      return;
   } else {
	strcat(buf, "!");
   	puts(buf);
	if ((f = fopen("key.txt", "wb")) == 0) {
    	puts("Cannot open key.txt");
    } else {
		fwrite(buf, 1, 49, f);
        fclose(f);
    }

   }



   if ((x = SetStaticKey(buf)) < 0) {
	strcpy(buf, "No Set: ");
    itoa(x, buf2, 10);
    strcat(buf, buf2);
   	puts(buf);
      getch();
      return;
   } else
   	puts("Set Static");
}
   if ((x = StaticEncrypt("yowee", buf, 50)) != 0) {
	strcpy(buf2, "NO ENC: ");
    itoa(x, buf3, 10);
    strcat(buf2, buf3);
   	puts(buf2);
   } else{
   	strcpy(buf2, "ENC: ");
    strcat(buf2, buf);
    puts(buf2);
    itoa(strlen(buf), buf2, 10);
    strcpy(buf3, "Length SE: ");
    strcat(buf3, buf2);
    puts(buf3);
   }

   if ((x = StaticDecrypt(buf, buf2, 50)) != 0) {
	strcpy(buf2, "NO DEC: ");
    itoa(x, buf3, 10);
    strcat(buf2, buf3);
   	puts(buf2);
   } else
   	puts(buf2);
   getch();
	return;



	if((in = fopen("c:\\des\\des_pc.c", "rb")) == 0)
   	return;

   if ((out = fopen("c:\\des\\des_pc.des", "wb")) == 0) {
   	fclose(in);
      return;
   }

   x = DES_Encrypt("yowee", &ReadIt, 0, &WriteIt, 0, &Junk);
   fclose(in);
   fclose(out);

   if (x!=0) return;

	if((in = fopen("c:\\des\\des_pc.des", "rb")) == 0)
   	return;

   if ((out = fopen("c:\\des\\des_pc2.c", "wb")) == 0) {
   	fclose(in);
      return;
   }

   x = DES_Decrypt("yowee", &ReadIt, 0, &WriteIt, 0);
   fclose(in);
   fclose(out);
}

#else
void _main() {
	int x;
   char buf[100], buf2[100];


	if (GenStaticKey("alice", buf, 50) < 0){
   	_puts("No Gen");
      _getch();
      return;
   } else {
	_strcat(buf, "!");
   	_puts(buf);
   }

   if ((x = SetStaticKey(buf)) < 0) {
	_strcpy(buf, "No Set: ");
    _itoa(x, buf2, 10);
    _strcat(buf, buf2);
   	_puts(buf);
      _getch();
      return;
   } else
   	_puts("Set Static");

   if (StaticEncrypt("yowee", buf, 50) < 0)
   	_puts("No Enc");
   else
   	_puts(buf);
   if (StaticDecrypt(buf, buf2, 50) < 0)
   	_puts("No Dec");
   else
   	_puts(buf2);
   _getch();
	return;



	if((in = _fopen("c:\\des\\des_pc.c", "rb")) == 0)
   	return;

   if ((out = _fopen("c:\\des\\des_pc.des", "wb")) == 0) {
   	_fclose(in);
      return;
   }

   x = DES_Encrypt("yowee", &ReadIt, 0, &WriteIt, 0, &Junk);
   _fclose(in);
   _fclose(out);

   if (x!=0) return;

	if((in = _fopen("c:\\des\\des_pc.des", "rb")) == 0)
   	return;

   if ((out = _fopen("c:\\des\\des_pc2.c", "wb")) == 0) {
   	_fclose(in);
      return;
   }

   x = DES_Decrypt("yowee", &ReadIt, 0, &WriteIt, 0);
   _fclose(in);
   _fclose(out);

}
#endif
#endif
