Introducing CI_Encryption (a CI_Encrypt replacement)
diff --git a/system/libraries/Encryption.php b/system/libraries/Encryption.php
new file mode 100644
index 0000000..76569f2
--- /dev/null
+++ b/system/libraries/Encryption.php
@@ -0,0 +1,718 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2013, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * CodeIgniter Encryption Class
+ *
+ * Provides two-way keyed encryption via PHP's MCrypt and/or OpenSSL extensions.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Libraries
+ * @category	Libraries
+ * @author		Andrey Andreev
+ * @link		http://codeigniter.com/user_guide/libraries/encryption.html
+ */
+class CI_Encryption {
+
+	/**
+	 * Encryption cipher
+	 *
+	 * @var	string
+	 */
+	protected $_cipher = 'rijndael-128';
+
+	/**
+	 * Cipher mode
+	 *
+	 * @var	string
+	 */
+	protected $_mode = 'cbc';
+
+	/**
+	 * Cipher handle
+	 *
+	 * @var	mixed
+	 */
+	protected $_handle;
+
+	/**
+	 * Encryption key
+	 *
+	 * @var	string
+	 */
+	protected $_key;
+
+	/**
+	 * PHP extension to be used
+	 *
+	 * @var	string
+	 */
+	protected $_driver;
+
+	/**
+	 * List of usable drivers (PHP extensions)
+	 *
+	 * @var	array
+	 */
+	protected $_drivers = array();
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Class constructor
+	 *
+	 * @param	array	$params	Configuration parameters
+	 * @return	void
+	 */
+	public function __construct(array $params = array())
+	{
+		$this->_drivers = array(
+			'mcrypt' => extension_loaded('mcrypt'),
+			// While OpenSSL is available for PHP 5.3.0, an IV parameter
+			// for the encrypt/decrypt functions is only available since 5.3.3
+			'openssl' => (is_php('5.3.3') && extension_loaded('openssl'))
+		);
+
+		if ( ! $this->_drivers['mcrypt'] && ! $this->_drivers['openssl'])
+		{
+			return show_error('Encryption: Unable to find an available encryption driver.');
+		}
+
+		$this->initialize($params);
+
+		if (empty($this->_driver))
+		{
+			$this->_driver = ($this->_drivers['mcrypt'] === TRUE)
+				? 'mcrypt'
+				: 'openssl';
+
+			log_message('debug', "Encryption: Auto-configured driver '".$params['driver']."'.");
+		}
+
+		isset($this->_key) OR $this->_key = config_item('encryption_key');
+		if (empty($this->_key))
+		{
+			return show_error('Encryption: You are required to set an encryption key in your configuration.');
+		}
+
+		log_message('debug', 'Encryption Class Initialized');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Initialize
+	 *
+	 * @param	array	$params	Configuration parameters
+	 * @return	CI_Encryption
+	 */
+	public function initialize(array $params)
+	{
+		if ( ! empty($params['driver']))
+		{
+			if (isset($this->_drivers[$params['driver']]))
+			{
+				if ($this->_driver[$params['driver']])
+				{
+					$this->_driver = $params['driver'];
+				}
+				else
+				{
+					log_message('error', "Encryption: Driver '".$params['driver']."' is not available.");
+				}
+			}
+			else
+			{
+				log_message('error', "Encryption: Unknown driver '".$params['driver']."' cannot be configured.");
+			}
+		}
+
+		empty($params['key']) OR $this->_key = $params['key'];
+		$this->{'_'.$this->_driver.'_initialize'}($params);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Initialize MCrypt
+	 *
+	 * @param	array	$params	Configuration parameters
+	 * @return	void
+	 */
+	protected function _mcrypt_initialize($params)
+	{
+		if ( ! empty($params['cipher']))
+		{
+			$params['cipher'] = strtolower($params['cipher']);
+			$this->_cipher_alias($params['cipher']);
+
+			if ( ! in_array($params['cipher'], mcrypt_list_algorithms(), TRUE))
+			{
+				log_message('error', 'Encryption: MCrypt cipher '.strtoupper($params['cipher']).' is not available.');
+			}
+			else
+			{
+				$this->_cipher = $params['cipher'];
+			}
+		}
+
+		if ( ! empty($params['mode']))
+		{
+			if ( ! defined('MCRYPT_MODE_'.$params['mode']))
+			{
+				log_message('error', 'Encryption: MCrypt mode '.strtotupper($params['mode']).' is not available.');
+			}
+			else
+			{
+				$this->_mode = constant('MCRYPT_MODE_'.$params['mode']);
+			}
+		}
+
+		if (isset($this->_cipher, $this->_mode))
+		{
+			if (is_resource($this->_handle)
+				&& (strtolower(mcrypt_enc_get_algorithms_name($this->_handle)) !== $this->_cipher
+					OR strtolower(mcrypt_enc_get_modes_name($this->_handle)) !== $this->_mode)
+			)
+			{
+				mcrypt_module_close($this->_handle);
+			}
+
+			if ($this->_handle = mcrypt_module_open($this->_cipher, '', $this->_mode, ''))
+			{
+				log_message('debug', 'Encryption: MCrypt cipher '.strtoupper($this->_cipher).' initialized in '.strtoupper($this->_mode).' mode.');
+			}
+			else
+			{
+				log_message('error', 'Encryption: Unable to initialize MCrypt with cipher '.strtoupper($this->_cipher).' in '.strtoupper($this->_mode).' mode.');
+			}
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Initialize OpenSSL
+	 *
+	 * @param	array	$params	Configuration parameters
+	 * @return	void
+	 */
+	protected function _openssl_initialize($params)
+	{
+		if ( ! empty($params['cipher']))
+		{
+			$params['cipher'] = strtolower($params['cipher']);
+			$this->_cipher_alias($params['cipher']);
+			$this->_cipher = $params['cipher'];
+		}
+
+		if ( ! empty($params['mode']))
+		{
+			$this->_mode = strtolower($params['mode']);
+		}
+
+		if (isset($this->_cipher, $this->_mode))
+		{
+			// OpenSSL methods aren't suffixed with '-stream' for this mode
+			$handle = ($this->_mode === 'stream')
+				? $this->_cipher
+				: $this->_cipher.'-'.$this->_mode;
+
+			if ( ! in_array($handle, openssl_get_cipher_methods, TRUE))
+			{
+				$this->_handle = NULL;
+				log_message('error', 'Encryption: Unable to initialize OpenSSL with method '.strtoupper($handle).'.');
+			}
+			else
+			{
+				$this->_handle = $handle;
+				log_message('debug', 'Encryption: OpenSSL initialized with method '.strtoupper($handle).'.');
+			}
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Encrypt
+	 *
+	 * @param	string	$data	Input data
+	 * @param	array	$params	Input parameters
+	 * @return	string
+	 */
+	public function encrypt($data, array $params = NULL)
+	{
+		if (($params = $this->{'_'.$this->_driver.'_get_params'}($params)) === FALSE)
+		{
+			return FALSE;
+		}
+		elseif ( ! isset($params['iv']))
+		{
+			$params['iv'] = ($iv_size = $this->{'_'.$this->_driver.'_get_iv_size'}($params['handle']))
+				? $this->{'_'.$this->_driver.'_get_iv'}($iv_size)
+				: NULL;
+		}
+
+		if (($data = $this->{'_'.$this->_driver.'_encrypt'}($data, $params)) === FALSE)
+		{
+			return FALSE;
+		}
+
+		return ($params['base64'])
+			? base64_encode($data)
+			: $data;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Encrypt via MCrypt
+	 *
+	 * @param	string	$data	Input data
+	 * @param	array	$params	Input parameters
+	 * @return	string
+	 */
+	protected function _mcrypt_encrypt($data, $params)
+	{
+		if (mcrypt_generic_init($params['handle'], $params['key'], $params['iv']) < 0)
+		{
+			if ($params['handle'] !== $this->_handle)
+			{
+				mcrypt_module_close($params['handle']);
+			}
+
+			return FALSE;
+		}
+
+		// Use PKCS#7 padding in order to ensure compatibility with OpenSSL
+		// and other implementations outside of PHP
+		$block_size = mcrypt_enc_get_block_size($params['handle']);
+		$pad = $block_size - (strlen($data) % $block_size);
+		$data .= str_repeat(chr($pad), $pad);
+
+		$data = $params['iv'].mcrypt_generic($params['handle'], $data);
+
+		mcrypt_generic_deinit($params['handle']);
+		if ($params['handle'] !== $this->_handle)
+		{
+			mcrypt_module_close($handle);
+		}
+
+		return $data;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Encrypt via OpenSSL
+	 *
+	 * @param	string	$data	Input data
+	 * @param	array	$params	Input parameters
+	 * @return	string
+	 */
+	protected function _openssl_encrypt($data, $params)
+	{
+		$data = openssl_encrypt(
+			$data,
+			$params['handle'],
+			$params['key'],
+			1, // DO NOT TOUCH!
+			$params['iv']
+		);
+
+		if ($data === FALSE)
+		{
+			return FALSE;
+		}
+
+		return $params['iv'].$data;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Decrypt
+	 *
+	 * @param	string	$data	Encrypted data
+	 * @param	array	$params	Input parameters
+	 * @return	string
+	 */
+	public function decrypt($data, array $params = NULL)
+	{
+		if (($params = $this->{'_'.$this->_driver.'_get_params'}($params)) === FALSE)
+		{
+			return FALSE;
+		}
+		elseif ($params['base64'])
+		{
+			$data = base64_decode($data);
+		}
+
+		if ( ! isset($params['iv']))
+		{
+			if ($iv_size)
+			{
+				$params['iv'] = substr($data, 0, $iv_size);
+				$data = substr($data, $iv_size);
+			}
+			else
+			{
+				$params['iv'] = NULL;
+			}
+		}
+		elseif (strncmp($params['iv'], $data, $iv_size = strlen($params['iv'])) === 0)
+		{
+			$data = substr($data, $iv_size);
+		}
+
+		return $this->{'_'.$this->_driver.'_decrypt'}($data, $params);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Decrypt via MCrypt
+	 *
+	 * @param	string	$data	Encrypted data
+	 * @param	array	$params	Input parameters
+	 * @return	string
+	 */
+	protected function _mcrypt_decrypt($data, $params)
+	{
+		if (mcrypt_generic_init($params['handle'], $params['key'], $params['iv']) < 0)
+		{
+			if ($params['handle'] !== $this->_handle)
+			{
+				mcrypt_module_close($params['handle']);
+			}
+
+			return FALSE;
+		}
+
+		$data = mdecrypt_generic($params['handle'], $data);
+
+		mcrypt_generic_deinit($params['handle']);
+		if ($params['handle'] !== $this->_handle)
+		{
+			mcrypt_module_close($handle);
+		}
+
+		// Remove PKCS#7 padding
+		return substr($data, 0, -ord($data[strlen($data)-1]));
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Decrypt via OpenSSL
+	 *
+	 * @param	string	$data	Encrypted data
+	 * @param	array	$params	Input parameters
+	 * @return	string
+	 */
+	protected function _openssl_decrypt($data, $params)
+	{
+		return openssl_decrypt(
+			$data,
+			$params['handle'],
+			$params['key'],
+			1, // DO NOT TOUCH!
+			$params['iv']
+		);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get IV size via MCrypt
+	 *
+	 * @param	resource	$handle	MCrypt module resource
+	 * @return	int
+	 */
+	protected function _mcrypt_get_iv_size($handle)
+	{
+		return mcrypt_enc_get_iv_size($handle);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get IV size via OpenSSL
+	 *
+	 * @param	string	$handle	OpenSSL cipher method
+	 * @return	int
+	 */
+	protected function _openssl_get_iv_size($handle)
+	{
+		return openssl_cipher_iv_length($handle);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get IV via MCrypt
+	 *
+	 * @param	int	$size
+	 * @return	int
+	 */
+	protected function _mcrypt_get_iv($size)
+	{
+		// If /dev/urandom is available - use it, otherwise there's
+		// also /dev/random, but it is highly unlikely that it would
+		// be available while /dev/urandom is not and it is known to be
+		// blocking anyway.
+		if (defined(MCRYPT_DEV_URANDOM))
+		{
+			$source = MCRYPT_DEV_URANDOM;
+		}
+		else
+		{
+			$source = MCRYPT_RAND;
+			is_php('5.3') OR srand(microtime(TRUE));
+		}
+
+		return mcrypt_create_iv($size, $source);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get IV via OpenSSL
+	 *
+	 * @param	int	$size	IV size
+	 * @return	int
+	 */
+	protected function _openssl_get_iv($size)
+	{
+		return openssl_random_pseudo_bytes($size);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get MCrypt parameters
+	 *
+	 * @param	array	$params	Input parameters
+	 * @return	array
+	 */
+	protected function _mcrypt_get_params($params)
+	{
+		if (empty($params))
+		{
+			if ( ! isset($this->_cipher, $this->_mode, $params->_key, $this->_handle) OR ! is_resource($this->_handle))
+			{
+				return FALSE;
+			}
+
+			return array(
+				'handle' => $this->_handle,
+				'cipher' => $this->_cipher,
+				'mode' => $this->_mode,
+				'key' => $this->_key,
+				'base64' => TRUE
+			);
+		}
+
+		$params = array(
+			'handle' => NULL,
+			'cipher' => isset($params['cipher']) ? $params['cipher'] : $this->_cipher,
+			'mode' => isset($params['mode']) ? $params['mode'] : $this->_mode,
+			'key' => isset($params['key']) ? $params['key'] : $this->_key,
+			'iv' => isset($params['iv']) ? $params['iv'] : NULL,
+			'base64' => isset($params['base64']) ? $params['base64'] : TRUE
+		);
+
+		if ( ! isset($params['cipher'], $params['mode'], $params['key']))
+		{
+			return FALSE;
+		}
+
+		$this->_cipher_alias($params['cipher']);
+
+		if ($params['cipher'] !== $this->_cipher OR $params['mode'] !== $this->_mode)
+		{
+			if (($params['handle'] = mcrypt_module_open($params['cipher'], '', $params['mode'], '')) === FALSE)
+			{
+				return FALSE;
+			}
+		}
+		else
+		{
+			if  ( ! is_resource($this->_handle))
+			{
+				return FALSE;
+			}
+
+			$params['handle'] = $this->_handle;
+		}
+
+		return $params;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get OpenSSL parameters
+	 *
+	 * @param	array	$params	Input parameters
+	 * @return	array
+	 */
+	protected function _openssl_get_params($params)
+	{
+		if (empty($params))
+		{
+			if ( ! isset($this->_cipher, $this->_mode, $params->_key, $this->_handle))
+			{
+				return FALSE;
+			}
+
+			return array(
+				'handle' => $this->_handle,
+				'cipher' => $this->_cipher,
+				'mode' => $this->_mode,
+				'key' => $this->_key,
+				'base64' => TRUE
+			);
+		}
+
+		$params = array(
+			'handle' => NULL,
+			'cipher' => isset($params['cipher']) ? $params['cipher'] : $this->_cipher,
+			'mode' => isset($params['mode']) ? $params['mode'] : $this->_mode,
+			'key' => isset($params['key']) ? $params['key'] : $this->_key,
+			'iv' => isset($params['iv']) ? $params['iv'] : NULL,
+			'base64' => isset($params['base64']) ? $params['base64'] : TRUE
+		);
+
+		if ( ! isset($params['cipher'], $params['mode'], $params['key']))
+		{
+			return FALSE;
+		}
+
+		$this->_cipher_alias($params['cipher']);
+
+		if ($params['cipher'] !== $this->_cipher OR $params['mode'] !== $this->_mode)
+		{
+			// OpenSSL methods aren't suffixed with '-stream' for this mode
+			$params['handle'] = ($params['mode']  === 'stream')
+				? $params['cipher']
+				: $params['cipher'].'-'.$params['mode'];
+			$params['handle'] = $params['cipher'].'-'.$params['mode'];
+		}
+		else
+		{
+			if  (empty($this->_handle))
+			{
+				return FALSE;
+			}
+
+			$params['handle'] = $this->_handle;
+		}
+
+		return $params;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Cipher alias
+	 *
+	 * Tries to translate cipher names between MCrypt and OpenSSL's "dialects".
+	 *
+	 * @param	string	$cipher	Cipher name
+	 * @return	void
+	 */
+	protected function _cipher_alias(&$cipher)
+	{
+		static $dictionary;
+
+		if (empty($dictionary))
+		{
+			$dictionary = array(
+				'mcrypt' => array(
+					'rijndael-128',
+					'tripledes',
+					'arcfour'
+				),
+				'openssl' => array(
+					'aes-128',
+					'des-ede3',
+					'rc4-40'
+				)
+			);
+
+			// Notes regarding other seemingly matching ciphers between
+			// MCrypt and OpenSSL:
+			//
+			// - DES is compatible, but doesn't need an alias
+			// - Blowfish is NOT compatible
+			//	mcrypt: 'blowfish', 'blowfish-compat'
+			//	openssl: 'bf'
+			// - CAST-128/CAST5 is NOT compatible
+			//	mcrypt: 'cast-128'
+			//	openssl: 'cast5'
+			// - RC2 is NOT compatible
+			//	mcrypt: 'rc2'
+			//	openssl: 'rc2', 'rc2-40', 'rc2-64'
+			//
+			// To avoid any other confusion due to a popular (but incorrect)
+			// belief, it should also be noted that Rijndael-192/256 are NOT
+			// the same ciphers as AES-192/256 like Rijndael-128 and AES-256 is.
+			//
+			// All compatibility tests were done in CBC mode.
+		}
+
+		$dialect = ($this->_driver === 'mcrypt')
+			? 'openssl'
+			: 'mcrypt';
+		if (($index = array_search($cipher, $dictionary[$dialect], TRUE)) !== FALSE)
+		{
+			$cipher = $dictionary[$this->_driver][$index];
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * __get() magic
+	 *
+	 * @param	string	$key	Property name
+	 * @return	mixed
+	 */
+	public function __get($key)
+	{
+		return in_array($key, array('cipher', 'mode', 'driver', 'drivers'), TRUE)
+			? $this->{'_'.$key}
+			: NULL;
+	}
+
+}
+
+/* End of file Encryption.php */
+/* Location: ./system/libraries/Encryption.php */
\ No newline at end of file