// MD5/IDEA Cipher Block Chaining
// Adapted from Andy Brown files to Win32 by Immortalware JTHZ

#include "idea.h"

BYTE key[96]; // largest key is 768 bits for MDC
BYTE iv[32]; // largest block size is 256 bits for MDC
BYTE idea_key[104]; // the full 832 bit IDEA key
BYTE inv_idea_key[104]; // inverted full IDEA key

// md5
BYTE PADDING[64]={
	0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};

// F, G, H and I are basic MD5 functions
#define F(x,y,z) ((x&y)|((~x)&z))
#define G(x,y,z) ((x&z)|(y&(~z)))
#define H(x,y,z) (x^y^z)
#define I(x,y,z) (y^(x|(~z)))

// ROTATE_LEFT rotates x left n bits
#define ROTATE_LEFT(x,n) ((x<<n)|(x>>(32-n)))

// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4
// Rotation is separate from addition to prevent recomputation
#define FF(a,b,c,d,x,s,ac) {a+=F(b,c,d)+x+(DWORD)ac;a=ROTATE_LEFT(a,s);a+=b;}
#define GG(a,b,c,d,x,s,ac) {a+=G(b,c,d)+x+(DWORD)ac;a=ROTATE_LEFT(a,s);a+=b;}
#define HH(a,b,c,d,x,s,ac) {a+=H(b,c,d)+x+(DWORD)ac;a=ROTATE_LEFT(a,s);a+=b;}
#define II(a,b,c,d,x,s,ac) {a+=I(b,c,d)+x+(DWORD)ac;a=ROTATE_LEFT(a,s);a+=b;}

// idea defs
#define mulMod 0x10001 // 2**16 + 1
#define addMod 0x10000 // 2**16
#define ones 0xFFFF // 2**16 - 1

#define nofKeyPerRound 6 // number of used keys per round
#define nofRound 8 // number of rounds

#define dataSize 8 // bytes = 64 bits
#define dataLen 4
#define keySize 104 // bytes = 832 bits
#define keyLen 52
#define userKeySize 16 // bytes = 128 bits
#define userKeyLen 8

#define data_t(v) WORD v[dataLen]
#define key_t(v) WORD v[keyLen]
#define userkey_t(v) WORD v[userKeyLen]

#define S11 7
#define S12 12
#define S13 17
#define S14 22
#define S21 5
#define S22 9
#define S23 14
#define S24 20
#define S31 4
#define S32 11
#define S33 16
#define S34 23
#define S41 6
#define S42 10
#define S43 15
#define S44 21

void Encrypt(BYTE *buffer,DWORD len,LPBYTE passphrase){
	generate_keys(passphrase);
	encrypt_buffer(buffer,len);
	zero_vars();
}

void Decrypt(BYTE *buffer,DWORD len,LPBYTE passphrase){
	generate_keys(passphrase);
	decrypt_buffer(buffer,len);
	zero_vars();
}

void generate_keys(LPBYTE passphrase){
	// first generate a key by multiple hashing
	MD5(passphrase,lstrlen((LPCTSTR)passphrase),&key[0]);
	MD5(&key[0],16,&key[16]);
	MD5(&key[16],16,&key[32]);
	MD5(&key[32],16,&key[48]);
	MD5(&key[48],16,&key[64]);
	MD5(&key[64],16,&key[80]);
	// now generate an IV by further hashing
	MD5(&key[80],16,&iv[0]);
	MD5(&iv[0],16,&iv[16]);
}

void MD5(LPBYTE data,int bytes,LPBYTE hash){
	// calculate the MD5 of a file
	int i;
	static MD5_CTX mdContext;
	MD5Init(&mdContext);
 	MD5Update(&mdContext,data,(WORD)bytes);
	MD5Final(&mdContext);
	for(i=0;i<16;i++){
		hash[i]=mdContext.digest[i];
		mdContext.digest[i]=0; // zero it for secrecy
	}
}

void MD5Init(LPMD5_CTX mdContext){
	// The routine MD5Init initializes the message-digest context
	// mdContext. All fields are set to zero.
	mdContext->i[0]=mdContext->i[1]=(DWORD)0;
	// Load magic initialization constants
	mdContext->buf[0]=(DWORD)0x67452301;
	mdContext->buf[1]=(DWORD)0xefcdab89;
	mdContext->buf[2]=(DWORD)0x98badcfe;
	mdContext->buf[3]=(DWORD)0x10325476;
}

