/*
 * MBC2 reference implementation.
 *
 * Algorithm developed by Michael W. Bombardieri <bombardierix@gmail.com>
 *
 * Version 0.0.1 (2006.09.21)
 *
 * The following code is released into the public domain by its author.
 */


/* convert in_blk to 1 bit array blk */
#define get_blk()                                           \
	blk[ 0] = (u1byte)((in_blk[0] & 0x80000000) >> 31); \
	blk[ 1] = (u1byte)((in_blk[0] & 0x40000000) >> 30); \
	blk[ 2] = (u1byte)((in_blk[0] & 0x20000000) >> 29); \
	blk[ 3] = (u1byte)((in_blk[0] & 0x10000000) >> 28); \
	blk[ 4] = (u1byte)((in_blk[0] & 0x08000000) >> 27); \
	blk[ 5] = (u1byte)((in_blk[0] & 0x04000000) >> 26); \
	blk[ 6] = (u1byte)((in_blk[0] & 0x02000000) >> 25); \
	blk[ 7] = (u1byte)((in_blk[0] & 0x01000000) >> 24); \
	blk[ 8] = (u1byte)((in_blk[0] & 0x00800000) >> 23); \
	blk[ 9] = (u1byte)((in_blk[0] & 0x00400000) >> 22); \
	blk[10] = (u1byte)((in_blk[0] & 0x00200000) >> 21); \
	blk[11] = (u1byte)((in_blk[0] & 0x00100000) >> 20); \
	blk[12] = (u1byte)((in_blk[0] & 0x00080000) >> 19); \
	blk[13] = (u1byte)((in_blk[0] & 0x00040000) >> 18); \
	blk[14] = (u1byte)((in_blk[0] & 0x00020000) >> 17); \
	blk[15] = (u1byte)((in_blk[0] & 0x00010000) >> 16); \
	blk[16] = (u1byte)((in_blk[0] & 0x00008000) >> 15); \
	blk[17] = (u1byte)((in_blk[0] & 0x00004000) >> 14); \
	blk[18] = (u1byte)((in_blk[0] & 0x00002000) >> 13); \
	blk[19] = (u1byte)((in_blk[0] & 0x00001000) >> 12); \
	blk[20] = (u1byte)((in_blk[0] & 0x00000800) >> 11); \
	blk[21] = (u1byte)((in_blk[0] & 0x00000400) >> 10); \
	blk[22] = (u1byte)((in_blk[0] & 0x00000200) >>  9); \
	blk[23] = (u1byte)((in_blk[0] & 0x00000100) >>  8); \
	blk[24] = (u1byte)((in_blk[0] & 0x00000080) >>  7); \
	blk[25] = (u1byte)((in_blk[0] & 0x00000040) >>  6); \
	blk[26] = (u1byte)((in_blk[0] & 0x00000020) >>  5); \
	blk[27] = (u1byte)((in_blk[0] & 0x00000010) >>  4); \
	blk[28] = (u1byte)((in_blk[0] & 0x00000008) >>  3); \
	blk[29] = (u1byte)((in_blk[0] & 0x00000004) >>  2); \
	blk[30] = (u1byte)((in_blk[0] & 0x00000002) >>  1); \
	blk[31] = (u1byte)(in_blk[0] & 0x00000001);         \
	blk[32] = (u1byte)((in_blk[1] & 0x80000000) >> 31); \
	blk[33] = (u1byte)((in_blk[1] & 0x40000000) >> 30); \
	blk[34] = (u1byte)((in_blk[1] & 0x20000000) >> 29); \
	blk[35] = (u1byte)((in_blk[1] & 0x10000000) >> 28); \
	blk[36] = (u1byte)((in_blk[1] & 0x08000000) >> 27); \
	blk[37] = (u1byte)((in_blk[1] & 0x04000000) >> 26); \
	blk[38] = (u1byte)((in_blk[1] & 0x02000000) >> 25); \
	blk[39] = (u1byte)((in_blk[1] & 0x01000000) >> 24); \
	blk[40] = (u1byte)((in_blk[1] & 0x00800000) >> 23); \
	blk[41] = (u1byte)((in_blk[1] & 0x00400000) >> 22); \
	blk[42] = (u1byte)((in_blk[1] & 0x00200000) >> 21); \
	blk[43] = (u1byte)((in_blk[1] & 0x00100000) >> 20); \
	blk[44] = (u1byte)((in_blk[1] & 0x00080000) >> 19); \
	blk[45] = (u1byte)((in_blk[1] & 0x00040000) >> 18); \
	blk[46] = (u1byte)((in_blk[1] & 0x00020000) >> 17); \
	blk[47] = (u1byte)((in_blk[1] & 0x00010000) >> 16); \
	blk[48] = (u1byte)((in_blk[1] & 0x00008000) >> 15); \
	blk[49] = (u1byte)((in_blk[1] & 0x00004000) >> 14); \
	blk[50] = (u1byte)((in_blk[1] & 0x00002000) >> 13); \
	blk[51] = (u1byte)((in_blk[1] & 0x00001000) >> 12); \
	blk[52] = (u1byte)((in_blk[1] & 0x00000800) >> 11); \
	blk[53] = (u1byte)((in_blk[1] & 0x00000400) >> 10); \
	blk[54] = (u1byte)((in_blk[1] & 0x00000200) >>  9); \
	blk[55] = (u1byte)((in_blk[1] & 0x00000100) >>  8); \
	blk[56] = (u1byte)((in_blk[1] & 0x00000080) >>  7); \
	blk[57] = (u1byte)((in_blk[1] & 0x00000040) >>  6); \
	blk[58] = (u1byte)((in_blk[1] & 0x00000020) >>  5); \
	blk[59] = (u1byte)((in_blk[1] & 0x00000010) >>  4); \
	blk[60] = (u1byte)((in_blk[1] & 0x00000008) >>  3); \
	blk[61] = (u1byte)((in_blk[1] & 0x00000004) >>  2); \
	blk[62] = (u1byte)((in_blk[1] & 0x00000002) >>  1); \
	blk[63] = (u1byte)(in_blk[1] & 0x00000001)

