From e5dc7afe90de06beb814892f96ab960e4539a270 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 5 Jul 2012 12:19:25 +0200 Subject: [PATCH 1/8] moved the secure random class from JMSSecurityExtraBundle to Symfony (closes #3595) --- .../Security/DoctrineSeedProvider.php | 68 +++++++ .../EventListener/PrngSchemaListener.php | 33 ++++ .../Bridge/Doctrine/Security/PrngSchema.php | 43 +++++ .../Security/DoctrineSeedProviderTest.php | 48 +++++ .../Command/InitPrngCommand.php | 57 ++++++ .../DependencyInjection/MainConfiguration.php | 1 + .../DependencyInjection/SecurityExtension.php | 4 + .../Resources/config/security.xml | 7 + .../Resources/config/security_rememberme.xml | 1 + .../Fixtures/php/container1.php | 1 + .../Fixtures/xml/container1.xml | 2 +- .../Fixtures/yml/container1.yml | 1 + .../SecurityExtensionTest.php | 7 + src/Symfony/Component/Security/CHANGELOG.md | 1 + .../Core/Encoder/BasePasswordEncoder.php | 13 +- .../Component/Security/Core/Util/Prng.php | 104 ++++++++++ .../Core/Util/SeedProviderInterface.php | 37 ++++ .../Component/Security/Core/Util/String.php | 48 +++++ ...PersistentTokenBasedRememberMeServices.php | 35 +--- .../Security/Tests/Core/Util/PrngTest.php | 179 ++++++++++++++++++ .../Security/Tests/Core/Util/StringTest.php | 14 ++ ...istentTokenBasedRememberMeServicesTest.php | 6 +- 22 files changed, 673 insertions(+), 37 deletions(-) create mode 100644 src/Symfony/Bridge/Doctrine/Security/DoctrineSeedProvider.php create mode 100644 src/Symfony/Bridge/Doctrine/Security/EventListener/PrngSchemaListener.php create mode 100644 src/Symfony/Bridge/Doctrine/Security/PrngSchema.php create mode 100644 src/Symfony/Bridge/Doctrine/Tests/Security/DoctrineSeedProviderTest.php create mode 100755 src/Symfony/Bundle/SecurityBundle/Command/InitPrngCommand.php create mode 100644 src/Symfony/Component/Security/Core/Util/Prng.php create mode 100644 src/Symfony/Component/Security/Core/Util/SeedProviderInterface.php create mode 100644 src/Symfony/Component/Security/Core/Util/String.php create mode 100755 src/Symfony/Component/Security/Tests/Core/Util/PrngTest.php create mode 100755 src/Symfony/Component/Security/Tests/Core/Util/StringTest.php diff --git a/src/Symfony/Bridge/Doctrine/Security/DoctrineSeedProvider.php b/src/Symfony/Bridge/Doctrine/Security/DoctrineSeedProvider.php new file mode 100644 index 000000000000..80f131321c1a --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Security/DoctrineSeedProvider.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Security; + +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Connection; +use Symfony\Component\Security\Core\Util\SeedProviderInterface; + +/** + * Doctrine Seed Provider. + * + * @author Johannes M. Schmitt + * @author Fabien Potencier + */ +class DoctrineSeedProvider implements SeedProviderInterface +{ + private $con; + private $seedTableName; + + /** + * Constructor. + * + * @param Connection $con + * @param string $tableName + */ + public function __construct(Connection $con, $tableName) + { + $this->con = $con; + $this->seedTableName = $tableName; + } + + /** + * {@inheritdoc} + */ + public function loadSeed() + { + $stmt = $this->con->executeQuery("SELECT seed, updated_at FROM {$this->seedTableName}"); + + if (false === $seed = $stmt->fetchColumn(0)) { + throw new \RuntimeException('You need to initialize the generator by running the console command "init:prng".'); + } + + $seedLastUpdatedAt = new \DateTime($stmt->fetchColumn(1)); + + return array($seed, $seedLastUpdatedAt); + } + + /** + * {@inheritdoc} + */ + public function updateSeed($seed) + { + $params = array(':seed' => $seed, ':updatedAt' => new \DateTime()); + $types = array(':updatedAt' => Type::DATETIME); + if (!$this->con->executeUpdate("UPDATE {$this->seedTableName} SET seed = :seed, updated_at = :updatedAt", $params, $types)) { + $this->con->executeUpdate("INSERT INTO {$this->seedTableName} VALUES (:seed, :updatedAt)", $params, $types); + } + } +} diff --git a/src/Symfony/Bridge/Doctrine/Security/EventListener/PrngSchemaListener.php b/src/Symfony/Bridge/Doctrine/Security/EventListener/PrngSchemaListener.php new file mode 100644 index 000000000000..61d956e7119a --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Security/EventListener/PrngSchemaListener.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Security\EventListener; + +use Symfony\Bridge\Doctrine\Security\PrngSchema; +use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs; + +/** + * @author Johannes M. Schmitt + */ +class PrngSchemaListener +{ + private $schema; + + public function __construct(PrngSchema $schema) + { + $this->schema = $schema; + } + + public function postGenerateSchema(GenerateSchemaEventArgs $args) + { + $this->schema->addToSchema($args->getSchema()); + } +} diff --git a/src/Symfony/Bridge/Doctrine/Security/PrngSchema.php b/src/Symfony/Bridge/Doctrine/Security/PrngSchema.php new file mode 100644 index 000000000000..e4d3745f1e4f --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Security/PrngSchema.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Security; + +use Doctrine\DBAL\Schema\Schema; + +/** + * The DBAL schema that will be used if you choose the database-based seed provider. + * + * @author Johannes M. Schmitt + */ +final class PrngSchema extends Schema +{ + public function __construct($tableName) + { + parent::__construct(); + + $table = $this->createTable($tableName); + $table->addColumn('seed', 'string', array( + 'length' => 88, + 'not_null' => true, + )); + $table->addColumn('updated_at', 'datetime', array( + 'not_null' => true, + )); + } + + public function addToSchema(Schema $schema) + { + foreach ($this->getTables() as $table) { + $schema->_addTable($table); + } + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Security/DoctrineSeedProviderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Security/DoctrineSeedProviderTest.php new file mode 100644 index 000000000000..e4ad1cdc9911 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Security/DoctrineSeedProviderTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Security; + +use Symfony\Bridge\Doctrine\Security\DoctrineSeedProvider; +use Symfony\Bridge\Doctrine\Security\PrngSchema; +use Symfony\Component\Security\Core\Util\Prng; +use Symfony\Component\Security\Tests\Core\Util\PrngTest; +use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Connection; + +class DoctrineSeedProviderTest extends PrngTest +{ + public function getPrngs() + { + $con = DriverManager::getConnection(array( + 'driver' => 'pdo_sqlite', + 'memory' => true + )); + + $schema = new PrngSchema('seed_table'); + foreach ($schema->toSql($con->getDatabasePlatform()) as $sql) { + $con->executeQuery($sql); + } + $con->executeQuery("INSERT INTO seed_table VALUES (:seed, :updatedAt)", array( + ':seed' => base64_encode(hash('sha512', uniqid(mt_rand(), true), true)), + ':updatedAt' => date('Y-m-d H:i:s'), + )); + + // no-openssl with database seed provider + $prng = new Prng(new DoctrineSeedProvider($con, 'seed_table')); + $this->disableOpenSsl($prng); + + $prngs = parent::getPrngs(); + $prngs[] = array($prng); + + return $prngs; + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Command/InitPrngCommand.php b/src/Symfony/Bundle/SecurityBundle/Command/InitPrngCommand.php new file mode 100755 index 000000000000..6cff60d7b149 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Command/InitPrngCommand.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputArgument; + +/** + * Initializes a custom PRNG seed provider. + * + * @author Fabien Potencier + */ +class InitPrngCommand extends ContainerAwareCommand +{ + protected function configure() + { + $this + ->setName('init:prng'); + ->addArgument('phrase', InputArgument::REQUIRED, 'A random string'); + ->setDescription('Initialize a custom PRNG seed provider') + ->setHelp(<<%command.name% command initializes a custom PRNG seed provider: + +php %command.full_name% ABCDE... + +The argument should be a random string, whatever comes to your mind right now. +You do not need to remember it, it does not need to be cryptic, or long, and it +will not be stored in a decipherable way. One restriction however, you should +not let this be generated in an automated fashion. +EOF + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + if (!$this->getContainer()->has('security.prng_seed_provider')) { + throw new \RuntimeException('No seed provider has been configured under path "secure.prng".'); + } + + $this->getContainer()->get('security.prng_seed_provider')->updateSeed(base64_encode(hash('sha512', $input->getArgument('phrase'), true))); + + $output->writeln('The CSPRNG has been initialized successfully.'); + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php index 267e60482846..acdcec531ea9 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php @@ -87,6 +87,7 @@ private function addAclSection(ArrayNodeDefinition $rootNode) { $rootNode ->children() + ->scalarNode('prng_seed_provider')->defaultNull()->end() ->arrayNode('acl') ->children() ->scalarNode('connection') diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index fd8fe2355a3f..9749dad780bd 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -88,6 +88,10 @@ public function load(array $configs, ContainerBuilder $container) $this->aclLoad($config['acl'], $container); } + if (null !== $config['prng_seed_provider']) { + $container->setAlias('security.prng_seed_provider', $config['prng_seed_provider']); + } + // add some required classes for compilation $this->addClassesToCompile(array( 'Symfony\\Component\\Security\\Http\\Firewall', diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml index 6f9257a42a46..52aa35f7f333 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -138,5 +138,12 @@ + + + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml index 9beab1df0ea9..6ccbff51e92d 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml @@ -45,6 +45,7 @@ class="%security.authentication.rememberme.services.persistent.class%" parent="security.authentication.rememberme.services.abstract" abstract="true"> + loadFromExtension('security', array( 'acl' => array(), + 'prng_seed_provider' => 'custom_seed_provider', 'encoders' => array( 'JMS\FooBundle\Entity\User1' => 'plaintext', 'JMS\FooBundle\Entity\User2' => array( diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml index 7180503f3ce4..6e76bb400ace 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml @@ -5,7 +5,7 @@ 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"> - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml index bf05e049f7c8..c78a7b0f745b 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml @@ -1,5 +1,6 @@ security: acl: ~ + prng_seed_provider: custom_seed_provider encoders: JMS\FooBundle\Entity\User1: plaintext JMS\FooBundle\Entity\User2: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php index 1d486d60204a..cde9a8f452b1 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php @@ -168,6 +168,13 @@ public function testCustomAclProvider() $this->assertEquals('foo', (string) $container->getAlias('security.acl.provider')); } + public function testSeedProvider() + { + $container = $this->getContainer('container1'); + + $this->assertEquals('custom_seed_provider', (string) $container->getAlias('security.prng_seed_provider')); + } + protected function getContainer($file) { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/Security/CHANGELOG.md b/src/Symfony/Component/Security/CHANGELOG.md index b1c8192253e6..251666a2ca63 100644 --- a/src/Symfony/Component/Security/CHANGELOG.md +++ b/src/Symfony/Component/Security/CHANGELOG.md @@ -9,6 +9,7 @@ CHANGELOG 2.1.0 ----- + * added secure random number generator * [BC BREAK] The signature of ExceptionListener has changed * changed the HttpUtils constructor signature to take a UrlGenerator and a UrlMatcher instead of a Router * EncoderFactoryInterface::getEncoder() can now also take a class name as an argument diff --git a/src/Symfony/Component/Security/Core/Encoder/BasePasswordEncoder.php b/src/Symfony/Component/Security/Core/Encoder/BasePasswordEncoder.php index ae1c7d4c4d48..e73bbbdec923 100644 --- a/src/Symfony/Component/Security/Core/Encoder/BasePasswordEncoder.php +++ b/src/Symfony/Component/Security/Core/Encoder/BasePasswordEncoder.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Security\Core\Encoder; +use Symfony\Component\Security\Core\Util\String; + /** * BasePasswordEncoder is the base class for all password encoders. * @@ -77,15 +79,6 @@ protected function mergePasswordAndSalt($password, $salt) */ protected function comparePasswords($password1, $password2) { - if (strlen($password1) !== strlen($password2)) { - return false; - } - - $result = 0; - for ($i = 0; $i < strlen($password1); $i++) { - $result |= ord($password1[$i]) ^ ord($password2[$i]); - } - - return 0 === $result; + return String::equals($password1, $password2); } } diff --git a/src/Symfony/Component/Security/Core/Util/Prng.php b/src/Symfony/Component/Security/Core/Util/Prng.php new file mode 100644 index 000000000000..ab8baa7f1498 --- /dev/null +++ b/src/Symfony/Component/Security/Core/Util/Prng.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Util; + +use Symfony\Component\HttpKernel\Log\LoggerInterface; + +/** + * A secure random number generator implementation. + * + * @author Johannes M. Schmitt + */ +final class Prng +{ + private $logger; + private $useOpenSsl; + private $seed; + private $seedUpdated; + private $seedLastUpdatedAt; + private $seedProvider; + + /** + * Constructor. + * + * Be aware that a guessable seed will severely compromise the PRNG + * algorithm that is employed. + * + * @param SeedProviderInterface $provider + * @param LoggerInterface $logger + */ + public function __construct(SeedProviderInterface $provider = null, LoggerInterface $logger = null) + { + $this->seedProvider = $provider; + $this->logger = $logger; + + // determine whether to use OpenSSL + if (defined('PHP_WINDOWS_VERSION_BUILD') && version_compare(PHP_VERSION, '5.3.4', '<')) { + $this->useOpenSsl = false; + } elseif (!function_exists('openssl_random_pseudo_bytes')) { + if (null !== $this->logger) { + $this->logger->notice('It is recommended that you enable the "openssl" extension for random number generation.'); + } + $this->useOpenSsl = false; + } else { + $this->useOpenSsl = true; + } + } + + /** + * Generates the specified number of secure random bytes. + * + * @param integer $nbBytes + * @return string + */ + public function nextBytes($nbBytes) + { + // try OpenSSL + if ($this->useOpenSsl) { + $bytes = openssl_random_pseudo_bytes($nbBytes, $strong); + + if (false !== $bytes && true === $strong) { + return $bytes; + } + + if (null !== $this->logger) { + $this->logger->info('OpenSSL did not produce a secure random number.'); + } + } + + // initialize seed + if (null === $this->seed) { + if (null === $this->seedProvider) { + throw new \RuntimeException('You need to specify a custom seed provider.'); + } + + list($this->seed, $this->seedLastUpdatedAt) = $this->seedProvider->loadSeed(); + } + + $bytes = ''; + while (strlen($bytes) < $nbBytes) { + static $incr = 1; + $bytes .= hash('sha512', $incr++.$this->seed.uniqid(mt_rand(), true).$nbBytes, true); + $this->seed = base64_encode(hash('sha512', $this->seed.$bytes.$nbBytes, true)); + + if (!$this->seedUpdated && $this->seedLastUpdatedAt->getTimestamp() < time() - mt_rand(1, 10)) { + if (null !== $this->seedProvider) { + $this->seedProvider->updateSeed($this->seed); + } + + $this->seedUpdated = true; + } + } + + return substr($bytes, 0, $nbBytes); + } +} diff --git a/src/Symfony/Component/Security/Core/Util/SeedProviderInterface.php b/src/Symfony/Component/Security/Core/Util/SeedProviderInterface.php new file mode 100644 index 000000000000..dd960b94313c --- /dev/null +++ b/src/Symfony/Component/Security/Core/Util/SeedProviderInterface.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Util; + +/** + * Seed Provider Interface. + * + * @author Johannes M. Schmitt + */ +interface SeedProviderInterface +{ + /** + * Loads the initial seed. + * + * Whatever is returned from this method, it should not be guessable. + * + * @return array of the format array(string, DateTime) where string is the + * initial seed, and DateTime is the last time it was updated + */ + function loadSeed(); + + /** + * Updates the seed. + * + * @param string $seed + */ + function updateSeed($seed); +} diff --git a/src/Symfony/Component/Security/Core/Util/String.php b/src/Symfony/Component/Security/Core/Util/String.php new file mode 100644 index 000000000000..096878b255c3 --- /dev/null +++ b/src/Symfony/Component/Security/Core/Util/String.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Util; + +/** + * String utility functions. + * + * @author Fabien Potencier + */ +final class String +{ + private final function __construct() + { + } + + /** + * Compares two strings. + * + * This method implements a constant-time algorithm to compare strings. + * + * @param string $str1 The first string + * @param string $str2 The second string + * + * @return Boolean true if the two strings are the same, false otherwise + */ + public static function equals($str1, $str2) + { + if (strlen($str1) !== $c = strlen($str2)) { + return false; + } + + $result = 0; + for ($i = 0; $i < $c; $i++) { + $result |= ord($str1[$i]) ^ ord($str2[$i]); + } + + return 0 === $result; + } +} diff --git a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php index 8944672f29b0..d36eb0151a7d 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php @@ -19,6 +19,7 @@ use Symfony\Component\Security\Core\Exception\CookieTheftException; use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Util\Prng; /** * Concrete implementation of the RememberMeServicesInterface which needs @@ -30,6 +31,12 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices { private $tokenProvider; + private $prng; + + public function setPrng(Prng $prng) + { + $this->prng = $prng; + } /** * Sets the token provider @@ -79,7 +86,7 @@ protected function processAutoLoginCookie(array $cookieParts, Request $request) } $series = $persistentToken->getSeries(); - $tokenValue = $this->generateRandomValue(); + $tokenValue = $this->prng->nextBytes(64); $this->tokenProvider->updateToken($series, $tokenValue, new \DateTime()); $request->attributes->set(self::COOKIE_ATTR_NAME, new Cookie( @@ -101,8 +108,8 @@ protected function processAutoLoginCookie(array $cookieParts, Request $request) */ protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token) { - $series = $this->generateRandomValue(); - $tokenValue = $this->generateRandomValue(); + $series = $this->prng->nextBytes(64); + $tokenValue = $this->prng->nextBytes(64); $this->tokenProvider->createNewToken( new PersistentToken( @@ -126,26 +133,4 @@ protected function onLoginSuccess(Request $request, Response $response, TokenInt ) ); } - - /** - * Generates a cryptographically strong random value - * - * @return string - */ - protected function generateRandomValue() - { - if (function_exists('openssl_random_pseudo_bytes')) { - $bytes = openssl_random_pseudo_bytes(64, $strong); - - if (true === $strong && false !== $bytes) { - return base64_encode($bytes); - } - } - - if (null !== $this->logger) { - $this->logger->warn('Could not produce a cryptographically strong random value. Please install/update the OpenSSL extension.'); - } - - return base64_encode(hash('sha512', uniqid(mt_rand(), true), true)); - } } diff --git a/src/Symfony/Component/Security/Tests/Core/Util/PrngTest.php b/src/Symfony/Component/Security/Tests/Core/Util/PrngTest.php new file mode 100755 index 000000000000..7c9b2e2a3bd8 --- /dev/null +++ b/src/Symfony/Component/Security/Tests/Core/Util/PrngTest.php @@ -0,0 +1,179 @@ +getBitSequence($prng, 20000), '1'); + $this->assertTrue($nbOnBits > 9654 && $nbOnBits < 10346, 'Monobit test failed, number of turned on bits: '.$nbOnBits); + } + + /** + * T2: Chi-square test with 15 degrees of freedom (chi-Quadrat-Anpassungstest) + * + * @dataProvider getPrngs + */ + public function testPoker($prng) + { + $b = $this->getBitSequence($prng, 20000); + $c = array(); + for ($i=0;$i<=15;$i++) { + $c[$i] = 0; + } + + for ($j=1; $j<=5000; $j++) { + $k = 4 * $j - 1; + $c[8 * $b[$k - 3] + 4 * $b[$k - 2] + 2 * $b[$k - 1] + $b[$k]] += 1; + } + + $f = 0; + for ($i=0; $i<= 15; $i++) { + $f += $c[$i] * $c[$i]; + } + + $Y = 16/5000 * $f - 5000; + + $this->assertTrue($Y > 1.03 && $Y < 57.4, 'Poker test failed, Y = '.$Y); + } + + /** + * Run test + * + * @dataProvider getPrngs + */ + public function testRun($prng) + { + $b = $this->getBitSequence($prng, 20000); + + $runs = array(); + for ($i=1; $i<=6; $i++) { + $runs[$i] = 0; + } + + $addRun = function($run) use (&$runs) { + if ($run > 6) { + $run = 6; + } + + $runs[$run] += 1; + }; + + $currentRun = 0; + $lastBit = null; + for ($i=0; $i<20000; $i++) { + if ($lastBit === $b[$i]) { + $currentRun += 1; + } else { + if ($currentRun > 0) { + $addRun($currentRun); + } + + $lastBit = $b[$i]; + $currentRun = 0; + } + } + if ($currentRun > 0) { + $addRun($currentRun); + } + + $this->assertTrue($runs[1] > 2267 && $runs[1] < 2733, 'Runs of length 1 outside of defined interval: '.$runs[1]); + $this->assertTrue($runs[2] > 1079 && $runs[2] < 1421, 'Runs of length 2 outside of defined interval: '.$runs[2]); + $this->assertTrue($runs[3] > 502 && $runs[3] < 748, 'Runs of length 3 outside of defined interval: '.$runs[3]); + $this->assertTrue($runs[4] > 233 && $runs[4] < 402, 'Runs of length 4 outside of defined interval: '.$runs[4]); + $this->assertTrue($runs[5] > 90 && $runs[5] < 223, 'Runs of length 5 outside of defined interval: '.$runs[5]); + $this->assertTrue($runs[6] > 90 && $runs[6] < 233, 'Runs of length 6 outside of defined interval: '.$runs[6]); + } + + /** + * Long-run test + * + * @dataProvider getPrngs + */ + public function testLongRun($prng) + { + $b = $this->getBitSequence($prng, 20000); + + $longestRun = 0; + $currentRun = $lastBit = null; + for ($i=0;$i<20000;$i++) { + if ($lastBit === $b[$i]) { + $currentRun += 1; + } else { + if ($currentRun > $longestRun) { + $longestRun = $currentRun; + } + $lastBit = $b[$i]; + $currentRun = 0; + } + } + if ($currentRun > $longestRun) { + $longestRun = $currentRun; + } + + $this->assertTrue($longestRun < 34, 'Failed longest run test: '.$longestRun); + } + + /** + * Serial Correlation (Autokorrelationstest) + * + * @dataProvider getPrngs + */ + public function testSerialCorrelation($prng) + { + $shift = rand(1, 5000); + $b = $this->getBitSequence($prng, 20000); + + $Z = 0; + for ($i=0; $i<5000; $i++) { + $Z += $b[$i] === $b[$i+$shift] ? 1 : 0; + } + + $this->assertTrue($Z > 2326 && $Z < 2674, 'Failed serial correlation test: '.$Z); + } + + public function getPrngs() + { + $prngs = array(); + + // openssl with fallback + $prng = new Prng(new NullSeedProvider()); + $prngs[] = array($prng); + + // no-openssl with custom seed provider + $prng = new Prng(new NullSeedProvider()); + $this->disableOpenSsl($prng); + $prngs[] = array($prng); + + return $prngs; + } + + protected function disableOpenSsl($prng) + { + $ref = new \ReflectionProperty($prng, 'useOpenSsl'); + $ref->setAccessible(true); + $ref->setValue($prng, false); + } + + private function getBitSequence($prng, $length) + { + $bitSequence = ''; + for ($i=0;$i<$length; $i+=40) { + $value = unpack('H*', $prng->nextBytes(5)); + $value = str_pad(base_convert($value[1], 16, 2), 40, '0', STR_PAD_LEFT); + $bitSequence .= $value; + } + + return substr($bitSequence, 0, $length); + } +} diff --git a/src/Symfony/Component/Security/Tests/Core/Util/StringTest.php b/src/Symfony/Component/Security/Tests/Core/Util/StringTest.php new file mode 100755 index 000000000000..fe4eae4f7065 --- /dev/null +++ b/src/Symfony/Component/Security/Tests/Core/Util/StringTest.php @@ -0,0 +1,14 @@ +assertTrue(String::equals('password', 'password')); + $this->assertFalse(String::equals('password', 'foo')); + } +} diff --git a/src/Symfony/Component/Security/Tests/Http/RememberMe/PersistentTokenBasedRememberMeServicesTest.php b/src/Symfony/Component/Security/Tests/Http/RememberMe/PersistentTokenBasedRememberMeServicesTest.php index 3b3691d3c6b0..846ee9ba2937 100644 --- a/src/Symfony/Component/Security/Tests/Http/RememberMe/PersistentTokenBasedRememberMeServicesTest.php +++ b/src/Symfony/Component/Security/Tests/Http/RememberMe/PersistentTokenBasedRememberMeServicesTest.php @@ -22,6 +22,7 @@ use Symfony\Component\Security\Http\RememberMe\PersistentTokenBasedRememberMeServices; use Symfony\Component\Security\Core\Exception\TokenNotFoundException; use Symfony\Component\Security\Core\Exception\CookieTheftException; +use Symfony\Component\Security\Core\Util\Prng; class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase { @@ -318,7 +319,10 @@ protected function getService($userProvider = null, $options = array(), $logger $userProvider = $this->getProvider(); } - return new PersistentTokenBasedRememberMeServices(array($userProvider), 'fookey', 'fookey', $options, $logger); + $r = new PersistentTokenBasedRememberMeServices(array($userProvider), 'fookey', 'fookey', $options, $logger); + $r->setPrng(new Prng()); + + return $r; } protected function getProvider() From c0c89724b04632fa68e08e482fe7c29c28cc6a44 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 5 Jul 2012 17:51:07 +0200 Subject: [PATCH 2/8] simplified the Prng code --- .../Security/DoctrineSeedProvider.php | 68 ------------------- .../EventListener/PrngSchemaListener.php | 33 --------- .../Bridge/Doctrine/Security/PrngSchema.php | 43 ------------ .../Security/DoctrineSeedProviderTest.php | 48 ------------- .../Command/InitPrngCommand.php | 57 ---------------- .../DependencyInjection/MainConfiguration.php | 1 - .../DependencyInjection/SecurityExtension.php | 4 -- .../Resources/config/security.xml | 2 +- .../Fixtures/php/container1.php | 1 - .../Fixtures/xml/container1.xml | 2 +- .../Fixtures/yml/container1.yml | 1 - .../SecurityExtensionTest.php | 7 -- .../Component/Security/Core/Util/Prng.php | 43 ++++++++---- .../Core/Util/SeedProviderInterface.php | 37 ---------- .../Security/Tests/Core/Util/PrngTest.php | 4 +- 15 files changed, 32 insertions(+), 319 deletions(-) delete mode 100644 src/Symfony/Bridge/Doctrine/Security/DoctrineSeedProvider.php delete mode 100644 src/Symfony/Bridge/Doctrine/Security/EventListener/PrngSchemaListener.php delete mode 100644 src/Symfony/Bridge/Doctrine/Security/PrngSchema.php delete mode 100644 src/Symfony/Bridge/Doctrine/Tests/Security/DoctrineSeedProviderTest.php delete mode 100755 src/Symfony/Bundle/SecurityBundle/Command/InitPrngCommand.php delete mode 100644 src/Symfony/Component/Security/Core/Util/SeedProviderInterface.php diff --git a/src/Symfony/Bridge/Doctrine/Security/DoctrineSeedProvider.php b/src/Symfony/Bridge/Doctrine/Security/DoctrineSeedProvider.php deleted file mode 100644 index 80f131321c1a..000000000000 --- a/src/Symfony/Bridge/Doctrine/Security/DoctrineSeedProvider.php +++ /dev/null @@ -1,68 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\Doctrine\Security; - -use Doctrine\DBAL\Types\Type; -use Doctrine\DBAL\Connection; -use Symfony\Component\Security\Core\Util\SeedProviderInterface; - -/** - * Doctrine Seed Provider. - * - * @author Johannes M. Schmitt - * @author Fabien Potencier - */ -class DoctrineSeedProvider implements SeedProviderInterface -{ - private $con; - private $seedTableName; - - /** - * Constructor. - * - * @param Connection $con - * @param string $tableName - */ - public function __construct(Connection $con, $tableName) - { - $this->con = $con; - $this->seedTableName = $tableName; - } - - /** - * {@inheritdoc} - */ - public function loadSeed() - { - $stmt = $this->con->executeQuery("SELECT seed, updated_at FROM {$this->seedTableName}"); - - if (false === $seed = $stmt->fetchColumn(0)) { - throw new \RuntimeException('You need to initialize the generator by running the console command "init:prng".'); - } - - $seedLastUpdatedAt = new \DateTime($stmt->fetchColumn(1)); - - return array($seed, $seedLastUpdatedAt); - } - - /** - * {@inheritdoc} - */ - public function updateSeed($seed) - { - $params = array(':seed' => $seed, ':updatedAt' => new \DateTime()); - $types = array(':updatedAt' => Type::DATETIME); - if (!$this->con->executeUpdate("UPDATE {$this->seedTableName} SET seed = :seed, updated_at = :updatedAt", $params, $types)) { - $this->con->executeUpdate("INSERT INTO {$this->seedTableName} VALUES (:seed, :updatedAt)", $params, $types); - } - } -} diff --git a/src/Symfony/Bridge/Doctrine/Security/EventListener/PrngSchemaListener.php b/src/Symfony/Bridge/Doctrine/Security/EventListener/PrngSchemaListener.php deleted file mode 100644 index 61d956e7119a..000000000000 --- a/src/Symfony/Bridge/Doctrine/Security/EventListener/PrngSchemaListener.php +++ /dev/null @@ -1,33 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\Doctrine\Security\EventListener; - -use Symfony\Bridge\Doctrine\Security\PrngSchema; -use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs; - -/** - * @author Johannes M. Schmitt - */ -class PrngSchemaListener -{ - private $schema; - - public function __construct(PrngSchema $schema) - { - $this->schema = $schema; - } - - public function postGenerateSchema(GenerateSchemaEventArgs $args) - { - $this->schema->addToSchema($args->getSchema()); - } -} diff --git a/src/Symfony/Bridge/Doctrine/Security/PrngSchema.php b/src/Symfony/Bridge/Doctrine/Security/PrngSchema.php deleted file mode 100644 index e4d3745f1e4f..000000000000 --- a/src/Symfony/Bridge/Doctrine/Security/PrngSchema.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\Doctrine\Security; - -use Doctrine\DBAL\Schema\Schema; - -/** - * The DBAL schema that will be used if you choose the database-based seed provider. - * - * @author Johannes M. Schmitt - */ -final class PrngSchema extends Schema -{ - public function __construct($tableName) - { - parent::__construct(); - - $table = $this->createTable($tableName); - $table->addColumn('seed', 'string', array( - 'length' => 88, - 'not_null' => true, - )); - $table->addColumn('updated_at', 'datetime', array( - 'not_null' => true, - )); - } - - public function addToSchema(Schema $schema) - { - foreach ($this->getTables() as $table) { - $schema->_addTable($table); - } - } -} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Security/DoctrineSeedProviderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Security/DoctrineSeedProviderTest.php deleted file mode 100644 index e4ad1cdc9911..000000000000 --- a/src/Symfony/Bridge/Doctrine/Tests/Security/DoctrineSeedProviderTest.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\Doctrine\Tests\Security; - -use Symfony\Bridge\Doctrine\Security\DoctrineSeedProvider; -use Symfony\Bridge\Doctrine\Security\PrngSchema; -use Symfony\Component\Security\Core\Util\Prng; -use Symfony\Component\Security\Tests\Core\Util\PrngTest; -use Doctrine\DBAL\DriverManager; -use Doctrine\DBAL\Connection; - -class DoctrineSeedProviderTest extends PrngTest -{ - public function getPrngs() - { - $con = DriverManager::getConnection(array( - 'driver' => 'pdo_sqlite', - 'memory' => true - )); - - $schema = new PrngSchema('seed_table'); - foreach ($schema->toSql($con->getDatabasePlatform()) as $sql) { - $con->executeQuery($sql); - } - $con->executeQuery("INSERT INTO seed_table VALUES (:seed, :updatedAt)", array( - ':seed' => base64_encode(hash('sha512', uniqid(mt_rand(), true), true)), - ':updatedAt' => date('Y-m-d H:i:s'), - )); - - // no-openssl with database seed provider - $prng = new Prng(new DoctrineSeedProvider($con, 'seed_table')); - $this->disableOpenSsl($prng); - - $prngs = parent::getPrngs(); - $prngs[] = array($prng); - - return $prngs; - } -} diff --git a/src/Symfony/Bundle/SecurityBundle/Command/InitPrngCommand.php b/src/Symfony/Bundle/SecurityBundle/Command/InitPrngCommand.php deleted file mode 100755 index 6cff60d7b149..000000000000 --- a/src/Symfony/Bundle/SecurityBundle/Command/InitPrngCommand.php +++ /dev/null @@ -1,57 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\SecurityBundle\Command; - -use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputArgument; - -/** - * Initializes a custom PRNG seed provider. - * - * @author Fabien Potencier - */ -class InitPrngCommand extends ContainerAwareCommand -{ - protected function configure() - { - $this - ->setName('init:prng'); - ->addArgument('phrase', InputArgument::REQUIRED, 'A random string'); - ->setDescription('Initialize a custom PRNG seed provider') - ->setHelp(<<%command.name% command initializes a custom PRNG seed provider: - -php %command.full_name% ABCDE... - -The argument should be a random string, whatever comes to your mind right now. -You do not need to remember it, it does not need to be cryptic, or long, and it -will not be stored in a decipherable way. One restriction however, you should -not let this be generated in an automated fashion. -EOF - ) - ; - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - if (!$this->getContainer()->has('security.prng_seed_provider')) { - throw new \RuntimeException('No seed provider has been configured under path "secure.prng".'); - } - - $this->getContainer()->get('security.prng_seed_provider')->updateSeed(base64_encode(hash('sha512', $input->getArgument('phrase'), true))); - - $output->writeln('The CSPRNG has been initialized successfully.'); - } -} diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php index acdcec531ea9..267e60482846 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php @@ -87,7 +87,6 @@ private function addAclSection(ArrayNodeDefinition $rootNode) { $rootNode ->children() - ->scalarNode('prng_seed_provider')->defaultNull()->end() ->arrayNode('acl') ->children() ->scalarNode('connection') diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 9749dad780bd..fd8fe2355a3f 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -88,10 +88,6 @@ public function load(array $configs, ContainerBuilder $container) $this->aclLoad($config['acl'], $container); } - if (null !== $config['prng_seed_provider']) { - $container->setAlias('security.prng_seed_provider', $config['prng_seed_provider']); - } - // add some required classes for compilation $this->addClassesToCompile(array( 'Symfony\\Component\\Security\\Http\\Firewall', diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml index 52aa35f7f333..12f0ca827df6 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -142,7 +142,7 @@ - + %kernel.cache_dir%/prng.seed diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php index d3ea91c969c6..826b621bb4e0 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php @@ -2,7 +2,6 @@ $container->loadFromExtension('security', array( 'acl' => array(), - 'prng_seed_provider' => 'custom_seed_provider', 'encoders' => array( 'JMS\FooBundle\Entity\User1' => 'plaintext', 'JMS\FooBundle\Entity\User2' => array( diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml index 6e76bb400ace..7180503f3ce4 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml @@ -5,7 +5,7 @@ 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"> - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml index c78a7b0f745b..bf05e049f7c8 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml @@ -1,6 +1,5 @@ security: acl: ~ - prng_seed_provider: custom_seed_provider encoders: JMS\FooBundle\Entity\User1: plaintext JMS\FooBundle\Entity\User2: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php index cde9a8f452b1..1d486d60204a 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php @@ -168,13 +168,6 @@ public function testCustomAclProvider() $this->assertEquals('foo', (string) $container->getAlias('security.acl.provider')); } - public function testSeedProvider() - { - $container = $this->getContainer('container1'); - - $this->assertEquals('custom_seed_provider', (string) $container->getAlias('security.prng_seed_provider')); - } - protected function getContainer($file) { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/Security/Core/Util/Prng.php b/src/Symfony/Component/Security/Core/Util/Prng.php index ab8baa7f1498..f3a0b2430a9d 100644 --- a/src/Symfony/Component/Security/Core/Util/Prng.php +++ b/src/Symfony/Component/Security/Core/Util/Prng.php @@ -16,6 +16,7 @@ /** * A secure random number generator implementation. * + * @author Fabien Potencier * @author Johannes M. Schmitt */ final class Prng @@ -25,7 +26,7 @@ final class Prng private $seed; private $seedUpdated; private $seedLastUpdatedAt; - private $seedProvider; + private $seedFile; /** * Constructor. @@ -33,12 +34,12 @@ final class Prng * Be aware that a guessable seed will severely compromise the PRNG * algorithm that is employed. * - * @param SeedProviderInterface $provider - * @param LoggerInterface $logger + * @param string $seedFile + * @param LoggerInterface $logger */ - public function __construct(SeedProviderInterface $provider = null, LoggerInterface $logger = null) + public function __construct($seedFile = null, LoggerInterface $logger = null) { - $this->seedProvider = $provider; + $this->seedFile = $seedFile; $this->logger = $logger; // determine whether to use OpenSSL @@ -77,11 +78,16 @@ public function nextBytes($nbBytes) // initialize seed if (null === $this->seed) { - if (null === $this->seedProvider) { - throw new \RuntimeException('You need to specify a custom seed provider.'); + if (null === $this->seedFile) { + throw new \RuntimeException('You need to specify a file path to store the seed.'); } - list($this->seed, $this->seedLastUpdatedAt) = $this->seedProvider->loadSeed(); + if (is_file($this->seedFile)) { + list($this->seed, $this->seedLastUpdatedAt) = $this->readSeed(); + } else { + $this->seed = uniqid(mt_rand(), true); + $this->updateSeed(); + } } $bytes = ''; @@ -89,16 +95,23 @@ public function nextBytes($nbBytes) static $incr = 1; $bytes .= hash('sha512', $incr++.$this->seed.uniqid(mt_rand(), true).$nbBytes, true); $this->seed = base64_encode(hash('sha512', $this->seed.$bytes.$nbBytes, true)); + $this->updateSeed(); + } - if (!$this->seedUpdated && $this->seedLastUpdatedAt->getTimestamp() < time() - mt_rand(1, 10)) { - if (null !== $this->seedProvider) { - $this->seedProvider->updateSeed($this->seed); - } + return substr($bytes, 0, $nbBytes); + } - $this->seedUpdated = true; - } + private function readSeed() + { + return json_decode(file_get_contents($this->seedFile)); + } + + private function updateSeed() + { + if (!$this->seedUpdated && $this->seedLastUpdatedAt < time() - mt_rand(1, 10)) { + file_put_contents($this->seedFile, json_encode(array($this->seed, microtime(true)))); } - return substr($bytes, 0, $nbBytes); + $this->seedUpdated = true; } } diff --git a/src/Symfony/Component/Security/Core/Util/SeedProviderInterface.php b/src/Symfony/Component/Security/Core/Util/SeedProviderInterface.php deleted file mode 100644 index dd960b94313c..000000000000 --- a/src/Symfony/Component/Security/Core/Util/SeedProviderInterface.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\Util; - -/** - * Seed Provider Interface. - * - * @author Johannes M. Schmitt - */ -interface SeedProviderInterface -{ - /** - * Loads the initial seed. - * - * Whatever is returned from this method, it should not be guessable. - * - * @return array of the format array(string, DateTime) where string is the - * initial seed, and DateTime is the last time it was updated - */ - function loadSeed(); - - /** - * Updates the seed. - * - * @param string $seed - */ - function updateSeed($seed); -} diff --git a/src/Symfony/Component/Security/Tests/Core/Util/PrngTest.php b/src/Symfony/Component/Security/Tests/Core/Util/PrngTest.php index 7c9b2e2a3bd8..23afd30f8ec3 100755 --- a/src/Symfony/Component/Security/Tests/Core/Util/PrngTest.php +++ b/src/Symfony/Component/Security/Tests/Core/Util/PrngTest.php @@ -147,11 +147,11 @@ public function getPrngs() $prngs = array(); // openssl with fallback - $prng = new Prng(new NullSeedProvider()); + $prng = new Prng(); $prngs[] = array($prng); // no-openssl with custom seed provider - $prng = new Prng(new NullSeedProvider()); + $prng = new Prng(sys_get_temp_dir().'/_sf2.seed'); $this->disableOpenSsl($prng); $prngs[] = array($prng); From 248703f6d8a10883f3912f15b89d3e64d6e42048 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 27 Oct 2012 09:05:47 +0200 Subject: [PATCH 3/8] renamed Prng to SecureRandom --- .../Resources/config/security.xml | 4 +- .../Resources/config/security_rememberme.xml | 2 +- src/Symfony/Component/Security/CHANGELOG.md | 4 +- .../Core/Util/{Prng.php => SecureRandom.php} | 2 +- ...PersistentTokenBasedRememberMeServices.php | 14 ++--- .../{PrngTest.php => SecureRandomTest.php} | 61 +++++++++---------- ...istentTokenBasedRememberMeServicesTest.php | 4 +- 7 files changed, 45 insertions(+), 46 deletions(-) rename src/Symfony/Component/Security/Core/Util/{Prng.php => SecureRandom.php} (99%) rename src/Symfony/Component/Security/Tests/Core/Util/{PrngTest.php => SecureRandomTest.php} (71%) diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml index 12f0ca827df6..536b40cca67b 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -140,9 +140,9 @@ - + - %kernel.cache_dir%/prng.seed + %kernel.cache_dir%/secure_random.seed diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml index 6ccbff51e92d..c85993d04fcb 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml @@ -45,7 +45,7 @@ class="%security.authentication.rememberme.services.persistent.class%" parent="security.authentication.rememberme.services.abstract" abstract="true"> - + * @author Johannes M. Schmitt */ -final class Prng +final class SecureRandom { private $logger; private $useOpenSsl; diff --git a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php index d36eb0151a7d..456d8be72d84 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php @@ -19,7 +19,7 @@ use Symfony\Component\Security\Core\Exception\CookieTheftException; use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Util\Prng; +use Symfony\Component\Security\Core\Util\SecureRandom; /** * Concrete implementation of the RememberMeServicesInterface which needs @@ -31,11 +31,11 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices { private $tokenProvider; - private $prng; + private $secureRandom; - public function setPrng(Prng $prng) + public function setSecureRandom(SecureRandom $secureRandom) { - $this->prng = $prng; + $this->secureRandom = $secureRandom; } /** @@ -86,7 +86,7 @@ protected function processAutoLoginCookie(array $cookieParts, Request $request) } $series = $persistentToken->getSeries(); - $tokenValue = $this->prng->nextBytes(64); + $tokenValue = $this->secureRandom->nextBytes(64); $this->tokenProvider->updateToken($series, $tokenValue, new \DateTime()); $request->attributes->set(self::COOKIE_ATTR_NAME, new Cookie( @@ -108,8 +108,8 @@ protected function processAutoLoginCookie(array $cookieParts, Request $request) */ protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token) { - $series = $this->prng->nextBytes(64); - $tokenValue = $this->prng->nextBytes(64); + $series = $this->secureRandom->nextBytes(64); + $tokenValue = $this->secureRandom->nextBytes(64); $this->tokenProvider->createNewToken( new PersistentToken( diff --git a/src/Symfony/Component/Security/Tests/Core/Util/PrngTest.php b/src/Symfony/Component/Security/Tests/Core/Util/SecureRandomTest.php similarity index 71% rename from src/Symfony/Component/Security/Tests/Core/Util/PrngTest.php rename to src/Symfony/Component/Security/Tests/Core/Util/SecureRandomTest.php index 23afd30f8ec3..f3662728d94a 100755 --- a/src/Symfony/Component/Security/Tests/Core/Util/PrngTest.php +++ b/src/Symfony/Component/Security/Tests/Core/Util/SecureRandomTest.php @@ -3,30 +3,29 @@ namespace Symfony\Component\Security\Tests\Core\Util; use Symfony\Component\Security\Core\Util\NullSeedProvider; -use Symfony\Component\Security\Core\Util\PrngSchema; -use Symfony\Component\Security\Core\Util\Prng; +use Symfony\Component\Security\Core\Util\SecureRandom; -class PrngTest extends \PHPUnit_Framework_TestCase +class SecureRandomTest extends \PHPUnit_Framework_TestCase { /** * T1: Monobit test * - * @dataProvider getPrngs + * @dataProvider getSecureRandoms */ - public function testMonobit($prng) + public function testMonobit($secureRandom) { - $nbOnBits = substr_count($this->getBitSequence($prng, 20000), '1'); + $nbOnBits = substr_count($this->getBitSequence($secureRandom, 20000), '1'); $this->assertTrue($nbOnBits > 9654 && $nbOnBits < 10346, 'Monobit test failed, number of turned on bits: '.$nbOnBits); } /** * T2: Chi-square test with 15 degrees of freedom (chi-Quadrat-Anpassungstest) * - * @dataProvider getPrngs + * @dataProvider getSecureRandoms */ - public function testPoker($prng) + public function testPoker($secureRandom) { - $b = $this->getBitSequence($prng, 20000); + $b = $this->getBitSequence($secureRandom, 20000); $c = array(); for ($i=0;$i<=15;$i++) { $c[$i] = 0; @@ -50,11 +49,11 @@ public function testPoker($prng) /** * Run test * - * @dataProvider getPrngs + * @dataProvider getSecureRandoms */ - public function testRun($prng) + public function testRun($secureRandom) { - $b = $this->getBitSequence($prng, 20000); + $b = $this->getBitSequence($secureRandom, 20000); $runs = array(); for ($i=1; $i<=6; $i++) { @@ -98,11 +97,11 @@ public function testRun($prng) /** * Long-run test * - * @dataProvider getPrngs + * @dataProvider getSecureRandoms */ - public function testLongRun($prng) + public function testLongRun($secureRandom) { - $b = $this->getBitSequence($prng, 20000); + $b = $this->getBitSequence($secureRandom, 20000); $longestRun = 0; $currentRun = $lastBit = null; @@ -127,12 +126,12 @@ public function testLongRun($prng) /** * Serial Correlation (Autokorrelationstest) * - * @dataProvider getPrngs + * @dataProvider getSecureRandoms */ - public function testSerialCorrelation($prng) + public function testSerialCorrelation($secureRandom) { $shift = rand(1, 5000); - $b = $this->getBitSequence($prng, 20000); + $b = $this->getBitSequence($secureRandom, 20000); $Z = 0; for ($i=0; $i<5000; $i++) { @@ -142,34 +141,34 @@ public function testSerialCorrelation($prng) $this->assertTrue($Z > 2326 && $Z < 2674, 'Failed serial correlation test: '.$Z); } - public function getPrngs() + public function getSecureRandoms() { - $prngs = array(); + $secureRandoms = array(); // openssl with fallback - $prng = new Prng(); - $prngs[] = array($prng); + $secureRandom = new SecureRandom(); + $secureRandoms[] = array($secureRandom); // no-openssl with custom seed provider - $prng = new Prng(sys_get_temp_dir().'/_sf2.seed'); - $this->disableOpenSsl($prng); - $prngs[] = array($prng); + $secureRandom = new SecureRandom(sys_get_temp_dir().'/_sf2.seed'); + $this->disableOpenSsl($secureRandom); + $secureRandoms[] = array($secureRandom); - return $prngs; + return $secureRandoms; } - protected function disableOpenSsl($prng) + protected function disableOpenSsl($secureRandom) { - $ref = new \ReflectionProperty($prng, 'useOpenSsl'); + $ref = new \ReflectionProperty($secureRandom, 'useOpenSsl'); $ref->setAccessible(true); - $ref->setValue($prng, false); + $ref->setValue($secureRandom, false); } - private function getBitSequence($prng, $length) + private function getBitSequence($secureRandom, $length) { $bitSequence = ''; for ($i=0;$i<$length; $i+=40) { - $value = unpack('H*', $prng->nextBytes(5)); + $value = unpack('H*', $secureRandom->nextBytes(5)); $value = str_pad(base_convert($value[1], 16, 2), 40, '0', STR_PAD_LEFT); $bitSequence .= $value; } diff --git a/src/Symfony/Component/Security/Tests/Http/RememberMe/PersistentTokenBasedRememberMeServicesTest.php b/src/Symfony/Component/Security/Tests/Http/RememberMe/PersistentTokenBasedRememberMeServicesTest.php index 846ee9ba2937..24f15703d58d 100644 --- a/src/Symfony/Component/Security/Tests/Http/RememberMe/PersistentTokenBasedRememberMeServicesTest.php +++ b/src/Symfony/Component/Security/Tests/Http/RememberMe/PersistentTokenBasedRememberMeServicesTest.php @@ -22,7 +22,7 @@ use Symfony\Component\Security\Http\RememberMe\PersistentTokenBasedRememberMeServices; use Symfony\Component\Security\Core\Exception\TokenNotFoundException; use Symfony\Component\Security\Core\Exception\CookieTheftException; -use Symfony\Component\Security\Core\Util\Prng; +use Symfony\Component\Security\Core\Util\SecureRandom; class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase { @@ -320,7 +320,7 @@ protected function getService($userProvider = null, $options = array(), $logger } $r = new PersistentTokenBasedRememberMeServices(array($userProvider), 'fookey', 'fookey', $options, $logger); - $r->setPrng(new Prng()); + $r->setSecureRandom(new SecureRandom()); return $r; } From 5849855eb9b33bd74231f48e7ee1e2b9eaa26588 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 27 Oct 2012 10:25:20 +0200 Subject: [PATCH 4/8] moved the secure random dep for remember me as a constructor argument --- .../Resources/config/security_rememberme.xml | 7 ++++++- .../PersistentTokenBasedRememberMeServices.php | 14 +++++++++++++- .../PersistentTokenBasedRememberMeServicesTest.php | 5 +---- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml index c85993d04fcb..e508a743c51e 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml @@ -45,7 +45,12 @@ class="%security.authentication.rememberme.services.persistent.class%" parent="security.authentication.rememberme.services.abstract" abstract="true"> - + + + + + + secureRandom = $secureRandom; } diff --git a/src/Symfony/Component/Security/Tests/Http/RememberMe/PersistentTokenBasedRememberMeServicesTest.php b/src/Symfony/Component/Security/Tests/Http/RememberMe/PersistentTokenBasedRememberMeServicesTest.php index 24f15703d58d..3cd1528f5546 100644 --- a/src/Symfony/Component/Security/Tests/Http/RememberMe/PersistentTokenBasedRememberMeServicesTest.php +++ b/src/Symfony/Component/Security/Tests/Http/RememberMe/PersistentTokenBasedRememberMeServicesTest.php @@ -319,10 +319,7 @@ protected function getService($userProvider = null, $options = array(), $logger $userProvider = $this->getProvider(); } - $r = new PersistentTokenBasedRememberMeServices(array($userProvider), 'fookey', 'fookey', $options, $logger); - $r->setSecureRandom(new SecureRandom()); - - return $r; + return new PersistentTokenBasedRememberMeServices(array($userProvider), 'fookey', 'fookey', $options, $logger, new SecureRandom()); } protected function getProvider() From 234f7255bb1c07847d2ae868b762f95a19236063 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 27 Oct 2012 10:27:26 +0200 Subject: [PATCH 5/8] rename String to StringUtils --- .../Component/Security/Core/Encoder/BasePasswordEncoder.php | 4 ++-- .../Security/Core/Util/{String.php => StringUtils.php} | 2 +- .../Component/Security/Tests/Core/Util/StringTest.php | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) rename src/Symfony/Component/Security/Core/Util/{String.php => StringUtils.php} (97%) diff --git a/src/Symfony/Component/Security/Core/Encoder/BasePasswordEncoder.php b/src/Symfony/Component/Security/Core/Encoder/BasePasswordEncoder.php index e73bbbdec923..1ef134b74e16 100644 --- a/src/Symfony/Component/Security/Core/Encoder/BasePasswordEncoder.php +++ b/src/Symfony/Component/Security/Core/Encoder/BasePasswordEncoder.php @@ -11,7 +11,7 @@ namespace Symfony\Component\Security\Core\Encoder; -use Symfony\Component\Security\Core\Util\String; +use Symfony\Component\Security\Core\Util\StringUtils; /** * BasePasswordEncoder is the base class for all password encoders. @@ -79,6 +79,6 @@ protected function mergePasswordAndSalt($password, $salt) */ protected function comparePasswords($password1, $password2) { - return String::equals($password1, $password2); + return StringUtils::equals($password1, $password2); } } diff --git a/src/Symfony/Component/Security/Core/Util/String.php b/src/Symfony/Component/Security/Core/Util/StringUtils.php similarity index 97% rename from src/Symfony/Component/Security/Core/Util/String.php rename to src/Symfony/Component/Security/Core/Util/StringUtils.php index 096878b255c3..adc6fe967f6e 100644 --- a/src/Symfony/Component/Security/Core/Util/String.php +++ b/src/Symfony/Component/Security/Core/Util/StringUtils.php @@ -16,7 +16,7 @@ * * @author Fabien Potencier */ -final class String +final class StringUtils { private final function __construct() { diff --git a/src/Symfony/Component/Security/Tests/Core/Util/StringTest.php b/src/Symfony/Component/Security/Tests/Core/Util/StringTest.php index fe4eae4f7065..e5f8160d3f1e 100755 --- a/src/Symfony/Component/Security/Tests/Core/Util/StringTest.php +++ b/src/Symfony/Component/Security/Tests/Core/Util/StringTest.php @@ -2,13 +2,13 @@ namespace Symfony\Component\Security\Tests\Core\Util; -use Symfony\Component\Security\Core\Util\String; +use Symfony\Component\Security\Core\Util\StringUtils; class StringTest extends \PHPUnit_Framework_TestCase { public function testEquals() { - $this->assertTrue(String::equals('password', 'password')); - $this->assertFalse(String::equals('password', 'foo')); + $this->assertTrue(StringUtils::equals('password', 'password')); + $this->assertFalse(StringUtils::equals('password', 'foo')); } } From 5cdf696bde7e60b5ac0c1203a7bf6a004fb7b2e0 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 27 Oct 2012 10:45:19 +0200 Subject: [PATCH 6/8] added a SecureRandomInterface --- .../Security/Core/Util/SecureRandom.php | 7 ++--- .../Core/Util/SecureRandomInterface.php | 31 +++++++++++++++++++ ...PersistentTokenBasedRememberMeServices.php | 4 +-- 3 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 src/Symfony/Component/Security/Core/Util/SecureRandomInterface.php diff --git a/src/Symfony/Component/Security/Core/Util/SecureRandom.php b/src/Symfony/Component/Security/Core/Util/SecureRandom.php index 14a1f3d06972..77f1d8c26518 100644 --- a/src/Symfony/Component/Security/Core/Util/SecureRandom.php +++ b/src/Symfony/Component/Security/Core/Util/SecureRandom.php @@ -19,7 +19,7 @@ * @author Fabien Potencier * @author Johannes M. Schmitt */ -final class SecureRandom +final class SecureRandom implements SecureRandomInterface { private $logger; private $useOpenSsl; @@ -56,10 +56,7 @@ public function __construct($seedFile = null, LoggerInterface $logger = null) } /** - * Generates the specified number of secure random bytes. - * - * @param integer $nbBytes - * @return string + * {@inheritdoc} */ public function nextBytes($nbBytes) { diff --git a/src/Symfony/Component/Security/Core/Util/SecureRandomInterface.php b/src/Symfony/Component/Security/Core/Util/SecureRandomInterface.php new file mode 100644 index 000000000000..f2d81750463a --- /dev/null +++ b/src/Symfony/Component/Security/Core/Util/SecureRandomInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Util; + +use Symfony\Component\HttpKernel\Log\LoggerInterface; + +/** + * A secure random number generator implementation. + * + * @author Fabien Potencier + */ +interface SecureRandomInterface +{ + /** + * Generates the specified number of secure random bytes. + * + * @param integer $nbBytes + * + * @return string + */ + public function nextBytes($nbBytes); +} diff --git a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php index 65266b580709..de46017184b3 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php @@ -19,7 +19,7 @@ use Symfony\Component\Security\Core\Exception\CookieTheftException; use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Util\SecureRandom; +use Symfony\Component\Security\Core\Util\SecureRandomInterface; /** * Concrete implementation of the RememberMeServicesInterface which needs @@ -43,7 +43,7 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices * @param array $options * @param LoggerInterface $logger */ - public function __construct(array $userProviders, $key, $providerKey, array $options = array(), LoggerInterface $logger = null, SecureRandom $secureRandom) + public function __construct(array $userProviders, $key, $providerKey, array $options = array(), LoggerInterface $logger = null, SecureRandomInterface $secureRandom) { parent::__construct($userProviders, $key, $providerKey, $options, $logger); From ca567b51098bbeae32946811feeb25574e34686f Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 27 Oct 2012 12:50:37 +0200 Subject: [PATCH 7/8] fixed CS --- .../Core/Util/SecureRandomInterface.php | 2 +- .../Security/Core/Util/StringUtils.php | 2 +- .../PersistentTokenBasedRememberMeServices.php | 14 +++++++------- .../Tests/Core/Util/SecureRandomTest.php | 18 +++++++++--------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Symfony/Component/Security/Core/Util/SecureRandomInterface.php b/src/Symfony/Component/Security/Core/Util/SecureRandomInterface.php index f2d81750463a..64830a9d1f2d 100644 --- a/src/Symfony/Component/Security/Core/Util/SecureRandomInterface.php +++ b/src/Symfony/Component/Security/Core/Util/SecureRandomInterface.php @@ -14,7 +14,7 @@ use Symfony\Component\HttpKernel\Log\LoggerInterface; /** - * A secure random number generator implementation. + * Interface that needs to be implemented by all secure random number generators. * * @author Fabien Potencier */ diff --git a/src/Symfony/Component/Security/Core/Util/StringUtils.php b/src/Symfony/Component/Security/Core/Util/StringUtils.php index adc6fe967f6e..a73b4715b3f4 100644 --- a/src/Symfony/Component/Security/Core/Util/StringUtils.php +++ b/src/Symfony/Component/Security/Core/Util/StringUtils.php @@ -18,7 +18,7 @@ */ final class StringUtils { - private final function __construct() + final private function __construct() { } diff --git a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php index de46017184b3..dbb64299112e 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php @@ -34,14 +34,14 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices private $secureRandom; /** - * Constructor + * Constructor. * - * @param array $userProviders - * @param string $key - * @param string $providerKey - * @param SecureRandom $secureRandom - * @param array $options - * @param LoggerInterface $logger + * @param array $userProviders + * @param string $key + * @param string $providerKey + * @param array $options + * @param LoggerInterface $logger + * @param SecureRandomInterface $secureRandom */ public function __construct(array $userProviders, $key, $providerKey, array $options = array(), LoggerInterface $logger = null, SecureRandomInterface $secureRandom) { diff --git a/src/Symfony/Component/Security/Tests/Core/Util/SecureRandomTest.php b/src/Symfony/Component/Security/Tests/Core/Util/SecureRandomTest.php index f3662728d94a..fae7806b28e6 100755 --- a/src/Symfony/Component/Security/Tests/Core/Util/SecureRandomTest.php +++ b/src/Symfony/Component/Security/Tests/Core/Util/SecureRandomTest.php @@ -27,17 +27,17 @@ public function testPoker($secureRandom) { $b = $this->getBitSequence($secureRandom, 20000); $c = array(); - for ($i=0;$i<=15;$i++) { + for ($i = 0; $i <= 15; $i++) { $c[$i] = 0; } - for ($j=1; $j<=5000; $j++) { + for ($j = 1; $j <= 5000; $j++) { $k = 4 * $j - 1; $c[8 * $b[$k - 3] + 4 * $b[$k - 2] + 2 * $b[$k - 1] + $b[$k]] += 1; } $f = 0; - for ($i=0; $i<= 15; $i++) { + for ($i = 0; $i <= 15; $i++) { $f += $c[$i] * $c[$i]; } @@ -56,7 +56,7 @@ public function testRun($secureRandom) $b = $this->getBitSequence($secureRandom, 20000); $runs = array(); - for ($i=1; $i<=6; $i++) { + for ($i = 1; $i <= 6; $i++) { $runs[$i] = 0; } @@ -70,7 +70,7 @@ public function testRun($secureRandom) $currentRun = 0; $lastBit = null; - for ($i=0; $i<20000; $i++) { + for ($i = 0; $i < 20000; $i++) { if ($lastBit === $b[$i]) { $currentRun += 1; } else { @@ -105,7 +105,7 @@ public function testLongRun($secureRandom) $longestRun = 0; $currentRun = $lastBit = null; - for ($i=0;$i<20000;$i++) { + for ($i = 0; $i < 20000; $i++) { if ($lastBit === $b[$i]) { $currentRun += 1; } else { @@ -134,8 +134,8 @@ public function testSerialCorrelation($secureRandom) $b = $this->getBitSequence($secureRandom, 20000); $Z = 0; - for ($i=0; $i<5000; $i++) { - $Z += $b[$i] === $b[$i+$shift] ? 1 : 0; + for ($i = 0; $i < 5000; $i++) { + $Z += $b[$i] === $b[$i + $shift] ? 1 : 0; } $this->assertTrue($Z > 2326 && $Z < 2674, 'Failed serial correlation test: '.$Z); @@ -167,7 +167,7 @@ protected function disableOpenSsl($secureRandom) private function getBitSequence($secureRandom, $length) { $bitSequence = ''; - for ($i=0;$i<$length; $i+=40) { + for ($i = 0; $i < $length; $i += 40) { $value = unpack('H*', $secureRandom->nextBytes(5)); $value = str_pad(base_convert($value[1], 16, 2), 40, '0', STR_PAD_LEFT); $bitSequence .= $value; From aecc9b12a388026aedbdfa933d0eb14162952ea9 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 28 Oct 2012 07:37:44 +0100 Subject: [PATCH 8/8] fixed tests when OpenSsl is not enabled in PHP, renamed a missnamed test, added missing license doc blocks --- .../Tests/Core/Util/ClassUtilsTest.php | 9 ++++++ .../Tests/Core/Util/SecureRandomTest.php | 31 +++++++++++++++++-- .../{StringTest.php => StringUtilsTest.php} | 11 ++++++- 3 files changed, 48 insertions(+), 3 deletions(-) rename src/Symfony/Component/Security/Tests/Core/Util/{StringTest.php => StringUtilsTest.php} (51%) diff --git a/src/Symfony/Component/Security/Tests/Core/Util/ClassUtilsTest.php b/src/Symfony/Component/Security/Tests/Core/Util/ClassUtilsTest.php index 16378a646b22..edfd779a8eac 100644 --- a/src/Symfony/Component/Security/Tests/Core/Util/ClassUtilsTest.php +++ b/src/Symfony/Component/Security/Tests/Core/Util/ClassUtilsTest.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Symfony\Component\Security\Tests\Core\Util { use Symfony\Component\Security\Core\Util\ClassUtils; diff --git a/src/Symfony/Component/Security/Tests/Core/Util/SecureRandomTest.php b/src/Symfony/Component/Security/Tests/Core/Util/SecureRandomTest.php index fae7806b28e6..1a4bdbd8b9f2 100755 --- a/src/Symfony/Component/Security/Tests/Core/Util/SecureRandomTest.php +++ b/src/Symfony/Component/Security/Tests/Core/Util/SecureRandomTest.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Symfony\Component\Security\Tests\Core\Util; use Symfony\Component\Security\Core\Util\NullSeedProvider; @@ -145,9 +154,14 @@ public function getSecureRandoms() { $secureRandoms = array(); - // openssl with fallback + // openssl $secureRandom = new SecureRandom(); - $secureRandoms[] = array($secureRandom); + // only add if openssl is indeed present + if ($this->hasOpenSsl($secureRandom)) { + $secureRandoms[] = array($secureRandom); + } else { + $this->markTestSkipped('OpenSSL is not available'); + } // no-openssl with custom seed provider $secureRandom = new SecureRandom(sys_get_temp_dir().'/_sf2.seed'); @@ -162,6 +176,19 @@ protected function disableOpenSsl($secureRandom) $ref = new \ReflectionProperty($secureRandom, 'useOpenSsl'); $ref->setAccessible(true); $ref->setValue($secureRandom, false); + $ref->setAccessible(false); + } + + protected function hasOpenSsl($secureRandom) + { + $ref = new \ReflectionProperty($secureRandom, 'useOpenSsl'); + $ref->setAccessible(true); + + $ret = $ref->getValue($secureRandom); + + $ref->setAccessible(false); + + return $ret; } private function getBitSequence($secureRandom, $length) diff --git a/src/Symfony/Component/Security/Tests/Core/Util/StringTest.php b/src/Symfony/Component/Security/Tests/Core/Util/StringUtilsTest.php similarity index 51% rename from src/Symfony/Component/Security/Tests/Core/Util/StringTest.php rename to src/Symfony/Component/Security/Tests/Core/Util/StringUtilsTest.php index e5f8160d3f1e..aac4139254a4 100755 --- a/src/Symfony/Component/Security/Tests/Core/Util/StringTest.php +++ b/src/Symfony/Component/Security/Tests/Core/Util/StringUtilsTest.php @@ -1,10 +1,19 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Symfony\Component\Security\Tests\Core\Util; use Symfony\Component\Security\Core\Util\StringUtils; -class StringTest extends \PHPUnit_Framework_TestCase +class StringUtilsTest extends \PHPUnit_Framework_TestCase { public function testEquals() {