Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7e82a64

Browse filesBrowse files
author
Robin Chalas
committed
[Security] Add Argon2idPasswordEncoder
1 parent ca29039 commit 7e82a64
Copy full SHA for 7e82a64

File tree

Expand file treeCollapse file tree

17 files changed

+430
-50
lines changed
Filter options
Expand file treeCollapse file tree

17 files changed

+430
-50
lines changed

‎UPGRADE-4.3.md

Copy file name to clipboardExpand all lines: UPGRADE-4.3.md
+10-1Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,17 @@ Security
145145
}
146146
```
147147

148+
* Using `Argon2iPasswordEncoder` while only the `argon2id` algorithm is supported
149+
is deprecated, use `Argon2idPasswordEncoder` instead
150+
151+
SecurityBundle
152+
--------------
153+
154+
* Configuring encoders using `argon2i` as algorithm while only `argon2id` is
155+
supported is deprecated, use `argon2id` instead
156+
148157
TwigBridge
149-
==========
158+
----------
150159

151160
* deprecated the `$requestStack` and `$requestContext` arguments of the
152161
`HttpFoundationExtension`, pass a `Symfony\Component\HttpFoundation\UrlHelper`

‎UPGRADE-5.0.md

Copy file name to clipboardExpand all lines: UPGRADE-5.0.md
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,9 @@ Security
323323
}
324324
```
325325

326+
* Using `Argon2iPasswordEncoder` while only the `argon2id` algorithm is supported
327+
now throws a \LogicException`, use `Argon2idPasswordEncoder` instead
328+
326329
SecurityBundle
327330
--------------
328331

@@ -342,6 +345,8 @@ SecurityBundle
342345
changed to underscores.
343346
Before: `my-cookie` deleted the `my_cookie` cookie (with an underscore).
344347
After: `my-cookie` deletes the `my-cookie` cookie (with a dash).
348+
* Configuring encoders using `argon2i` as algorithm while only `argon2id` is supported
349+
now throws a `\LogicException`, use `argon2id` instead
345350

346351
Serializer
347352
----------

‎src/Symfony/Bundle/SecurityBundle/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/CHANGELOG.md
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ CHANGELOG
88
option is deprecated and will be disabled in Symfony 5.0. This affects to cookies
99
with dashes in their names. For example, starting from Symfony 5.0, the `my-cookie`
1010
name will delete `my-cookie` (with a dash) instead of `my_cookie` (with an underscore).
11+
* Deprecated configuring encoders using `argon2i` as algorithm while only `argon2id` is supported,
12+
use `argon2id` instead
13+
1114

1215
4.2.0
1316
-----

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+19Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use Symfony\Component\DependencyInjection\Reference;
3030
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
3131
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
32+
use Symfony\Component\Security\Core\Encoder\Argon2idPasswordEncoder;
3233
use Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder;
3334
use Symfony\Component\Security\Core\User\UserProviderInterface;
3435
use Symfony\Component\Security\Http\Controller\UserValueResolver;
@@ -570,6 +571,8 @@ private function createEncoder($config, ContainerBuilder $container)
570571
}
571572

572573
throw new InvalidConfigurationException('Argon2i algorithm is not supported. Install the libsodium extension or use BCrypt instead.');
574+
} elseif (\defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
575+
@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);
573576
}
574577

575578
return [
@@ -582,6 +585,22 @@ private function createEncoder($config, ContainerBuilder $container)
582585
];
583586
}
584587

588+
// Argon2id encoder
589+
if ('argon2id' === $config['algorithm']) {
590+
if (!Argon2idPasswordEncoder::isSupported()) {
591+
throw new InvalidConfigurationException('Argon2i algorithm is not supported. Install the libsodium extension or use BCrypt instead.');
592+
}
593+
594+
return [
595+
'class' => 'Symfony\Component\Security\Core\Encoder\Argon2idPasswordEncoder',
596+
'arguments' => [
597+
$config['memory_cost'],
598+
$config['time_cost'],
599+
$config['threads'],
600+
],
601+
];
602+
}
603+
585604
// run-time configured encoder
586605
return $config;
587606
}

‎src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php
+55-1Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Symfony\Component\DependencyInjection\ContainerBuilder;
1919
use Symfony\Component\DependencyInjection\Reference;
2020
use Symfony\Component\Security\Core\Authorization\AccessDecisionManager;
21+
use Symfony\Component\Security\Core\Encoder\Argon2idPasswordEncoder;
2122
use Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder;
2223

2324
abstract class CompleteConfigurationTest extends TestCase
@@ -313,7 +314,7 @@ public function testEncoders()
313314

314315
public function testEncodersWithLibsodium()
315316
{
316-
if (!Argon2iPasswordEncoder::isSupported()) {
317+
if (!Argon2iPasswordEncoder::isSupported() || \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
317318
$this->markTestSkipped('Argon2i algorithm is not supported.');
318319
}
319320

@@ -364,6 +365,59 @@ public function testEncodersWithLibsodium()
364365
]], $container->getDefinition('security.encoder_factory.generic')->getArguments());
365366
}
366367

368+
public function testEncodersWithArgon2id()
369+
{
370+
if (!Argon2idPasswordEncoder::isSupported()) {
371+
$this->markTestSkipped('Argon2i algorithm is not supported.');
372+
}
373+
374+
$container = $this->getContainer('argon2id_encoder');
375+
376+
$this->assertEquals([[
377+
'JMS\FooBundle\Entity\User1' => [
378+
'class' => 'Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder',
379+
'arguments' => [false],
380+
],
381+
'JMS\FooBundle\Entity\User2' => [
382+
'algorithm' => 'sha1',
383+
'encode_as_base64' => false,
384+
'iterations' => 5,
385+
'hash_algorithm' => 'sha512',
386+
'key_length' => 40,
387+
'ignore_case' => false,
388+
'cost' => 13,
389+
'memory_cost' => null,
390+
'time_cost' => null,
391+
'threads' => null,
392+
],
393+
'JMS\FooBundle\Entity\User3' => [
394+
'algorithm' => 'md5',
395+
'hash_algorithm' => 'sha512',
396+
'key_length' => 40,
397+
'ignore_case' => false,
398+
'encode_as_base64' => true,
399+
'iterations' => 5000,
400+
'cost' => 13,
401+
'memory_cost' => null,
402+
'time_cost' => null,
403+
'threads' => null,
404+
],
405+
'JMS\FooBundle\Entity\User4' => new Reference('security.encoder.foo'),
406+
'JMS\FooBundle\Entity\User5' => [
407+
'class' => 'Symfony\Component\Security\Core\Encoder\Pbkdf2PasswordEncoder',
408+
'arguments' => ['sha1', false, 5, 30],
409+
],
410+
'JMS\FooBundle\Entity\User6' => [
411+
'class' => 'Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder',
412+
'arguments' => [15],
413+
],
414+
'JMS\FooBundle\Entity\User7' => [
415+
'class' => 'Symfony\Component\Security\Core\Encoder\Argon2idPasswordEncoder',
416+
'arguments' => [256, 1, 2],
417+
],
418+
]], $container->getDefinition('security.encoder_factory.generic')->getArguments());
419+
}
420+
367421
public function testRememberMeThrowExceptionsDefault()
368422
{
369423
$container = $this->getContainer('container1');
+14Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
$this->load('container1.php', $container);
4+
5+
$container->loadFromExtension('security', [
6+
'encoders' => [
7+
'JMS\FooBundle\Entity\User7' => [
8+
'algorithm' => 'argon2id',
9+
'memory_cost' => 256,
10+
'time_cost' => 1,
11+
'threads' => 2,
12+
],
13+
],
14+
]);
+16Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xmlns:sec="http://symfony.com/schema/dic/security"
6+
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
7+
8+
<imports>
9+
<import resource="container1.xml"/>
10+
</imports>
11+
12+
<sec:config>
13+
<sec:encoder class="JMS\FooBundle\Entity\User7" algorithm="argon2id" memory_cost="256" time_cost="1" threads="2" />
14+
</sec:config>
15+
16+
</container>
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
imports:
2+
- { resource: container1.yml }
3+
4+
security:
5+
encoders:
6+
JMS\FooBundle\Entity\User7:
7+
algorithm: argon2id
8+
memory_cost: 256
9+
time_cost: 1
10+
threads: 2

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php
+54-3Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Bundle\SecurityBundle\Command\UserPasswordEncoderCommand;
1616
use Symfony\Component\Console\Application as ConsoleApplication;
1717
use Symfony\Component\Console\Tester\CommandTester;
18+
use Symfony\Component\Security\Core\Encoder\Argon2idPasswordEncoder;
1819
use Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder;
1920
use Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder;
2021
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
@@ -72,7 +73,7 @@ public function testEncodePasswordBcrypt()
7273

7374
public function testEncodePasswordArgon2i()
7475
{
75-
if (!Argon2iPasswordEncoder::isSupported()) {
76+
if (!Argon2iPasswordEncoder::isSupported() || \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
7677
$this->markTestSkipped('Argon2i algorithm not available.');
7778
}
7879
$this->setupArgon2i();
@@ -85,6 +86,27 @@ public function testEncodePasswordArgon2i()
8586
$output = $this->passwordEncoderCommandTester->getDisplay();
8687
$this->assertContains('Password encoding succeeded', $output);
8788

89+
$encoder = new Argon2iPasswordEncoder();
90+
preg_match('# Encoded password\s+(\$argon2i?\$[\w,=\$+\/]+={0,2})\s+#', $output, $matches);
91+
$hash = $matches[1];
92+
$this->assertTrue($encoder->isPasswordValid($hash, 'password', null));
93+
}
94+
95+
public function testEncodePasswordArgon2id()
96+
{
97+
if (!Argon2idPasswordEncoder::isSupported()) {
98+
$this->markTestSkipped('Argon2i algorithm not available.');
99+
}
100+
$this->setupArgon2id();
101+
$this->passwordEncoderCommandTester->execute([
102+
'command' => 'security:encode-password',
103+
'password' => 'password',
104+
'user-class' => 'Custom\Class\Argon2id\User',
105+
], ['interactive' => false]);
106+
107+
$output = $this->passwordEncoderCommandTester->getDisplay();
108+
$this->assertContains('Password encoding succeeded', $output);
109+
88110
$encoder = new Argon2iPasswordEncoder();
89111
preg_match('# Encoded password\s+(\$argon2id?\$[\w,=\$+\/]+={0,2})\s+#', $output, $matches);
90112
$hash = $matches[1];
@@ -153,8 +175,8 @@ public function testEncodePasswordBcryptOutput()
153175

154176
public function testEncodePasswordArgon2iOutput()
155177
{
156-
if (!Argon2iPasswordEncoder::isSupported()) {
157-
$this->markTestSkipped('Argon2i algorithm not available.');
178+
if (!Argon2iPasswordEncoder::isSupported() || \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
179+
$this->markTestSkipped('Argon2id algorithm not available.');
158180
}
159181

160182
$this->setupArgon2i();
@@ -167,6 +189,22 @@ public function testEncodePasswordArgon2iOutput()
167189
$this->assertNotContains(' Generated salt ', $this->passwordEncoderCommandTester->getDisplay());
168190
}
169191

192+
public function testEncodePasswordArgon2idOutput()
193+
{
194+
if (!Argon2idPasswordEncoder::isSupported()) {
195+
$this->markTestSkipped('Argon2id algorithm not available.');
196+
}
197+
198+
$this->setupArgon2id();
199+
$this->passwordEncoderCommandTester->execute([
200+
'command' => 'security:encode-password',
201+
'password' => 'p@ssw0rd',
202+
'user-class' => 'Custom\Class\Argon2id\User',
203+
], ['interactive' => false]);
204+
205+
$this->assertNotContains(' Generated salt ', $this->passwordEncoderCommandTester->getDisplay());
206+
}
207+
170208
public function testEncodePasswordNoConfigForGivenUserClass()
171209
{
172210
if (method_exists($this, 'expectException')) {
@@ -259,4 +297,17 @@ private function setupArgon2i()
259297

260298
$this->passwordEncoderCommandTester = new CommandTester($passwordEncoderCommand);
261299
}
300+
301+
private function setupArgon2id()
302+
{
303+
putenv('COLUMNS='.(119 + \strlen(PHP_EOL)));
304+
$kernel = $this->createKernel(['test_case' => 'PasswordEncode', 'root_config' => 'argon2id.yml']);
305+
$kernel->boot();
306+
307+
$application = new Application($kernel);
308+
309+
$passwordEncoderCommand = $application->get('security:encode-password');
310+
311+
$this->passwordEncoderCommandTester = new CommandTester($passwordEncoderCommand);
312+
}
262313
}
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
imports:
2+
- { resource: config.yml }
3+
4+
security:
5+
encoders:
6+
Custom\Class\Argon2id\User:
7+
algorithm: argon2id

‎src/Symfony/Component/Security/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/CHANGELOG.md
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ CHANGELOG
1919
* Dispatch `AuthenticationFailureEvent` on `security.authentication.failure`
2020
* Dispatch `InteractiveLoginEvent` on `security.interactive_login`
2121
* Dispatch `SwitchUserEvent` on `security.switch_user`
22+
* Added `Argon2idPasswordEncoder`
23+
* Deprecated using `Argon2iPasswordEncoder` while only the `argon2id` algorithm
24+
is supported, use `Argon2idPasswordEncoder` instead
2225

2326
4.2.0
2427
-----
+52Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Security\Core\Encoder;
13+
14+
/**
15+
* @internal
16+
*
17+
* @author Robin Chalas <robin.chalas@gmail.com>
18+
*/
19+
trait Argon2Trait
20+
{
21+
private $memoryCost;
22+
private $timeCost;
23+
private $threads;
24+
25+
public function __construct(int $memoryCost = null, int $timeCost = null, int $threads = null)
26+
{
27+
$this->memoryCost = $memoryCost;
28+
$this->timeCost = $timeCost;
29+
$this->threads = $threads;
30+
}
31+
32+
private function encodePasswordNative(string $raw, int $algorithm)
33+
{
34+
return password_hash($raw, $algorithm, [
35+
'memory_cost' => $this->memoryCost ?? \PASSWORD_ARGON2_DEFAULT_MEMORY_COST,
36+
'time_cost' => $this->timeCost ?? \PASSWORD_ARGON2_DEFAULT_TIME_COST,
37+
'threads' => $this->threads ?? \PASSWORD_ARGON2_DEFAULT_THREADS,
38+
]);
39+
}
40+
41+
private function encodePasswordSodiumFunction(string $raw)
42+
{
43+
$hash = \sodium_crypto_pwhash_str(
44+
$raw,
45+
\SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
46+
\SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
47+
);
48+
\sodium_memzero($raw);
49+
50+
return $hash;
51+
}
52+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.