#!/usr/bin/perl -PX
use strict;

## MBC1 perl 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.


## is perl using 32-bit or 64-bit integers...
#define INT32

#ifdef INT32
	#define add32(a,b) (((a) + (b)) % 0x100000000)
	#define sub32(a,b) (((a) - (b)) % 0x100000000)
#endif

#ifdef INT64
	#define add32(a,b) (((a) + (b)) & 0xffffffff)
	#define sub32(a,b) (((a) - (b)) & 0xffffffff)
#endif

#define rotr32(a,b) ((((a) >> (b)) | ((a) << (32 - (b)))) & 0xffffffff)
#define rotl32(a,b) ((((a) << (b)) | ((a) >> (32 - (b)))) & 0xffffffff)

#define Pa ($p[$i][0])
#define Pb ($p[$i][1])
#define Pc ($p[$i][2])
#define Pd ($p[$i][3])

my @iv_tbl = 
(
        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
);

my @p = 	## 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]
);


my @key = ();

sub set_key
{
	my @in_key = @_;
	my @blk = ();
	my @key_exp = ();
	my $fbk = 0x19860719;
	my $t;	## temp. variable for shuffle
	my $x;	## index into iv_tbl[]
	my $j;	## index into key[]
	my $i; 

	$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++) {
		$j = $key_exp[$i] % 32;
		$t = $key[$i % 32];
		$key[$i % 32] = $key[$j];
		$key[$j] = $t;
	}
}

sub encrypt
{
	my @blk = @_;
	my $i;
	my $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);
	}

	return @blk;
}

sub decrypt
{
	my @blk = @_;
	my $i;
	my $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];  
	}

	return @blk;
}

sub main
{
	my @in_key = (0x11111111, 0x22222222, 0x33333333, 0x44444444);
	my @blk = (0x00000000, 0x11111111, 0x22222222, 0x33333333);
	my @ct = ();
	my @pt = ();

	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);

	@ct = encrypt(@blk);

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

	@pt = decrypt(@ct);

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

	exit 0;
}

main();
 