/* convert blk back to in_blk */
#define put_blk()                             \
	in_blk[0] = 0;                        \
	in_blk[1] = 0;                        \
	in_blk[0] += (u4byte)(blk[ 0] << 31); \
	in_blk[0] += (u4byte)(blk[ 1] << 30); \
	in_blk[0] += (u4byte)(blk[ 2] << 29); \
	in_blk[0] += (u4byte)(blk[ 3] << 28); \
	in_blk[0] += (u4byte)(blk[ 4] << 27); \
	in_blk[0] += (u4byte)(blk[ 5] << 26); \
	in_blk[0] += (u4byte)(blk[ 6] << 25); \
	in_blk[0] += (u4byte)(blk[ 7] << 24); \
	in_blk[0] += (u4byte)(blk[ 8] << 23); \
	in_blk[0] += (u4byte)(blk[ 9] << 22); \
	in_blk[0] += (u4byte)(blk[10] << 21); \
	in_blk[0] += (u4byte)(blk[11] << 20); \
	in_blk[0] += (u4byte)(blk[12] << 19); \
	in_blk[0] += (u4byte)(blk[13] << 18); \
	in_blk[0] += (u4byte)(blk[14] << 17); \
	in_blk[0] += (u4byte)(blk[15] << 16); \
	in_blk[0] += (u4byte)(blk[16] << 15); \
	in_blk[0] += (u4byte)(blk[17] << 14); \
	in_blk[0] += (u4byte)(blk[18] << 13); \
	in_blk[0] += (u4byte)(blk[19] << 12); \
	in_blk[0] += (u4byte)(blk[20] << 11); \
	in_blk[0] += (u4byte)(blk[21] << 10); \
	in_blk[0] += (u4byte)(blk[22] <<  9); \
	in_blk[0] += (u4byte)(blk[23] <<  8); \
	in_blk[0] += (u4byte)(blk[24] <<  7); \
	in_blk[0] += (u4byte)(blk[25] <<  6); \
	in_blk[0] += (u4byte)(blk[26] <<  5); \
	in_blk[0] += (u4byte)(blk[27] <<  4); \
	in_blk[0] += (u4byte)(blk[28] <<  3); \
	in_blk[0] += (u4byte)(blk[29] <<  2); \
	in_blk[0] += (u4byte)(blk[30] <<  1); \
	in_blk[0] += (u4byte)(blk[31]);       \
	in_blk[1] += (u4byte)(blk[32] << 31); \
	in_blk[1] += (u4byte)(blk[33] << 30); \
	in_blk[1] += (u4byte)(blk[34] << 29); \
	in_blk[1] += (u4byte)(blk[35] << 28); \
	in_blk[1] += (u4byte)(blk[36] << 27); \
	in_blk[1] += (u4byte)(blk[37] << 26); \
	in_blk[1] += (u4byte)(blk[38] << 25); \
	in_blk[1] += (u4byte)(blk[39] << 24); \
	in_blk[1] += (u4byte)(blk[40] << 23); \
	in_blk[1] += (u4byte)(blk[41] << 22); \
	in_blk[1] += (u4byte)(blk[42] << 21); \
	in_blk[1] += (u4byte)(blk[43] << 20); \
	in_blk[1] += (u4byte)(blk[44] << 19); \
	in_blk[1] += (u4byte)(blk[45] << 18); \
	in_blk[1] += (u4byte)(blk[46] << 17); \
	in_blk[1] += (u4byte)(blk[47] << 16); \
	in_blk[1] += (u4byte)(blk[48] << 15); \
	in_blk[1] += (u4byte)(blk[49] << 14); \
	in_blk[1] += (u4byte)(blk[50] << 13); \
	in_blk[1] += (u4byte)(blk[51] << 12); \
	in_blk[1] += (u4byte)(blk[52] << 11); \
	in_blk[1] += (u4byte)(blk[53] << 10); \
	in_blk[1] += (u4byte)(blk[54] <<  9); \
	in_blk[1] += (u4byte)(blk[55] <<  8); \
	in_blk[1] += (u4byte)(blk[56] <<  7); \
	in_blk[1] += (u4byte)(blk[57] <<  6); \
	in_blk[1] += (u4byte)(blk[58] <<  5); \
	in_blk[1] += (u4byte)(blk[59] <<  4); \
	in_blk[1] += (u4byte)(blk[60] <<  3); \
	in_blk[1] += (u4byte)(blk[61] <<  2); \
	in_blk[1] += (u4byte)(blk[62] <<  1); \
	in_blk[1] += (u4byte)(blk[63])

