diff --git a/administrator/components/com_users/src/Model/BackupcodesModel.php b/administrator/components/com_users/src/Model/BackupcodesModel.php index 33271b4f73c99..aff8effd799a1 100644 --- a/administrator/components/com_users/src/Model/BackupcodesModel.php +++ b/administrator/components/com_users/src/Model/BackupcodesModel.php @@ -10,7 +10,6 @@ namespace Joomla\Component\Users\Administrator\Model; -use Joomla\CMS\Crypt\Crypt; use Joomla\CMS\Date\Date; use Joomla\CMS\Language\Text; use Joomla\CMS\MVC\Model\BaseDatabaseModel; @@ -259,7 +258,7 @@ public function isBackupCode($code, ?User $user = null): bool } for ($i = 0; $i < $restLength; $i++) { - if (Crypt::timingSafeCompare($temp1[$i], $code)) { + if (hash_equals($temp1[$i], $code)) { $otherResult = $otherResult || true; $newArray[] = ''; $dummyArray[] = $temp1[$i]; diff --git a/libraries/src/Authentication/Password/MD5Handler.php b/libraries/src/Authentication/Password/MD5Handler.php index 7d4e39dad2857..750971807c44b 100644 --- a/libraries/src/Authentication/Password/MD5Handler.php +++ b/libraries/src/Authentication/Password/MD5Handler.php @@ -10,7 +10,6 @@ namespace Joomla\CMS\Authentication\Password; use Joomla\Authentication\Password\HandlerInterface; -use Joomla\CMS\Crypt\Crypt; use Joomla\CMS\User\UserHelper; // phpcs:disable PSR1.Files.SideEffects @@ -91,6 +90,6 @@ public function validatePassword($plaintext, $hashed) // If the salt is empty AND there is a ':' in the original hash, we must append ':' at the end $testcrypt = md5($plaintext . $salt) . ($salt ? ':' . $salt : (str_contains($hashed, ':') ? ':' : '')); - return Crypt::timingSafeCompare($hashed, $testcrypt); + return hash_equals($hashed, $testcrypt); } } diff --git a/libraries/src/Crypt/Cipher/CryptoCipher.php b/libraries/src/Crypt/Cipher/CryptoCipher.php index 79f84a875d02b..769141c5942dc 100644 --- a/libraries/src/Crypt/Cipher/CryptoCipher.php +++ b/libraries/src/Crypt/Cipher/CryptoCipher.php @@ -9,8 +9,7 @@ namespace Joomla\CMS\Crypt\Cipher; -use Joomla\Crypt\CipherInterface; -use Joomla\Crypt\Key; +use Joomla\Crypt\Cipher\Crypto; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; @@ -24,108 +23,6 @@ * @deprecated 4.3 will be removed in 7.0 * Will be removed without replacement use SodiumCipher instead */ -class CryptoCipher implements CipherInterface +class CryptoCipher extends Crypto { - /** - * Method to decrypt a data string. - * - * @param string $data The encrypted string to decrypt. - * @param Key $key The key object to use for decryption. - * - * @return string The decrypted data string. - * - * @since 3.5 - * @throws \RuntimeException - */ - public function decrypt($data, Key $key) - { - // Validate key. - if ($key->getType() !== 'crypto') { - throw new \InvalidArgumentException('Invalid key of type: ' . $key->getType() . '. Expected crypto.'); - } - - // Decrypt the data. - try { - return \Crypto::Decrypt($data, $key->getPublic()); - } catch (\InvalidCiphertextException $ex) { - throw new \RuntimeException('DANGER! DANGER! The ciphertext has been tampered with!', $ex->getCode(), $ex); - } catch (\CryptoTestFailedException $ex) { - throw new \RuntimeException('Cannot safely perform decryption', $ex->getCode(), $ex); - } catch (\CannotPerformOperationException $ex) { - throw new \RuntimeException('Cannot safely perform decryption', $ex->getCode(), $ex); - } - } - - /** - * Method to encrypt a data string. - * - * @param string $data The data string to encrypt. - * @param Key $key The key object to use for encryption. - * - * @return string The encrypted data string. - * - * @since 3.5 - * @throws \RuntimeException - */ - public function encrypt($data, Key $key) - { - // Validate key. - if ($key->getType() !== 'crypto') { - throw new \InvalidArgumentException('Invalid key of type: ' . $key->getType() . '. Expected crypto.'); - } - - // Encrypt the data. - try { - return \Crypto::Encrypt($data, $key->getPublic()); - } catch (\CryptoTestFailedException $ex) { - throw new \RuntimeException('Cannot safely perform encryption', $ex->getCode(), $ex); - } catch (\CannotPerformOperationException $ex) { - throw new \RuntimeException('Cannot safely perform encryption', $ex->getCode(), $ex); - } - } - - /** - * Method to generate a new encryption key object. - * - * @param array $options Key generation options. - * - * @return Key - * - * @since 3.5 - * @throws \RuntimeException - */ - public function generateKey(array $options = []) - { - // Generate the encryption key. - try { - $public = \Crypto::CreateNewRandomKey(); - } catch (\CryptoTestFailedException $ex) { - throw new \RuntimeException('Cannot safely create a key', $ex->getCode(), $ex); - } catch (\CannotPerformOperationException $ex) { - throw new \RuntimeException('Cannot safely create a key', $ex->getCode(), $ex); - } - - // Explicitly flag the private as unused in this cipher. - $private = 'unused'; - - return new Key('crypto', $private, $public); - } - - /** - * Check if the cipher is supported in this environment. - * - * @return boolean - * - * @since 4.0.0 - */ - public static function isSupported(): bool - { - try { - \Crypto::RuntimeTest(); - - return true; - } catch (\CryptoTestFailedException) { - return false; - } - } } diff --git a/libraries/src/Crypt/Cipher/SodiumCipher.php b/libraries/src/Crypt/Cipher/SodiumCipher.php index 8cf156aedb7ed..e9024071c9db7 100644 --- a/libraries/src/Crypt/Cipher/SodiumCipher.php +++ b/libraries/src/Crypt/Cipher/SodiumCipher.php @@ -9,9 +9,7 @@ namespace Joomla\CMS\Crypt\Cipher; -use Joomla\Crypt\CipherInterface; -use Joomla\Crypt\Key; -use ParagonIE\Sodium\Compat; +use Joomla\Crypt\Cipher\Sodium; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; @@ -21,122 +19,9 @@ * JCrypt cipher for sodium algorithm encryption, decryption and key generation. * * @since 3.8.0 + * @deprecated __DEPLOY_VERSION__ will be removed in 8.0 + * Please use \Joomla\Crypt\Cipher\Sodium instead */ -class SodiumCipher implements CipherInterface +class SodiumCipher extends Sodium { - /** - * The message nonce to be used with encryption/decryption - * - * @var string - * @since 3.8.0 - */ - private $nonce; - - /** - * Method to decrypt a data string. - * - * @param string $data The encrypted string to decrypt. - * @param Key $key The key object to use for decryption. - * - * @return string The decrypted data string. - * - * @since 3.8.0 - * @throws \RuntimeException - */ - public function decrypt($data, Key $key) - { - // Validate key. - if ($key->getType() !== 'sodium') { - throw new \InvalidArgumentException('Invalid key of type: ' . $key->getType() . '. Expected sodium.'); - } - - if (!$this->nonce) { - throw new \RuntimeException('Missing nonce to decrypt data'); - } - - $decrypted = Compat::crypto_box_open( - $data, - $this->nonce, - Compat::crypto_box_keypair_from_secretkey_and_publickey($key->getPrivate(), $key->getPublic()) - ); - - if ($decrypted === false) { - throw new \RuntimeException('Malformed message or invalid MAC'); - } - - return $decrypted; - } - - /** - * Method to encrypt a data string. - * - * @param string $data The data string to encrypt. - * @param Key $key The key object to use for encryption. - * - * @return string The encrypted data string. - * - * @since 3.8.0 - * @throws \RuntimeException - */ - public function encrypt($data, Key $key) - { - // Validate key. - if ($key->getType() !== 'sodium') { - throw new \InvalidArgumentException('Invalid key of type: ' . $key->getType() . '. Expected sodium.'); - } - - if (!$this->nonce) { - throw new \RuntimeException('Missing nonce to decrypt data'); - } - - return Compat::crypto_box( - $data, - $this->nonce, - Compat::crypto_box_keypair_from_secretkey_and_publickey($key->getPrivate(), $key->getPublic()) - ); - } - - /** - * Method to generate a new encryption key object. - * - * @param array $options Key generation options. - * - * @return Key - * - * @since 3.8.0 - * @throws \RuntimeException - */ - public function generateKey(array $options = []) - { - // Generate the encryption key. - $pair = Compat::crypto_box_keypair(); - - return new Key('sodium', Compat::crypto_box_secretkey($pair), Compat::crypto_box_publickey($pair)); - } - - /** - * Check if the cipher is supported in this environment. - * - * @return boolean - * - * @since 4.0.0 - */ - public static function isSupported(): bool - { - return class_exists(Compat::class); - } - - /** - * Set the nonce to use for encrypting/decrypting messages - * - * @param string $nonce The message nonce - * - * @return void - * - * @since 3.8.0 - */ - public function setNonce($nonce) - { - $this->nonce = $nonce; - } } diff --git a/libraries/src/Crypt/Crypt.php b/libraries/src/Crypt/Crypt.php index 2b134dee8addc..598dd33a47768 100644 --- a/libraries/src/Crypt/Crypt.php +++ b/libraries/src/Crypt/Crypt.php @@ -19,6 +19,8 @@ * Crypt is a Joomla Platform class for handling basic encryption/decryption of data. * * @since 3.0.0 + * @deprecated __DEPLOY_VERSION__ will be removed in 8.0 + * Please use \Joomla\Crypt\Crypt instead */ class Crypt extends JCrypt { @@ -35,6 +37,8 @@ class Crypt extends JCrypt * @return boolean True if the two strings are exactly the same. * * @since 3.2 + * @deprecated __DEPLOY_VERSION__ will be removed in 8.0 + * Please use hash_equals() instead */ public static function timingSafeCompare($known, $unknown) { @@ -80,9 +84,11 @@ public static function timingSafeCompare($known, $unknown) * * @return integer * - * @since 3.5 - * @ref mbstring.func_overload * @throws \RuntimeException + * @deprecated __DEPLOY_VERSION__ will be removed in 8.0 + * Please use mb_strlen() instead + * @ref mbstring.func_overload + * @since 3.5 */ public static function safeStrlen($str) { @@ -118,6 +124,8 @@ public static function safeStrlen($str) * @return string * * @since 3.5 + * @deprecated __DEPLOY_VERSION__ will be removed in 8.0 + * Please use mb_substr() instead */ public static function safeSubstr($str, $start, $length = null) { diff --git a/libraries/src/User/UserHelper.php b/libraries/src/User/UserHelper.php index 2ccb4e9c412d5..491b73f3e35f5 100644 --- a/libraries/src/User/UserHelper.php +++ b/libraries/src/User/UserHelper.php @@ -17,13 +17,13 @@ use Joomla\CMS\Authentication\Password\CheckIfRehashNeededHandlerInterface; use Joomla\CMS\Authentication\Password\MD5Handler; use Joomla\CMS\Authentication\Password\PHPassHandler; -use Joomla\CMS\Crypt\Crypt; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Session\SessionManager; use Joomla\CMS\Uri\Uri; +use Joomla\Crypt\Crypt; use Joomla\Database\Exception\ExecutionFailureException; use Joomla\Database\ParameterType; use Joomla\Utilities\ArrayHelper; diff --git a/plugins/api-authentication/token/src/Extension/Token.php b/plugins/api-authentication/token/src/Extension/Token.php index 1ae00e7853907..34ee82ef59460 100644 --- a/plugins/api-authentication/token/src/Extension/Token.php +++ b/plugins/api-authentication/token/src/Extension/Token.php @@ -11,7 +11,6 @@ namespace Joomla\Plugin\ApiAuthentication\Token\Extension; use Joomla\CMS\Authentication\Authentication; -use Joomla\CMS\Crypt\Crypt; use Joomla\CMS\Event\User\AuthenticationEvent; use Joomla\CMS\Plugin\CMSPlugin; use Joomla\CMS\User\UserFactoryAwareTrait; @@ -207,7 +206,7 @@ public function onUserAuthenticate(AuthenticationEvent $event): void $enabled = $this->isTokenEnabledForUser($userId); // Do the tokens match? Use a timing safe string comparison to prevent timing attacks. - $hashesMatch = Crypt::timingSafeCompare($referenceHMAC, $tokenHMAC); + $hashesMatch = hash_equals($referenceHMAC, $tokenHMAC); // Is the user in the allowed user groups? $inAllowedUserGroups = $this->isInAllowedUserGroup($userId); diff --git a/plugins/user/token/src/Extension/Token.php b/plugins/user/token/src/Extension/Token.php index 204a4a5ae2085..bbfc5ede8007b 100644 --- a/plugins/user/token/src/Extension/Token.php +++ b/plugins/user/token/src/Extension/Token.php @@ -10,7 +10,6 @@ namespace Joomla\Plugin\User\Token\Extension; -use Joomla\CMS\Crypt\Crypt; use Joomla\CMS\Event\Model\PrepareDataEvent; use Joomla\CMS\Event\Model\PrepareFormEvent; use Joomla\CMS\Event\User\AfterDeleteEvent; @@ -19,6 +18,7 @@ use Joomla\CMS\Plugin\CMSPlugin; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\User\UserFactoryAwareTrait; +use Joomla\Crypt\Crypt; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\ParameterType; use Joomla\Event\SubscriberInterface;