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

[Security] Add Argon2idPasswordEncoder #30968

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 8, 2019
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
11 changes: 10 additions & 1 deletion 11 UPGRADE-4.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,17 @@ Security
}
```

* Using `Argon2iPasswordEncoder` while only the `argon2id` algorithm is supported
is deprecated, use `Argon2idPasswordEncoder` instead

SecurityBundle
--------------

* Configuring encoders using `argon2i` as algorithm while only `argon2id` is
supported is deprecated, use `argon2id` instead

TwigBridge
==========
----------

* deprecated the `$requestStack` and `$requestContext` arguments of the
`HttpFoundationExtension`, pass a `Symfony\Component\HttpFoundation\UrlHelper`
Expand Down
5 changes: 5 additions & 0 deletions 5 UPGRADE-5.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,9 @@ Security
}
```

* Using `Argon2iPasswordEncoder` while only the `argon2id` algorithm is supported
now throws a \LogicException`, use `Argon2idPasswordEncoder` instead
fabpot marked this conversation as resolved.
Show resolved Hide resolved

SecurityBundle
--------------

Expand All @@ -342,6 +345,8 @@ 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

Serializer
----------
Expand Down
3 changes: 3 additions & 0 deletions 3 src/Symfony/Bundle/SecurityBundle/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ 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


4.2.0
-----
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
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\User\UserProviderInterface;
use Symfony\Component\Security\Http\Controller\UserValueResolver;
Expand Down Expand Up @@ -570,6 +571,8 @@ private function createEncoder($config, ContainerBuilder $container)
}

throw new InvalidConfigurationException('Argon2i algorithm is not supported. Install the libsodium extension or use BCrypt instead.');
} elseif (\defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
chalasr marked this conversation as resolved.
Show resolved Hide resolved
@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 [
Expand All @@ -582,6 +585,22 @@ 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.');
}

return [
'class' => Argon2idPasswordEncoder::class,
'arguments' => [
$config['memory_cost'],
$config['time_cost'],
$config['threads'],
],
];
}

// run-time configured encoder
return $config;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
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;

abstract class CompleteConfigurationTest extends TestCase
Expand Down Expand Up @@ -313,7 +314,7 @@ public function testEncoders()

public function testEncodersWithLibsodium()
{
if (!Argon2iPasswordEncoder::isSupported()) {
if (!Argon2iPasswordEncoder::isSupported() || \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
$this->markTestSkipped('Argon2i algorithm is not supported.');
}

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

public function testEncodersWithArgon2id()
{
if (!Argon2idPasswordEncoder::isSupported()) {
$this->markTestSkipped('Argon2i algorithm is not supported.');
Copy link

@bigfoot90 bigfoot90 Apr 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Argon2id algorithm is not supported.

}

$container = $this->getContainer('argon2id_encoder');

$this->assertEquals([[
'JMS\FooBundle\Entity\User1' => [
'class' => 'Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder',
'arguments' => [false],
],
'JMS\FooBundle\Entity\User2' => [
'algorithm' => 'sha1',
'encode_as_base64' => false,
'iterations' => 5,
'hash_algorithm' => 'sha512',
'key_length' => 40,
'ignore_case' => false,
'cost' => 13,
'memory_cost' => null,
'time_cost' => null,
'threads' => null,
],
'JMS\FooBundle\Entity\User3' => [
'algorithm' => 'md5',
'hash_algorithm' => 'sha512',
'key_length' => 40,
'ignore_case' => false,
'encode_as_base64' => true,
'iterations' => 5000,
'cost' => 13,
'memory_cost' => null,
'time_cost' => null,
'threads' => null,
],
'JMS\FooBundle\Entity\User4' => new Reference('security.encoder.foo'),
'JMS\FooBundle\Entity\User5' => [
'class' => 'Symfony\Component\Security\Core\Encoder\Pbkdf2PasswordEncoder',
'arguments' => ['sha1', false, 5, 30],
],
'JMS\FooBundle\Entity\User6' => [
'class' => 'Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder',
'arguments' => [15],
],
'JMS\FooBundle\Entity\User7' => [
'class' => 'Symfony\Component\Security\Core\Encoder\Argon2idPasswordEncoder',
'arguments' => [256, 1, 2],
],
]], $container->getDefinition('security.encoder_factory.generic')->getArguments());
}

public function testRememberMeThrowExceptionsDefault()
{
$container = $this->getContainer('container1');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

$this->load('container1.php', $container);

$container->loadFromExtension('security', [
'encoders' => [
'JMS\FooBundle\Entity\User7' => [
'algorithm' => 'argon2id',
'memory_cost' => 256,
'time_cost' => 1,
'threads' => 2,
],
],
]);
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sec="http://symfony.com/schema/dic/security"
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">

<imports>
<import resource="container1.xml"/>
</imports>

<sec:config>
<sec:encoder class="JMS\FooBundle\Entity\User7" algorithm="argon2id" memory_cost="256" time_cost="1" threads="2" />
</sec:config>

</container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
imports:
- { resource: container1.yml }

security:
encoders:
JMS\FooBundle\Entity\User7:
algorithm: argon2id
memory_cost: 256
time_cost: 1
threads: 2
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
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;
Expand Down Expand Up @@ -72,7 +73,7 @@ public function testEncodePasswordBcrypt()

public function testEncodePasswordArgon2i()
{
if (!Argon2iPasswordEncoder::isSupported()) {
if (!Argon2iPasswordEncoder::isSupported() || \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
$this->markTestSkipped('Argon2i algorithm not available.');
}
$this->setupArgon2i();
Expand All @@ -85,6 +86,27 @@ public function testEncodePasswordArgon2i()
$output = $this->passwordEncoderCommandTester->getDisplay();
$this->assertContains('Password encoding succeeded', $output);

$encoder = new Argon2iPasswordEncoder();
preg_match('# Encoded password\s+(\$argon2i?\$[\w,=\$+\/]+={0,2})\s+#', $output, $matches);
$hash = $matches[1];
$this->assertTrue($encoder->isPasswordValid($hash, 'password', null));
}

public function testEncodePasswordArgon2id()
{
if (!Argon2idPasswordEncoder::isSupported()) {
$this->markTestSkipped('Argon2i algorithm not available.');
}
$this->setupArgon2id();
$this->passwordEncoderCommandTester->execute([
'command' => 'security:encode-password',
'password' => 'password',
'user-class' => 'Custom\Class\Argon2id\User',
], ['interactive' => false]);

$output = $this->passwordEncoderCommandTester->getDisplay();
$this->assertContains('Password encoding succeeded', $output);

$encoder = new Argon2iPasswordEncoder();
preg_match('# Encoded password\s+(\$argon2id?\$[\w,=\$+\/]+={0,2})\s+#', $output, $matches);
$hash = $matches[1];
Expand Down Expand Up @@ -153,8 +175,8 @@ public function testEncodePasswordBcryptOutput()

public function testEncodePasswordArgon2iOutput()
{
if (!Argon2iPasswordEncoder::isSupported()) {
$this->markTestSkipped('Argon2i algorithm not available.');
if (!Argon2iPasswordEncoder::isSupported() || \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
$this->markTestSkipped('Argon2id algorithm not available.');
}

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

public function testEncodePasswordArgon2idOutput()
{
if (!Argon2idPasswordEncoder::isSupported()) {
$this->markTestSkipped('Argon2id algorithm not available.');
}

$this->setupArgon2id();
$this->passwordEncoderCommandTester->execute([
'command' => 'security:encode-password',
'password' => 'p@ssw0rd',
'user-class' => 'Custom\Class\Argon2id\User',
], ['interactive' => false]);

$this->assertNotContains(' Generated salt ', $this->passwordEncoderCommandTester->getDisplay());
}

public function testEncodePasswordNoConfigForGivenUserClass()
{
if (method_exists($this, 'expectException')) {
Expand Down Expand Up @@ -259,4 +297,17 @@ private function setupArgon2i()

$this->passwordEncoderCommandTester = new CommandTester($passwordEncoderCommand);
}

private function setupArgon2id()
{
putenv('COLUMNS='.(119 + \strlen(PHP_EOL)));
$kernel = $this->createKernel(['test_case' => 'PasswordEncode', 'root_config' => 'argon2id.yml']);
$kernel->boot();

$application = new Application($kernel);

$passwordEncoderCommand = $application->get('security:encode-password');

$this->passwordEncoderCommandTester = new CommandTester($passwordEncoderCommand);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
imports:
- { resource: config.yml }

security:
encoders:
Custom\Class\Argon2id\User:
algorithm: argon2id
3 changes: 3 additions & 0 deletions 3 src/Symfony/Component/Security/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ 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

4.2.0
-----
Expand Down
52 changes: 52 additions & 0 deletions 52 src/Symfony/Component/Security/Core/Encoder/Argon2Trait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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 <robin.chalas@gmail.com>
*/
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;
}
}
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.