diff options
author | Fabien Potencier <fabien.potencier@gmail.com> | 2012-07-05 12:19:25 +0200 |
---|---|---|
committer | Fabien Potencier <fabien.potencier@gmail.com> | 2012-10-28 08:03:00 +0100 |
commit | 255196983ec0c1dc944057816fbba25b9ff8276c (patch) | |
tree | 9abc7b351b5a5dc0adcbde72f6ad645a652e04f1 /Core/Util | |
parent | e3d359180c41a80803e06a5d277b3b319952c8ee (diff) | |
download | symfony-security-255196983ec0c1dc944057816fbba25b9ff8276c.zip symfony-security-255196983ec0c1dc944057816fbba25b9ff8276c.tar.gz symfony-security-255196983ec0c1dc944057816fbba25b9ff8276c.tar.bz2 |
moved the secure random class from JMSSecurityExtraBundle to Symfony (closes #3595)
Diffstat (limited to 'Core/Util')
-rw-r--r-- | Core/Util/Prng.php | 104 | ||||
-rw-r--r-- | Core/Util/SeedProviderInterface.php | 37 | ||||
-rw-r--r-- | Core/Util/String.php | 48 |
3 files changed, 189 insertions, 0 deletions
diff --git a/Core/Util/Prng.php b/Core/Util/Prng.php new file mode 100644 index 0000000..ab8baa7 --- /dev/null +++ b/Core/Util/Prng.php @@ -0,0 +1,104 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Util; + +use Symfony\Component\HttpKernel\Log\LoggerInterface; + +/** + * A secure random number generator implementation. + * + * @author Johannes M. Schmitt <schmittjoh@gmail.com> + */ +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/Core/Util/SeedProviderInterface.php b/Core/Util/SeedProviderInterface.php new file mode 100644 index 0000000..dd960b9 --- /dev/null +++ b/Core/Util/SeedProviderInterface.php @@ -0,0 +1,37 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Util; + +/** + * Seed Provider Interface. + * + * @author Johannes M. Schmitt <schmittjoh@gmail.com> + */ +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/Core/Util/String.php b/Core/Util/String.php new file mode 100644 index 0000000..096878b --- /dev/null +++ b/Core/Util/String.php @@ -0,0 +1,48 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Util; + +/** + * String utility functions. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +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; + } +} |