//
// ICQDecrypt.c
//
// This code is provided AS IS and is placed
// within the public domain.
//
// This code has been compiled on an Amiga
// running SAS/C 6.57, without errors.
// No Amiga-specific functions are called,
// so it should run on any other system too.
//

#include <exec/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

void HexDump(UBYTE *data, LONG size)
{
	LONG i;
	LONG x;
	BOOL r=TRUE;
	UBYTE bfr[64];

	for(i=0, x=0;;i++) {

		if(i<size) {
			if(x==0) printf("%04lx: ", i);

			printf("%02x ", data[i]);
			bfr[x]=data[i];

			if(bfr[x]<0x20) bfr[x]='.';
			if(bfr[x]>0x7f) bfr[x]='.';
		} else {
			if(x==0) break;
			else {
				printf("   ");
				bfr[x]=' ';
				r=FALSE;
			}
		}

		x++;

		if(!(x<16)) {
			bfr[x]=0;
			printf("%s\n", bfr);
			x=0;
			if(!r) break;
		}
	}
}

BOOL LoadBlock(FILE *fp, UBYTE *bfr, int len, int *size)
{
	UBYTE *p=bfr;
	*size=0;

	while(!feof(fp)) {
		ULONG b;

		do {
			b=fgetc(fp);
			if(b!=' ' && b!='\t' && b!='\n' && b!='r') break;
		} while(b!=EOF);

		if(b==EOF) {
			return TRUE;
		}

		if(b>='0' && b<='9')
			*p=(b-'0')*0x10;
		else if(toupper(b)>='A' && toupper(b)<='F')
			*p=(b-'A'+0x0a)*0x10;
		else {
			printf("Invalid character: %c\n", b);
			return FALSE;
		}

		b=fgetc(fp);
		if(b==EOF) {
			printf("EOF\n");
			return FALSE;
		}

		if(b>='0' && b<='9')
			*p|=b-'0';
		else if(toupper(b)>='A' && toupper(b)<='F')
			*p|=(b-'A'+0x0a)&0x0f;
		else {
			printf("Invalid character: %c\n", b);
			return FALSE;
		}

		p++; *size=*size+1;

		if(*size>len) {
			printf("Buffer too small. (%ld > %ld)\n", *size, len);
			return FALSE;
		}
	}

	return TRUE;
}

UBYTE icq_check_data[256] = {
	0x0a, 0x5b, 0x31, 0x5d, 0x20, 0x59, 0x6f, 0x75, 
	0x20, 0x63, 0x61, 0x6e, 0x20, 0x6d, 0x6f, 0x64, 
	0x69, 0x66, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 
	0x73, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x20, 0x49,
	0x43, 0x51, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x73, 
	0x2e, 0x20, 0x4a, 0x75, 0x73, 0x74, 0x20, 0x73, 
	0x65, 0x6c, 0x65, 0x63, 0x74, 0x20, 0x22, 0x53, 
	0x6f, 0x75, 0x6e, 0x64, 0x73, 0x22, 0x20, 0x66, 
	0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20,
	0x22, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
	0x6e, 0x63, 0x65, 0x73, 0x2f, 0x6d, 0x69, 0x73,
	0x63, 0x22, 0x20, 0x69, 0x6e, 0x20, 0x49, 0x43,
	0x51, 0x20, 0x6f, 0x72, 0x20, 0x66, 0x72, 0x6f,
	0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x22, 0x53,
	0x6f, 0x75, 0x6e, 0x64, 0x73, 0x22, 0x20, 0x69,
	0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
	0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x20, 0x70, 0x61,
	0x6e, 0x65, 0x6c, 0x2e, 0x20, 0x43, 0x72, 0x65,
	0x64, 0x69, 0x74, 0x3a, 0x20, 0x45, 0x72, 0x61,
	0x6e, 0x0a, 0x5b, 0x32, 0x5d, 0x20, 0x43, 0x61,
	0x6e, 0x27, 0x74, 0x20, 0x72, 0x65, 0x6d, 0x65,
	0x6d, 0x62, 0x65, 0x72, 0x20, 0x77, 0x68, 0x61,
	0x74, 0x20, 0x77, 0x61, 0x73, 0x20, 0x73, 0x61,
	0x69, 0x64, 0x3f, 0x20, 0x20, 0x44, 0x6f, 0x75,
	0x62, 0x6c, 0x65, 0x2d, 0x63, 0x6c, 0x69, 0x63,
	0x6b, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x75,
	0x73, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x67,
	0x65, 0x74, 0x20, 0x61, 0x20, 0x64, 0x69, 0x61,
	0x6c, 0x6f, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x61,
	0x6c, 0x6c, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61,
	0x67, 0x65, 0x73, 0x20, 0x73, 0x65, 0x6e, 0x74,
	0x20, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e
};

