CI_Encryption: Fix more errors and add a 'portability' test case
diff --git a/system/libraries/Encryption.php b/system/libraries/Encryption.php
index 3c13475..2d3f82d 100644
--- a/system/libraries/Encryption.php
+++ b/system/libraries/Encryption.php
@@ -148,7 +148,7 @@
 		{
 			if (isset($this->_drivers[$params['driver']]))
 			{
-				if ($this->_driver[$params['driver']])
+				if ($this->_drivers[$params['driver']])
 				{
 					$this->_driver = $params['driver'];
 				}
@@ -264,7 +264,7 @@
 				? $this->_cipher
 				: $this->_cipher.'-'.$this->_mode;
 
-			if ( ! in_array($handle, openssl_get_cipher_methods, TRUE))
+			if ( ! in_array($handle, openssl_get_cipher_methods(), TRUE))
 			{
 				$this->_handle = NULL;
 				log_message('error', 'Encryption: Unable to initialize OpenSSL with method '.strtoupper($handle).'.');
@@ -364,7 +364,7 @@
 		mcrypt_generic_deinit($params['handle']);
 		if ($params['handle'] !== $this->_handle)
 		{
-			mcrypt_module_close($handle);
+			mcrypt_module_close($params['handle']);
 		}
 
 		return $data;
@@ -452,7 +452,8 @@
 
 		if ( ! isset($params['iv']))
 		{
-			if ($iv_size)
+			$iv_size = $this->{'_'.$this->_driver.'_get_iv_size'}($params['handle']);
+			if ($iv_size = $this->{'_'.$this->_driver.'_get_iv_size'}($params['handle']))
 			{
 				$params['iv'] = substr($data, 0, $iv_size);
 				$data = substr($data, $iv_size);
@@ -500,7 +501,7 @@
 		mcrypt_generic_deinit($params['handle']);
 		if ($params['handle'] !== $this->_handle)
 		{
-			mcrypt_module_close($handle);
+			mcrypt_module_close($params['handle']);
 		}
 
 		// Remove PKCS#7 padding
@@ -607,7 +608,7 @@
 	{
 		if (empty($params))
 		{
-			return isset($this->_cipher, $this->_mode, $params->_key, $this->_handle)
+			return isset($this->_cipher, $this->_mode, $this->_key, $this->_handle)
 				? array(
 					'handle' => $this->_handle,
 					'cipher' => $this->_cipher,
@@ -714,14 +715,16 @@
 		{
 			$dictionary = array(
 				'mcrypt' => array(
-					'rijndael-128',
-					'tripledes',
-					'arcfour'
+					'aes-128' => 'rijndael-128',
+					'aes-192' => 'rijndael-128',
+					'aes-256' => 'rijndael-128',
+					'des3-ede3' => 'tripledes',
+					'rc4-40' => 'arcfour'
 				),
 				'openssl' => array(
-					'aes-128',
-					'des-ede3',
-					'rc4-40'
+					'rijndael-128' => 'aes-128',
+					'tripledes' => 'des-ede3',
+					'arcfour' => 'rc4-40'
 				)
 			);
 
@@ -746,12 +749,9 @@
 			// All compatibility tests were done in CBC mode.
 		}
 
-		$dialect = ($this->_driver === 'mcrypt')
-			? 'openssl'
-			: 'mcrypt';
-		if (($index = array_search($cipher, $dictionary[$dialect], TRUE)) !== FALSE)
+		if (isset($dictionary[$this->_driver][$cipher]))
 		{
-			$cipher = $dictionary[$this->_driver][$index];
+			$cipher = $dictionary[$this->_driver][$cipher];
 		}
 	}
 
diff --git a/tests/codeigniter/libraries/Encryption_test.php b/tests/codeigniter/libraries/Encryption_test.php
index 94a9526..3d091e8 100644
--- a/tests/codeigniter/libraries/Encryption_test.php
+++ b/tests/codeigniter/libraries/Encryption_test.php
@@ -4,13 +4,62 @@
 
 	public function set_up()
 	{
-		$this->ci_set_config('encryption_key', "Encryptin'glike@boss!");
+		$this->ci_set_config('encryption_key', "\xd0\xc9\x08\xc4\xde\x52\x12\x6e\xf8\xcc\xdb\x03\xea\xa0\x3a\x5c");
 		$this->encryption = new CI_Encryption();
 		$this->ci_instance_var('encryption', $this->encryption);
 	}
 
 	// --------------------------------------------------------------------
 
+	public function test_portability()
+	{
+		if ( ! $this->encryption->drivers['mcrypt'] OR ! $this->encryption->drivers['openssl'])
+		{
+			$this->markTestAsSkipped('Both MCrypt and OpenSSL support are required for portability tests.');
+			return;
+		}
+
+		$message = 'This is a message encrypted via MCrypt and decrypted via OpenSSL, or vice-versa.';
+
+		// As it turns out, only ciphers that happened to be a US standard have a
+		// somewhat consistent implementation between MCrypt and OpenSSL, so
+		// we can only test AES, DES and TripleDES.
+		//
+		// Format is: <MCrypt cipher name>, <OpenSSL cipher name>, <key size>
+		$portable = array(
+			array('rijndael-128', 'aes-128', 16),
+			array('rijndael-128', 'aes-192', 24),
+			array('rijndael-128', 'aes-256', 32),
+			array('des', 'des', 7),
+			array('tripledes', 'des-ede3', 7),
+			array('tripledes', 'des-ede3', 14),
+			array('tripledes', 'des-ede3', 21)
+		);
+		$driver_index = array('mcrypt', 'openssl');
+
+		foreach ($portable as &$test)
+		{
+			// Add some randomness to the selected driver
+			$driver = mt_rand(0,1);
+			$params = array(
+				'cipher' => $test[$driver],
+				'mode' => 'cbc',
+				'key' => openssl_random_pseudo_bytes($test[2])
+			);
+
+			$this->encryption->initialize(array('driver' => $driver_index[$driver]));
+			$ciphertext = $this->encryption->encrypt($message, $params);
+
+			$driver = (int) ! $driver;
+			$params['cipher'] = $test[$driver];
+
+			$this->encryption->initialize(array('driver' => $driver_index[$driver]));
+			$this->assertEquals($message, $this->encryption->decrypt($ciphertext, $params));
+		}
+	}
+
+	// --------------------------------------------------------------------
+
 	public function test_hkdf()
 	{
 		// Test vectors are described in RFC5869, Appendix A(1-3).