// MD4 cipher by Ruptor

#include <stdlib.h>
#include "md4.h"

// MD4 "magic constants".

#define I0  0x67452301		// Initial values for MD buffer
#define I1  0xefcdab89
#define I2  0x98badcfe
#define I3  0x10325476
#define C2  0x5A827999		// round 2 constant = sqrt(2)
#define C3  0x6ED9EBA1		// round 3 constant = sqrt(3)

// C2 and C3 are from Knuth, The Art of Programming, Volume 2
// (Seminumerical Algorithms), Second Edition (1981), Addison-Wesley.
// Table 2, page 660.

#define fs1  3               // round 1 shift amounts
#define fs2  7
#define fs3 11
#define fs4 19
#define gs1  3               // round 2 shift amounts
#define gs2  5
#define gs3  9
#define gs4 13
#define hs1  3               // round 3 shift amounts
#define hs2  9
#define hs3 11
#define hs4 15

// Macro declarations for MD4

#define f(X,Y,Z)			((X&(Y^Z))^Z)
#define g(X,Y,Z)			(((X|Y)&Z)|(X&Y))
#define h(X,Y,Z)			(X^Y^Z)

#define MD4_F0(A,B,C,D,i,s)		A = _lrotl((A + f(B,C,D) + key[i]), s)
#define MD4_F1(A,B,C,D,i,s)		A = _lrotl((A + g(B,C,D) + key[i] + C2), s)
#define MD4_F2(A,B,C,D,i,s)		A = _lrotl((A + h(B,C,D) + key[i] + C3), s)

#define MD4_R0(A,B,C,D,i,s)		A = (_lrotr(A, s) - f(B,C,D) - key[i])
#define MD4_R1(A,B,C,D,i,s)		A = (_lrotr(A, s) - g(B,C,D) - key[i] - C2)
#define MD4_R2(A,B,C,D,i,s)		A = (_lrotr(A, s) - h(B,C,D) - key[i] - C3)

static void __fastcall MD4_encrypt (const unsigned long *data_in, unsigned long *data_out, const unsigned long *key)
{
	register unsigned long		A = data_in[0], B = data_in[1], C = data_in[2], D = data_in[3];
	
	MD4_F0(A , B , C , D ,  0 , fs1);
	MD4_F0(D , A , B , C ,  1 , fs2);
	MD4_F0(C , D , A , B ,  2 , fs3);
	MD4_F0(B , C , D , A ,  3 , fs4);
	MD4_F0(A , B , C , D ,  4 , fs1);
	MD4_F0(D , A , B , C ,  5 , fs2);
	MD4_F0(C , D , A , B ,  6 , fs3);
	MD4_F0(B , C , D , A ,  7 , fs4);
	MD4_F0(A , B , C , D ,  8 , fs1);
	MD4_F0(D , A , B , C ,  9 , fs2);
	MD4_F0(C , D , A , B , 10 , fs3);
	MD4_F0(B , C , D , A , 11 , fs4);
	MD4_F0(A , B , C , D , 12 , fs1);
	MD4_F0(D , A , B , C , 13 , fs2);
	MD4_F0(C , D , A , B , 14 , fs3);
	MD4_F0(B , C , D , A , 15 , fs4);
	
	MD4_F1(A , B , C , D ,  0 , gs1);
	MD4_F1(D , A , B , C ,  4 , gs2);
	MD4_F1(C , D , A , B ,  8 , gs3);
	MD4_F1(B , C , D , A , 12 , gs4);
	MD4_F1(A , B , C , D ,  1 , gs1);
	MD4_F1(D , A , B , C ,  5 , gs2);
	MD4_F1(C , D , A , B ,  9 , gs3);
	MD4_F1(B , C , D , A , 13 , gs4);
	MD4_F1(A , B , C , D ,  2 , gs1);
	MD4_F1(D , A , B , C ,  6 , gs2);
	MD4_F1(C , D , A , B , 10 , gs3);
	MD4_F1(B , C , D , A , 14 , gs4);
	MD4_F1(A , B , C , D ,  3 , gs1);
	MD4_F1(D , A , B , C ,  7 , gs2);
	MD4_F1(C , D , A , B , 11 , gs3);
	MD4_F1(B , C , D , A , 15 , gs4);
	
	MD4_F2(A , B , C , D ,  0 , hs1);
	MD4_F2(D , A , B , C ,  8 , hs2);
	MD4_F2(C , D , A , B ,  4 , hs3);
	MD4_F2(B , C , D , A , 12 , hs4);
	MD4_F2(A , B , C , D ,  2 , hs1);
	MD4_F2(D , A , B , C , 10 , hs2);
	MD4_F2(C , D , A , B ,  6 , hs3);
	MD4_F2(B , C , D , A , 14 , hs4);
	MD4_F2(A , B , C , D ,  1 , hs1);
	MD4_F2(D , A , B , C ,  9 , hs2);
	MD4_F2(C , D , A , B ,  5 , hs3);
	MD4_F2(B , C , D , A , 13 , hs4);
	MD4_F2(A , B , C , D ,  3 , hs1);
	MD4_F2(D , A , B , C , 11 , hs2);
	MD4_F2(C , D , A , B ,  7 , hs3);
	MD4_F2(B , C , D , A , 15 , hs4);
	
	data_out[0] = A; data_out[1] = B; data_out[2] = C; data_out[3] = D;
}

