From 6dc69e68b55e1eece655c4716f0969693b4aba0e Mon Sep 17 00:00:00 2001 From: ekerazha Date: Sun, 29 Sep 2013 17:21:41 +0200 Subject: [PATCH 1/2] Add data padding and key derivation. --- framework/yii/helpers/BaseSecurity.php | 85 ++++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 9 deletions(-) diff --git a/framework/yii/helpers/BaseSecurity.php b/framework/yii/helpers/BaseSecurity.php index 3be07b4..41caade 100644 --- a/framework/yii/helpers/BaseSecurity.php +++ b/framework/yii/helpers/BaseSecurity.php @@ -24,20 +24,40 @@ use yii\base\InvalidParamException; class BaseSecurity { /** + * Uses AES, block size is 128-bit (16 bytes). + */ + const CRYPT_BLOCK_SIZE = 16; + + /** + * Uses AES-192, key size is 192-bit (24 bytes). + */ + const CRYPT_KEY_SIZE = 24; + + /** + * Uses SHA-256. + */ + const DERIVATION_HASH = 'sha256'; + + /** + * Uses 1000 iterations. + */ + const DERIVATION_ITERATIONS = 1000; + + /** * Encrypts data. * @param string $data data to be encrypted. - * @param string $key the encryption secret key + * @param string $password the encryption password * @return string the encrypted data * @throws Exception if PHP Mcrypt extension is not loaded or failed to be initialized * @see decrypt() */ - public static function encrypt($data, $key) + public static function encrypt($data, $password) { $module = static::openCryptModule(); - // 192-bit (24 bytes) key size - $key = StringHelper::substr($key, 0, 24); + $data = static::addPadding($data); srand(); $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND); + $key = static::deriveKey($password, $iv); mcrypt_generic_init($module, $key, $iv); $encrypted = $iv . mcrypt_generic($module, $data); mcrypt_generic_deinit($module); @@ -48,23 +68,70 @@ class BaseSecurity /** * Decrypts data * @param string $data data to be decrypted. - * @param string $key the decryption secret key + * @param string $password the decryption password * @return string the decrypted data * @throws Exception if PHP Mcrypt extension is not loaded or failed to be initialized * @see encrypt() */ - public static function decrypt($data, $key) + public static function decrypt($data, $password) { $module = static::openCryptModule(); - // 192-bit (24 bytes) key size - $key = StringHelper::substr($key, 0, 24); $ivSize = mcrypt_enc_get_iv_size($module); $iv = StringHelper::substr($data, 0, $ivSize); + $key = static::deriveKey($password, $iv); mcrypt_generic_init($module, $key, $iv); $decrypted = mdecrypt_generic($module, StringHelper::substr($data, $ivSize, StringHelper::strlen($data))); mcrypt_generic_deinit($module); mcrypt_module_close($module); - return rtrim($decrypted, "\0"); + return static::stripPadding($decrypted); + } + + /** + * Adds a padding to the given data (PKCS #7). + * @param string $data the data to pad + * @return string the padded data + */ + protected static function addPadding($data) + { + $pad = self::CRYPT_BLOCK_SIZE - (StringHelper::strlen($data) % self::CRYPT_BLOCK_SIZE); + return $data . str_repeat(chr($pad), $pad); + } + + /** + * Strips the padding from the given data. + * @param string $data the data to trim + * @return string the trimmed data + */ + protected static function stripPadding($data) + { + $end = StringHelper::substr($data, -1); + $last = ord($end); + $n = StringHelper::strlen($data) - $last; + if (StringHelper::substr($data, $n) == str_repeat($end, $last)) { + return StringHelper::substr($data, 0, $n); + } + return false; + } + + /** + * Derives a key from the given password (PBKDF2). + * @param string $password the source password + * @param string $salt the random salt + * @param int $iterations the number of iterations + * @return string the derived key + */ + protected static function deriveKey($password, $salt) + { + if (function_exists('hash_pbkdf2')) { + return hash_pbkdf2(self::DERIVATION_HASH, $password, $salt, self::DERIVATION_ITERATIONS, self::CRYPT_KEY_SIZE, true); + } + $hmac = hash_hmac(self::DERIVATION_HASH, $salt . pack('N', 1), $password, true); + $xorsum = $hmac; + for ($i = 1; $i < self::DERIVATION_ITERATIONS; $i++) { + $hmac = hash_hmac(self::DERIVATION_HASH, $hmac, $password, true); + $xorsum ^= $hmac; + } + return substr($xorsum, 0, self::CRYPT_KEY_SIZE); } /** From cb7921b8a5ecd391076f869fd344b78f789adf70 Mon Sep 17 00:00:00 2001 From: ekerazha Date: Sun, 29 Sep 2013 17:59:31 +0200 Subject: [PATCH 2/2] Fix StringHelper::substr() call. --- framework/yii/helpers/BaseSecurity.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/yii/helpers/BaseSecurity.php b/framework/yii/helpers/BaseSecurity.php index 41caade..ca42d37 100644 --- a/framework/yii/helpers/BaseSecurity.php +++ b/framework/yii/helpers/BaseSecurity.php @@ -104,10 +104,10 @@ class BaseSecurity */ protected static function stripPadding($data) { - $end = StringHelper::substr($data, -1); + $end = StringHelper::substr($data, -1, NULL); $last = ord($end); $n = StringHelper::strlen($data) - $last; - if (StringHelper::substr($data, $n) == str_repeat($end, $last)) { + if (StringHelper::substr($data, $n, NULL) == str_repeat($end, $last)) { return StringHelper::substr($data, 0, $n); } return false;