[Security] Add configuration for Argon2i encryption
This commit is contained in:
parent
49759e3138
commit
1300fece5f
@ -385,6 +385,9 @@ class MainConfiguration implements ConfigurationInterface
|
|||||||
->max(31)
|
->max(31)
|
||||||
->defaultValue(13)
|
->defaultValue(13)
|
||||||
->end()
|
->end()
|
||||||
|
->scalarNode('memory_cost')->defaultNull()->end()
|
||||||
|
->scalarNode('time_cost')->defaultNull()->end()
|
||||||
|
->scalarNode('threads')->defaultNull()->end()
|
||||||
->scalarNode('id')->end()
|
->scalarNode('id')->end()
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
|
@ -523,7 +523,11 @@ class SecurityExtension extends Extension
|
|||||||
|
|
||||||
return array(
|
return array(
|
||||||
'class' => 'Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder',
|
'class' => 'Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder',
|
||||||
'arguments' => array(),
|
'arguments' => array(
|
||||||
|
$config['memory_cost'],
|
||||||
|
$config['time_cost'],
|
||||||
|
$config['threads'],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,6 +285,9 @@ abstract class CompleteConfigurationTest extends TestCase
|
|||||||
'key_length' => 40,
|
'key_length' => 40,
|
||||||
'ignore_case' => false,
|
'ignore_case' => false,
|
||||||
'cost' => 13,
|
'cost' => 13,
|
||||||
|
'memory_cost' => null,
|
||||||
|
'time_cost' => null,
|
||||||
|
'threads' => null,
|
||||||
),
|
),
|
||||||
'JMS\FooBundle\Entity\User3' => array(
|
'JMS\FooBundle\Entity\User3' => array(
|
||||||
'algorithm' => 'md5',
|
'algorithm' => 'md5',
|
||||||
@ -294,6 +297,9 @@ abstract class CompleteConfigurationTest extends TestCase
|
|||||||
'encode_as_base64' => true,
|
'encode_as_base64' => true,
|
||||||
'iterations' => 5000,
|
'iterations' => 5000,
|
||||||
'cost' => 13,
|
'cost' => 13,
|
||||||
|
'memory_cost' => null,
|
||||||
|
'time_cost' => null,
|
||||||
|
'threads' => null,
|
||||||
),
|
),
|
||||||
'JMS\FooBundle\Entity\User4' => new Reference('security.encoder.foo'),
|
'JMS\FooBundle\Entity\User4' => new Reference('security.encoder.foo'),
|
||||||
'JMS\FooBundle\Entity\User5' => array(
|
'JMS\FooBundle\Entity\User5' => array(
|
||||||
@ -307,16 +313,57 @@ abstract class CompleteConfigurationTest extends TestCase
|
|||||||
)), $container->getDefinition('security.encoder_factory.generic')->getArguments());
|
)), $container->getDefinition('security.encoder_factory.generic')->getArguments());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testArgon2iEncoder()
|
public function testEncodersWithLibsodium()
|
||||||
{
|
{
|
||||||
if (!Argon2iPasswordEncoder::isSupported()) {
|
if (!Argon2iPasswordEncoder::isSupported()) {
|
||||||
$this->markTestSkipped('Argon2i algorithm is not supported.');
|
$this->markTestSkipped('Argon2i algorithm is not supported.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->assertSame(array(array('JMS\FooBundle\Entity\User7' => array(
|
$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',
|
'class' => 'Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder',
|
||||||
'arguments' => array(),
|
'arguments' => array(256, 1, 2),
|
||||||
))), $this->getContainer('argon2i_encoder')->getDefinition('security.encoder_factory.generic')->getArguments());
|
),
|
||||||
|
)), $container->getDefinition('security.encoder_factory.generic')->getArguments());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRememberMeThrowExceptionsDefault()
|
public function testRememberMeThrowExceptionsDefault()
|
||||||
|
@ -1,18 +1,14 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
$this->load('container1.php', $container);
|
||||||
|
|
||||||
$container->loadFromExtension('security', array(
|
$container->loadFromExtension('security', array(
|
||||||
'encoders' => array(
|
'encoders' => array(
|
||||||
'JMS\FooBundle\Entity\User7' => array(
|
'JMS\FooBundle\Entity\User7' => array(
|
||||||
'algorithm' => 'argon2i',
|
'algorithm' => 'argon2i',
|
||||||
),
|
'memory_cost' => 256,
|
||||||
),
|
'time_cost' => 1,
|
||||||
'providers' => array(
|
'threads' => 2,
|
||||||
'default' => array('id' => 'foo'),
|
|
||||||
),
|
|
||||||
'firewalls' => array(
|
|
||||||
'main' => array(
|
|
||||||
'form_login' => false,
|
|
||||||
'http_basic' => null,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<srv:container xmlns="http://symfony.com/schema/dic/security"
|
<container xmlns="http://symfony.com/schema/dic/services"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns:srv="http://symfony.com/schema/dic/services"
|
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">
|
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||||
|
|
||||||
<config>
|
<imports>
|
||||||
<encoder class="JMS\FooBundle\Entity\User7" algorithm="argon2i" />
|
<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">
|
</container>
|
||||||
<form-login login-path="/login" />
|
|
||||||
</firewall>
|
|
||||||
</config>
|
|
||||||
|
|
||||||
</srv:container>
|
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
|
imports:
|
||||||
|
- { resource: container1.yml }
|
||||||
|
|
||||||
security:
|
security:
|
||||||
encoders:
|
encoders:
|
||||||
JMS\FooBundle\Entity\User7:
|
JMS\FooBundle\Entity\User7:
|
||||||
algorithm: argon2i
|
algorithm: argon2i
|
||||||
|
memory_cost: 256
|
||||||
providers:
|
time_cost: 1
|
||||||
default: { id: foo }
|
threads: 2
|
||||||
|
|
||||||
firewalls:
|
|
||||||
main:
|
|
||||||
form_login: false
|
|
||||||
http_basic: ~
|
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
"require": {
|
"require": {
|
||||||
"php": "^7.1.3",
|
"php": "^7.1.3",
|
||||||
"ext-xml": "*",
|
"ext-xml": "*",
|
||||||
"symfony/security": "~3.4|~4.0",
|
"symfony/security": "~4.1",
|
||||||
"symfony/dependency-injection": "^3.4.3|^4.0.3",
|
"symfony/dependency-injection": "^3.4.3|^4.0.3",
|
||||||
"symfony/http-kernel": "~3.4|~4.0"
|
"symfony/http-kernel": "~3.4|~4.0"
|
||||||
},
|
},
|
||||||
|
@ -17,9 +17,30 @@ use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
|||||||
* Argon2iPasswordEncoder uses the Argon2i hashing algorithm.
|
* Argon2iPasswordEncoder uses the Argon2i hashing algorithm.
|
||||||
*
|
*
|
||||||
* @author Zan Baldwin <hello@zanbaldwin.com>
|
* @author Zan Baldwin <hello@zanbaldwin.com>
|
||||||
|
* @author Dominik Müller <dominik.mueller@jkweb.ch>
|
||||||
*/
|
*/
|
||||||
class Argon2iPasswordEncoder extends BasePasswordEncoder implements SelfSaltingEncoderInterface
|
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()
|
public static function isSupported()
|
||||||
{
|
{
|
||||||
if (\defined('PASSWORD_ARGON2I')) {
|
if (\defined('PASSWORD_ARGON2I')) {
|
||||||
@ -81,7 +102,7 @@ class Argon2iPasswordEncoder extends BasePasswordEncoder implements SelfSaltingE
|
|||||||
|
|
||||||
private function encodePasswordNative($raw)
|
private function encodePasswordNative($raw)
|
||||||
{
|
{
|
||||||
return password_hash($raw, \PASSWORD_ARGON2I);
|
return password_hash($raw, \PASSWORD_ARGON2I, $this->config);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function encodePasswordSodiumFunction($raw)
|
private function encodePasswordSodiumFunction($raw)
|
||||||
|
@ -111,7 +111,11 @@ class EncoderFactory implements EncoderFactoryInterface
|
|||||||
case 'argon2i':
|
case 'argon2i':
|
||||||
return array(
|
return array(
|
||||||
'class' => Argon2iPasswordEncoder::class,
|
'class' => Argon2iPasswordEncoder::class,
|
||||||
'arguments' => array(),
|
'arguments' => array(
|
||||||
|
$config['memory_cost'],
|
||||||
|
$config['time_cost'],
|
||||||
|
$config['threads'],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,14 @@ class Argon2iPasswordEncoderTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
public function testValidation()
|
||||||
{
|
{
|
||||||
$encoder = new Argon2iPasswordEncoder();
|
$encoder = new Argon2iPasswordEncoder();
|
||||||
|
Reference in New Issue
Block a user