<?php
/*
 * Author: Ryan Gilfether
 * URL: http://www.gilfether.com/phpCrypt
 * Date: April 3, 2013
 * Copyright (C) 2013 Ryan Gilfether
 *
 * This file is part of phpCrypt
 *
 * phpCrypt is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 */


require_once(dirname(__FILE__)."/../Cipher.php");


/**
 * Base class to implement Rijndael (and AES)
 * This class can not be used directly, instead one
 * one of the child classes that inherit this class
 * should be used.
 * References used to implement this cipher:
 * http://www.net-security.org/dl/articles/AESbyExample.pdf
 * http://www.quadibloc.com/crypto/co040401.htm
 * http://www.inconteam.com/software-development/41-encryption/55-aes-test-vectors
 * FIPS 197 (fips-197.pdf)
 *
 * @author Ryan Gilfether
 * @link http://www.gilfether.com/phpcrypt
 * @copyright 2013 Ryan Gilfether
 */
abstract class PhpCrypt_Cipher_Rijndael extends PhpCrypt_Cipher
{
	/** @type string $xkey The expanded key */
	private $xkey = "";

	/**
	 * @type array $_key_sizes The accepted key sizes in bytes,
	 * this should be considered a constant
	 */
	private static $_key_sizes = array(16, 24, 32);


	// THE FOLLOWING TABLES ARE INITIALIZED IN initTables()

	/**
	 * @type array $_s The sbox,
	 * this should be considered a constant
	 */
	private static $_s = array();

	/**
	 * @type array $_s_inv The inverse sbox
	 * this should be considered a constant
	 */
	private static $_s_inv = array();

	/**
	 * @type array $_rcon The round constant,
	 * this should be considered a constant
	 */
	private static $_rcon = array();

	/**
	 * @type array $s_matrix_mult The matrix multiplication table,
	 * this should be considered a constant
	 */
	private static $_matrix_mult = array();

	/**
	 * @type array $_matrix_mult_inv The matrix multiplication
	 * inverse table, this should be considered a constant
	 */
	private static $_matrix_mult_inv = array();

	/*
	 * Galois Multiplication lookup tables,
	 * initialized in initTables()
	 */
	/**
	 * @type array $_gm2 The Galois Multiplication table
	 * for muliplying by 2, this should be considered a constant
	 */
	private static $_gm2 = array();

	/**
	 * @type array $_gm3 The Galois Multiplication table
	 * for muliplying by 3, this should be considered a constant
	 */
	private static $_gm3 = array();

	/**
	 * @type array $_gm9 The Galois Multiplication table
	 * for muliplying by 9, this should be considered a constant
	 */
	private static $_gm9 = array();

	/**
	 * @type array $_gm11 The Galois Multiplication table
	 * for muliplying by 11, this should be considered a constant
	 */
	private static $_gm11 = array();

	/**
	 * @type array $_gm13 The Galois Multiplication table
	 * for muliplying by 13, this should be considered a constant
	 */
	private static $_gm13 = array();

	/**
	 * @type array $_gm14 The Galois Multiplication table
	 * for muliplying by 14, this should be considered a constant
	 */
	private static $_gm14 = array();


	/**
	 * Constructor
	 * Sets the key used for encryption. Also sets the requied block size
	 *
	 * @param string The cipher name as set in a constant in the child class
	 * @param string $key string containing the user supplied encryption key
	 * @param integer $len Optional, the key size in bytes - used only by the AES child classes
	 * @return void
	 */
	public function __construct($cipher_name, $key, $len = 0)
	{
		// AES will pass in a $len, since it has a fixed key size, other
		// rijndael implementations can use variable key sizes, supported
		// sizes are stored in self::$_key_sizes
		if($len == 0)
		{
			// the key must be one of the following lengths: 16, 24, 32 bytes
			$len = strlen($key);
			if(!in_array($len, self::$_key_sizes))
			{
				$msg  = "Incorrect key length for ".strtoupper($cipher_name).". ";
				$msg .= "Received $len bytes.";
				trigger_error($msg, E_USER_WARNING);
			}
		}

		// Setup the key
		parent::__construct($cipher_name, $key, $len);

		// initialize the tables used for rijndael/aes
		$this->initTables();
	}


	/**
	 * Destructor
	 *
	 * @return void
	 */
	public function __destruct()
	{
		parent::__destruct();
	}


