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

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


my @key = ();	## key schedule

sub set_key
{
	my @in_key = @_;
	my @blk = ();	## expansion of in_key
	my @f = ();	## cipher feedback
	my $i;

	for ($i = 0; $i < 588; $i++) {	## initialise key
		$key[$i] = $i % 256;
	}

	## initialise blk[] using half of key data 
	$f[0] = ($in_key[0] & 0xff000000) >> 24;
	$f[1] = ($in_key[1] & 0xff000000) >> 24;
	$f[2] = ($in_key[2] & 0xff000000) >> 24;
	$f[3] = ($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] = ($in_key[0] & 0xff0000) >> 16;
	$f[1] = ($in_key[1] & 0xff0000) >> 16;
	$f[2] = ($in_key[2] & 0xff0000) >> 16;
	$f[3] = ($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++) {	## first pass of 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] = ($in_key[0] & 0xff00) >> 8;
	$f[1] = ($in_key[1] & 0xff00) >> 8;
	$f[2] = ($in_key[2] & 0xff00) >> 8;
	$f[3] = ($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] = ($in_key[0] & 0xff);
	$f[1] = ($in_key[1] & 0xff);
	$f[2] = ($in_key[2] & 0xff);
	$f[3] = ($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];		
	}
}

sub encrypt
{
	my @in_blk = @_;
	my @blk = ();		## data block bits
	my @sk = ();		## substitution key
	my @tk = ();		## transposition key
	my $t;			## temp. for bit swap & feedback
	my $pc;			## prev. ciphertext bit
	my $ki;			## index into @key
	my $bi;			## index into @blk
	my $i;
	my $j;

	for ($i = 31; $i >= 0; $i--) {	## in_blk[] --> blk[] 
		$blk[31 - $i] = ($in_blk[0] & (1 << $i)) >> $i;
		$blk[63 - $i] = ($in_blk[1] & (1 << $i)) >> $i;
	}

	for ($i = 0; $i < 16; $i++) {	## round loop
		$ki = 36 * $i; 
		for ($j = 0; $j < 48; $j++) {  ## build round key 
			$t = $key[$ki + $j];
			$tk[$j] = $t >> 2;
			$sk[$j] = ($t & 0x1) ^ (($t & 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 = $tk[$j];
			$t = $blk[$j];
			$blk[$j] = $blk[$bi];
			$blk[$bi] = $t;
		}

		$pc = 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];
 		}

	}
		
	@in_blk = (0, 0);
	for ($i = 0; $i < 32; $i++) { 	## blk --> in_blk
		$in_blk[0] +=    $blk[$i] << (31 - $i); 
		$in_blk[1] += $blk[$i+32] << (31 - $i); 
	}

	return @in_blk;
}

sub decrypt
{
	my @in_blk = @_;
	my @blk = ();		## data block bits
	my @sk = ();		## substitution key
	my @tk = ();		## transposition key
	my $t;			## temp. for bit swap & feedback
	my $pc;			## prev. ciphertext bit
	my $ki;			## index into @key
	my $bi;			## index into @blk
	my $i;
	my $j;

	for ($i = 31; $i >= 0; $i--) {	## in_blk --> blk 
		$blk[32 - $i] = ($in_blk[0] & (1 << $i)) >> $i;
		$blk[64 - $i] = ($in_blk[1] & (1 << $i)) >> $i;
	}


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

		for ($j = 64; $j >= 49; $j--) {	## undo feedback loop
			$blk[$j] ^= $blk[$j - 36];
		}

		$blk[0] = 0;
		for ($j = 48; $j >= 1; $j--) {	## undo substitution loop
			$t = $sk[$j - 1];
			$blk[$j] ^= ($t + $blk[$j - 1]) % 2;
		}

		for ($j = 63; $j >= 0; $j--) { 	## undo transposition
			$bi = $tk[$j] + 1;
			$t = $blk[$j+1];
			$blk[$j+1] = $blk[$bi];
			$blk[$bi] = $t;
		}

	}

	@in_blk = (0, 0);
	for ($i = 0; $i < 32; $i++) { 	## blk --> in_blk
		$in_blk[0] +=    $blk[$i+1] << (31 - $i); 
		$in_blk[1] += $blk[$i+33] << (31 - $i); 
	}

	return @in_blk;
}


sub main	## demonstrate algorithm usage
{
	my @in_key = (0xAAAABBBB, 0xCCCCDDDD, 0xEEEEFFFF, 0x11112222);
	my @in_blk = (0x11111111, 0x11111111);
	my @out_blk = ();
	my @out_blk2 = ();

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

	@out_blk = encrypt(@in_blk);

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

	@out_blk2 = decrypt(@out_blk);

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

	return 0;
}

main();
