Browse Source

Implement rijndael optional cookie encryption.

Heath Nail 14 years ago
parent
commit
304d001dfb

+ 16 - 8
lib/Cake/Controller/Component/CookieComponent.php

@@ -130,11 +130,10 @@ class CookieComponent extends Component {
 /**
  * Type of encryption to use.
  *
- * Currently only one method is available
+ * Currently two methods are available: cipher and rijndael
  * Defaults to Security::cipher();
  *
  * @var string
- * @todo add additional encryption methods
  */
 	protected $_type = 'cipher';
 
@@ -343,14 +342,23 @@ class CookieComponent extends Component {
 	}
 
 /**
- * Will allow overriding default encryption method.
+ * Will allow overriding default encryption method. Use this method
+ * in ex: AppController::beforeFilter() before you have read or
+ * written any cookies.
  *
  * @param string $type Encryption method
  * @return void
- * @todo NOT IMPLEMENTED
  */
 	public function type($type = 'cipher') {
-		$this->_type = 'cipher';
+		$availableTypes = array(
+			'cipher',
+			'rijndael'
+		);
+		if (!in_array($type, $availableTypes)) {
+			trigger_error(__d('cake_dev', 'You must use cipher or rijndael for cookie encryption type'), E_USER_WARNING);
+			$type = 'cipher';
+		}
+		$this->_type = $type;
 	}
 
 /**
@@ -439,7 +447,7 @@ class CookieComponent extends Component {
 
 		if ($this->_encrypted === true) {
 			$type = $this->_type;
-			$value = "Q2FrZQ==." . base64_encode(Security::$type($value, $this->key));
+			$value = "Q2FrZQ==." . base64_encode(Security::$type($value, $this->key, 'encrypt'));
 		}
 		return $value;
 	}
@@ -462,7 +470,7 @@ class CookieComponent extends Component {
 
 					if ($pos !== false) {
 						$val = substr($val, 8);
-						$decrypted[$name][$key] = $this->_explode(Security::$type(base64_decode($val), $this->key));
+						$decrypted[$name][$key] = $this->_explode(Security::$type(base64_decode($val), $this->key, 'decrypt'));
 					}
 				}
 			} else {
@@ -471,7 +479,7 @@ class CookieComponent extends Component {
 
 				if ($pos !== false) {
 					$value = substr($value, 8);
-					$decrypted[$name] = $this->_explode(Security::$type(base64_decode($value), $this->key));
+					$decrypted[$name] = $this->_explode(Security::$type(base64_decode($value), $this->key, 'decrypt'));
 				}
 			}
 		}

+ 47 - 0
lib/Cake/Test/Case/Utility/SecurityTest.php

@@ -157,4 +157,51 @@ class SecurityTest extends CakeTestCase {
 		$key = '';
 		$result = Security::cipher($txt, $key);
 	}
+
+/**
+ * testRijndael method
+ *
+ * @return void
+ */
+	public function testRijndael() {
+		$txt = 'The quick brown fox jumped over the lazy dog.';
+		$key = 'DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi';
+
+		$result = Security::rijndael($txt, $key, 'encrypt');
+		$this->assertEquals($txt, Security::rijndael($result, $key, 'decrypt'));
+
+		$result = Security::rijndael($key, $txt, 'encrypt');
+		$this->assertEquals($key, Security::rijndael($result, $txt, 'decrypt'));
+
+		$result = Security::rijndael('', $key, 'encrypt');
+		$this->assertEquals('', Security::rijndael($result, $key, 'decrypt'));
+
+		$result = Security::rijndael($txt, $key = 'this is my key of over 32 chars, yes it is', 'encrypt');
+		$this->assertEquals($txt, Security::rijndael($result, $key, 'decrypt'));
+	}
+
+/**
+ * testRijndaelInvalidOperation method
+ *
+ * @expectedException PHPUnit_Framework_Error
+ * @return void
+ */
+	public function testRijndaelInvalidOperation() {
+		$txt = 'The quick brown fox jumped over the lazy dog.';
+		$key = 'DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi';
+		$result = Security::rijndael($txt, $key, 'foo');
+	}
+
+/**
+ * testRijndaelInvalidKey method
+ *
+ * @expectedException PHPUnit_Framework_Error
+ * @return void
+ */
+	public function testRijndaelInvalidKey() {
+		$txt = 'The quick brown fox jumped over the lazy dog.';
+		$key = 'too small';
+		$result = Security::rijndael($txt, $key, 'encrypt');
+	}
+
 }

+ 34 - 0
lib/Cake/Utility/Security.php

@@ -155,4 +155,38 @@ class Security {
 		return $out;
 	}
 
+/**
+ * Encrypts/Decrypts a text using the given key using rijndael method.
+ *
+ * @param string $text Encrypted string to decrypt, normal string to encrypt
+ * @param string $key Key to use
+ * @param string $operation Operation to perform, encrypt or decrypt
+ * @return string Encrypted/Descrypted string
+ */
+	public static function rijndael($text, $key, $operation) {
+		if (empty($key)) {
+			trigger_error(__d('cake_dev', 'You cannot use an empty key for Security::rijndael()'), E_USER_WARNING);
+			return '';
+		}
+		if (empty($operation) || !in_array($operation, array('encrypt', 'decrypt'))) {
+			trigger_error(__d('cake_dev', 'You must specify the operation for Security::rijndael(), either encrypt or decrypt'), E_USER_WARNING);
+			return '';
+		}
+		if (strlen($key) < 32) {
+			trigger_error(__d('cake_dev', 'You must use a key larger than 32 bytes for Security::rijndael()'), E_USER_WARNING);
+			return '';
+		}
+		$algorithm = 'rijndael-256';
+		$mode = 'cbc';
+		$cryptKey = substr($key, 0, 32);
+		$iv = substr($key, strlen($key) - 32, 32);
+		$out = '';
+		if ($operation === 'encrypt') {
+			$out .= mcrypt_encrypt($algorithm, $cryptKey, $text, $mode, $iv);
+		} elseif ($operation === 'decrypt') {
+			$out .= rtrim(mcrypt_decrypt($algorithm, $cryptKey, $text, $mode, $iv), "\0");
+		}
+		return $out;
+	}
+
 }