	/**
	 * Performs Rijndael encryption
	 *
	 * @param string @text The string to encrypt
	 * @return boolean Returns true
	 */
	public function encrypt(&$text)
	{
		// set the operation to decryption
		$this->operation(parent::ENCRYPT);

		$loops = 0;
		$key_sz = $this->keySize();
		$blk_sz = $this->blockSize();

		// if the key and block size is 16, do 10 rounds
		// if the key or block size is 24, and neither is longer than 24, do 12 rounds
		// if either key or block size is 32, do 14 rounds
		if($key_sz == 16 && $blk_sz == 16)
			$loops = 10;
		else if(($key_sz == 24 || $blk_sz == 24) && $key_sz <= 24 && $blk_sz <= 24)
			$loops = 12;
		else if($key_sz == 32 || $blk_sz == 32)
			$loops = 14;

		// now begin the encryption
		$this->addRoundKey($text, 0);

		for($i = 1; $i <= $loops; ++$i)
		{
			$this->byteSub($text);
			$this->shiftRow($text);

			// the last iteration does not use mixColumn
			if($i < $loops)
				$this->mixColumn($text);

			$this->addRoundKey($text, $i);
		}

		return true;
	}


	/**
	 * Performs Rijndael decryption
	 *
	 * @param string @text The string to decrypt
	 * @return boolean Returns true
	 */
	public function decrypt(&$text)
	{
		// set the operation to decryption
		$this->operation(parent::DECRYPT);

		$loops = 0;
		$key_sz = $this->keySize();
		$blk_sz = $this->blockSize();

		// if the key and block size is 16, do 10 rounds
		// if the key or block size is 24, and neither is longer than 24, do 12 rounds
		// if either key or block size is 32, do 14 rounds
		if($key_sz == 16 && $blk_sz == 16)
			$loops = 10;
		else if(($key_sz == 24 || $blk_sz == 24) && $key_sz <= 24 && $blk_sz <= 24)
			$loops = 12;
		else if($key_sz == 32 || $blk_sz == 32)
			$loops = 14;

		// now begin the decryption
		$this->addRoundKey($text, 0);

		for($i = 1; $i <= $loops; ++$i)
		{
			$this->shiftRow($text);
			$this->byteSub($text);
			$this->addRoundKey($text, $i);

			// the last iteration does not use mixColumn
			if($i < $loops)
				$this->mixColumn($text);
		}

		return true;
	}


	/**
	 * Indicates that this is a block cipher
	 *
	 * @return integer Returns Cipher::BLOCK
	 */
	public function type()
	{
		return parent::BLOCK;
	}


	/**
	 * Do the multiplication required in mixColumn()
	 * We follow the description the multiplication from Wikipedia:
	 * http://en.wikipedia.org/wiki/Advanced_Encryption_Standard#The_MixColumns_step
	 *
	 * @param integer $m A value from self::$_matrix_mult or self::$_matrix_mult_inv
	 * @param integer $byte The value to multipy by $m
	 * @return integer The result of the multiplication
	 */
	protected function mixColumnMultiply($m, $byte)
	{
		// if multiplying by 1, then we just return the same number
		if($m == 0x01)
			return $byte;

		$hex = parent::dec2Hex($byte);
		$row = parent::hex2Dec($hex[0]);
		$col = parent::hex2Dec($hex[1]);
		$pos = ($row * 16) + $col;

		// multiply by 2 (comes from self::$_matrix_mult during encryption)
		if($m == 0x02)
			return self::$_gm2[$pos];

		// multiply by 3 (comes from self::$_matrix_mult during encryption)
		if($m == 0x03)
			return self::$_gm3[$pos];

		// multiply by 9 (comes from self::$_matrix_mult_inv during decryption)
		if($m == 0x09)
			return self::$_gm9[$pos];

		// multiply by 11 (comes from self::$_matrix_mult_inv during decryption)
		if($m == 0x0b)
			return self::$_gm11[$pos];

		// multiply by 13 (comes from self::$_matrix_mult_inv during decryption)
		if($m == 0x0d)
			return self::$_gm13[$pos];

		// multiply by 14 (comes from self::$_matrix_mult_inv during decryption)
		if($m == 0x0e)
			return self::$_gm14[$pos];
	}