void MD5Update (LPMD5_CTX mdContext,LPBYTE inBuf,WORD inLen){
	// The routine MD5Update updates the message-digest context to
	// account for the presence of each of the characters inBuf[0..inLen-1]
	// in the message whose digest is being computed.
	static DWORD in[16];
	int mdi;
	WORD i,ii;
	// compute number of bytes mod 64
	mdi=(int)((mdContext->i[0]>>3)&0x3F);
	// update number of bits
	if((mdContext->i[0]+((DWORD)inLen<<3))<mdContext->i[0])
		mdContext->i[1]++;
	mdContext->i[0]+=((DWORD)inLen<<3);
	mdContext->i[1]+=((DWORD)inLen>>29);
	while(inLen--){
   	// add new character to buffer, increment mdi
    	mdContext->in[mdi++]=*inBuf++;
		if(mdi==0x40){	// transform if necessary
			for(i=0,ii=0;i<16;i++,ii+=(WORD)4)
				in[i]=(((DWORD)mdContext->in[ii+3])<<24) |
						(((DWORD)mdContext->in[ii+2])<<16) |
						(((DWORD)mdContext->in[ii+1])<<8)  |
						((DWORD)mdContext->in[ii]);
			Transform(mdContext->buf,in);
			mdi=0;
		}
	}
}

void MD5Final(LPMD5_CTX mdContext){
	// The routine MD5Final terminates the message-digest computation and
	// ends with the desired message digest in mdContext->digest[0...15].
	static DWORD in[16];
	int mdi;
	WORD i,ii,padLen;
	// save number of bits
	in[14]=mdContext->i[0];
	in[15]=mdContext->i[1];
	// compute number of bytes mod 64
	mdi=(int)((mdContext->i[0]>>3) & 0x3F);
	// pad out to 56 mod 64
	padLen=(WORD)((mdi<56)?(56-mdi):(120-mdi));
	MD5Update(mdContext,PADDING,padLen);
	// append length in bits and transform
	for(i=0,ii=0;i<14;i++,ii+=(WORD)4)
		in[i]=(((DWORD)mdContext->in[ii+3])<<24) |
				(((DWORD)mdContext->in[ii+2])<<16) |
				(((DWORD)mdContext->in[ii+1])<<8)  |
				((DWORD)mdContext->in[ii]);
	Transform(mdContext->buf,in);
	// store buffer in digest
	for(i=0,ii=0;i<4;i++,ii+=(WORD)4){
		mdContext->digest[ii]=(BYTE)(mdContext->buf[i]&0xFF);
		mdContext->digest[ii+1]=(BYTE)((mdContext->buf[i]>>8)&0xFF);
		mdContext->digest[ii+2]=(BYTE)((mdContext->buf[i]>>16)&0xFF);
		mdContext->digest[ii+3]=(BYTE)((mdContext->buf[i]>>24)&0xFF);
	}
}

