<?php

namespace LowEntryUE4PHP\Classes\Internal\Aes;

use LowEntryUE4PHP\Classes\AesKey1D;
use LowEntryUE4PHP\LowEntry;


class EncryptionAesFastEngine1D
{
	/** @var int */
	private $ROUNDS;
	/** @var int[] Integer array */
	private $EW;
	/** @var int[] Integer array */
	private $DW;
	
	/** @var int */
	public $C0 = 0;
	/** @var int */
	public $C1 = 0;
	/** @var int */
	public $C2 = 0;
	/** @var int */
	public $C3 = 0;
	
	/** @var int */
	public $C0store1 = 0;
	/** @var int */
	public $C1store1 = 0;
	/** @var int */
	public $C2store1 = 0;
	/** @var int */
	public $C3store1 = 0;
	
	public $C0store2 = 0;
	/** @var int */
	public $C1store2 = 0;
	/** @var int */
	public $C2store2 = 0;
	/** @var int */
	public $C3store2 = 0;
	
	
	/**
	 * @param AesKey1D $key
	 */
	public function __construct($key)
	{
		$this->ROUNDS = $key->rounds;
		$this->EW = $key->encryptionW;
		$this->DW = $key->decryptionW;
	}
	
	
	/**
	 * @param int $a
	 * @param int $b
	 *
	 * @return int
	 */
	private static function s($a, $b)
	{
		return LowEntry::tripleShiftRight($a, $b);
	}
	
	
	/**
	 * @param int[] $key Byte array
	 *
	 * @return AesKey1D|null
	 */
	public static function generateAesKey($key)
	{
		return static::generateAesKeyCustom($key, true, true);
	}
	
	/**
	 * @param int[] $key Byte array
	 * @param bool  $forEncryption
	 * @param bool  $forDecryption
	 *
	 * @return AesKey1D|null
	 */
	public static function generateAesKeyCustom($key, $forEncryption, $forDecryption)
	{
		if(!isset($key))
		{
			return null;
		}
		
		$keyLen = \count($key);
		if(($keyLen != 16) && (($keyLen != 24) & ($keyLen != 32)))
		{
			//throw new IllegalArgumentException("Key length not 128/192/256 bits ($it is " + keyLen + ", it needs to be either 16 for AES-128, 24 for AES-192, or 32 for AES-256)");
			return null;
		}
		
		$kc = static::s($keyLen, 2);
		$rounds = $kc + 6;
		$roundsx4 = $rounds * 4;
		
		if(!$forEncryption && !$forDecryption)
		{
			return new AesKey1D($rounds, null, null);
		}
		
		$ew = LowEntry::createArray((($rounds + 1) * 4), 0);
		
		switch($kc)
		{
			case 4:
			{
				$t0 = EncryptionAesFastEngineGeneral::littleEndianToInt($key, 0);
				$ew[0] = $t0;
				$t1 = EncryptionAesFastEngineGeneral::littleEndianToInt($key, 4);
				$ew[1] = $t1;
				$t2 = EncryptionAesFastEngineGeneral::littleEndianToInt($key, 8);
				$ew[2] = $t2;
				$t3 = EncryptionAesFastEngineGeneral::littleEndianToInt($key, 12);
				$ew[3] = $t3;
				
				for($i = 1; $i <= 10; $i++)
				{
					$ix = $i * 4;
					$u = EncryptionAesFastEngineGeneral::subWord(EncryptionAesFastEngineGeneral::shift($t3, 8)) ^ EncryptionAesFastEngineGeneral::$rcon[$i - 1];
					$t0 ^= $u;
					$ew[$ix] = $t0;
					$t1 ^= $t0;
					$ew[$ix + 1] = $t1;
					$t2 ^= $t1;
					$ew[$ix + 2] = $t2;
					$t3 ^= $t2;
					$ew[$ix + 3] = $t3;
				}
				
				break;
			}
			case 6:
			{
				$t0 = EncryptionAesFastEngineGeneral::littleEndianToInt($key, 0);
				$ew[0] = $t0;
				$t1 = EncryptionAesFastEngineGeneral::littleEndianToInt($key, 4);
				$ew[1] = $t1;
				$t2 = EncryptionAesFastEngineGeneral::littleEndianToInt($key, 8);
				$ew[2] = $t2;
				$t3 = EncryptionAesFastEngineGeneral::littleEndianToInt($key, 12);
				$ew[3] = $t3;
				$t4 = EncryptionAesFastEngineGeneral::littleEndianToInt($key, 16);
				$ew[4] = $t4;
				$t5 = EncryptionAesFastEngineGeneral::littleEndianToInt($key, 20);
				$ew[5] = $t5;
				
				$rcon = 1;
				$u = EncryptionAesFastEngineGeneral::subWord(EncryptionAesFastEngineGeneral::shift($t5, 8)) ^ $rcon;
				$rcon <<= 1;
				$t0 ^= $u;
				$ew[6] = $t0;
				$t1 ^= $t0;
				$ew[7] = $t1;
				$t2 ^= $t1;
				$ew[8] = $t2;
				$t3 ^= $t2;
				$ew[9] = $t3;
				$t4 ^= $t3;
				$ew[10] = $t4;
				$t5 ^= $t4;
				$ew[11] = $t5;
				
				for($i = 3; $i < 12; $i += 3)
				{
					$ix = $i * 4;
					$u = EncryptionAesFastEngineGeneral::subWord(EncryptionAesFastEngineGeneral::shift($t5, 8)) ^ $rcon;
					$rcon <<= 1;
					$t0 ^= $u;
					$ew[$ix] = $t0;
					$t1 ^= $t0;
					$ew[$ix + 1] = $t1;
					$t2 ^= $t1;
					$ew[$ix + 2] = $t2;
					$t3 ^= $t2;
					$ew[$ix + 3] = $t3;
					$t4 ^= $t3;
					$ew[$ix + 4] = $t4;
					$t5 ^= $t4;
					$ew[$ix + 5] = $t5;
					$u = EncryptionAesFastEngineGeneral::subWord(EncryptionAesFastEngineGeneral::shift($t5, 8)) ^ $rcon;
					$rcon <<= 1;
					$t0 ^= $u;
					$ew[$ix + 6] = $t0;
					$t1 ^= $t0;
					$ew[$ix + 7] = $t1;
					$t2 ^= $t1;
					$ew[$ix + 8] = $t2;
					$t3 ^= $t2;
					$ew[$ix + 9] = $t3;
					$t4 ^= $t3;
					$ew[$ix + 10] = $t4;
					$t5 ^= $t4;
					$ew[$ix + 11] = $t5;
				}
				
				$u = EncryptionAesFastEngineGeneral::subWord(EncryptionAesFastEngineGeneral::shift($t5, 8)) ^ $rcon;
				$t0 ^= $u;
				$ew[48] = $t0;
				$t1 ^= $t0;
				$ew[49] = $t1;
				$t2 ^= $t1;
				$ew[50] = $t2;
				$t3 ^= $t2;
				$ew[51] = $t3;
				
				break;
			}
			case 8:
			{
				$t0 = EncryptionAesFastEngineGeneral::littleEndianToInt($key, 0);
				$ew[0] = $t0;
				$t1 = EncryptionAesFastEngineGeneral::littleEndianToInt($key, 4);
				$ew[1] = $t1;
				$t2 = EncryptionAesFastEngineGeneral::littleEndianToInt($key, 8);
				$ew[2] = $t2;
				$t3 = EncryptionAesFastEngineGeneral::littleEndianToInt($key, 12);
				$ew[3] = $t3;
				$t4 = EncryptionAesFastEngineGeneral::littleEndianToInt($key, 16);
				$ew[4] = $t4;
				$t5 = EncryptionAesFastEngineGeneral::littleEndianToInt($key, 20);
				$ew[5] = $t5;
				$t6 = EncryptionAesFastEngineGeneral::littleEndianToInt($key, 24);
				$ew[6] = $t6;
				$t7 = EncryptionAesFastEngineGeneral::littleEndianToInt($key, 28);
				$ew[7] = $t7;

//				$u = 0;
				$rcon = 1;
				
				for($i = 2; $i < 14; $i += 2)
				{
					$ix = $i * 4;
					$u = EncryptionAesFastEngineGeneral::subWord(EncryptionAesFastEngineGeneral::shift($t7, 8)) ^ $rcon;
					$rcon <<= 1;
					$t0 ^= $u;
					$ew[$ix] = $t0;
					$t1 ^= $t0;
					$ew[$ix + 1] = $t1;
					$t2 ^= $t1;
					$ew[$ix + 2] = $t2;
					$t3 ^= $t2;
					$ew[$ix + 3] = $t3;
					$u = EncryptionAesFastEngineGeneral::subWord($t3);
					$t4 ^= $u;
					$ew[$ix + 4] = $t4;
					$t5 ^= $t4;
					$ew[$ix + 5] = $t5;
					$t6 ^= $t5;
					$ew[$ix + 6] = $t6;
					$t7 ^= $t6;
					$ew[$ix + 7] = $t7;
				}
				
				$u = EncryptionAesFastEngineGeneral::subWord(EncryptionAesFastEngineGeneral::shift($t7, 8)) ^ $rcon;
				$t0 ^= $u;
				$ew[56] = $t0;
				$t1 ^= $t0;
				$ew[57] = $t1;
				$t2 ^= $t1;
				$ew[58] = $t2;
				$t3 ^= $t2;
				$ew[59] = $t3;
				
				break;
			}
			default:
			{
				//throw new IllegalStateException("Should never get here");
				return null;
			}
		}
		
		if(!$forDecryption)
		{
			return new AesKey1D($rounds, $ew, null);
		}
		else
		{
			if(!$forEncryption)
			{
				//int[] dw = ew;
				//ew = null;
				
				for($i = 4; $i < $roundsx4; $i++)
				{
					$ew[$i] = EncryptionAesFastEngineGeneral::inv_mcol($ew[$i]);
				}
				
				return new AesKey1D($rounds, null, $ew);
			}
			else
			{
				$dw = LowEntry::createArray(count($ew), 0);
				
				$dw[0] = $ew[0];
				$dw[1] = $ew[1];
				$dw[2] = $ew[2];
				$dw[3] = $ew[3];
				
				$dw[$roundsx4] = $ew[$roundsx4];
				$dw[$roundsx4 + 1] = $ew[$roundsx4 + 1];
				$dw[$roundsx4 + 2] = $ew[$roundsx4 + 2];
				$dw[$roundsx4 + 3] = $ew[$roundsx4 + 3];
				
				for($i = 4; $i < $roundsx4; $i++)
				{
					$dw[$i] = EncryptionAesFastEngineGeneral::inv_mcol($ew[$i]);
				}
				
				return new AesKey1D($rounds, $ew, $dw);
			}
		}
	}
	
	
	/**
	 * @param int[] $bytes Byte array
	 */
	public function loadIv($bytes)
	{
		$this->unpackBlockStore1($bytes, 0);
	}
	