	/**
	 * Each time this function is called, it XORs the 16 bytes of $text
	 * with the next 16 bytes from the $this->xkey. The expanded key
	 * never has the same bytes used twice. Note that the first time
	 * addRoundKey() is called, it will be outside of the rounds, so no
	 * $round is given. Every call after that will be inside the rounds.
	 *
	 * @param string $text The text to encrypt/decrypt
	 * @param integer $round The round we are on inside of rijndael()
	 * @return void
	 */
	private function addRoundKey(&$text, $round)
	{
		// length of the xkey
		$ek_len = strlen($this->xkey);
		$len = $this->blockSize();

		if($this->operation() == parent::ENCRYPT)
			$offset = $round * $len;
		else
			$offset = ($ek_len - ($round * $len)) - $len;

		for($i = 0; $i < $len; ++$i)
			$text[$i] = $text[$i] ^ $this->xkey[$offset + $i];
	}


	/**
	 * Applies the Sbox to each byte of the string passed in
	 * This is used in key expansione
	 *
	 * @param string $text The string to peform the byte subsitution
	 * @return void
	 */
	private function byteSub(&$text)
	{
		$max = strlen($text);
		for($i = 0; $i < $max; ++$i)
		{
			// the sbox is arrange in a 16 x 16 grid, where each row
			// and column is numbered in hex (from 0 - f)
			$hex = parent::str2Hex($text[$i]);
			$row = parent::hex2Dec($hex[0]);
			$col = parent::hex2Dec($hex[1]);
			$pos = ($row * 16) + $col;

			// return the corresponding value from the sbox
			if($this->operation() == parent::ENCRYPT)
				$text[$i] = chr(self::$_s[$pos]);
			else // parent::DECRYPT uses the inverse sbox
				$text[$i] = chr(self::$_s_inv[$pos]);
		}
	}


	/**
	 * This function is hard to explain, the easiest way to understand it is to read
	 * http://www.net-security.org/dl/articles/AESbyExample.pdf, Section 5.4
	 *
	 * @param string $t The string to multiply bytes by the Galois Multiplication lookup tables
	 * @return void
	 */
	private function mixColumn(&$t)
	{
		$tmp = $t;

		// the matrix we use depends on if we are encrypting or decrypting
		if($this->operation() == parent::ENCRYPT)
			$m = self::$_matrix_mult;
		else // parent::DECRYPT
			$m = self::$_matrix_mult_inv;

		// the number of rounds we make depends on the block size of the text
		// used during encryption/decryption
		// 128 has a 4x4 matrix, loop 4 times
		// 192 has a 4x6 matrix, loop 6 times
		// 256 has a 4x8 matrix, loop 8 times
		$max_col = ($this->blockSize() * 8) / 32;

		// loop through each column of the matrix
		for($col = 0; $col < $max_col; ++$col)
		{
			$pos = $col * 4;

			$a = $this->mixColumnMultiply($m[0],  ord($tmp[$pos + 0]));
			$b = $this->mixColumnMultiply($m[4],  ord($tmp[$pos + 1]));
			$c = $this->mixColumnMultiply($m[8],  ord($tmp[$pos + 2]));
			$d = $this->mixColumnMultiply($m[12], ord($tmp[$pos + 3]));
			$t[$pos + 0] = chr($a ^ $b ^ $c ^ $d);

			$a = $this->mixColumnMultiply($m[1],  ord($tmp[$pos + 0]));
			$b = $this->mixColumnMultiply($m[5],  ord($tmp[$pos + 1]));
			$c = $this->mixColumnMultiply($m[9],  ord($tmp[$pos + 2]));
			$d = $this->mixColumnMultiply($m[13], ord($tmp[$pos + 3]));
			$t[$pos + 1] = chr($a ^ $b ^ $c ^ $d);

			$a = $this->mixColumnMultiply($m[2],  ord($tmp[$pos + 0]));
			$b = $this->mixColumnMultiply($m[6],  ord($tmp[$pos + 1]));
			$c = $this->mixColumnMultiply($m[10], ord($tmp[$pos + 2]));
			$d = $this->mixColumnMultiply($m[14], ord($tmp[$pos + 3]));
			$t[$pos + 2] = chr($a ^ $b ^ $c ^ $d);

			$a = $this->mixColumnMultiply($m[3],  ord($tmp[$pos + 0]));
			$b = $this->mixColumnMultiply($m[7],  ord($tmp[$pos + 1]));
			$c = $this->mixColumnMultiply($m[11], ord($tmp[$pos + 2]));
			$d = $this->mixColumnMultiply($m[15], ord($tmp[$pos + 3]));
			$t[$pos + 3] = chr($a ^ $b ^ $c ^ $d);
		}
	}


