From 529211d7eded6592ba769b105ff663064b0a3201 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Mon, 8 Apr 2019 19:01:45 +0200 Subject: [PATCH] [Security] Replace Argon2*PasswordEncoder by SodiumPasswordEncoder This reverts commit dc95a6fec6d44e1dfcd7ab5895d6e68bf71676bb. --- UPGRADE-4.3.md | 6 +- UPGRADE-5.0.md | 6 +- .../Bundle/SecurityBundle/CHANGELOG.md | 4 +- .../DependencyInjection/SecurityExtension.php | 21 ++--- .../CompleteConfigurationTest.php | 25 +++--- ...rgon2id_encoder.php => sodium_encoder.php} | 5 +- ...rgon2id_encoder.xml => sodium_encoder.xml} | 2 +- .../Fixtures/yml/argon2id_encoder.yml | 10 --- .../Fixtures/yml/sodium_encoder.yml | 7 ++ .../UserPasswordEncoderCommandTest.php | 45 +++++----- .../app/PasswordEncode/argon2id.yml | 7 -- .../Functional/app/PasswordEncode/sodium.yml | 7 ++ src/Symfony/Component/Security/CHANGELOG.md | 4 +- .../Security/Core/Encoder/Argon2Trait.php | 52 ----------- .../Core/Encoder/Argon2iPasswordEncoder.php | 68 +++++++++----- .../Core/Encoder/Argon2idPasswordEncoder.php | 88 ------------------- .../Security/Core/Encoder/EncoderFactory.php | 16 ++-- .../Core/Encoder/SodiumPasswordEncoder.php | 84 ++++++++++++++++++ .../Encoder/Argon2iPasswordEncoderTest.php | 43 ++------- ...Test.php => SodiumPasswordEncoderTest.php} | 24 ++--- 20 files changed, 223 insertions(+), 301 deletions(-) rename src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/{argon2id_encoder.php => sodium_encoder.php} (57%) rename src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/{argon2id_encoder.xml => sodium_encoder.xml} (89%) delete mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/argon2id_encoder.yml create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/sodium_encoder.yml delete mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/PasswordEncode/argon2id.yml create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/PasswordEncode/sodium.yml delete mode 100644 src/Symfony/Component/Security/Core/Encoder/Argon2Trait.php delete mode 100644 src/Symfony/Component/Security/Core/Encoder/Argon2idPasswordEncoder.php create mode 100644 src/Symfony/Component/Security/Core/Encoder/SodiumPasswordEncoder.php rename src/Symfony/Component/Security/Core/Tests/Encoder/{Argon2idPasswordEncoderTest.php => SodiumPasswordEncoderTest.php} (64%) diff --git a/UPGRADE-4.3.md b/UPGRADE-4.3.md index 7442600260a8c..1191a75218d7e 100644 --- a/UPGRADE-4.3.md +++ b/UPGRADE-4.3.md @@ -151,14 +151,12 @@ Security } ``` - * Using `Argon2iPasswordEncoder` while only the `argon2id` algorithm is supported - is deprecated, use `Argon2idPasswordEncoder` instead + * The `Argon2iPasswordEncoder` class has been deprecated, use `SodiumPasswordEncoder` instead. SecurityBundle -------------- - * Configuring encoders using `argon2i` as algorithm while only `argon2id` is - supported is deprecated, use `argon2id` instead + * Configuring encoders using `argon2i` as algorithm has been deprecated, use `sodium` instead. TwigBridge ---------- diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md index b163e76678286..fbcc53cf4ac6b 100644 --- a/UPGRADE-5.0.md +++ b/UPGRADE-5.0.md @@ -326,8 +326,7 @@ Security } ``` - * Using `Argon2iPasswordEncoder` while only the `argon2id` algorithm is supported - now throws a `\LogicException`, use `Argon2idPasswordEncoder` instead + * The `Argon2iPasswordEncoder` class has been removed, use `SodiumPasswordEncoder` instead. SecurityBundle -------------- @@ -348,8 +347,7 @@ SecurityBundle changed to underscores. Before: `my-cookie` deleted the `my_cookie` cookie (with an underscore). After: `my-cookie` deletes the `my-cookie` cookie (with a dash). - * Configuring encoders using `argon2i` as algorithm while only `argon2id` is supported - now throws a `\LogicException`, use `argon2id` instead + * Configuring encoders using `argon2i` as algorithm is not supported anymore, use `sodium` instead. Serializer ---------- diff --git a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md index fd0edcb7dd8e2..28bbd1286e021 100644 --- a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md @@ -8,9 +8,7 @@ CHANGELOG option is deprecated and will be disabled in Symfony 5.0. This affects to cookies with dashes in their names. For example, starting from Symfony 5.0, the `my-cookie` name will delete `my-cookie` (with a dash) instead of `my_cookie` (with an underscore). - * Deprecated configuring encoders using `argon2i` as algorithm while only `argon2id` is supported, - use `argon2id` instead - + * Deprecated configuring encoders using `argon2i` as algorithm, use `sodium` instead 4.2.0 ----- diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index dd7fe75cc8b19..4519b66a94313 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -29,8 +29,8 @@ use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; -use Symfony\Component\Security\Core\Encoder\Argon2idPasswordEncoder; use Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder; +use Symfony\Component\Security\Core\Encoder\SodiumPasswordEncoder; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Controller\UserValueResolver; use Symfony\Component\Templating\PhpEngine; @@ -565,14 +565,14 @@ private function createEncoder($config, ContainerBuilder $container) // Argon2i encoder if ('argon2i' === $config['algorithm']) { + @trigger_error('Configuring an encoder with "argon2i" as algorithm is deprecated since Symfony 4.3, use "sodium" instead.', E_USER_DEPRECATED); + if (!Argon2iPasswordEncoder::isSupported()) { if (\extension_loaded('sodium') && !\defined('SODIUM_CRYPTO_PWHASH_SALTBYTES')) { throw new InvalidConfigurationException('The installed libsodium version does not have support for Argon2i. Use Bcrypt instead.'); } throw new InvalidConfigurationException('Argon2i algorithm is not supported. Install the libsodium extension or use BCrypt instead.'); - } elseif (!\defined('PASSWORD_ARGON2I') && Argon2idPasswordEncoder::isDefaultSodiumAlgorithm()) { - @trigger_error('Configuring an encoder based on the "argon2i" algorithm while only "argon2id" is supported is deprecated since Symfony 4.3, use "argon2id" instead.', E_USER_DEPRECATED); } return [ @@ -585,19 +585,14 @@ private function createEncoder($config, ContainerBuilder $container) ]; } - // Argon2id encoder - if ('argon2id' === $config['algorithm']) { - if (!Argon2idPasswordEncoder::isSupported()) { - throw new InvalidConfigurationException('Argon2i algorithm is not supported. Install the libsodium extension or use BCrypt instead.'); + if ('sodium' === $config['algorithm']) { + if (!SodiumPasswordEncoder::isSupported()) { + throw new InvalidConfigurationException('Libsodium is not available. Install the sodium extension or use BCrypt instead.'); } return [ - 'class' => Argon2idPasswordEncoder::class, - 'arguments' => [ - $config['memory_cost'], - $config['time_cost'], - $config['threads'], - ], + 'class' => SodiumPasswordEncoder::class, + 'arguments' => [], ]; } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php index 5c95500dc3dd3..0727c79a52139 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php @@ -18,8 +18,8 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; -use Symfony\Component\Security\Core\Encoder\Argon2idPasswordEncoder; use Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder; +use Symfony\Component\Security\Core\Encoder\SodiumPasswordEncoder; abstract class CompleteConfigurationTest extends TestCase { @@ -314,11 +314,11 @@ public function testEncoders() public function testEncodersWithLibsodium() { - if (!Argon2iPasswordEncoder::isSupported() || \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) { - $this->markTestSkipped('Argon2i algorithm is not supported.'); + if (!SodiumPasswordEncoder::isSupported()) { + $this->markTestSkipped('Libsodium is not available.'); } - $container = $this->getContainer('argon2i_encoder'); + $container = $this->getContainer('sodium_encoder'); $this->assertEquals([[ 'JMS\FooBundle\Entity\User1' => [ @@ -359,19 +359,24 @@ public function testEncodersWithLibsodium() 'arguments' => [15], ], 'JMS\FooBundle\Entity\User7' => [ - 'class' => 'Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder', - 'arguments' => [256, 1, 2], + 'class' => 'Symfony\Component\Security\Core\Encoder\SodiumPasswordEncoder', + 'arguments' => [], ], ]], $container->getDefinition('security.encoder_factory.generic')->getArguments()); } - public function testEncodersWithArgon2id() + /** + * @group legacy + * + * @expectedDeprecation Configuring an encoder with "argon2i" as algorithm is deprecated since Symfony 4.3, use "sodium" instead. + */ + public function testEncodersWithArgon2i() { - if (!Argon2idPasswordEncoder::isSupported()) { + if (!Argon2iPasswordEncoder::isSupported()) { $this->markTestSkipped('Argon2i algorithm is not supported.'); } - $container = $this->getContainer('argon2id_encoder'); + $container = $this->getContainer('argon2i_encoder'); $this->assertEquals([[ 'JMS\FooBundle\Entity\User1' => [ @@ -412,7 +417,7 @@ public function testEncodersWithArgon2id() 'arguments' => [15], ], 'JMS\FooBundle\Entity\User7' => [ - 'class' => 'Symfony\Component\Security\Core\Encoder\Argon2idPasswordEncoder', + 'class' => 'Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder', 'arguments' => [256, 1, 2], ], ]], $container->getDefinition('security.encoder_factory.generic')->getArguments()); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/argon2id_encoder.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/sodium_encoder.php similarity index 57% rename from src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/argon2id_encoder.php rename to src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/sodium_encoder.php index df63deb92eb24..e32969df2353c 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/argon2id_encoder.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/sodium_encoder.php @@ -5,10 +5,7 @@ $container->loadFromExtension('security', [ 'encoders' => [ 'JMS\FooBundle\Entity\User7' => [ - 'algorithm' => 'argon2id', - 'memory_cost' => 256, - 'time_cost' => 1, - 'threads' => 2, + 'algorithm' => 'sodium', ], ], ]); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/argon2id_encoder.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/sodium_encoder.xml similarity index 89% rename from src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/argon2id_encoder.xml rename to src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/sodium_encoder.xml index 8bb8fa91c9d51..3fb47213009fc 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/argon2id_encoder.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/sodium_encoder.xml @@ -10,7 +10,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/argon2id_encoder.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/argon2id_encoder.yml deleted file mode 100644 index f13de5ff63874..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/argon2id_encoder.yml +++ /dev/null @@ -1,10 +0,0 @@ -imports: - - { resource: container1.yml } - -security: - encoders: - JMS\FooBundle\Entity\User7: - algorithm: argon2id - memory_cost: 256 - time_cost: 1 - threads: 2 diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/sodium_encoder.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/sodium_encoder.yml new file mode 100644 index 0000000000000..175d945b7d1b5 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/sodium_encoder.yml @@ -0,0 +1,7 @@ +imports: + - { resource: container1.yml } + +security: + encoders: + JMS\FooBundle\Entity\User7: + algorithm: sodium diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php index 54d355cf0f3a6..6685d34948907 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php @@ -15,11 +15,11 @@ use Symfony\Bundle\SecurityBundle\Command\UserPasswordEncoderCommand; use Symfony\Component\Console\Application as ConsoleApplication; use Symfony\Component\Console\Tester\CommandTester; -use Symfony\Component\Security\Core\Encoder\Argon2idPasswordEncoder; use Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder; use Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder; use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; use Symfony\Component\Security\Core\Encoder\Pbkdf2PasswordEncoder; +use Symfony\Component\Security\Core\Encoder\SodiumPasswordEncoder; /** * Tests UserPasswordEncoderCommand. @@ -71,9 +71,12 @@ public function testEncodePasswordBcrypt() $this->assertTrue($encoder->isPasswordValid($hash, 'password', null)); } + /** + * @group legacy + */ public function testEncodePasswordArgon2i() { - if (!Argon2iPasswordEncoder::isSupported() || !\defined('PASSWORD_ARGON2I') && Argon2idPasswordEncoder::isDefaultSodiumAlgorithm()) { + if (!Argon2iPasswordEncoder::isSupported()) { $this->markTestSkipped('Argon2i algorithm not available.'); } $this->setupArgon2i(); @@ -87,30 +90,29 @@ public function testEncodePasswordArgon2i() $this->assertContains('Password encoding succeeded', $output); $encoder = new Argon2iPasswordEncoder(); - preg_match('# Encoded password\s+(\$argon2i?\$[\w,=\$+\/]+={0,2})\s+#', $output, $matches); + preg_match('# Encoded password\s+(\$argon2id?\$[\w,=\$+\/]+={0,2})\s+#', $output, $matches); $hash = $matches[1]; $this->assertTrue($encoder->isPasswordValid($hash, 'password', null)); } - public function testEncodePasswordArgon2id() + public function testEncodePasswordSodium() { - if (!Argon2idPasswordEncoder::isSupported()) { - $this->markTestSkipped('Argon2id algorithm not available.'); + if (!SodiumPasswordEncoder::isSupported()) { + $this->markTestSkipped('Libsodium is not available.'); } - $this->setupArgon2id(); + $this->setupSodium(); $this->passwordEncoderCommandTester->execute([ 'command' => 'security:encode-password', 'password' => 'password', - 'user-class' => 'Custom\Class\Argon2id\User', + 'user-class' => 'Custom\Class\Sodium\User', ], ['interactive' => false]); $output = $this->passwordEncoderCommandTester->getDisplay(); $this->assertContains('Password encoding succeeded', $output); - $encoder = new Argon2idPasswordEncoder(); - preg_match('# Encoded password\s+(\$argon2id?\$[\w,=\$+\/]+={0,2})\s+#', $output, $matches); + preg_match('# Encoded password\s+(\$?\$[\w,=\$+\/]+={0,2})\s+#', $output, $matches); $hash = $matches[1]; - $this->assertTrue($encoder->isPasswordValid($hash, 'password', null)); + $this->assertTrue((new SodiumPasswordEncoder())->isPasswordValid($hash, 'password', null)); } public function testEncodePasswordPbkdf2() @@ -173,10 +175,13 @@ public function testEncodePasswordBcryptOutput() $this->assertNotContains(' Generated salt ', $this->passwordEncoderCommandTester->getDisplay()); } + /** + * @group legacy + */ public function testEncodePasswordArgon2iOutput() { - if (!Argon2iPasswordEncoder::isSupported() || !\defined('PASSWORD_ARGON2I') && Argon2idPasswordEncoder::isDefaultSodiumAlgorithm()) { - $this->markTestSkipped('Argon2id algorithm not available.'); + if (!Argon2iPasswordEncoder::isSupported()) { + $this->markTestSkipped('Argon2i algorithm not available.'); } $this->setupArgon2i(); @@ -189,17 +194,17 @@ public function testEncodePasswordArgon2iOutput() $this->assertNotContains(' Generated salt ', $this->passwordEncoderCommandTester->getDisplay()); } - public function testEncodePasswordArgon2idOutput() + public function testEncodePasswordSodiumOutput() { - if (!Argon2idPasswordEncoder::isSupported()) { - $this->markTestSkipped('Argon2id algorithm not available.'); + if (!SodiumPasswordEncoder::isSupported()) { + $this->markTestSkipped('Libsodium is not available.'); } - $this->setupArgon2id(); + $this->setupSodium(); $this->passwordEncoderCommandTester->execute([ 'command' => 'security:encode-password', 'password' => 'p@ssw0rd', - 'user-class' => 'Custom\Class\Argon2id\User', + 'user-class' => 'Custom\Class\Sodium\User', ], ['interactive' => false]); $this->assertNotContains(' Generated salt ', $this->passwordEncoderCommandTester->getDisplay()); @@ -298,10 +303,10 @@ private function setupArgon2i() $this->passwordEncoderCommandTester = new CommandTester($passwordEncoderCommand); } - private function setupArgon2id() + private function setupSodium() { putenv('COLUMNS='.(119 + \strlen(PHP_EOL))); - $kernel = $this->createKernel(['test_case' => 'PasswordEncode', 'root_config' => 'argon2id.yml']); + $kernel = $this->createKernel(['test_case' => 'PasswordEncode', 'root_config' => 'sodium.yml']); $kernel->boot(); $application = new Application($kernel); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/PasswordEncode/argon2id.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/PasswordEncode/argon2id.yml deleted file mode 100644 index 481262acb7e6c..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/PasswordEncode/argon2id.yml +++ /dev/null @@ -1,7 +0,0 @@ -imports: - - { resource: config.yml } - -security: - encoders: - Custom\Class\Argon2id\User: - algorithm: argon2id diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/PasswordEncode/sodium.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/PasswordEncode/sodium.yml new file mode 100644 index 0000000000000..1ccc2a10d5f6e --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/PasswordEncode/sodium.yml @@ -0,0 +1,7 @@ +imports: + - { resource: config.yml } + +security: + encoders: + Custom\Class\Sodium\User: + algorithm: sodium diff --git a/src/Symfony/Component/Security/CHANGELOG.md b/src/Symfony/Component/Security/CHANGELOG.md index 776774e5b6e3a..42aca94dd0738 100644 --- a/src/Symfony/Component/Security/CHANGELOG.md +++ b/src/Symfony/Component/Security/CHANGELOG.md @@ -19,9 +19,7 @@ CHANGELOG * Dispatch `AuthenticationFailureEvent` on `security.authentication.failure` * Dispatch `InteractiveLoginEvent` on `security.interactive_login` * Dispatch `SwitchUserEvent` on `security.switch_user` - * Added `Argon2idPasswordEncoder` - * Deprecated using `Argon2iPasswordEncoder` while only the `argon2id` algorithm - is supported, use `Argon2idPasswordEncoder` instead + * deprecated `Argon2iPasswordEncoder`, use `SodiumPasswordEncoder` instead 4.2.0 ----- diff --git a/src/Symfony/Component/Security/Core/Encoder/Argon2Trait.php b/src/Symfony/Component/Security/Core/Encoder/Argon2Trait.php deleted file mode 100644 index de14becf47378..0000000000000 --- a/src/Symfony/Component/Security/Core/Encoder/Argon2Trait.php +++ /dev/null @@ -1,52 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\Encoder; - -/** - * @internal - * - * @author Robin Chalas - */ -trait Argon2Trait -{ - private $memoryCost; - private $timeCost; - private $threads; - - public function __construct(int $memoryCost = null, int $timeCost = null, int $threads = null) - { - $this->memoryCost = $memoryCost; - $this->timeCost = $timeCost; - $this->threads = $threads; - } - - private function encodePasswordNative(string $raw, int $algorithm) - { - return password_hash($raw, $algorithm, [ - 'memory_cost' => $this->memoryCost ?? \PASSWORD_ARGON2_DEFAULT_MEMORY_COST, - 'time_cost' => $this->timeCost ?? \PASSWORD_ARGON2_DEFAULT_TIME_COST, - 'threads' => $this->threads ?? \PASSWORD_ARGON2_DEFAULT_THREADS, - ]); - } - - private function encodePasswordSodiumFunction(string $raw) - { - $hash = \sodium_crypto_pwhash_str( - $raw, - \SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, - \SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE - ); - \sodium_memzero($raw); - - return $hash; - } -} diff --git a/src/Symfony/Component/Security/Core/Encoder/Argon2iPasswordEncoder.php b/src/Symfony/Component/Security/Core/Encoder/Argon2iPasswordEncoder.php index b1add537632c6..d7b53d34b0854 100644 --- a/src/Symfony/Component/Security/Core/Encoder/Argon2iPasswordEncoder.php +++ b/src/Symfony/Component/Security/Core/Encoder/Argon2iPasswordEncoder.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Security\Core\Encoder; +@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', Argon2iPasswordEncoder::class, SodiumPasswordEncoder::class), E_USER_DEPRECATED); + use Symfony\Component\Security\Core\Exception\BadCredentialsException; /** @@ -18,10 +20,30 @@ * * @author Zan Baldwin * @author Dominik Müller + * + * @deprecated since Symfony 4.3, use SodiumPasswordEncoder instead */ class Argon2iPasswordEncoder extends BasePasswordEncoder implements SelfSaltingEncoderInterface { - use Argon2Trait; + private $config = []; + + /** + * Argon2iPasswordEncoder constructor. + * + * @param int|null $memoryCost memory usage of the algorithm + * @param int|null $timeCost number of iterations + * @param int|null $threads number of parallel threads + */ + public function __construct(int $memoryCost = null, int $timeCost = null, int $threads = null) + { + if (\defined('PASSWORD_ARGON2I')) { + $this->config = [ + 'memory_cost' => $memoryCost ?? \PASSWORD_ARGON2_DEFAULT_MEMORY_COST, + 'time_cost' => $timeCost ?? \PASSWORD_ARGON2_DEFAULT_TIME_COST, + 'threads' => $threads ?? \PASSWORD_ARGON2_DEFAULT_THREADS, + ]; + } + } public static function isSupported() { @@ -46,12 +68,9 @@ public function encodePassword($raw, $salt) } if (\PHP_VERSION_ID >= 70200 && \defined('PASSWORD_ARGON2I')) { - return $this->encodePasswordNative($raw, \PASSWORD_ARGON2I); - } elseif (\function_exists('sodium_crypto_pwhash_str')) { - if (Argon2idPasswordEncoder::isDefaultSodiumAlgorithm()) { - @trigger_error(sprintf('Using "%s" while only the "argon2id" algorithm is supported is deprecated since Symfony 4.3, use "%s" instead.', __CLASS__, Argon2idPasswordEncoder::class), E_USER_DEPRECATED); - } - + return $this->encodePasswordNative($raw); + } + if (\function_exists('sodium_crypto_pwhash_str')) { return $this->encodePasswordSodiumFunction($raw); } if (\extension_loaded('libsodium')) { @@ -66,20 +85,10 @@ public function encodePassword($raw, $salt) */ public function isPasswordValid($encoded, $raw, $salt) { - if ($this->isPasswordTooLong($raw)) { - return false; - } - - // If $encoded was created via "sodium_crypto_pwhash_str()", the hashing algorithm may be "argon2id" instead of "argon2i" - if ($isArgon2id = (0 === strpos($encoded, Argon2idPasswordEncoder::HASH_PREFIX))) { - @trigger_error(sprintf('Calling "%s()" with a password hashed using argon2id is deprecated since Symfony 4.3, use "%s" instead.', __METHOD__, Argon2idPasswordEncoder::class), E_USER_DEPRECATED); - } - - if (\PHP_VERSION_ID >= 70200 && \defined('PASSWORD_ARGON2I')) { - // Remove the right part of the OR in 5.0 - if (\defined('PASSWORD_ARGON2I') || $isArgon2id && \defined('PASSWORD_ARGON2ID')) { - return password_verify($raw, $encoded); - } + // If $encoded was created via "sodium_crypto_pwhash_str()", the hashing algorithm may be "argon2id" instead of "argon2i". + // In this case, "password_verify()" cannot be used. + if (\PHP_VERSION_ID >= 70200 && \defined('PASSWORD_ARGON2I') && (false === strpos($encoded, '$argon2id$'))) { + return !$this->isPasswordTooLong($raw) && password_verify($raw, $encoded); } if (\function_exists('sodium_crypto_pwhash_str_verify')) { $valid = !$this->isPasswordTooLong($raw) && \sodium_crypto_pwhash_str_verify($encoded, $raw); @@ -97,6 +106,23 @@ public function isPasswordValid($encoded, $raw, $salt) throw new \LogicException('Argon2i algorithm is not supported. Please install the libsodium extension or upgrade to PHP 7.2+.'); } + private function encodePasswordNative($raw) + { + return password_hash($raw, \PASSWORD_ARGON2I, $this->config); + } + + private function encodePasswordSodiumFunction($raw) + { + $hash = \sodium_crypto_pwhash_str( + $raw, + \SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, + \SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE + ); + \sodium_memzero($raw); + + return $hash; + } + private function encodePasswordSodiumExtension($raw) { $hash = \Sodium\crypto_pwhash_str( diff --git a/src/Symfony/Component/Security/Core/Encoder/Argon2idPasswordEncoder.php b/src/Symfony/Component/Security/Core/Encoder/Argon2idPasswordEncoder.php deleted file mode 100644 index a201bca36b936..0000000000000 --- a/src/Symfony/Component/Security/Core/Encoder/Argon2idPasswordEncoder.php +++ /dev/null @@ -1,88 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\Encoder; - -use Symfony\Component\Security\Core\Exception\BadCredentialsException; -use Symfony\Component\Security\Core\Exception\LogicException; - -/** - * Hashes passwords using the Argon2id algorithm. - * - * @author Robin Chalas - * - * @final - */ -class Argon2idPasswordEncoder extends BasePasswordEncoder implements SelfSaltingEncoderInterface -{ - use Argon2Trait; - - /** - * @internal - */ - public const HASH_PREFIX = '$argon2id'; - - public static function isSupported() - { - return \defined('PASSWORD_ARGON2ID') || \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13'); - } - - /** - * {@inheritdoc} - */ - public function encodePassword($raw, $salt) - { - if ($this->isPasswordTooLong($raw)) { - throw new BadCredentialsException('Invalid password.'); - } - if (\defined('PASSWORD_ARGON2ID')) { - return $this->encodePasswordNative($raw, \PASSWORD_ARGON2ID); - } - if (!self::isDefaultSodiumAlgorithm()) { - throw new LogicException('Algorithm "argon2id" is not supported. Please install the libsodium extension or upgrade to PHP 7.3+.'); - } - - return $this->encodePasswordSodiumFunction($raw); - } - - /** - * {@inheritdoc} - */ - public function isPasswordValid($encoded, $raw, $salt) - { - if (0 !== strpos($encoded, self::HASH_PREFIX)) { - return false; - } - - if (\defined('PASSWORD_ARGON2ID')) { - return !$this->isPasswordTooLong($raw) && password_verify($raw, $encoded); - } - - if (\function_exists('sodium_crypto_pwhash_str_verify')) { - $valid = !$this->isPasswordTooLong($raw) && \sodium_crypto_pwhash_str_verify($encoded, $raw); - \sodium_memzero($raw); - - return $valid; - } - - throw new LogicException('Algorithm "argon2id" is not supported. Please install the libsodium extension or upgrade to PHP 7.3+.'); - } - - /** - * @internal - */ - public static function isDefaultSodiumAlgorithm() - { - return \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13') - && \defined('SODIUM_CRYPTO_PWHASH_ALG_DEFAULT') - && \SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13 === \SODIUM_CRYPTO_PWHASH_ALG_DEFAULT; - } -} diff --git a/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php b/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php index c5770b1e58226..01dd33f053de6 100644 --- a/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php +++ b/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php @@ -108,18 +108,16 @@ private function getEncoderConfigFromAlgorithm($config) 'arguments' => [$config['cost']], ]; - case 'argon2i': + case 'sodium': return [ - 'class' => Argon2iPasswordEncoder::class, - 'arguments' => [ - $config['memory_cost'], - $config['time_cost'], - $config['threads'], - ], + 'class' => SodiumPasswordEncoder::class, + 'arguments' => [], ]; - case 'argon2id': + + /* @deprecated since Symfony 4.3 */ + case 'argon2i': return [ - 'class' => Argon2idPasswordEncoder::class, + 'class' => Argon2iPasswordEncoder::class, 'arguments' => [ $config['memory_cost'], $config['time_cost'], diff --git a/src/Symfony/Component/Security/Core/Encoder/SodiumPasswordEncoder.php b/src/Symfony/Component/Security/Core/Encoder/SodiumPasswordEncoder.php new file mode 100644 index 0000000000000..febca05dd0f39 --- /dev/null +++ b/src/Symfony/Component/Security/Core/Encoder/SodiumPasswordEncoder.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Encoder; + +use Symfony\Component\Security\Core\Exception\BadCredentialsException; +use Symfony\Component\Security\Core\Exception\LogicException; + +/** + * Hashes passwords using libsodium. + * + * @author Robin Chalas + * @author Zan Baldwin + * @author Dominik Müller + * + * @final + */ +class SodiumPasswordEncoder extends BasePasswordEncoder implements SelfSaltingEncoderInterface +{ + public static function isSupported(): bool + { + if (\class_exists('ParagonIE_Sodium_Compat') && \method_exists('ParagonIE_Sodium_Compat', 'crypto_pwhash_is_available')) { + return \ParagonIE_Sodium_Compat::crypto_pwhash_is_available(); + } + + return \function_exists('sodium_crypto_pwhash_str') || \extension_loaded('libsodium'); + } + + /** + * {@inheritdoc} + */ + public function encodePassword($raw, $salt) + { + if ($this->isPasswordTooLong($raw)) { + throw new BadCredentialsException('Invalid password.'); + } + + if (\function_exists('sodium_crypto_pwhash_str')) { + return \sodium_crypto_pwhash_str( + $raw, + \SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, + \SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE + ); + } + + if (\extension_loaded('libsodium')) { + return \Sodium\crypto_pwhash_str( + $raw, + \Sodium\CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, + \Sodium\CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE + ); + } + + throw new LogicException('Libsodium is not available. You should either install the sodium extension, upgrade to PHP 7.2+ or use a different encoder.'); + } + + /** + * {@inheritdoc} + */ + public function isPasswordValid($encoded, $raw, $salt) + { + if ($this->isPasswordTooLong($raw)) { + return false; + } + + if (\function_exists('sodium_crypto_pwhash_str_verify')) { + return \sodium_crypto_pwhash_str_verify($encoded, $raw); + } + + if (\extension_loaded('libsodium')) { + return \Sodium\crypto_pwhash_str_verify($encoded, $raw); + } + + throw new LogicException('Libsodium is not available. You should either install the sodium extension, upgrade to PHP 7.2+ or use a different encoder.'); + } +} diff --git a/src/Symfony/Component/Security/Core/Tests/Encoder/Argon2iPasswordEncoderTest.php b/src/Symfony/Component/Security/Core/Tests/Encoder/Argon2iPasswordEncoderTest.php index 88f10b446d723..a9991749f01fd 100644 --- a/src/Symfony/Component/Security/Core/Tests/Encoder/Argon2iPasswordEncoderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Encoder/Argon2iPasswordEncoderTest.php @@ -12,22 +12,26 @@ namespace Symfony\Component\Security\Core\Tests\Encoder; use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Core\Encoder\Argon2idPasswordEncoder; use Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder; /** * @author Zan Baldwin + * + * @group legacy */ class Argon2iPasswordEncoderTest extends TestCase { const PASSWORD = 'password'; - public function testValidationWithConfig() + protected function setUp() { - if (!Argon2iPasswordEncoder::isSupported() || Argon2idPasswordEncoder::isDefaultSodiumAlgorithm()) { + if (!Argon2iPasswordEncoder::isSupported()) { $this->markTestSkipped('Argon2i algorithm is not supported.'); } + } + public function testValidationWithConfig() + { $encoder = new Argon2iPasswordEncoder(8, 4, 1); $result = $encoder->encodePassword(self::PASSWORD, null); $this->assertTrue($encoder->isPasswordValid($result, self::PASSWORD, null)); @@ -36,10 +40,6 @@ public function testValidationWithConfig() public function testValidation() { - if (!Argon2iPasswordEncoder::isSupported() || Argon2idPasswordEncoder::isDefaultSodiumAlgorithm()) { - $this->markTestSkipped('Argon2i algorithm is not supported.'); - } - $encoder = new Argon2iPasswordEncoder(); $result = $encoder->encodePassword(self::PASSWORD, null); $this->assertTrue($encoder->isPasswordValid($result, self::PASSWORD, null)); @@ -51,20 +51,12 @@ public function testValidation() */ public function testEncodePasswordLength() { - if (!Argon2iPasswordEncoder::isSupported() || Argon2idPasswordEncoder::isDefaultSodiumAlgorithm()) { - $this->markTestSkipped('Argon2i algorithm is not supported.'); - } - $encoder = new Argon2iPasswordEncoder(); $encoder->encodePassword(str_repeat('a', 4097), 'salt'); } public function testCheckPasswordLength() { - if (!Argon2iPasswordEncoder::isSupported() || Argon2idPasswordEncoder::isDefaultSodiumAlgorithm()) { - $this->markTestSkipped('Argon2i algorithm is not supported.'); - } - $encoder = new Argon2iPasswordEncoder(); $result = $encoder->encodePassword(str_repeat('a', 4096), null); $this->assertFalse($encoder->isPasswordValid($result, str_repeat('a', 4097), null)); @@ -73,29 +65,8 @@ public function testCheckPasswordLength() public function testUserProvidedSaltIsNotUsed() { - if (!Argon2iPasswordEncoder::isSupported() || Argon2idPasswordEncoder::isDefaultSodiumAlgorithm()) { - $this->markTestSkipped('Argon2i algorithm is not supported.'); - } - $encoder = new Argon2iPasswordEncoder(); $result = $encoder->encodePassword(self::PASSWORD, 'salt'); $this->assertTrue($encoder->isPasswordValid($result, self::PASSWORD, 'anotherSalt')); } - - /** - * @group legacy - * @exectedDeprecation Using "Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder" while only the "argon2id" algorithm is supported is deprecated since Symfony 4.3, use "Symfony\Component\Security\Core\Encoder\Argon2idPasswordEncoder" instead. - * @exectedDeprecation Calling "Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder::isPasswordValid()" with a password hashed using argon2id is deprecated since Symfony 4.3, use "Symfony\Component\Security\Core\Encoder\Argon2idPasswordEncoder" instead. - */ - public function testEncodeWithArgon2idSupportOnly() - { - if (!Argon2iPasswordEncoder::isSupported() || !Argon2idPasswordEncoder::isDefaultSodiumAlgorithm()) { - $this->markTestSkipped('Argon2id algorithm not available.'); - } - - $encoder = new Argon2iPasswordEncoder(); - $result = $encoder->encodePassword(self::PASSWORD, null); - $this->assertTrue($encoder->isPasswordValid($result, self::PASSWORD, null)); - $this->assertFalse($encoder->isPasswordValid($result, 'anotherPassword', null)); - } } diff --git a/src/Symfony/Component/Security/Core/Tests/Encoder/Argon2idPasswordEncoderTest.php b/src/Symfony/Component/Security/Core/Tests/Encoder/SodiumPasswordEncoderTest.php similarity index 64% rename from src/Symfony/Component/Security/Core/Tests/Encoder/Argon2idPasswordEncoderTest.php rename to src/Symfony/Component/Security/Core/Tests/Encoder/SodiumPasswordEncoderTest.php index 460777c124f5f..fe9e5db0eb4cb 100644 --- a/src/Symfony/Component/Security/Core/Tests/Encoder/Argon2idPasswordEncoderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Encoder/SodiumPasswordEncoderTest.php @@ -12,28 +12,20 @@ namespace Symfony\Component\Security\Core\Tests\Encoder; use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Core\Encoder\Argon2idPasswordEncoder; +use Symfony\Component\Security\Core\Encoder\SodiumPasswordEncoder; -class Argon2idPasswordEncoderTest extends TestCase +class SodiumPasswordEncoderTest extends TestCase { protected function setUp() { - if (!Argon2idPasswordEncoder::isSupported()) { - $this->markTestSkipped('Argon2i algorithm is not supported.'); + if (!SodiumPasswordEncoder::isSupported()) { + $this->markTestSkipped('Libsodium is not available.'); } } - public function testValidationWithConfig() - { - $encoder = new Argon2idPasswordEncoder(8, 4, 1); - $result = $encoder->encodePassword('password', null); - $this->assertTrue($encoder->isPasswordValid($result, 'password', null)); - $this->assertFalse($encoder->isPasswordValid($result, 'anotherPassword', null)); - } - public function testValidation() { - $encoder = new Argon2idPasswordEncoder(); + $encoder = new SodiumPasswordEncoder(); $result = $encoder->encodePassword('password', null); $this->assertTrue($encoder->isPasswordValid($result, 'password', null)); $this->assertFalse($encoder->isPasswordValid($result, 'anotherPassword', null)); @@ -44,13 +36,13 @@ public function testValidation() */ public function testEncodePasswordLength() { - $encoder = new Argon2idPasswordEncoder(); + $encoder = new SodiumPasswordEncoder(); $encoder->encodePassword(str_repeat('a', 4097), 'salt'); } public function testCheckPasswordLength() { - $encoder = new Argon2idPasswordEncoder(); + $encoder = new SodiumPasswordEncoder(); $result = $encoder->encodePassword(str_repeat('a', 4096), null); $this->assertFalse($encoder->isPasswordValid($result, str_repeat('a', 4097), null)); $this->assertTrue($encoder->isPasswordValid($result, str_repeat('a', 4096), null)); @@ -58,7 +50,7 @@ public function testCheckPasswordLength() public function testUserProvidedSaltIsNotUsed() { - $encoder = new Argon2idPasswordEncoder(); + $encoder = new SodiumPasswordEncoder(); $result = $encoder->encodePassword('password', 'salt'); $this->assertTrue($encoder->isPasswordValid($result, 'password', 'anotherSalt')); } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy