diff options
Diffstat (limited to 'Core/Util')
-rw-r--r-- | Core/Util/ClassUtils.php | 5 | ||||
-rw-r--r-- | Core/Util/SecureRandom.php | 114 | ||||
-rw-r--r-- | Core/Util/SecureRandomInterface.php | 29 | ||||
-rw-r--r-- | Core/Util/StringUtils.php | 60 |
4 files changed, 208 insertions, 0 deletions
diff --git a/Core/Util/ClassUtils.php b/Core/Util/ClassUtils.php index 7b583a3..26bf1a1 100644 --- a/Core/Util/ClassUtils.php +++ b/Core/Util/ClassUtils.php @@ -37,6 +37,11 @@ class ClassUtils const MARKER_LENGTH = 6; /** + * This class should not be instantiated + */ + private function __construct() {} + + /** * Gets the real class name of a class name that could be a proxy. * * @param string|object diff --git a/Core/Util/SecureRandom.php b/Core/Util/SecureRandom.php new file mode 100644 index 0000000..841b9af --- /dev/null +++ b/Core/Util/SecureRandom.php @@ -0,0 +1,114 @@ +<?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 Psr\Log\LoggerInterface; + +/** + * A secure random number generator implementation. + * + * @author Fabien Potencier <fabien@symfony.com> + * @author Johannes M. Schmitt <schmittjoh@gmail.com> + */ +final class SecureRandom implements SecureRandomInterface +{ + private $logger; + private $useOpenSsl; + private $seed; + private $seedUpdated; + private $seedLastUpdatedAt; + private $seedFile; + + /** + * Constructor. + * + * Be aware that a guessable seed will severely compromise the PRNG + * algorithm that is employed. + * + * @param string $seedFile + * @param LoggerInterface $logger + */ + public function __construct($seedFile = null, LoggerInterface $logger = null) + { + $this->seedFile = $seedFile; + $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; + } + } + + /** + * {@inheritdoc} + */ + 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->seedFile) { + throw new \RuntimeException('You need to specify a file path to store the seed.'); + } + + if (is_file($this->seedFile)) { + list($this->seed, $this->seedLastUpdatedAt) = $this->readSeed(); + } else { + $this->seed = uniqid(mt_rand(), true); + $this->updateSeed(); + } + } + + $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)); + $this->updateSeed(); + } + + return substr($bytes, 0, $nbBytes); + } + + 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)))); + } + + $this->seedUpdated = true; + } +} diff --git a/Core/Util/SecureRandomInterface.php b/Core/Util/SecureRandomInterface.php new file mode 100644 index 0000000..2c35a72 --- /dev/null +++ b/Core/Util/SecureRandomInterface.php @@ -0,0 +1,29 @@ +<?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; + +/** + * Interface that needs to be implemented by all secure random number generators. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +interface SecureRandomInterface +{ + /** + * Generates the specified number of secure random bytes. + * + * @param integer $nbBytes + * + * @return string + */ + public function nextBytes($nbBytes); +} diff --git a/Core/Util/StringUtils.php b/Core/Util/StringUtils.php new file mode 100644 index 0000000..2e8925d --- /dev/null +++ b/Core/Util/StringUtils.php @@ -0,0 +1,60 @@ +<?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> + */ +class StringUtils +{ + /** + * This class should not be instantiated + */ + private function __construct() {} + + /** + * Compares two strings. + * + * This method implements a constant-time algorithm to compare strings. + * + * @param string $knownString The string of known length to compare against + * @param string $userInput The string that the user can control + * + * @return Boolean true if the two strings are the same, false otherwise + */ + public static function equals($knownString, $userInput) + { + // Prevent issues if string length is 0 + $knownString .= chr(0); + $userInput .= chr(0); + + $knownLen = strlen($knownString); + $userLen = strlen($userInput); + + // Set the result to the difference between the lengths + $result = $knownLen - $userLen; + + // Note that we ALWAYS iterate over the user-supplied length + // This is to prevent leaking length information + for ($i = 0; $i < $userLen; $i++) { + // Using % here is a trick to prevent notices + // It's safe, since if the lengths are different + // $result is already non-0 + $result |= (ord($knownString[$i % $knownLen]) ^ ord($userInput[$i])); + } + + // They are only identical strings if $result is exactly 0... + return 0 === $result; + } +} |