	/**
	 * Convert the 16, 24, or 32 bytes of $text into a matrix, and shift each row
	 * n-bytes left for encryptiong, n-bytes right if we are decrypting.
	 * Row shifts depend on the bit size of the block $text.
	 * Rijndael-128/AES: 4x4 matrix
	 * Rijndael-192:	6x4 matrix
	 * Rijndael-256:	8x4 matrix
	 *
	 * @param string $text A 16, 24, or 32 byte string
	 * @return void
	 */
	private function shiftRow(&$text)
	{
		/*
		 * Rijndael-128 / AES
		 */
		if($this->blockSize() == 16)
		{
			if($this->operation() == parent::ENCRYPT)
			{
				// create a 4x4 matrix
				// row 0 is unchanged,
				// shift row 1 left 1 byte
				// shift row 2 left 2 bytes
				// shift row 3 left 3 bytes
				$text = $text[0].$text[5].$text[10].$text[15].$text[4].$text[9].
						$text[14].$text[3].$text[8].$text[13].$text[2].$text[7].
						$text[12].$text[1].$text[6].$text[11];
			}
			else // parent::DECRYPT
			{
				// create a 4x4 matrix
				// row 0 is unchanged,
				// shift row 1 right 1 byte
				// shift row 2 right 2 bytes
				// shift row 3 right 3 bytes
				$text = $text[0].$text[13].$text[10].$text[7].$text[4].$text[1].
						$text[14].$text[11].$text[8].$text[5].$text[2].$text[15].
						$text[12].$text[9].$text[6].$text[3];
			}
		}

		/*
		 * Rijndael-192
		 */
		if($this->blockSize() == 24)
		{
			if($this->operation() == parent::ENCRYPT)
			{
				// create a 6x4 matrix
				// row 0 is unchanged
				// shift row 1 left 1 byte
				// shift row 2 left 2 bytes
				// shift row 3 left 3 bytes
				$text = $text[0].$text[5].$text[10].$text[15].$text[4].$text[9].
						$text[14].$text[19].$text[8].$text[13].$text[18].$text[23].
						$text[12].$text[17].$text[22].$text[3].$text[16].$text[21].
						$text[2].$text[7].$text[20].$text[1].$text[6].$text[11];

			}
			else // parent::DECRYPT
			{
				// create a 6x4 matrix
				// row 0 is unchanged
				// shift row 1 right 1 byte
				// shift row 2 right 2 bytes
				// shift row 3 right 3 bytes
				$text = $text[0].$text[21].$text[18].$text[15].$text[4].$text[1].
						$text[22].$text[19].$text[8].$text[5].$text[2].$text[23].
						$text[12].$text[9].$text[6].$text[3].$text[16].$text[13].
						$text[10].$text[7].$text[20].$text[17].$text[14].$text[11];
			}
		}

		/*
		 * Rijndael-256
		 */
		if($this->blockSize() == 32)
		{
			if($this->operation() == parent::ENCRYPT)
			{
				// create an 8x4 matrix
				// row 0 is unchanged
				// shift row 1 left 1 byte
				// shift row 2 left 3 bytes
				// shift row 3 left 4 bytes
				$text = $text[0].$text[5].$text[14].$text[19].$text[4].$text[9].$text[18].
						$text[23].$text[8].$text[13].$text[22].$text[27].$text[12].$text[17].
						$text[26].$text[31].$text[16].$text[21].$text[30].$text[3].$text[20].
						$text[25].$text[2].$text[7].$text[24].$text[29].$text[6].$text[11].
						$text[28].$text[1].$text[10].$text[15];
			}
			else // parent::DECRYPT
			{
				// create an 8x4 matrix
				// row 0 is unchanged
				// shift row 1 right 1 byte
				// shift row 2 right 3 bytes
				// shift row 3 right 4 bytes
				$text = $text[0].$text[29].$text[22].$text[19].$text[4].$text[1].$text[26].
						$text[23].$text[8].$text[5].$text[30].$text[27].$text[12].$text[9].
						$text[2].$text[31].$text[16].$text[13].$text[6].$text[3].$text[20].
						$text[17].$text[10].$text[7].$text[24].$text[21].$text[14].$text[11].
						$text[28].$text[25].$text[18].$text[15];
			}
		}
	}


