Skip to content

Commit

Permalink
feature #26175 [Security] Add configuration for Argon2i encryption (C…
Browse files Browse the repository at this point in the history
…oalaJoe)

This PR was merged into the 4.1-dev branch.

Discussion
----------

[Security] Add configuration for Argon2i encryption

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #26174
| License       | MIT
| Doc PR        | [#9300](symfony/symfony-docs#9300)

Feedback?

Current situation: Configuration only applies if argon2i is natively supported.

Commits
-------

1300fec [Security] Add configuration for Argon2i encryption
  • Loading branch information
fabpot committed Feb 20, 2018
2 parents a347f46 + 1300fec commit 2711d14
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 39 deletions.
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

0 comments on commit 2711d14

Please sign in to comment.