typedef unsigned long u4byte;
typedef unsigned char u1byte;

static u1byte key[588];	 /* key schedule */

void
set_key(u4byte in_key[4])
{
	u1byte blk[16];	 	/* expansion of in_key */
	u1byte f[4];		/* cipher feedback */
	int i;

	for (i = 0; i < 588; i++) { /* initialise key[] */
		key[i] = (u1byte)(i % 256);
	}

	/* initialise blk[] using half of key data */
	f[0] = (u1byte)((in_key[0] & 0xff000000) >> 24);
	f[1] = (u1byte)((in_key[1] & 0xff000000) >> 24);
	f[2] = (u1byte)((in_key[2] & 0xff000000) >> 24);
	f[3] = (u1byte)((in_key[3] & 0xff000000) >> 24);

	blk[0] = f[0] ^ f[1];
	blk[1] = f[0] ^ f[2];
	blk[2] = f[0] ^ f[3];
	blk[3] = f[1] ^ f[2];
	blk[4] = f[1] ^ f[3];
	blk[5] = f[2] ^ f[3];
	blk[6] = f[0];
	blk[7] = f[1];
	blk[8] = f[2];
	blk[9] = f[3];

	f[0] = (u1byte)((in_key[0] & 0xff0000) >> 16);
	f[1] = (u1byte)((in_key[1] & 0xff0000) >> 16);
	f[2] = (u1byte)((in_key[2] & 0xff0000) >> 16);
	f[3] = (u1byte)((in_key[3] & 0xff0000) >> 16);

	blk[10] = blk[0] ^ f[0];
	blk[11] = blk[1] ^ f[1];
	blk[12] = blk[2] ^ f[2];
	blk[13] = blk[3] ^ f[3];
	blk[14] = f[0] ^ f[1];
	blk[15] = f[2] ^ f[3];

	for (i = 0; i < 588; i++) { /* primary key mix */
		key[i] ^= (blk[i % 16] + f[i % 4]) % 256;
		f[i % 4] = key[i];
	}

	blk[0] = f[0] ^ f[1];
	blk[1] = f[0] ^ f[2];
	blk[2] = f[0] ^ f[3];
	blk[3] = f[1] ^ f[2];
	blk[4] = f[1] ^ f[3];
	blk[5] = f[2] ^ f[3];

	f[0] = (u1byte)((in_key[0] & 0xff00) >> 8);
	f[1] = (u1byte)((in_key[1] & 0xff00) >> 8);
	f[2] = (u1byte)((in_key[2] & 0xff00) >> 8);
	f[3] = (u1byte)((in_key[3] & 0xff00) >> 8);

	blk[6] = f[0] ^ blk[2];
	blk[7] = f[1] ^ blk[3];
	blk[8] = f[2] ^ blk[4];
	blk[9] = f[3] ^ blk[5];

	f[0] = (u1byte)(in_key[0] & 0xff);
	f[1] = (u1byte)(in_key[1] & 0xff);
	f[2] = (u1byte)(in_key[2] & 0xff);
	f[3] = (u1byte)(in_key[3] & 0xff);

	blk[10] = f[0] ^ blk[6];
	blk[11] = f[1] ^ blk[7];
	blk[12] = f[2] ^ blk[8];
	blk[13] = f[3] ^ blk[9];
	blk[14] = f[0] ^ f[2];
	blk[15] = f[1] ^ f[3];

	f[1] ^= f[0];
	f[2] ^= f[0];
	f[3] ^= f[0];

	for (i = 0; i < 588; i++) {	 /* secondary key mix */
		key[i] = (key[i] + (blk[i % 16] ^ f[i % 4])) % 256;
		f[i % 4] = key[i];
	}
}

