8000 bug #31014 [Security] Fix argon2 availability checks (chalasr) · symfony/symfony@9a7a276 · GitHub
[go: up one dir, main page]

Skip to content

Commit 9a7a276

Browse files
committed
bug #31014 [Security] Fix argon2 availability checks (chalasr)
This PR was merged into the 4.3-dev branch. Discussion ---------- [Security] Fix argon2 availability checks | Q | A | ------------- | --- | Branch? | master | Bug fix? | yes (bug not released yet) | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | n/a | License | MIT | Doc PR | n/a <!-- Write a short README entry for your feature/bugfix here (replace this comment block.) This will help people understand your PR and can be used as a start of the Doc PR. Additionally: - Bug fixes must be submitted against the lowest branch where they apply (lowest branches are regularly merged to upper ones so they get the fixes too). - Features and deprecations must be submitted against the master branch. --> Commits ------- dc95a6f [Security] Fix argon2 availability checks
2 parents d935f40 + dc95a6f commit 9a7a276

File tree

5 files changed

+60
-26
lines changed

5 files changed

+60
-26
lines changed

src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,7 @@ private function createEncoder($config, ContainerBuilder $container)
571571
}
572572

573573
throw new InvalidConfigurationException('Argon2i algorithm is not supported. Install the libsodium extension or use BCrypt instead.');
574-
} elseif (\defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
574+
} elseif (!\defined('PASSWORD_ARGON2I') && Argon2idPasswordEncoder::isDefaultSodiumAlgorithm()) {
575575
@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);
576576
}
577577

src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public function testEncodePasswordBcrypt()
7373