ULONG CheckSum(UBYTE *pkt, ULONG size)
{
	ULONG chk1, chk, ofs;
	UBYTE check[4];

	check[3] = (UBYTE)(pkt[0x10]);
	check[2] = (UBYTE)(pkt[0x11]);
	check[1] = (UBYTE)(pkt[0x12]);
	check[0] = (UBYTE)(pkt[0x13]);

	chk=*((ULONG *)check);


	chk1=(*(pkt + 8) << 24) |
	     (*(pkt + 4) << 16) |
	     (*(pkt + 2) << 8 ) |
	     (*(pkt + 6));

	chk1^=chk;

	chk1^=0x00FF00FF;

	ofs=(chk1>>8) & 0x000000FF;
	printf("%08lx, %08lx (%ld)\n",icq_check_data[ofs], (chk1 & 0x000000FF), ofs);
	if(icq_check_data[ofs]==(chk1 & 0x000000FF)) {
		ofs=(chk1>>24) & 0x000000FF;
		if((*(pkt+ofs))==((chk1>>16) & 0x000000FF)) {
			printf("Checksum OK!\n");
			return 0;
		}
	}

	printf("Checksum FAILED\n");

	return 0;
}

ULONG ReverseLong(ULONG l)
{
	UBYTE z[4];

	z[3] = (UBYTE)((l)>>24)& 0x000000FF;
	z[2] = (UBYTE)((l)>>16)& 0x000000FF;
	z[1] = (UBYTE)((l)>>8)& 0x000000FF;
	z[0] = (UBYTE)(l) & 0x000000FF;

	return *((ULONG *)z);
}

ULONG Decrypt(UBYTE *pkt, ULONG size, ULONG key)
{
	int count;
	int i;

	count =(size + 3) / 4;
	count+=3;
	count/=4;
 
	for(i=0;i<count;i++) {
		ULONG *r;

		if(i!=4) {	// checksum is not encrypted
			r=(ULONG *)(pkt + (i*4));
			*r^=ReverseLong((key + icq_check_data[i*4]));
		}
	}

	pkt[0]=0x04;
	pkt[1]=0x00;
}


ULONG GetKey(char *bfr, ULONG size)
{
	ULONG key;
	UBYTE check[4];

	check[3] = (UBYTE)(bfr[0x10]);
	check[2] = (UBYTE)(bfr[0x11]);
	check[1] = (UBYTE)(bfr[0x12]);
	check[0] = (UBYTE)(bfr[0x13]);

	printf("Check: %lx\n", *((ULONG *)check));

	key=0x66756B65 * size;
	key+=*((ULONG *)check);

	return key;
}

void main(int ac, char *av[])
{
	FILE *fp;

	if(ac!=2) {
		printf("Argument missing\n");
		exit(0);
	}

	fp=fopen(av[1], "r");
	if(fp) {
		UBYTE bfr[512];
		int size;
		ULONG key;

		if(LoadBlock(fp, bfr, 512, &size)) {
			printf("Loaded packet:\n");
			HexDump(bfr, size);

			key=GetKey(bfr, size);
			
			printf("Key: %08lx\n", key);
			
			Decrypt(bfr, size, key);
			
			printf("Decrypted packet:\n");
			HexDump(bfr, size);

			CheckSum(bfr, size);
		} else printf("Error interpreting file. (%ld)\n", size);

		fclose(fp);
	} else printf("Could not open file\n");
}
