PageRenderTime 39ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Symfony/Component/Security/Core/Encoder/Pbkdf2PasswordEncoder.php

https://bitbucket.org/gencer/symfony
PHP | 103 lines | 49 code | 15 blank | 39 comment | 7 complexity | b47bc995e72dd266839ba6a011908688 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Security\Core\Encoder;
  11. use Symfony\Component\Security\Core\Exception\BadCredentialsException;
  12. /**
  13. * Pbkdf2PasswordEncoder uses the PBKDF2 (Password-Based Key Derivation Function 2).
  14. *
  15. * Providing a high level of Cryptographic security,
  16. * PBKDF2 is recommended by the National Institute of Standards and Technology (NIST).
  17. *
  18. * But also warrants a warning, using PBKDF2 (with a high number of iterations) slows down the process.
  19. * PBKDF2 should be used with caution and care.
  20. *
  21. * @author Sebastiaan Stok <s.stok@rollerscapes.net>
  22. * @author Andrew Johnson
  23. * @author Fabien Potencier <fabien@symfony.com>
  24. */
  25. class Pbkdf2PasswordEncoder extends BasePasswordEncoder
  26. {
  27. private $algorithm;
  28. private $encodeHashAsBase64;
  29. private $iterations;
  30. private $length;
  31. /**
  32. * Constructor.
  33. *
  34. * @param string $algorithm The digest algorithm to use
  35. * @param bool $encodeHashAsBase64 Whether to base64 encode the password hash
  36. * @param int $iterations The number of iterations to use to stretch the password hash
  37. * @param int $length Length of derived key to create
  38. */
  39. public function __construct($algorithm = 'sha512', $encodeHashAsBase64 = true, $iterations = 1000, $length = 40)
  40. {
  41. $this->algorithm = $algorithm;
  42. $this->encodeHashAsBase64 = $encodeHashAsBase64;
  43. $this->iterations = $iterations;
  44. $this->length = $length;
  45. }
  46. /**
  47. * {@inheritdoc}
  48. *
  49. * @throws \LogicException when the algorithm is not supported
  50. */
  51. public function encodePassword($raw, $salt)
  52. {
  53. if ($this->isPasswordTooLong($raw)) {
  54. throw new BadCredentialsException('Invalid password.');
  55. }
  56. if (!in_array($this->algorithm, hash_algos(), true)) {
  57. throw new \LogicException(sprintf('The algorithm "%s" is not supported.', $this->algorithm));
  58. }
  59. if (function_exists('hash_pbkdf2')) {
  60. $digest = hash_pbkdf2($this->algorithm, $raw, $salt, $this->iterations, $this->length, true);
  61. } else {
  62. $digest = $this->hashPbkdf2($this->algorithm, $raw, $salt, $this->iterations, $this->length);
  63. }
  64. return $this->encodeHashAsBase64 ? base64_encode($digest) : bin2hex($digest);
  65. }
  66. /**
  67. * {@inheritdoc}
  68. */
  69. public function isPasswordValid($encoded, $raw, $salt)
  70. {
  71. return !$this->isPasswordTooLong($raw) && $this->comparePasswords($encoded, $this->encodePassword($raw, $salt));
  72. }
  73. private function hashPbkdf2($algorithm, $password, $salt, $iterations, $length = 0)
  74. {
  75. // Number of blocks needed to create the derived key
  76. $blocks = ceil($length / strlen(hash($algorithm, null, true)));
  77. $digest = '';
  78. for ($i = 1; $i <= $blocks; $i++) {
  79. $ib = $block = hash_hmac($algorithm, $salt.pack('N', $i), $password, true);
  80. // Iterations
  81. for ($j = 1; $j < $iterations; $j++) {
  82. $ib ^= ($block = hash_hmac($algorithm, $block, $password, true));
  83. }
  84. $digest .= $ib;
  85. }
  86. return substr($digest, 0, $this->length);
  87. }
  88. }