/** * Decrypts a ciphertext (legacy -- before version tagging) * * $ciphertext is the ciphertext to decrypt. * $key is the key that the ciphertext was encrypted with. * You MUST catch exceptions thrown by this function. Read the docs. * * @param string $ciphertext * @param string $key * @return string * @throws Ex\CannotPerformOperationException * @throws Ex\CryptoTestFailedException * @throws Ex\InvalidCiphertextException */ public static function legacyDecrypt($ciphertext, $key) { RuntimeTests::runtimeTest(); $config = self::getVersionConfigFromHeader(Core::LEGACY_VERSION, Core::LEGACY_VERSION); // Extract the HMAC from the front of the ciphertext. if (Core::ourStrlen($ciphertext) <= $config->macByteSize()) { throw new Ex\InvalidCiphertextException("Ciphertext is too short."); } $hmac = Core::ourSubstr($ciphertext, 0, $config->macByteSize()); if ($hmac === false) { throw new Ex\CannotPerformOperationException(); } $ciphertext = Core::ourSubstr($ciphertext, $config->macByteSize()); if ($ciphertext === false) { throw new Ex\CannotPerformOperationException(); } // Regenerate the same authentication sub-key. $akey = Core::HKDF($config->hashFunctionName(), $key, $config->keyByteSize(), $config->authenticationInfoString(), null, $config); if (self::verifyHMAC($hmac, $ciphertext, $akey, $config)) { // Regenerate the same encryption sub-key. $ekey = Core::HKDF($config->hashFunctionName(), $key, $config->keyByteSize(), $config->encryptionInfoString(), null, $config); // Extract the initialization vector from the ciphertext. Core::EnsureFunctionExists("openssl_cipher_iv_length"); $ivsize = \openssl_cipher_iv_length($config->cipherMethod()); if ($ivsize === false || $ivsize <= 0) { throw new Ex\CannotPerformOperationException("Could not get the IV length from OpenSSL"); } if (Core::ourStrlen($ciphertext) <= $ivsize) { throw new Ex\InvalidCiphertextException("Ciphertext is too short."); } $iv = Core::ourSubstr($ciphertext, 0, $ivsize); if ($iv === false) { throw new Ex\CannotPerformOperationException(); } $ciphertext = Core::ourSubstr($ciphertext, $ivsize); if ($ciphertext === false) { throw new Ex\CannotPerformOperationException(); } $plaintext = self::plainDecrypt($ciphertext, $ekey, $iv, $config); return $plaintext; } else { /* * We throw an exception instead of returning false because we want * a script that doesn't handle this condition to CRASH, instead * of thinking the ciphertext decrypted to the value false. */ throw new Ex\InvalidCiphertextException("Integrity check failed."); } }