Skip to content

Commit

Permalink
Work more on crypto instances.
Browse files Browse the repository at this point in the history
OpenSSL does not support 256bit blocksized AES, this means we cannot
provide a compatible implementation or rijndael.

Make the engine() method accept an instance so that developers can force
mcrypt/openssl as necessary.
  • Loading branch information
markstory committed Dec 23, 2014
1 parent 39212ea commit d7d1277
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 10 deletions.
6 changes: 1 addition & 5 deletions src/Utility/Crypto/Mcrypt.php
Expand Up @@ -25,8 +25,7 @@ class Mcrypt {
* @param string $text Encrypted string to decrypt, normal string to encrypt
* @param string $key Key to use as the encryption key for encrypted data.
* @param string $operation Operation to perform, encrypt or decrypt
* @throws \InvalidArgumentException When there are errors.
* @return string Encrypted/Decrypted string
* @throws \LogicException When there are errors.
*/
public static function rijndael($text, $key, $operation) {
$algorithm = MCRYPT_RIJNDAEL_256;
Expand All @@ -53,7 +52,6 @@ public static function rijndael($text, $key, $operation) {
*
* @param string $plain The value to encrypt.
* @param string $key The 256 bit/32 byte key to use as a cipher key.
* @param string|null $hmacSalt The salt to use for the HMAC process. Leave null to use Security.salt.
* @return string Encrypted data.
* @throws \InvalidArgumentException On invalid data or key.
*/
Expand All @@ -73,7 +71,6 @@ public static function encrypt($plain, $key, $hmacSalt = null) {
*
* @param string $cipher The ciphertext to decrypt.
* @param string $key The 256 bit/32 byte key to use as a cipher key.
* @param string|null $hmacSalt The salt to use for the HMAC process. Leave null to use Security.salt.
* @return string Decrypted data. Any trailing null bytes will be removed.
* @throws InvalidArgumentException On invalid data or key.
*/
Expand All @@ -99,4 +96,3 @@ public static function decrypt($cipher, $key) {
return rtrim($plain, "\0");
}
}

88 changes: 88 additions & 0 deletions src/Utility/Crypto/OpenSsl.php
@@ -0,0 +1,88 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since 0.10.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace Cake\Utility\Crypto;

/**
* OpenSSL implementation of crypto features for Cake\Utility\Security
*
* OpenSSL should be favored over mcrypt as it is actively maintained and
* more widely available.
*/
class OpenSsl {

/**
* 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 as the encryption key for encrypted data.
* @param string $operation Operation to perform, encrypt or decrypt
* @throws \InvalidArgumentException When there are errors.
* @return string Encrypted/Decrypted string
*/
public static function rijndael($text, $key, $operation) {
throw new \LogicException('rijndael is not compatible with OpenSSL. Use mcrypt instead.');
}

/**
* Encrypt a value using AES-256.
*
* *Caveat* You cannot properly encrypt/decrypt data with trailing null bytes.
* Any trailing null bytes will be removed on decryption due to how PHP pads messages
* with nulls prior to encryption.
*
* @param string $plain The value to encrypt.
* @param string $key The 256 bit/32 byte key to use as a cipher key.
* @param string|null $hmacSalt The salt to use for the HMAC process. Leave null to use Security.salt.
* @return string Encrypted data.
* @throws \InvalidArgumentException On invalid data or key.
*/
public static function encrypt($plain, $key, $hmacSalt = null) {
$method = 'AES-128-CBC';
$ivSize = openssl_cipher_iv_length($method);
$iv = openssl_random_pseudo_bytes($ivSize);
$ciphertext = $iv . openssl_encrypt($plain, $method, $key, 0, $iv);
$hmac = hash_hmac('sha256', $ciphertext, $key);
return $hmac . $ciphertext;
}

/**
* Decrypt a value using AES-256.
*
* @param string $cipher The ciphertext to decrypt.
* @param string $key The 256 bit/32 byte key to use as a cipher key.
* @return string Decrypted data. Any trailing null bytes will be removed.
* @throws InvalidArgumentException On invalid data or key.
*/
public static function decrypt($cipher, $key) {
// Split out hmac for comparison
$macSize = 64;
$hmac = substr($cipher, 0, $macSize);
$cipher = substr($cipher, $macSize);

$compareHmac = hash_hmac('sha256', $cipher, $key);
// TODO time constant comparison?
if ($hmac !== $compareHmac) {
return false;
}
$method = 'AES-128-CBC';
$ivSize = openssl_cipher_iv_length($method);

$iv = substr($cipher, 0, $ivSize);
$cipher = substr($cipher, $ivSize);
$plain = openssl_decrypt($cipher, $method, $key, 0, $iv);
return rtrim($plain, "\0");
}
}

25 changes: 20 additions & 5 deletions src/Utility/Security.php
Expand Up @@ -39,6 +39,13 @@ class Security {
*/
protected static $_salt;

/**
* The crypto implementation to use.
*
* @var object
*/
protected static $_instance;

/**
* Generate authorization hash.
*
Expand Down Expand Up @@ -91,14 +98,22 @@ public static function setHash($hash) {
/**
* Get the crypto implementation based on the loaded extensions.
*
* @param object $instance The crypto instance to use.
* @return object Crypto instance.
*/
public static function engine() {
if (extension_loaded('openssl')) {
// return new Openssl();
public static function engine($instance = null) {
if ($instance === null && static::$_instance === null) {
if (extension_loaded('openssl')) {
$instance = new Openssl();
} elseif (extension_loaded('mcrypt')) {
$instance = new Mcrypt();
}
}
if ($instance) {
static::$_instance = $instance;
}
if (extension_loaded('mcrypt')) {
return new Mcrypt();
if (isset(static::$_instance)) {
return static::$_instance;
}
throw new InvalidArgumentException('No compatible crypto engine loaded. Load either mcrypt or openssl');
}
Expand Down

0 comments on commit d7d1277

Please sign in to comment.