	/**
	 * @param int[] $in  Byte array
	 * @param int   $inOff
	 * @param int[] $out Byte array
	 * @param int   $outOff
	 */
	public function processBlockEncryption($in, $inOff, &$out, $outOff)
	{
		$this->unpackBlock($in, $inOff);
		$this->xorBlockByBlockStore1();
		$this->encryptBlock();
		$this->putBlockInBlockStore1();
		$this->packBlock($out, $outOff);
	}
	
	/**
	 * @param int[] $in  Byte array
	 * @param int   $inOff
	 * @param int[] $out Byte array
	 * @param int   $outOff
	 */
	public function processBlockDecryption($in, $inOff, &$out, $outOff)
	{
		$this->unpackBlock($in, $inOff);
		$this->putBlockInBlockStore2();
		$this->decryptBlock();
		$this->xorBlockByBlockStore1();
		$this->putBlockStore2InBlockStore1();
		$this->packBlock($out, $outOff);
	}
	
	/**
	 * @param int[] $bytes Byte array
	 * @param int   $off
	 */
	private function unpackBlock($bytes, $off)
	{
		$this->C0 = EncryptionAesFastEngineGeneral::littleEndianToInt($bytes, $off);
		$this->C1 = EncryptionAesFastEngineGeneral::littleEndianToInt($bytes, $off + 4);
		$this->C2 = EncryptionAesFastEngineGeneral::littleEndianToInt($bytes, $off + 8);
		$this->C3 = EncryptionAesFastEngineGeneral::littleEndianToInt($bytes, $off + 12);
	}
	
	/**
	 * @param int[] $bytes Byte array
	 * @param int   $off
	 */
	private function unpackBlockStore1($bytes, $off)
	{
		$this->C0store1 = EncryptionAesFastEngineGeneral::littleEndianToInt($bytes, $off);
		$this->C1store1 = EncryptionAesFastEngineGeneral::littleEndianToInt($bytes, $off + 4);
		$this->C2store1 = EncryptionAesFastEngineGeneral::littleEndianToInt($bytes, $off + 8);
		$this->C3store1 = EncryptionAesFastEngineGeneral::littleEndianToInt($bytes, $off + 12);
	}
	
	/**
	 * @param int[] $bytes Byte array
	 * @param int   $off
	 */
	private function packBlock(&$bytes, $off)
	{
		EncryptionAesFastEngineGeneral::intToLittleEndian($this->C0, $bytes, $off);
		EncryptionAesFastEngineGeneral::intToLittleEndian($this->C1, $bytes, $off + 4);
		EncryptionAesFastEngineGeneral::intToLittleEndian($this->C2, $bytes, $off + 8);
		EncryptionAesFastEngineGeneral::intToLittleEndian($this->C3, $bytes, $off + 12);
	}
	