void Transform(LPDWORD buf,LPDWORD in){
	// Basic MD5 step. Transforms buf based on in
	DWORD a=buf[0],b=buf[1],c=buf[2],d=buf[3];
	// Round 1
	FF(a,b,c,d,in[ 0],S11,3614090360);
	FF(d,a,b,c,in[ 1],S12,3905402710);
	FF(c,d,a,b,in[ 2],S13,606105819);
	FF(b,c,d,a,in[ 3],S14,3250441966);
	FF(a,b,c,d,in[ 4],S11,4118548399);
	FF(d,a,b,c,in[ 5],S12,1200080426);
	FF(c,d,a,b,in[ 6],S13,2821735955);
	FF(b,c,d,a,in[ 7],S14,4249261313);
	FF(a,b,c,d,in[ 8],S11,1770035416);
	FF(d,a,b,c,in[ 9],S12,2336552879);
	FF(c,d,a,b,in[10],S13,4294925233);
	FF(b,c,d,a,in[11],S14,2304563134);
	FF(a,b,c,d,in[12],S11,1804603682);
	FF(d,a,b,c,in[13],S12,4254626195);
	FF(c,d,a,b,in[14],S13,2792965006);
	FF(b,c,d,a,in[15],S14,1236535329);
	// Round 2
	GG(a,b,c,d,in[ 1],S21,4129170786);
	GG(d,a,b,c,in[ 6],S22,3225465664);
	GG(c,d,a,b,in[11],S23,643717713);
	GG(b,c,d,a,in[ 0],S24,3921069994);
	GG(a,b,c,d,in[ 5],S21,3593408605);
	GG(d,a,b,c,in[10],S22,38016083);
	GG(c,d,a,b,in[15],S23,3634488961);
	GG(b,c,d,a,in[ 4],S24,3889429448);
	GG(a,b,c,d,in[ 9],S21,568446438);
	GG(d,a,b,c,in[14],S22,3275163606);
	GG(c,d,a,b,in[ 3],S23,4107603335);
	GG(b,c,d,a,in[ 8],S24,1163531501);
	GG(a,b,c,d,in[13],S21,2850285829);
	GG(d,a,b,c,in[ 2],S22,4243563512);
	GG(c,d,a,b,in[ 7],S23,1735328473);
	GG(b,c,d,a,in[12],S24,2368359562);
	// Round 3
	HH(a,b,c,d,in[ 5],S31,4294588738);
	HH(d,a,b,c,in[ 8],S32,2272392833);
	HH(c,d,a,b,in[11],S33,1839030562);
	HH(b,c,d,a,in[14],S34,4259657740);
	HH(a,b,c,d,in[ 1],S31,2763975236);
	HH(d,a,b,c,in[ 4],S32,1272893353);
	HH(c,d,a,b,in[ 7],S33,4139469664);
	HH(b,c,d,a,in[10],S34,3200236656);
	HH(a,b,c,d,in[13],S31,681279174);
	HH(d,a,b,c,in[ 0],S32,3936430074);
	HH(c,d,a,b,in[ 3],S33,3572445317);
	HH(b,c,d,a,in[ 6],S34,76029189);
	HH(a,b,c,d,in[ 9],S31,3654602809);
	HH(d,a,b,c,in[12],S32,3873151461);
	HH(c,d,a,b,in[15],S33,530742520);
	HH(b,c,d,a,in[ 2],S34,3299628645);
  	// Round 4
	II(a,b,c,d,in[ 0],S41,4096336452);
	II(d,a,b,c,in[ 7],S42,1126891415);
	II(c,d,a,b,in[14],S43,2878612391);
	II(b,c,d,a,in[ 5],S44,4237533241);
	II(a,b,c,d,in[12],S41,1700485571);
	II(d,a,b,c,in[ 3],S42,2399980690);
	II(c,d,a,b,in[10],S43,4293915773);
	II(b,c,d,a,in[ 1],S44,2240044497);
	II(a,b,c,d,in[ 8],S41,1873313359);
	II(d,a,b,c,in[15],S42,4264355552);
	II(c,d,a,b,in[ 6],S43,2734768916);
	II(b,c,d,a,in[13],S44,1309151649);
	II(a,b,c,d,in[ 4],S41,4149444226);
	II(d,a,b,c,in[11],S42,3174756917);
	II(c,d,a,b,in[ 2],S43,718787259);
	II(b,c,d,a,in[ 9],S44,3951481745);
	buf[0]+=a;
	buf[1]+=b;
	buf[2]+=c;
	buf[3]+=d;
}


void encrypt_buffer(BYTE *buffer,DWORD len){
	static BYTE block[32],civ[32];
	DWORD i,blocksize;
	// cipher initialisation
	ExpandUserKey((LPWORD)key,(LPWORD)idea_key);
	blocksize=8;
	CopyMemory(civ,iv,blocksize);
	for(i=0;i<len;i+=blocksize){
	   CopyMemory(block,&buffer[i],blocksize);
		xor(block,civ,blocksize);
		Idea((LPWORD)block,(LPWORD)block,(LPWORD)idea_key);
		CopyMemory(civ,block,blocksize);
		CopyMemory(&buffer[i],block,blocksize);
	}
}

void decrypt_buffer(BYTE *buffer,DWORD len){
	static BYTE block[32],temp[32],civ[32];
	DWORD i,blocksize;
	// cipher initialisation
	ExpandUserKey((LPWORD)key,(LPWORD)idea_key);
	InvertIdeaKey((LPWORD)idea_key,(LPWORD)inv_idea_key);
	blocksize=8;
	CopyMemory(civ,iv,blocksize);
	for(i=0;i<len;i+=blocksize){
		CopyMemory(block,&buffer[i],blocksize);
		CopyMemory(temp,block,blocksize);
		Idea((LPWORD)block,(LPWORD)block,(LPWORD)inv_idea_key);
		xor(block,civ,blocksize);
		CopyMemory(civ,temp,blocksize);
		CopyMemory(&buffer[i],block,blocksize);
	}
}

