Skip to content

[Security] Add configuration for Argon2i encryption #26175

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
Feb 20, 2018
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 @@ -385,6 +385,9 @@ private function addEncodersSection(ArrayNodeDefinition $rootNode)
->max(31)
->defaultValue(13)
->end()
->scalarNode('memory_cost')->defaultNull()->end()
->scalarNode('time_cost')->defaultNull()->end()
->scalarNode('threads')->defaultNull()->end()
->scalarNode('id')->end()
->end()
->end()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,11 @@ private function createEncoder($config, ContainerBuilder $container)

return array(
'class' => 'Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder',
'arguments' => array(),
'arguments' => array(
$config['memory_cost'],
$config['time_cost'],
$config['threads'],
),
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,9 @@ public function testEncoders()
'key_length' => 40,
'ignore_case' => false,
'cost' => 13,
'memory_cost' => null,
'time_cost' => null,
'threads' => null,
),
'JMS\FooBundle\Entity\User3' => array(
'algorithm' => 'md5',
Expand All @@ -294,6 +297,9 @@ public function testEncoders()
'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' => array(
Expand All @@ -307,16 +313,57 @@ public function testEncoders()
)), $container->getDefinition('security.encoder_factory.generic')->getArguments());
}

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

$this->assertSame(array(array('JMS\FooBundle\Entity\User7' => array(
'class' => 'Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder',
'arguments' => array(),
))), $this->getContainer('argon2i_encoder')->getDefinition('security.encoder_factory.generic')->getArguments());
$container = $this->getContainer('argon2i_encoder');

$this->assertEquals(array(array(
'JMS\FooBundle\Entity\User1' => array(
'class' => 'Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder',
'arguments' => array(false),
),
'JMS\FooBundle\Entity\User2' => array(
'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' => array(
'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' => array(
'class' => 'Symfony\Component\Security\Core\Encoder\Pbkdf2PasswordEncoder',
'arguments' => array('sha1', false, 5, 30),
),
'JMS\FooBundle\Entity\User6' => array(
'class' => 'Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder',
'arguments' => array(15),
),
'JMS\FooBundle\Entity\User7' => array(
'class' => 'Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder',
'arguments' => array(256, 1, 2),
),
)), $container->getDefinition('security.encoder_factory.generic')->getArguments());
}

public function testRememberMeThrowExceptionsDefault()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
<?php

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

$container->loadFromExtension('security', array(
'encoders' => array(
'JMS\FooBundle\Entity\User7' => array(
'algorithm' => 'argon2i',
),
),
'providers' => array(
'default' => array('id' => 'foo'),
),
'firewalls' => array(
'main' => array(
'form_login' => false,
'http_basic' => null,
'memory_cost' => 256,
'time_cost' => 1,
'threads' => 2,
),
),
));
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>

<srv:container xmlns="http://symfony.com/schema/dic/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:srv="http://symfony.com/schema/dic/services"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<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 http://symfony.com/schema/dic/services/services-1.0.xsd">

<config>
<encoder class="JMS\FooBundle\Entity\User7" algorithm="argon2i" />
<imports>
<import resource="container1.xml"/>
</imports>

<provider name="default" id="foo" />
<sec:config>
<sec:encoder class="JMS\FooBundle\Entity\User7" algorithm="argon2i" memory_cost="256" time_cost="1" threads="2" />
</sec:config>

<firewall name="main">
<form-login login-path="/login" />
</firewall>
</config>

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

security:
encoders:
JMS\FooBundle\Entity\User7:
algorithm: argon2i

providers:
default: { id: foo }

firewalls:
main:
form_login: false
http_basic: ~
memory_cost: 256
time_cost: 1
threads: 2
2 changes: 1 addition & 1 deletion src/Symfony/Bundle/SecurityBundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"require": {
"php": "^7.1.3",
"ext-xml": "*",
"symfony/security": "~3.4|~4.0",
"symfony/security": "~4.1",
"symfony/dependency-injection": "^3.4.3|^4.0.3",
"symfony/http-kernel": "~3.4|~4.0"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,30 @@
* Argon2iPasswordEncoder uses the Argon2i hashing algorithm.
*
* @author Zan Baldwin <hello@zanbaldwin.com>
* @author Dominik Müller <dominik.mueller@jkweb.ch>
*/
class Argon2iPasswordEncoder extends BasePasswordEncoder implements SelfSaltingEncoderInterface
{
private $config = array();

/**
* 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 = array(
'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()
{
if (\defined('PASSWORD_ARGON2I')) {
Expand Down Expand Up @@ -81,7 +102,7 @@ public function isPasswordValid($encoded, $raw, $salt)

private function encodePasswordNative($raw)
{
return password_hash($raw, \PASSWORD_ARGON2I);
return password_hash($raw, \PASSWORD_ARGON2I, $this->config);
}

private function encodePasswordSodiumFunction($raw)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,11 @@ private function getEncoderConfigFromAlgorithm($config)
case 'argon2i':
return array(
'class' => Argon2iPasswordEncoder::class,
'arguments' => array(),
'arguments' => array(
$config['memory_cost'],
$config['time_cost'],
$config['threads'],
),
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ protected function setUp()
}
}

public function testValidationWithConfig()
{
$encoder = new Argon2iPasswordEncoder(4, 4, 1);
$result = $encoder->encodePassword(self::PASSWORD, null);
$this->assertTrue($encoder->isPasswordValid($result, self::PASSWORD, null));
$this->assertFalse($encoder->isPasswordValid($result, 'anotherPassword', null));
}

public function testValidation()
{
$encoder = new Argon2iPasswordEncoder();
Expand Down