void
encrypt(u4byte in_blk[2])
{
	u1byte sk[48];		/* substitution key */
	u1byte tk[64];		/* transposition key */
	u1byte blk[64];		/* data block bits */
	u1byte t;	   	/* temp. for bit swap & feedback */
	u1byte pc;	  	/* prev. ciphertext bit */
	u1byte *kp;		/* keystream pointer */
	int ki;		 	/* index into key[] */
	int bi;		 	/* index into blk[] */
	int i;
	int j;

	get_blk();  /* in_blk --> blk */

	for (i = 0; i < 16; i++) {  /* round loop */
		ki = 36 * i;
		for (j = 0; j < 48; j++) {  /* build round key */
			kp = &key[ki + j];
			tk[j] = (*kp) >> 2;
			sk[j] = ((*kp) & 0x1) ^ (((*kp) & 0x2) >> 1);
		}
		for (j = 0; j < 16; j++) {  /* expand tk */
			tk[j + 48] = (tk[j] + tk[j + 1]) % 64;
		}

		for (j = 0; j < 64; j++) {  /* transposition loop */
			bi = (int) tk[j];
			t = blk[j];
			blk[j] = blk[bi];
			blk[bi] = t;
		}

		pc = (u1byte) 0;
		for (j = 0; j < 48; j++) {  /* substitution loop */
			t = sk[j];
			blk[j] ^= (pc + t) % 2;
			pc = blk[j];
		}

		for (j = 48; j < 64; j++) { /* feedback loop */
			blk[j] ^= blk[j - 36];
		}
	}

	put_blk();  /* blk --> in_blk */
}

void
decrypt(u4byte in_blk[2])
{
	u1byte *blk_t[65];  	/* C0, blk[] */
	u1byte sk[48];		/* substitution key */
	u1byte tk[64];		/* transposition key */
	u1byte blk[64];		/* data block bits */
	u1byte t;	   	/* temp. for bit swap & feedback */
	u1byte pc;	  	/* prev. ciphertext bit */
	u1byte *kp;		/* keystream pointer */
	int ki;		 	/* index into key[] */
	int bi;		 	/* index into blk[] */
	int i;
	int j;

	blk_t[0] = &pc;	 	/* static reference for blk_t */
	for (i = 0; i < 64; i++) {
		blk_t[i + 1] = &blk[i];
	}

	get_blk();  /* in_blk --> blk */

	for (i = 15; i >= 0; i--) { /* round loop */
		ki = 36 * i;
		for (j = 0; j < 48; j++) {  /* build round key */
			kp = &key[ki + j];
			tk[j] = (*kp) >> 2;
			sk[j] = ((*kp) & 0x1) ^ (((*kp) & 0x2) >> 1);
		}
		for (j = 0; j < 16; j++) {  /* expand tk */
			tk[j + 48] = (tk[j] + tk[j + 1]) % 64;
		}

		for (j = 63; j >= 48; j--) {	/* undo feedback loop */
			blk[j] ^= blk[j - 36];
		}

		pc = (u1byte) 0;
		for (j = 47; j >= 0; j--) {	/* undo substitution loop */
			t = sk[j];
			blk[j] ^= (t + (*blk_t[j])) % 2;	/* blk_t[j] points to Cn-1 */
		}

		for (j = 63; j >= 0; j--) {	 /* undo transposition loop */
			bi = (int) tk[j];
			t = blk[j];
			blk[j] = blk[bi];
			blk[bi] = t;
		}
	}

	put_blk();  /* blk --> in_blk */
}


#ifdef DEBUG

#include <stdio.h>

int
main(void)
{
	u4byte in_key[] = {0xAAAABBBB, 0xCCCCDDDD, 0xEEEEFFFF, 0x11112222};
	u4byte in_blk[] = {0x11111111, 0x11111111};

	printf("%08lX%08lX%08lX%08lX = User key\n", in_key[0], in_key[1], in_key[2], in_key[3]);

	set_key(in_key);

	printf("%08lX%08lX = Plaintext\n", in_blk[0], in_blk[1]);

	encrypt(in_blk);

	printf("%08lX%08lX = Ciphertext\n", in_blk[0], in_blk[1]);

	decrypt(in_blk);

	printf("%08lX%08lX = Plaintext\n", in_blk[0], in_blk[1]);

	return 0;
}

#endif