DWORD Mul(DWORD a,DWORD b){
	LONG p;
	DWORD q;
	if(!a)
   	p=mulMod-b;
	else if(!b)
   	p=mulMod-a;
	else{
		q=a*b;
    	p=(q&ones)-(q>>16);
    	if(p<=0)
      	p+=mulMod;
	}
	return(DWORD)(p&ones);
}

WORD MulInv(WORD x){
	// compute inverse of 'x' by Euclidean gcd algorithm
	LONG n1,n2,q,r,b1,b2,t;
	if(!x)
   	return 0;
	n1=mulMod;
	n2=(LONG)x;
	b2=1;
	b1=0;
	do{
		r=(n1%n2);
    	q=(n1-r)/n2;
   	if(!r){
    		if(b2<0)
      		b2=mulMod+b2;
		}
		else{
    		n1=n2;
      	n2=r;
      	t=b2;
      	b2=b1-q*b2;
      	b1=t;
    	}
	}while(r);
	return(WORD)b2;
}

void Idea(LPWORD dataIn,LPWORD dataOut,LPWORD key){
	// encryption and decryption algorithm IDEA
	DWORD round,x0,x1,x2,x3,t0,t1;
	x0=(DWORD)*(dataIn++);
	x1=(DWORD)*(dataIn++);
	x2=(DWORD)*(dataIn++);
	x3=(DWORD)*(dataIn);
	for(round=nofRound;round>0;round--){
		x0=Mul(x0,(DWORD)*(key++));
		x1=(x1+(DWORD)*(key++)) & ones;
		x2=(x2+(DWORD)*(key++)) & ones;
		x3=Mul(x3,(DWORD)*(key++));
		t0=Mul((DWORD)*(key++),x0^x2);
		t1=Mul((DWORD)*(key++),(t0+(x1^x3)) & ones);
		t0=(t0+t1) & ones;
		x0^=t1;
		x3^=t0;
		t0^=x1;
		x1=x2^t1;
		x2=t0;
  	}
	*(dataOut++)=(WORD)(Mul(x0,(DWORD)*(key++)));
	*(dataOut++)=(WORD)((x2+(DWORD)*(key++))&ones);
	*(dataOut++)=(WORD)((x1+(DWORD)*(key++))&ones);
	*(dataOut)=(WORD)(Mul(x3,(DWORD)*key));
}

void InvertIdeaKey(LPWORD key,LPWORD invKey){
	// invert decryption/encrytion key for IDEA
	int i;
	key_t(dk);
	dk[nofKeyPerRound*nofRound+0]=MulInv(*(key++));
	dk[nofKeyPerRound*nofRound+1]=(WORD)((addMod-*(key++))&ones);
	dk[nofKeyPerRound*nofRound+2]=(WORD)((addMod-*(key++))&ones);
	dk[nofKeyPerRound*nofRound+3]=MulInv(*(key++));
	for(i=nofKeyPerRound*(nofRound-1);i>=0;i-=nofKeyPerRound){
		dk[i+4]=*(key++);
		dk[i+5]=*(key++);
		dk[i+0]=MulInv(*(key++));
		if(i>0){
			dk[i+2]=(WORD)((addMod-*(key++))&ones);
			dk[i+1]=(WORD)((addMod-*(key++))&ones);
		}
		else{
			dk[i+1]=(WORD)((addMod-*(key++))&ones);
			dk[i+2]=(WORD)((addMod-*(key++))&ones);
		}
		dk[i+3]=MulInv(*(key++));
	}
	for(i=0;i<keyLen;i++)
   	invKey[i]=dk[i];
}

void ExpandUserKey(LPWORD userKey,LPWORD key){
	// expand user key of 128 bits to full key of 832 bits
	int i;
	for(i=0;i<userKeyLen;i++)
		key[i]=userKey[i];
	for(i=userKeyLen;i<keyLen;i++){ // shifts
		if((i+2)%8==0) // for key[14],key[22],..
			key[i]=(WORD)(((key[i-7]&127)<<9)^(key[i-14]>>7));
		else if((i+1)%8==0) // for key[15],key[23],..
			key[i]=(WORD)(((key[i-15]&127)<<9)^(key[i-14]>>7));
		else
		key[i]=(WORD)(((key[i-7]&127)<<9)^(key[i-6]>>7));
	}
}

void xor(LPBYTE dest,LPBYTE source,DWORD size){
	for(DWORD i=0;i<size;i++)
	  	dest[i]^=source[i];
}

void zero_vars(){
	// Zero the key and IV
	ZeroMemory(key,96);
	ZeroMemory(iv,32);
	ZeroMemory(idea_key,104);
	ZeroMemory(inv_idea_key,104);
}