static void __fastcall MD4_decrypt (const unsigned long *data_in, unsigned long *data_out, const unsigned long *key)
{
	register unsigned long		A = data_in[0], B = data_in[1], C = data_in[2], D = data_in[3];
	
	MD4_R2(B , C , D , A , 15 , hs4);
	MD4_R2(C , D , A , B ,  7 , hs3);
	MD4_R2(D , A , B , C , 11 , hs2);
	MD4_R2(A , B , C , D ,  3 , hs1);
	MD4_R2(B , C , D , A , 13 , hs4);
	MD4_R2(C , D , A , B ,  5 , hs3);
	MD4_R2(D , A , B , C ,  9 , hs2);
	MD4_R2(A , B , C , D ,  1 , hs1);
	MD4_R2(B , C , D , A , 14 , hs4);
	MD4_R2(C , D , A , B ,  6 , hs3);
	MD4_R2(D , A , B , C , 10 , hs2);
	MD4_R2(A , B , C , D ,  2 , hs1);
	MD4_R2(B , C , D , A , 12 , hs4);
	MD4_R2(C , D , A , B ,  4 , hs3);
	MD4_R2(D , A , B , C ,  8 , hs2);
	MD4_R2(A , B , C , D ,  0 , hs1);
	
	MD4_R1(B , C , D , A , 15 , gs4);
	MD4_R1(C , D , A , B , 11 , gs3);
	MD4_R1(D , A , B , C ,  7 , gs2);
	MD4_R1(A , B , C , D ,  3 , gs1);
	MD4_R1(B , C , D , A , 14 , gs4);
	MD4_R1(C , D , A , B , 10 , gs3);
	MD4_R1(D , A , B , C ,  6 , gs2);
	MD4_R1(A , B , C , D ,  2 , gs1);
	MD4_R1(B , C , D , A , 13 , gs4);
	MD4_R1(C , D , A , B ,  9 , gs3);
	MD4_R1(D , A , B , C ,  5 , gs2);
	MD4_R1(A , B , C , D ,  1 , gs1);
	MD4_R1(B , C , D , A , 12 , gs4);
	MD4_R1(C , D , A , B ,  8 , gs3);
	MD4_R1(D , A , B , C ,  4 , gs2);
	MD4_R1(A , B , C , D ,  0 , gs1);
	
	MD4_R0(B , C , D , A , 15 , fs4);
	MD4_R0(C , D , A , B , 14 , fs3);
	MD4_R0(D , A , B , C , 13 , fs2);
	MD4_R0(A , B , C , D , 12 , fs1);
	MD4_R0(B , C , D , A , 11 , fs4);
	MD4_R0(C , D , A , B , 10 , fs3);
	MD4_R0(D , A , B , C ,  9 , fs2);
	MD4_R0(A , B , C , D ,  8 , fs1);
	MD4_R0(B , C , D , A ,  7 , fs4);
	MD4_R0(C , D , A , B ,  6 , fs3);
	MD4_R0(D , A , B , C ,  5 , fs2);
	MD4_R0(A , B , C , D ,  4 , fs1);
	MD4_R0(B , C , D , A ,  3 , fs4);
	MD4_R0(C , D , A , B ,  2 , fs3);
	MD4_R0(D , A , B , C ,  1 , fs2);
	MD4_R0(A , B , C , D ,  0 , fs1);
	
	data_out[0] = A; data_out[1] = B; data_out[2] = C; data_out[3] = D;
}
