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

/* rotate 32-bit integers */
#define rotr32(a,b) ((((a) >> (b)) | ((a) << (32 - (b)))) & 0xffffffff)
#define rotl32(a,b) ((((a) << (b)) | ((a) >> (32 - (b)))) & 0xffffffff)

/* add b to a */
#define add32(a,b) (((a) + (b)) & 0xffffffff)

/* subtract b from a */
#define sub32(a,b) (((a) - (b)) & 0xffffffff)

/* permutation index shorthand */
#define Pa (p[i][0])
#define Pb (p[i][1])
#define Pc (p[i][2])
#define Pd (p[i][3])

typedef unsigned char u1byte;
typedef unsigned long u4byte;	/* may be wider than 32-bit */

/* initialisation vector for the key schedule */
static u4byte iv_tbl[64] = 
{
	0xFA5D7CEC, 0x734533F8, 0x484EA4B2, 0x2BF0E46B,
	0xA853884B, 0xCBE7DEE0, 0x976DF92A, 0xE2B17575,
	0xF9BC1EFD, 0xF8ADC573, 0x2E5B6EA9, 0x1C5A4FFD,
	0x3DF27762, 0x2D7BF0BD, 0x039D26D6, 0x0A937D09,
	0x01052BC2, 0x8AEEEA1C, 0xFB7D3EBE, 0x2449A182,
	0x17B71BA3, 0xC6E68955, 0x74C7A0CD, 0xE1B88745,
	0x4D71958C, 0x149CEB88, 0x5344E0B9, 0xC3F0A21F,
	0xA348429B, 0xF672C8CD, 0xD01FA55C, 0x4C6FD3DF,
	0xC05B564E, 0xE0D81861, 0xA3B3EAAC, 0x17AE4A08,
	0x9D8DF3BF, 0x1353CBFE, 0x25C0F1C8, 0xD52818EC,
	0x1BA102B0, 0xC2FDC394, 0x9AB0505E, 0xF8D5F8CB,
	0x708749DA, 0x34C5D60B, 0xD1C046B0, 0x4C8E4672,
	0x0E236A84, 0x5A36C5AB, 0xDC9B9EB5, 0x0ADB5718,
	0x57D27CC5, 0xC4ECAEFD, 0xB57C0D9E, 0x764E92EA,
	0x4CA01ED6, 0xA7314405, 0xEE5E6B4B, 0xF9560189,
	0xC50CD4D5, 0xBFE0AFD8, 0x8322F984, 0x65601B60
};

static u1byte p[16][4] =	/* permutations */
{
	{3, 0, 1, 2},
	{3, 0, 2, 1},
	{3, 1, 0, 2},
	{3, 1, 2, 0},
	{3, 2, 0, 1},
	{3, 2, 1, 0},
	{1, 0, 2, 3},
	{1, 0, 3, 2},
	{1, 2, 0, 3},
	{1, 2, 3, 0},
	{0, 1, 2, 3},
	{0, 1, 3, 2},
	{0, 2, 1, 3},
	{0, 2, 3, 1},
	{0, 3, 1, 2},
	{0, 3, 2, 1}
};

static u4byte key[32];

