diff --git a/lib/SimpleSAML/Utils/Crypto.php b/lib/SimpleSAML/Utils/Crypto.php index 22335b3235932e7878408b690b886b4198f7748f..a4ca9012684d4e629639066c8524a42d1e176fea 100644 --- a/lib/SimpleSAML/Utils/Crypto.php +++ b/lib/SimpleSAML/Utils/Crypto.php @@ -13,7 +13,7 @@ class Crypto /** * Decrypt data using AES-256-CBC and the key provided as a parameter. * - * @param string $ciphertext The IV and the encrypted data, concatenated. + * @param string $ciphertext The HMAC of the encrypted data, the IV used and the encrypted data, concatenated. * @param string $secret The secret to use to decrypt the data. * * @return string The decrypted data. @@ -31,21 +31,36 @@ class Crypto throw new \SimpleSAML_Error_Exception("The openssl PHP module is not loaded."); } - $raw = defined('OPENSSL_RAW_DATA') ? OPENSSL_RAW_DATA : true; - $key = openssl_digest($secret, 'sha256'); - $method = 'AES-256-CBC'; - $ivSize = 16; - $iv = substr($ciphertext, 0, $ivSize); - $data = substr($ciphertext, $ivSize); + // derive encryption and authentication keys from the secret + $key = openssl_digest($secret, 'sha512'); + + $hmac = mb_substr($ciphertext, 0, 32, '8bit'); + $iv = mb_substr($ciphertext, 32, 16, '8bit'); + $msg = mb_substr($ciphertext, 48, null, '8bit'); + + // authenticate the ciphertext + if (self::secureCompare(hash_hmac('sha256', $iv.$msg, substr($key, 64, 64), true), $hmac)) { + $plaintext = openssl_decrypt( + $msg, + 'AES-256-CBC', + substr($key, 0, 64), + defined('OPENSSL_RAW_DATA') ? OPENSSL_RAW_DATA : true, + $iv + ); + + if ($plaintext != false) { + return $plaintext; + } + } - return openssl_decrypt($data, $method, $key, $raw, $iv); + throw new \SimpleSAML_Error_Exception("Failed to decrypt ciphertext."); } /** * Decrypt data using AES-256-CBC and the system-wide secret salt as key. * - * @param string $ciphertext The IV used and the encrypted data, concatenated. + * @param string $ciphertext The HMAC of the encrypted data, the IV used and the encrypted data, concatenated. * * @return string The decrypted data. * @throws \InvalidArgumentException If $ciphertext is not a string. @@ -66,7 +81,7 @@ class Crypto * @param string $data The data to encrypt. * @param string $secret The secret to use to encrypt the data. * - * @return string The IV and encrypted data concatenated. + * @return string An HMAC of the encrypted data, the IV and the encrypted data, concatenated. * @throws \InvalidArgumentException If $data is not a string. * @throws \SimpleSAML_Error_Exception If the openssl module is not loaded. * @@ -82,13 +97,27 @@ class Crypto throw new \SimpleSAML_Error_Exception('The openssl PHP module is not loaded.'); } - $raw = defined('OPENSSL_RAW_DATA') ? OPENSSL_RAW_DATA : true; - $key = openssl_digest($secret, 'sha256'); - $method = 'AES-256-CBC'; - $ivSize = 16; - $iv = openssl_random_pseudo_bytes($ivSize); + // derive encryption and authentication keys from the secret + $key = openssl_digest($secret, 'sha512'); + + // generate a random IV + $iv = openssl_random_pseudo_bytes(16); + + // encrypt the message + $ciphertext = $iv.openssl_encrypt( + $data, + 'AES-256-CBC', + substr($key, 0, 64), + defined('OPENSSL_RAW_DATA') ? OPENSSL_RAW_DATA : true, + $iv + ); + + if ($ciphertext === false) { + throw new \SimpleSAML_Error_Exception("Failed to encrypt plaintext."); + } - return $iv.openssl_encrypt($data, $method, $key, $raw, $iv); + // return the ciphertext with proper authentication + return hash_hmac('sha256', $ciphertext, substr($key, 64, 64), true).$ciphertext; } @@ -97,7 +126,7 @@ class Crypto * * @param string $data The data to encrypt. * - * @return string The IV and encrypted data concatenated. + * @return string An HMAC of the encrypted data, the IV and the encrypted data, concatenated. * @throws \InvalidArgumentException If $data is not a string. * @throws \SimpleSAML_Error_Exception If the openssl module is not loaded. * diff --git a/tests/lib/SimpleSAML/Utils/CryptoTest.php b/tests/lib/SimpleSAML/Utils/CryptoTest.php index e0f67de19abd314d89615e09020921d9fb267555..9563d665c4ce648119af86fa221aa95e380a9753 100644 --- a/tests/lib/SimpleSAML/Utils/CryptoTest.php +++ b/tests/lib/SimpleSAML/Utils/CryptoTest.php @@ -59,7 +59,7 @@ class CryptoTest extends \PHPUnit_Framework_TestCase $m->setAccessible(true); $plaintext = 'SUPER_SECRET_TEXT'; - $ciphertext = 'NmRkODJlZGE2OTA3YTYwMm9En+KAReUk2z7Xi/b3c39kF/c1n6Vdj/zNARQt+UHU'; + $ciphertext = 'uR2Yu0r4itInKx91D/l9y/08L5CIQyev9nAr27fh3Sshous4vbXRRcMcjqHDOrquD+2vqLyw7ygnbA9jA9TpB4hLZocvAWcTN8tyO82hiSY='; $this->assertEquals($plaintext, $m->invokeArgs(null, array(base64_decode($ciphertext), $secret))); }