	/**
	 * Applies the Sbox to each byte of the string passed in.
	 * This is similar to subByte(), but Unlike subByte() we do not use
	 * the _s_inv[] table. This function is only used in expandKey(),
	 * which is implemented by the class that inherits this class
	 *
	 * @param string $text The string to peform the byte subsitution
	 * @return string The string with the subsituted bytes
	 */
	private function subWord(&$text)
	{
		$max = strlen($text);
		for($i = 0; $i < $max; ++$i)
		{
			// the sbox is arrange in a 16 x 16 grid, where each row
			// and column is numbered in hex (from 0 - f)
			$hex = parent::str2Hex($text[$i]);
			$row = parent::hex2Dec($hex[0]);
			$col = parent::hex2Dec($hex[1]);
			$pos = ($row * 16) + $col;

			$text[$i] = chr(self::$_s[$pos]);
		}
	}


	/**
	 * Rotate a 4 byte block of the key, moving the first byte to
	 * to the end, and shifting everything left
	 * Used in key Expandsion
	 *
	 * @param string $key_block A 4 byte string
	 * @return string The shifted 4 byte string
	 */
	private function rotWord($key_block)
	{
		return substr($key_block, 1, 3).$key_block[0];
	}


	/**
	 * Returns 4 bytes from the expanded key starting at the given offset
	 * Used during expandKey()
	 *
	 * @param integer $offset The offset within $this->xkey to grab the 4 bytes
	 * @return string A 4 byte string from the key
	 */
	private function ek($offset)
	{
		return substr($this->xkey, $offset, 4);
	}


	/**
	 * Returns 4 bytes of the original key from the given offset
	 * Used during expandKey()
	 *
	 * @param integer $offset The offset within $this->key to grab the 4 bytes
	 * @return string A 4 byte string from the key
	 */
	private function k($offset)
	{
		return substr($this->key(), $offset, 4);
	}


	/**
	 * Return the 4 byte round constant used during expandKey().
	 * Gets the 1 byte value from self::$_rcon and multiplies it by
	 * 0x01000000 to create a 4 byte value
	 *
	 * @param integer $pos The position in self::$_rcon array to grab 1 byte
	 * @return integer A 4 byte value
	 */
	private function rcon($pos)
	{
		return (self::$_rcon[$pos] * 0x01000000);
	}


	/**
	 * Expands the key
	 * The key expands based on the block size as well as the key size
	 *
	 * @return void
	 */
	protected function expandKey()
	{
		if($this->keySize() == 16)
			return $this->expandKey128();
		else if($this->keySize() == 24)
			return $this->expandKey192();
		else if($this->keySize() == 32)
			return $this->expandKey256();
	}


	/**
	 * Expand a 16 byte key, the size it is expanded to varies
	 * based on the block size of the Rijndael implementation chosen
	 *
	 * @return void
	 */
	private function expandKey128()
	{
		// clear the xkey, we're creating a new one
		$this->xkey = "";
		$max = 0;

		// the number of rounds we make depends on the block size of the text
		// used during encryption/decryption
		if($this->blockSize() == 16)
			$max = 44;
		if($this->blockSize() == 24)
			$max = 78;
		if($this->blockSize() == 32)
			$max = 120;

		// 16 byte key expands to 176 bytes
		for($i = 0; $i < $max; ++$i)
		{
			if($i >= 0 && $i <= 3)
				$this->xkey .= $this->k($i * 4);
			else if(($i % 4) == 0)
			{
				// rotate the 4 bytes
				$subword = $this->rotWord($this->ek(($i - 1) * 4));

				// apply the sbox
				$this->subWord($subword);

				// return 4 byte value based on self::$_rcon table
				//$rcon = $this->rcon(($i / 4) - 1);
				$rcon = $this->rcon(($i / 4));

				// grab 4 bytes from $this->extended_key
				$ek = $this->ek(($i - 4) * 4);

				$h1 = parent::str2Hex($subword);
				$h2 = parent::dec2Hex($rcon);
				$h3 = parent::str2Hex($ek);
				$res = parent::xorHex($h1, $h2, $h3);
				$this->xkey .= parent::hex2Str($res);
			}
			else
			{
				$h1 = parent::str2Hex($this->ek(($i - 1) * 4));
				$h2 = parent::str2Hex($this->ek(($i - 4) * 4));
				$res = parent::xorHex($h1, $h2);
				$this->xkey .= parent::hex2Str($res);
			}
		}

		return true;
	}