void
set_key(const u4byte in_key[4])		/* prepare key schedule */
{
	u4byte blk[4];
	u4byte fbk = 0x19860719;	/* scheduler constant */
	u4byte t;
	u1byte key_exp[64];
	int x;
	int i, j;

	blk[0] = add32(in_key[0], rotl32(fbk, 3));
	blk[1] = add32(in_key[1], rotl32(blk[0], 5));
	blk[2] = add32(in_key[2], rotl32(blk[1], 7));
	blk[3] = add32(in_key[3], rotl32(blk[2], 11));

	x = rotl32(blk[3], 1) % 64;
	fbk ^= add32(rotl32(blk[3], 2), iv_tbl[x]);

	blk[0] ^= rotl32(fbk, 1);
	blk[1] ^= rotl32(blk[0], 2);
	blk[2] ^= rotl32(blk[1], 3);
	blk[3] ^= rotl32(blk[2], 4);
	
	x = (x + (blk[0] & 0xff)) % 64;
	for (i = 0; i < 4; i++) {
		blk[Pd] = add32(blk[Pd], iv_tbl[x]);
		blk[Pc] = add32(blk[Pc], iv_tbl[(x + 1) % 64]); 
		blk[Pb] = add32(blk[Pb], iv_tbl[(x + 2) % 64]); 
		blk[Pa] = add32(blk[Pa], iv_tbl[(x + 3) % 64]);
	}

	key[0] = rotl32(blk[0], 5); 
	key[1] = rotl32(blk[1], 9); 
	key[2] = rotl32(blk[2], 13); 
	key[3] = rotl32(blk[3], 17);

	j = 4;
	for (i = 0; i < 7; i++) {
		blk[Pa] = add32(blk[Pa], iv_tbl[x]); 
		blk[Pb] = add32(blk[Pb], iv_tbl[(x + 1) % 64]); 
		blk[Pc] = add32(blk[Pc], iv_tbl[(x + 2) % 64]); 
		blk[Pd] = add32(blk[Pd], iv_tbl[(x + 3) % 64]);

		key[j] = rotl32(blk[Pa], 5); 
		key[j + 1] = rotl32(blk[Pb], 9); 
		key[j + 2] = rotl32(blk[Pc], 13); 
		key[j + 3] = rotl32(blk[Pd], 17);

		x = (x + 4) % 64;
		j += 4;
	}

	for (i = 0; i < 32; i++) {
		key_exp[i] = key[i] & 0xff;
		key_exp[i + 32] = (key[i] & 0xff00) >> 8;
	}

	for (i = 0; i < 64; i++) {
		x = key_exp[i] % 32;
		t = key[i % 32];
		key[i % 32] = key[x];
		key[x] = t;
	}
}

void 
encrypt(u4byte blk[4])		/* encrypt a block of text */
{
	int i;
	u4byte fbk;

	for (i = 0; i < 16; i++) {
		blk[Pa] ^= key[(i + 16) % 32];
		blk[Pb] ^= key[(i + 19) % 32];
		blk[Pc] ^= key[(i + 21) % 32];
		blk[Pd] ^= key[(i + 23) % 32];

		blk[Pa] = add32(blk[Pa], key[i]);
		blk[Pb] = add32(blk[Pb], rotl32(blk[Pa], 1));
		blk[Pc] = add32(blk[Pc], rotl32(blk[Pb], 2));
		blk[Pd] = add32(blk[Pd], rotl32(blk[Pc], 3));

		fbk = add32(rotl32(blk[Pd], 5), key[i]) ^ key[i + 1];
		blk[Pa] ^= fbk;
		blk[Pb] ^= rotl32(fbk, 1);
		blk[Pc] ^= rotl32(fbk, 2);
		blk[Pd] ^= rotl32(key[i], 1);
	}
}

void 
decrypt(u4byte blk[4])		/* decrypt a block of text */
{
	int i;
	u4byte fbk;

	for (i = 15; i > -1; i--) {
		blk[Pd] ^= rotl32(key[i], 1); 
		fbk = add32(rotl32(blk[Pd], 5), key[i]) ^ key[i + 1];
		blk[Pa] ^= fbk; 
		blk[Pb] ^= rotl32(fbk, 1); 
		blk[Pc] ^= rotl32(fbk, 2);
	
		blk[Pd] = sub32(blk[Pd], rotl32(blk[Pc], 3)); 
		blk[Pc] = sub32(blk[Pc], rotl32(blk[Pb], 2));
		blk[Pb] = sub32(blk[Pb], rotl32(blk[Pa], 1));
		blk[Pa] = sub32(blk[Pa], key[i]); 

		blk[Pa] ^= key[(i + 16) % 32]; 
		blk[Pb] ^= key[(i + 19) % 32];
		blk[Pc] ^= key[(i + 21) % 32]; 
		blk[Pd] ^= key[(i + 23) % 32];  
	}
}

#if defined DEBUG

#include <stdio.h>

int 
main(void)	/* demonstrate algorithm usage */
{
	u4byte in_key[4] = {0x11111111, 0x22222222, 0x33333333, 0x44444444};
	u4byte blk[4] = {0x00000000, 0x11111111, 0x22222222, 0x33333333};

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

	printf("%08lx%08lx%08lx%08lx = %s\n", blk[0], blk[1], blk[2], 
	       blk[3], "Plaintext");

	set_key(&in_key[0]);

	encrypt(&blk[0]);

	printf("%08lx%08lx%08lx%08lx = %s\n", blk[0], blk[1], blk[2], 
	       blk[3], "Ciphertext");

	decrypt(&blk[0]);

	printf("%08lx%08lx%08lx%08lx = %s\n", blk[0], blk[1], blk[2], 
	       blk[3], "Plaintext");

	return 0;
}
#endif
