Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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];
Expand Down
3 changes: 1 addition & 2 deletions libraries/src/Authentication/Password/MD5Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
}
107 changes: 2 additions & 105 deletions libraries/src/Crypt/Cipher/CryptoCipher.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
}
}
123 changes: 4 additions & 119 deletions libraries/src/Crypt/Cipher/SodiumCipher.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
}
12 changes: 10 additions & 2 deletions libraries/src/Crypt/Crypt.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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)
{
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
{
Expand Down
2 changes: 1 addition & 1 deletion libraries/src/User/UserHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 1 addition & 2 deletions plugins/api-authentication/token/src/Extension/Token.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion plugins/user/token/src/Extension/Token.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
Loading