	/**
	 * Expand a 24 byte key, the size it is expanded to varies
	 * based on the block size of the Rijndael implementation chosen
	 *
	 * @return void
	 */
	private function expandKey192()
	{
		// clear the xkey, we're creating a new one
		$this->xkey = "";
		$max = 0;

		// the number of rounds we make depends on the block size of the text
		// used during encryption/decryption
		if($this->blockSize() == 16)
			$max = 52;
		if($this->blockSize() == 24)
			$max = 78;
		if($this->blockSize() == 32)
			$max = 120;

		// 24 byte key expands to 208 bytes
		for($i = 0; $i < $max; ++$i)
		{
			if($i >= 0 && $i <= 5)
				$this->xkey .= $this->k($i * 4);
			else if(($i % 6) == 0)
			{
				// rotate the 4 bytes
				$subword = $this->rotWord($this->ek(($i - 1) * 4));

				// apply the sbox
				$this->subWord($subword);

				// return 4 byte value based on self::$_rcon table
				//$rcon = $this->rcon(($i / 6) - 1);
				$rcon = $this->rcon(($i / 6));

				// grab 4 bytes from $this->extended_key
				$ek = $this->ek(($i - 6) * 4);

				$h1 = parent::str2Hex($subword);
				$h2 = parent::dec2Hex($rcon);
				$h3 = parent::str2Hex($ek);
				$res = parent::xorHex($h1, $h2, $h3);
				$this->xkey .= parent::hex2Str($res);
			}
			else
			{
				$h1 = parent::str2Hex($this->ek(($i - 1) * 4));
				$h2 = parent::str2Hex($this->ek(($i - 6) * 4));
				$res = parent::xorHex($h1, $h2);
				$this->xkey .= parent::hex2Str($res);
			}
		}

		return true;
	}


	/**
	 * Expand a 32 byte key, the size it is expanded to varies
	 * based on the block size of the Rijndael implementation chosen
	 *
	 * @return void
	 */
	private function expandKey256()
	{
		// clear the xkey, we're creating a new one
		$this->xkey = "";
		$max = 0;

		// the number of rounds we make depends on the block size of the text
		// used during encryption/decryption
		if($this->blockSize() == 16)
			$max = 60;
		if($this->blockSize() == 24)
			$max = 90;
		if($this->blockSize() == 32)
			$max = 120;

		// 32 byte key expands to 240 bytes
		for($i = 0; $i < $max; ++$i)
		{
			if($i >= 0 && $i <= 7)
				$this->xkey .= $this->k($i * 4);
			else if($i % 8 == 0)
			{
				// rotate the 4 bytes
				$subword = $this->rotWord($this->ek(($i - 1) * 4));

				// apply the sbox
				$this->subWord($subword);

				// return 4 byte value based on self::$_rcon table
				$rcon = $this->rcon(($i / 8));

				// grab 4 bytes from $this->extended_key
				$ek = $this->ek(($i - 8) * 4);

				$h1 = parent::str2Hex($subword);
				$h2 = parent::dec2Hex($rcon);
				$h3 = parent::str2Hex($ek);
				$res = parent::xorHex($h1, $h2, $h3);
				$this->xkey .= parent::hex2Str($res);
			}
			else if($i % 4 == 0)
			{
				// get the subsitution from the s-box
				$subword = $this->ek(($i - 1) * 4);
				$this->subWord($subword);

				// get the extended key part
				$ek = $this->ek(($i - 8) * 4);

				// xor the two parts
				$h1 = parent::str2Hex($subword);
				$h2 = parent::str2Hex($ek);
				$res = parent::xorHex($h1, $h2);
				$this->xkey .= parent::hex2Str($res);
			}
			else
			{
				$h1 = parent::str2Hex($this->ek(($i - 1) * 4));
				$h2 = parent::str2Hex($this->ek(($i - 8) * 4));
				$res = parent::xorHex($h1, $h2);
				$this->xkey .= parent::hex2Str($res);
			}
		}

		return true;
	}