	/**
	 */
	private function putBlockInBlockStore1()
	{
		$this->C0store1 = $this->C0;
		$this->C1store1 = $this->C1;
		$this->C2store1 = $this->C2;
		$this->C3store1 = $this->C3;
	}
	
	/**
	 */
	private function putBlockInBlockStore2()
	{
		$this->C0store2 = $this->C0;
		$this->C1store2 = $this->C1;
		$this->C2store2 = $this->C2;
		$this->C3store2 = $this->C3;
	}
	
	/**
	 */
	private function putBlockStore2InBlockStore1()
	{
		$this->C0store1 = $this->C0store2;
		$this->C1store1 = $this->C1store2;
		$this->C2store1 = $this->C2store2;
		$this->C3store1 = $this->C3store2;
	}
	
	/**
	 */
	private function xorBlockByBlockStore1()
	{
		$this->C0 ^= $this->C0store1;
		$this->C1 ^= $this->C1store1;
		$this->C2 ^= $this->C2store1;
		$this->C3 ^= $this->C3store1;
	}
	
	/**
	 */
	private function encryptBlock()
	{
		$t0 = $this->C0 ^ $this->EW[0];
		$t1 = $this->C1 ^ $this->EW[1];
		$t2 = $this->C2 ^ $this->EW[2];
		
		$r = 1;
		$rx = 4;
//		$r0 = 0;
//		$r1 = 0;
//		$r2 = 0;
		$r3 = $this->C3 ^ $this->EW[3];
//		$i0 = 0;
//		$i1 = 0;
//		$i2 = 0;
//		$i3 = 0;
		
		while($r < ($this->ROUNDS - 1))
		{
			$i0 = $t0;
			$i1 = static::s($t1, 8);
			$i2 = static::s($t2, 16);
			$i3 = static::s($r3, 24);
			$i0 &= 255;
			$i1 &= 255;
			$i2 &= 255;
			$i3 &= 255;
			$r0 = EncryptionAesFastEngineGeneral::$T[$i0] ^ EncryptionAesFastEngineGeneral::$T[256 + $i1] ^ EncryptionAesFastEngineGeneral::$T[512 + $i2] ^ EncryptionAesFastEngineGeneral::$T[768 + $i3] ^ $this->EW[$rx];
			
			$i0 = $t1;
			$i1 = static::s($t2, 8);
			$i2 = static::s($r3, 16);
			$i3 = static::s($t0, 24);
			$i0 &= 255;
			$i1 &= 255;
			$i2 &= 255;
			$i3 &= 255;
			$r1 = EncryptionAesFastEngineGeneral::$T[$i0] ^ EncryptionAesFastEngineGeneral::$T[256 + $i1] ^ EncryptionAesFastEngineGeneral::$T[512 + $i2] ^ EncryptionAesFastEngineGeneral::$T[768 + $i3] ^ $this->EW[$rx + 1];
			
			$i0 = $t2;
			$i1 = static::s($r3, 8);
			$i2 = static::s($t0, 16);
			$i3 = static::s($t1, 24);
			$i0 &= 255;
			$i1 &= 255;
			$i2 &= 255;
			$i3 &= 255;
			$r2 = EncryptionAesFastEngineGeneral::$T[$i0] ^ EncryptionAesFastEngineGeneral::$T[256 + $i1] ^ EncryptionAesFastEngineGeneral::$T[512 + $i2] ^ EncryptionAesFastEngineGeneral::$T[768 + $i3] ^ $this->EW[$rx + 2];
			
			$i0 = $r3;
			$i1 = static::s($t0, 8);
			$i2 = static::s($t1, 16);
			$i3 = static::s($t2, 24);
			$i0 &= 255;
			$i1 &= 255;
			$i2 &= 255;
			$i3 &= 255;
			$r3 = EncryptionAesFastEngineGeneral::$T[$i0] ^ EncryptionAesFastEngineGeneral::$T[256 + $i1] ^ EncryptionAesFastEngineGeneral::$T[512 + $i2] ^ EncryptionAesFastEngineGeneral::$T[768 + $i3] ^ $this->EW[$rx + 3];
			$r++;
			$rx += 4;
			
			$i0 = $r0;
			$i1 = static::s($r1, 8);
			$i2 = static::s($r2, 16);
			$i3 = static::s($r3, 24);
			$i0 &= 255;
			$i1 &= 255;
			$i2 &= 255;
			$i3 &= 255;
			$t0 = EncryptionAesFastEngineGeneral::$T[$i0] ^ EncryptionAesFastEngineGeneral::$T[256 + $i1] ^ EncryptionAesFastEngineGeneral::$T[512 + $i2] ^ EncryptionAesFastEngineGeneral::$T[768 + $i3] ^ $this->EW[$rx];
			
			$i0 = $r1;
			$i1 = static::s($r2, 8);
			$i2 = static::s($r3, 16);
			$i3 = static::s($r0, 24);
			$i0 &= 255;
			$i1 &= 255;
			$i2 &= 255;
			$i3 &= 255;
			$t1 = EncryptionAesFastEngineGeneral::$T[$i0] ^ EncryptionAesFastEngineGeneral::$T[256 + $i1] ^ EncryptionAesFastEngineGeneral::$T[512 + $i2] ^ EncryptionAesFastEngineGeneral::$T[768 + $i3] ^ $this->EW[$rx + 1];
			
			$i0 = $r2;
			$i1 = static::s($r3, 8);
			$i2 = static::s($r0, 16);
			$i3 = static::s($r1, 24);
			$i0 &= 255;
			$i1 &= 255;
			$i2 &= 255;
			$i3 &= 255;
			$t2 = EncryptionAesFastEngineGeneral::$T[$i0] ^ EncryptionAesFastEngineGeneral::$T[256 + $i1] ^ EncryptionAesFastEngineGeneral::$T[512 + $i2] ^ EncryptionAesFastEngineGeneral::$T[768 + $i3] ^ $this->EW[$rx + 2];
			
			$i0 = $r3;
			$i1 = static::s($r0, 8);
			$i2 = static::s($r1, 16);
			$i3 = static::s($r2, 24);
			$i0 &= 255;
			$i1 &= 255;
			$i2 &= 255;
			$i3 &= 255;
			$r3 = EncryptionAesFastEngineGeneral::$T[$i0] ^ EncryptionAesFastEngineGeneral::$T[256 + $i1] ^ EncryptionAesFastEngineGeneral::$T[512 + $i2] ^ EncryptionAesFastEngineGeneral::$T[768 + $i3] ^ $this->EW[$rx + 3];
			$r++;
			$rx += 4;
		}
		
		$i0 = $t0;
		$i1 = static::s($t1, 8);
		$i2 = static::s($t2, 16);
		$i3 = static::s($r3, 24);
		$i0 &= 255;
		$i1 &= 255;
		$i2 &= 255;
		$i3 &= 255;
		$r0 = EncryptionAesFastEngineGeneral::$T[$i0] ^ EncryptionAesFastEngineGeneral::$T[256 + $i1] ^ EncryptionAesFastEngineGeneral::$T[512 + $i2] ^ EncryptionAesFastEngineGeneral::$T[768 + $i3] ^ $this->EW[$rx];
		
		$i0 = $t1;
		$i1 = static::s($t2, 8);
		$i2 = static::s($r3, 16);
		$i3 = static::s($t0, 24);
		$i0 &= 255;
		$i1 &= 255;
		$i2 &= 255;
		$i3 &= 255;
		$r1 = EncryptionAesFastEngineGeneral::$T[$i0] ^ EncryptionAesFastEngineGeneral::$T[256 + $i1] ^ EncryptionAesFastEngineGeneral::$T[512 + $i2] ^ EncryptionAesFastEngineGeneral::$T[768 + $i3] ^ $this->EW[$rx + 1];
		
		$i0 = $t2;
		$i1 = static::s($r3, 8);
		$i2 = static::s($t0, 16);
		$i3 = static::s($t1, 24);
		$i0 &= 255;
		$i1 &= 255;
		$i2 &= 255;
		$i3 &= 255;
		$r2 = EncryptionAesFastEngineGeneral::$T[$i0] ^ EncryptionAesFastEngineGeneral::$T[256 + $i1] ^ EncryptionAesFastEngineGeneral::$T[512 + $i2] ^ EncryptionAesFastEngineGeneral::$T[768 + $i3] ^ $this->EW[$rx + 2];
		
		$i0 = $r3;
		$i1 = static::s($t0, 8);
		$i2 = static::s($t1, 16);
		$i3 = static::s($t2, 24);
		$i0 &= 255;
		$i1 &= 255;
		$i2 &= 255;
		$i3 &= 255;
		$r3 = EncryptionAesFastEngineGeneral::$T[$i0] ^ EncryptionAesFastEngineGeneral::$T[256 + $i1] ^ EncryptionAesFastEngineGeneral::$T[512 + $i2] ^ EncryptionAesFastEngineGeneral::$T[768 + $i3] ^ $this->EW[$rx + 3];
//		$r++;
		$rx += 4;
		
		$i0 = $r0;
		$i1 = static::s($r1, 8);
		$i2 = static::s($r2, 16);
		$i3 = static::s($r3, 24);
		$i0 = EncryptionAesFastEngineGeneral::$S[$i0 & 255] & 255;
		$i1 = EncryptionAesFastEngineGeneral::$S[$i1 & 255] & 255;
		$i2 = EncryptionAesFastEngineGeneral::$S[$i2 & 255] & 255;
		$i3 = EncryptionAesFastEngineGeneral::$S[$i3 & 255] & 255;
		$this->C0 = $i0 ^ ($i1 << 8) ^ ($i2 << 16) ^ ($i3 << 24) ^ $this->EW[$rx];
		
		$i0 = $r1;
		$i1 = static::s($r2, 8);
		$i2 = static::s($r3, 16);
		$i3 = static::s($r0, 24);
		$i0 = EncryptionAesFastEngineGeneral::$S[$i0 & 255] & 255;
		$i1 = EncryptionAesFastEngineGeneral::$S[$i1 & 255] & 255;
		$i2 = EncryptionAesFastEngineGeneral::$S[$i2 & 255] & 255;
		$i3 = EncryptionAesFastEngineGeneral::$S[$i3 & 255] & 255;
		$this->C1 = $i0 ^ ($i1 << 8) ^ ($i2 << 16) ^ ($i3 << 24) ^ $this->EW[$rx + 1];
		
		$i0 = $r2;
		$i1 = static::s($r3, 8);
		$i2 = static::s($r0, 16);
		$i3 = static::s($r1, 24);
		$i0 = EncryptionAesFastEngineGeneral::$S[$i0 & 255] & 255;
		$i1 = EncryptionAesFastEngineGeneral::$S[$i1 & 255] & 255;
		$i2 = EncryptionAesFastEngineGeneral::$S[$i2 & 255] & 255;
		$i3 = EncryptionAesFastEngineGeneral::$S[$i3 & 255] & 255;
		$this->C2 = $i0 ^ ($i1 << 8) ^ ($i2 << 16) ^ ($i3 << 24) ^ $this->EW[$rx + 2];
		
		$i0 = $r3;
		$i1 = static::s($r0, 8);
		$i2 = static::s($r1, 16);
		$i3 = static::s($r2, 24);
		$i0 = EncryptionAesFastEngineGeneral::$S[$i0 & 255] & 255;
		$i1 = EncryptionAesFastEngineGeneral::$S[$i1 & 255] & 255;
		$i2 = EncryptionAesFastEngineGeneral::$S[$i2 & 255] & 255;
		$i3 = EncryptionAesFastEngineGeneral::$S[$i3 & 255] & 255;
		$this->C3 = $i0 ^ ($i1 << 8) ^ ($i2 << 16) ^ ($i3 << 24) ^ $this->EW[$rx + 3];
	}
	