7474
public function testEncodePasswordArgon2i()
7575
{
76-
if (!Argon2iPasswordEncoder::isSupported() || \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
76+
if (!Argon2iPasswordEncoder::isSupported() || !\defined('PASSWORD_ARGON2I') && Argon2idPasswordEncoder::isDefaultSodiumAlgorithm()) {
7777
$this->markTestSkipped('Argon2i algorithm not available.');
7878
}
7979
$this->setupArgon2i();
@@ -95,7 +95,7 @@ public function testEncodePasswordArgon2i()
9595
public function testEncodePasswordArgon2id()
9696
{
9797
if (!Argon2idPasswordEncoder::isSupported()) {
98-
$this->markTestSkipped('Argon2i algorithm not available.');
98+
$this->markTestSkipped('Argon2id algorithm not available.');
9999
}
100100
$this->setupArgon2id();
101101
$this->passwordEncoderCommandTester->execute([
@@ -107,7 +107,7 @@ public function testEncodePasswordArgon2id()
107107
$output = $this->passwordEncoderCommandTester->getDisplay();
108108
$this->assertContains('Password encoding succeeded', $output);
109109

110-
$encoder = new Argon2iPasswordEncoder();
110+
$encoder = new Argon2idPasswordEncoder();
111111
preg_match('# Encoded password\s+(\$argon2id?\$[\w,=\$+\/]+={0,2})\s+#', $output, $matches);
112112
$hash = $matches[1];
113113
$this->assertTrue($encoder->isPasswordValid($hash, 'password', null));
@@ -175,7 +175,7 @@ public function testEncodePasswordBcryptOutput()
175175

176176
public function testEncodePasswordArgon2iOutput()
177177
{
178-
if (!Argon2iPasswordEncoder::isSupported() || \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
178+
if (!Argon2iPasswordEncoder::isSupported() || !\defined('PASSWORD_ARGON2I') && Argon2idPasswordEncoder::isDefaultSodiumAlgorithm()) {
179179
$this->markTestSkipped('Argon2id algorithm not available.');
180180
}
181181

src/Symfony/Component/Security/Core/Encoder/Argon2iPasswordEncoder.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@ public function encodePassword($raw, $salt)
4848
if (\PHP_VERSION_ID >= 70200 && \defined('PASSWORD_ARGON2I')) {
4949
return $this->encodePasswordNative($raw, \PASSWORD_ARGON2I);
5050
} elseif (\function_exists('sodium_crypto_pwhash_str')) {
51-
if (0 === strpos($hash = $this->encodePasswordSodiumFunction($raw), Argon2idPasswordEncoder::HASH_PREFIX)) {
51+
if (Argon2idPasswordEncoder::isDefaultSodiumAlgorithm()) {
5252
@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);
5353
}
5454

55-
return $hash;
55+
return $this->encodePasswordSodiumFunction($raw);
5656
}
5757
if (\extension_loaded('libsodium')) {
5858
return $this->encodePasswordSodiumExtension($raw);
@@ -70,12 +70,12 @@ public function isPasswordValid($encoded, $raw, $salt)
7070
return false;
7171
}
7272

73-
if (\PHP_VERSION_ID >= 70200 && \defined('PASSWORD_ARGON2I')) {
74-
// If $encoded was created via "sodium_crypto_pwhash_str()", the hashing algorithm may be "argon2id" instead of "argon2i"
75-
if ($isArgon2id = (0 === strpos($encoded, Argon2idPasswordEncoder::HASH_PREFIX))) {
76-
@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);
77-
}
73+
// If $encoded was created via "sodium_crypto_pwhash_str()", the hashing algorithm may be "argon2id" instead of "argon2i"
74+
if ($isArgon2id = (0 === strpos($encoded, Argon2idPasswordEncoder::HASH_PREFIX))) {
75+
@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);
76+
}
7877

78+
if (\PHP_VERSION_ID >= 70200 && \defined('PASSWORD_ARGON2I')) {
7979
// Remove the right part of the OR in 5.0
8080
if (\defined('PASSWORD_ARGON2I') || $isArgon2id && \defined('PASSWORD_ARGON2ID')) {
8181
return password_verify($raw, $encoded);

src/Symfony/Component/Security/Core/Encoder/Argon2idPasswordEncoder.php

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,11 @@ public function encodePassword($raw, $salt)
4646
if (\defined('PASSWORD_ARGON2ID')) {
4747
return $this->encodePasswordNative($raw, \PASSWORD_ARGON2ID);
4848
}
49-
if (!\defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
49+
if (!self::isDefaultSodiumAlgorithm()) {
5050
throw new LogicException('Algorithm "argon2id" is not supported. Please install the libsodium extension or upgrade to PHP 7.3+.');
5151
}
5252

53-
$hash = \sodium_crypto_pwhash_str(
54-
$raw,
55-
\SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
56-
\SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
57-
);
58-
\sodium_memzero($raw);
59-
60-
return $hash;
53+
return $this->encodePasswordSodiumFunction($raw);
6154
}
6255

6356
/**
@@ -82,4 +75,14 @@ public function isPasswordValid($encoded, $raw, $salt)
8275

8376
throw new LogicException('Algorithm "argon2id" is not supported. Please install the libsodium extension or upgrade to PHP 7.3+.');
8477
}
78+
79+
/**
80+
* @internal
81+
*/
82+
public static function isDefaultSodiumAlgorithm()
83+
{
84+
return \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')
85+
&& \defined('SODIUM_CRYPTO_PWHASH_ALG_DEFAULT')
86+
&& \SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13 === \SODIUM_CRYPTO_PWHASH_ALG_DEFAULT;
87+
}
8588
}

src/Symfony/Component/Security/Core/Tests/Encoder/Argon2iPasswordEncoderTest.php

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Security\Core\Tests\Encoder;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Security\Core\Encoder\Argon2idPasswordEncoder;
1516
use Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder;
1617

1718
/**
@@ -21,15 +22,12 @@ class Argon2iPasswordEncoderTest extends TestCase
2122
{
2223
const PASSWORD = 'password';
2324

24-
protected function setUp()
25+
public function testValidationWithConfig()
2526
{
26-
if (!Argon2iPasswordEncoder::isSupported() || \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
27+
if (!Argon2iPasswordEncoder::isSupported() || Argon2idPasswordEncoder::isDefaultSodiumAlgorithm()) {
2728
$this->markTestSkipped('Argon2i algorithm is not supported.');
2829
}
29-
}
3030

31-
public function testValidationWithConfig()
32-
{
3331
$encoder = new Argon2iPasswordEncoder(8, 4, 1);
3432
$result = $encoder->encodePassword(self::PASSWORD, null);
3533
$this->assertTrue($encoder->isPasswordValid($result, self::PASSWORD, null));
@@ -38,6 +36,10 @@ public function testValidationWithConfig()
3836

3937
public function testValidation()
4038
{
39+
if (!Argon2iPasswordEncoder::isSupported() || Argon2idPasswordEncoder::isDefaultSodiumAlgorithm()) {
40+
$this->markTestSkipped('Argon2i algorithm is not supported.');
41+
}
42+
4143
$encoder = new Argon2iPasswordEncoder();
4244
$result = $encoder->encodePassword(self::PASSWORD, null);
4345
$this->assertTrue($encoder->isPasswordValid($result, self::PASSWORD, null));
@@ -49,12 +51,20 @@ public function testValidation()
4951
*/
5052
public function testEncodePasswordLength()
5153
{
54+
if (!Argon2iPasswordEncoder::isSupported() || Argon2idPasswordEncoder::isDefaultSodiumAlgorithm()) {
55+
$this->markTestSkipped('Argon2i algorithm is not supported.');
56+
}
57+
5258
$encoder = new Argon2iPasswordEncoder();
5359
$encoder->encodePassword(str_repeat('a', 4097), 'salt');
5460
}
5561

5662
public function testCheckPasswordLength()
5763
{
64+
if (!Argon2iPasswordEncoder::isSupported() || Argon2idPasswordEncoder::isDefaultSodiumAlgorithm()) {
65+
$this->markTestSkipped('Argon2i algorithm is not supported.');
66+
}
67+
5868
$encoder = new Argon2iPasswordEncoder();
5969
$result = $encoder->encodePassword(str_repeat('a', 4096), null);
6070
$this->assertFalse($encoder->isPasswordValid($result, str_repeat('a', 4097), null));
@@ -63,8 +73,29 @@ public function testCheckPasswordLength()
6373

6474
public function testUserProvidedSaltIsNotUsed()
6575
{
76+
if (!Argon2iPasswordEncoder::isSupported() || Argon2idPasswordEncoder::isDefaultSodiumAlgorithm()) {
77+
$this->markTestSkipped('Argon2i algorithm is not supported.');
78+
}
79+
6680
$encoder = new Argon2iPasswordEncoder();
6781
$result = $encoder->encodePassword(self::PASSWORD, 'salt');
6882
$this->assertTrue($encoder->isPasswordValid($result, self::PASSWORD, 'anotherSalt'));
6983
}
84+
85+
/**
86+
* @group legacy
87+
* @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.
88+
* @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.
89+
*/
90+
public function testEncodeWithArgon2idSupportOnly()
91+
{
92+
if (!Argon2iPasswordEncoder::isSupported() || !Argon2idPasswordEncoder::isDefaultSodiumAlgorithm()) {
93+
$this->markTestSkipped('Argon2id algorithm not available.');
94+
}
95+
96+
$encoder = new Argon2iPasswordEncoder();
97+
$result = $encoder->encodePassword(self::PASSWORD, null);
98+
$this->assertTrue($encoder->isPasswordValid($result, self::PASSWORD, null));
99+
$this->assertFalse($encoder->isPasswordValid($result, 'anotherPassword', null));
100+
}
70101
}

0 commit comments

Comments
 (0)
0