	/**
	 * Initalizes the tables used for Rijndael/AES encryption
	 *
	 * @return void
	 */
	private function initTables()
	{
		// the sbox used for encryption
		self::$_s = array(
			0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
			0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
			0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
			0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
			0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
			0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
			0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
			0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
			0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
			0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
			0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
			0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
			0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
			0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
			0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
			0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
		);

		// the inverse sbox used for decryption
		self::$_s_inv = array(
			0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
			0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
			0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
			0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
			0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
			0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
			0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
			0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
			0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
			0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
			0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
			0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
			0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
			0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
			0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
			0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
		);

		// used in mixColumn() during encryption
		self::$_matrix_mult = array(
			0x02, 0x01, 0x01, 0x03,
			0x03, 0x02, 0x01, 0x01,
			0x01, 0x03, 0x02, 0x01,
			0x01, 0x01, 0x03, 0x02
		);

		// used in mixColumn() during decryption
		self::$_matrix_mult_inv = array(
			0x0e, 0x09, 0x0d, 0x0b,
			0x0b, 0x0e, 0x09, 0x0d,
			0x0d, 0x0b, 0x0e, 0x09,
			0x09, 0x0d, 0x0b, 0x0e
		);

		// The round constants, each round is a 1 byte value which should be multiplied by 0x01000000
		// to create a 4 byte value before being used in expandKey(). This is done in rcon()
		// NOTE: AES only needs the first row of values, since AES only uses 16 byte blocks,
		// the other values are used for larger block/key combinations supported by Rijndael
		// NOTE: self::$_rcon[0] is never used
		self::$_rcon = array(
			0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
			0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
			0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
			0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
			0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
			0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
			0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
			0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
			0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
			0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
			0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
			0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
			0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
			0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
			0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
			0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d
		);

		/*
		 * Galois Multiplication lookup tables
		 * See http://en.wikipedia.org/wiki/Rijndael_mix_columns#InverseMixColumns
		 */

		// multiply a byte by 2 (the value 2 will come from self::$_matrix_mult)
		self::$_gm2 = array(
			0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
			0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
			0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
			0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
			0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
			0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
			0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
			0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
			0x1b, 0x19, 0x1f, 0x1d, 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05,
			0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, 0x23, 0x21, 0x27, 0x25,
			0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45,
			0x7b, 0x79, 0x7f, 0x7d, 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65,
			0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85,
			0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5,
			0xdb, 0xd9, 0xdf, 0xdd, 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5,
			0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5
		);

		// multiply a byte by 3 (the value 3 will come from self::$_matrix_mult)
		self::$_gm3 = array(
			0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11,
			0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21,
			0x60, 0x63, 0x66, 0x65, 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71,
			0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d, 0x44, 0x47, 0x42, 0x41,
			0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9, 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1,
			0xf0, 0xf3, 0xf6, 0xf5, 0xfc, 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1,
			0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9, 0xb8, 0xbb, 0xbe, 0xbd, 0xb4, 0xb7, 0xb2, 0xb1,
			0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99, 0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81,
			0x9b, 0x98, 0x9d, 0x9e, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8f, 0x8c, 0x89, 0x8a,
			0xab, 0xa8, 0xad, 0xae, 0xa7, 0xa4, 0xa1, 0xa2, 0xb3, 0xb0, 0xb5, 0xb6, 0xbf, 0xbc, 0xb9, 0xba,
			0xfb, 0xf8, 0xfd, 0xfe, 0xf7, 0xf4, 0xf1, 0xf2, 0xe3, 0xe0, 0xe5, 0xe6, 0xef, 0xec, 0xe9, 0xea,
			0xcb, 0xc8, 0xcd, 0xce, 0xc7, 0xc4, 0xc1, 0xc2, 0xd3, 0xd0, 0xd5, 0xd6, 0xdf, 0xdc, 0xd9, 0xda,
			0x5b, 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 0x4f, 0x4c, 0x49, 0x4a,
			0x6b, 0x68, 0x6d, 0x6e, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a,
			0x3b, 0x38, 0x3d, 0x3e, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a,
			0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a
		);

		// multiply a byte by 9 (the value 9 will come from self::$_matrix_mult_inv)
		self::$_gm9 = array(
			0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
			0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7,
			0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
			0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, 0xc7, 0xce, 0xd5, 0xdc,
			0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49, 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01,
			0xe6, 0xef, 0xf4, 0xfd, 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91,
			0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e, 0x21, 0x28, 0x33, 0x3a,
			0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa,
			0xec, 0xe5, 0xfe, 0xf7, 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b,
			0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, 0x10, 0x19, 0x02, 0x0b,
			0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0,
			0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30,
			0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed,
			0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d,
			0xa1, 0xa8, 0xb3, 0xba, 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6,
			0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46
		);

		// multiply a byte by 11 (the value 11 will come from self::$_matrix_mult_inv)
		self::$_gm11 = array(
			0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69,
			0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9,
			0x7b, 0x70, 0x6d, 0x66, 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12,
			0xcb, 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa, 0x93, 0x98, 0x85, 0x8e, 0xbf, 0xb4, 0xa9, 0xa2,
			0xf6, 0xfd, 0xe0, 0xeb, 0xda, 0xd1, 0xcc, 0xc7, 0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f,
			0x46, 0x4d, 0x50, 0x5b, 0x6a, 0x61, 0x7c, 0x77, 0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f,
			0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, 0xbc, 0xd5, 0xde, 0xc3, 0xc8, 0xf9, 0xf2, 0xef, 0xe4,
			0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c, 0x65, 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54,
			0xf7, 0xfc, 0xe1, 0xea, 0xdb, 0xd0, 0xcd, 0xc6, 0xaf, 0xa4, 0xb9, 0xb2, 0x83, 0x88, 0x95, 0x9e,
			0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76, 0x1f, 0x14, 0x09, 0x02, 0x33, 0x38, 0x25, 0x2e,
			0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd, 0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, 0xe5,
			0x3c, 0x37, 0x2a, 0x21, 0x10, 0x1b, 0x06, 0x0d, 0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55,
			0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44, 0x75, 0x7e, 0x63, 0x68,
			0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80, 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8,
			0x7a, 0x71, 0x6c, 0x67, 0x56, 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13,
			0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3
		);

		// multiply a byte by 13 (the value 13 will come from self::$_matrix_mult_inv)
		self::$_gm13 = array(
			0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b,
			0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b,
			0xbb, 0xb6, 0xa1, 0xac, 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0,
			0x6b, 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48, 0x03, 0x0e, 0x19, 0x14, 0x37, 0x3a, 0x2d, 0x20,
			0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e, 0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26,
			0xbd, 0xb0, 0xa7, 0xaa, 0x89, 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6,
			0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, 0xf5, 0xbe, 0xb3, 0xa4, 0xa9, 0x8a, 0x87, 0x90, 0x9d,
			0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25, 0x6e, 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d,
			0xda, 0xd7, 0xc0, 0xcd, 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91,
			0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75, 0x56, 0x5b, 0x4c, 0x41,
			0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42, 0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, 0x2a,
			0xb1, 0xbc, 0xab, 0xa6, 0x85, 0x88, 0x9f, 0x92, 0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa,
			0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc,
			0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44, 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c,
			0x0c, 0x01, 0x16, 0x1b, 0x38, 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47,
			0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97
		);

		// multiply a byte by 14 (the value 14 will come from self::$_matrix_mult_inv)
		self::$_gm14 = array(
			0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a,
			0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba,
			0xdb, 0xd5, 0xc7, 0xc9, 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81,
			0x3b, 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11, 0x4b, 0x45, 0x57, 0x59, 0x73, 0x7d, 0x6f, 0x61,
			0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87, 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7,
			0x4d, 0x43, 0x51, 0x5f, 0x75, 0x7b, 0x69, 0x67, 0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17,
			0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, 0x5c, 0x06, 0x08, 0x1a, 0x14, 0x3e, 0x30, 0x22, 0x2c,
			0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc, 0xe6, 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc,
			0x41, 0x4f, 0x5d, 0x53, 0x79, 0x77, 0x65, 0x6b, 0x31, 0x3f, 0x2d, 0x23, 0x09, 0x07, 0x15, 0x1b,
			0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b, 0xd1, 0xdf, 0xcd, 0xc3, 0xe9, 0xe7, 0xf5, 0xfb,
			0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0, 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0,
			0x7a, 0x74, 0x66, 0x68, 0x42, 0x4c, 0x5e, 0x50, 0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20,
			0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e, 0xa4, 0xaa, 0xb8, 0xb6,
			0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26, 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56,
			0x37, 0x39, 0x2b, 0x25, 0x0f, 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d,
			0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d
		);
	}
}
?>