	/**
	 */
	private function decryptBlock()
	{
		$ROUNDSx4 = $this->ROUNDS * 4;
		
		$t0 = $this->C0 ^ $this->DW[$ROUNDSx4];
		$t1 = $this->C1 ^ $this->DW[$ROUNDSx4 + 1];
		$t2 = $this->C2 ^ $this->DW[$ROUNDSx4 + 2];
		
		$r = $this->ROUNDS - 1;
		$rx = $ROUNDSx4 - 4;
//		$r0 = 0;
//		$r1 = 0;
//		$r2 = 0;
		$r3 = $this->C3 ^ $this->DW[$ROUNDSx4 + 3];
//		$i0 = 0;
//		$i1 = 0;
//		$i2 = 0;
//		$i3 = 0;
		
		while($r > 1)
		{
			$i0 = $t0;
			$i1 = static::s($r3, 8);
			$i2 = static::s($t2, 16);
			$i3 = static::s($t1, 24);
			$i0 &= 255;
			$i1 &= 255;
			$i2 &= 255;
			$i3 &= 255;
			$r0 = EncryptionAesFastEngineGeneral::$Tinv[$i0] ^ EncryptionAesFastEngineGeneral::$Tinv[256 + $i1] ^ EncryptionAesFastEngineGeneral::$Tinv[512 + $i2] ^ EncryptionAesFastEngineGeneral::$Tinv[768 + $i3] ^ $this->DW[$rx];
			
			$i0 = $t1;
			$i1 = static::s($t0, 8);
			$i2 = static::s($r3, 16);
			$i3 = static::s($t2, 24);
			$i0 &= 255;
			$i1 &= 255;
			$i2 &= 255;
			$i3 &= 255;
			$r1 = EncryptionAesFastEngineGeneral::$Tinv[$i0] ^ EncryptionAesFastEngineGeneral::$Tinv[256 + $i1] ^ EncryptionAesFastEngineGeneral::$Tinv[512 + $i2] ^ EncryptionAesFastEngineGeneral::$Tinv[768 + $i3] ^ $this->DW[$rx + 1];
			
			$i0 = $t2;
			$i1 = static::s($t1, 8);
			$i2 = static::s($t0, 16);
			$i3 = static::s($r3, 24);
			$i0 &= 255;
			$i1 &= 255;
			$i2 &= 255;
			$i3 &= 255;
			$r2 = EncryptionAesFastEngineGeneral::$Tinv[$i0] ^ EncryptionAesFastEngineGeneral::$Tinv[256 + $i1] ^ EncryptionAesFastEngineGeneral::$Tinv[512 + $i2] ^ EncryptionAesFastEngineGeneral::$Tinv[768 + $i3] ^ $this->DW[$rx + 2];
			
			$i0 = $r3;
			$i1 = static::s($t2, 8);
			$i2 = static::s($t1, 16);
			$i3 = static::s($t0, 24);
			$i0 &= 255;
			$i1 &= 255;
			$i2 &= 255;
			$i3 &= 255;
			$r3 = EncryptionAesFastEngineGeneral::$Tinv[$i0] ^ EncryptionAesFastEngineGeneral::$Tinv[256 + $i1] ^ EncryptionAesFastEngineGeneral::$Tinv[512 + $i2] ^ EncryptionAesFastEngineGeneral::$Tinv[768 + $i3] ^ $this->DW[$rx + 3];
			$r--;
			$rx -= 4;
			
			$i0 = $r0;
			$i1 = static::s($r3, 8);
			$i2 = static::s($r2, 16);
			$i3 = static::s($r1, 24);
			$i0 &= 255;
			$i1 &= 255;
			$i2 &= 255;
			$i3 &= 255;
			$t0 = EncryptionAesFastEngineGeneral::$Tinv[$i0] ^ EncryptionAesFastEngineGeneral::$Tinv[256 + $i1] ^ EncryptionAesFastEngineGeneral::$Tinv[512 + $i2] ^ EncryptionAesFastEngineGeneral::$Tinv[768 + $i3] ^ $this->DW[$rx];
			
			$i0 = $r1;
			$i1 = static::s($r0, 8);
			$i2 = static::s($r3, 16);
			$i3 = static::s($r2, 24);
			$i0 &= 255;
			$i1 &= 255;
			$i2 &= 255;
			$i3 &= 255;
			$t1 = EncryptionAesFastEngineGeneral::$Tinv[$i0] ^ EncryptionAesFastEngineGeneral::$Tinv[256 + $i1] ^ EncryptionAesFastEngineGeneral::$Tinv[512 + $i2] ^ EncryptionAesFastEngineGeneral::$Tinv[768 + $i3] ^ $this->DW[$rx + 1];
			
			$i0 = $r2;
			$i1 = static::s($r1, 8);
			$i2 = static::s($r0, 16);
			$i3 = static::s($r3, 24);
			$i0 &= 255;
			$i1 &= 255;
			$i2 &= 255;
			$i3 &= 255;
			$t2 = EncryptionAesFastEngineGeneral::$Tinv[$i0] ^ EncryptionAesFastEngineGeneral::$Tinv[256 + $i1] ^ EncryptionAesFastEngineGeneral::$Tinv[512 + $i2] ^ EncryptionAesFastEngineGeneral::$Tinv[768 + $i3] ^ $this->DW[$rx + 2];
			
			$i0 = $r3;
			$i1 = static::s($r2, 8);
			$i2 = static::s($r1, 16);
			$i3 = static::s($r0, 24);
			$i0 &= 255;
			$i1 &= 255;
			$i2 &= 255;
			$i3 &= 255;
			$r3 = EncryptionAesFastEngineGeneral::$Tinv[$i0] ^ EncryptionAesFastEngineGeneral::$Tinv[256 + $i1] ^ EncryptionAesFastEngineGeneral::$Tinv[512 + $i2] ^ EncryptionAesFastEngineGeneral::$Tinv[768 + $i3] ^ $this->DW[$rx + 3];
			$r--;
			$rx -= 4;
		}
		
		$i0 = $t0;
		$i1 = static::s($r3, 8);
		$i2 = static::s($t2, 16);
		$i3 = static::s($t1, 24);
		$i0 &= 255;
		$i1 &= 255;
		$i2 &= 255;
		$i3 &= 255;
		$r0 = EncryptionAesFastEngineGeneral::$Tinv[$i0] ^ EncryptionAesFastEngineGeneral::$Tinv[256 + $i1] ^ EncryptionAesFastEngineGeneral::$Tinv[512 + $i2] ^ EncryptionAesFastEngineGeneral::$Tinv[768 + $i3] ^ $this->DW[4];
		
		$i0 = $t1;
		$i1 = static::s($t0, 8);
		$i2 = static::s($r3, 16);
		$i3 = static::s($t2, 24);
		$i0 &= 255;
		$i1 &= 255;
		$i2 &= 255;
		$i3 &= 255;
		$r1 = EncryptionAesFastEngineGeneral::$Tinv[$i0] ^ EncryptionAesFastEngineGeneral::$Tinv[256 + $i1] ^ EncryptionAesFastEngineGeneral::$Tinv[512 + $i2] ^ EncryptionAesFastEngineGeneral::$Tinv[768 + $i3] ^ $this->DW[5];
		
		$i0 = $t2;
		$i1 = static::s($t1, 8);
		$i2 = static::s($t0, 16);
		$i3 = static::s($r3, 24);
		$i0 &= 255;
		$i1 &= 255;
		$i2 &= 255;
		$i3 &= 255;
		$r2 = EncryptionAesFastEngineGeneral::$Tinv[$i0] ^ EncryptionAesFastEngineGeneral::$Tinv[256 + $i1] ^ EncryptionAesFastEngineGeneral::$Tinv[512 + $i2] ^ EncryptionAesFastEngineGeneral::$Tinv[768 + $i3] ^ $this->DW[6];
		
		$i0 = $r3;
		$i1 = static::s($t2, 8);
		$i2 = static::s($t1, 16);
		$i3 = static::s($t0, 24);
		$i0 &= 255;
		$i1 &= 255;
		$i2 &= 255;
		$i3 &= 255;
		$r3 = EncryptionAesFastEngineGeneral::$Tinv[$i0] ^ EncryptionAesFastEngineGeneral::$Tinv[256 + $i1] ^ EncryptionAesFastEngineGeneral::$Tinv[512 + $i2] ^ EncryptionAesFastEngineGeneral::$Tinv[768 + $i3] ^ $this->DW[7];
		
		$i0 = $r0;
		$i1 = static::s($r3, 8);
		$i2 = static::s($r2, 16);
		$i3 = static::s($r1, 24);
		$i0 = EncryptionAesFastEngineGeneral::$Si[$i0 & 255] & 255;
		$i1 = EncryptionAesFastEngineGeneral::$Si[$i1 & 255] & 255;
		$i2 = EncryptionAesFastEngineGeneral::$Si[$i2 & 255] & 255;
		$i3 = EncryptionAesFastEngineGeneral::$Si[$i3 & 255] & 255;
		$this->C0 = $i0 ^ ($i1 << 8) ^ ($i2 << 16) ^ ($i3 << 24) ^ $this->DW[0];
		
		$i0 = $r1;
		$i1 = static::s($r0, 8);
		$i2 = static::s($r3, 16);
		$i3 = static::s($r2, 24);
		$i0 = EncryptionAesFastEngineGeneral::$Si[$i0 & 255] & 255;
		$i1 = EncryptionAesFastEngineGeneral::$Si[$i1 & 255] & 255;
		$i2 = EncryptionAesFastEngineGeneral::$Si[$i2 & 255] & 255;
		$i3 = EncryptionAesFastEngineGeneral::$Si[$i3 & 255] & 255;
		$this->C1 = $i0 ^ ($i1 << 8) ^ ($i2 << 16) ^ ($i3 << 24) ^ $this->DW[1];
		
		$i0 = $r2;
		$i1 = static::s($r1, 8);
		$i2 = static::s($r0, 16);
		$i3 = static::s($r3, 24);
		$i0 = EncryptionAesFastEngineGeneral::$Si[$i0 & 255] & 255;
		$i1 = EncryptionAesFastEngineGeneral::$Si[$i1 & 255] & 255;
		$i2 = EncryptionAesFastEngineGeneral::$Si[$i2 & 255] & 255;
		$i3 = EncryptionAesFastEngineGeneral::$Si[$i3 & 255] & 255;
		$this->C2 = $i0 ^ ($i1 << 8) ^ ($i2 << 16) ^ ($i3 << 24) ^ $this->DW[2];
		
		$i0 = $r3;
		$i1 = static::s($r2, 8);
		$i2 = static::s($r1, 16);
		$i3 = static::s($r0, 24);
		$i0 = EncryptionAesFastEngineGeneral::$Si[$i0 & 255] & 255;
		$i1 = EncryptionAesFastEngineGeneral::$Si[$i1 & 255] & 255;
		$i2 = EncryptionAesFastEngineGeneral::$Si[$i2 & 255] & 255;
		$i3 = EncryptionAesFastEngineGeneral::$Si[$i3 & 255] & 255;
		$this->C3 = $i0 ^ ($i1 << 8) ^ ($i2 << 16) ^ ($i3 << 24) ^ $this->DW[3];
	}
}
