‪TYPO3CMS  9.5
BlowfishPasswordHash.php
Go to the documentation of this file.
1 <?php
2 declare(strict_types = 1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
21 
30 {
32 
36  private ‪$deprecatedPublicMethods = [
37  'isValidSalt' => 'Using BlowfishPasswordHash::isValidSalt() is deprecated and will not be possible anymore in TYPO3 v10.0.',
38  'base64Encode' => 'Using BlowfishPasswordHash::base64Encode() is deprecated and will not be possible anymore in TYPO3 v10.0.',
39  ];
40 
44  protected const ‪PREFIX = '$2a$';
45 
49  protected ‪$options = [
50  'hash_count' => 7
51  ];
52 
59  const ‪ITOA64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
60 
66  const ‪HASH_COUNT = 7;
67 
74  const ‪MAX_HASH_COUNT = 17;
75 
82  const ‪MIN_HASH_COUNT = 4;
83 
90  public function ‪__construct(array ‪$options = [])
91  {
92  $newOptions = ‪$this->options;
93  if (isset(‪$options['hash_count'])) {
94  if ((int)‪$options['hash_count'] < 4 || (int)‪$options['hash_count'] > 17) {
95  throw new \InvalidArgumentException(
96  'hash_count must not be lower than 4 or bigger than 17',
97  1533903545
98  );
99  }
100  $newOptions['hash_count'] = (int)‪$options['hash_count'];
101  }
102  $this->options = $newOptions;
103  }
104 
113  public function ‪checkPassword(string $plainPW, string $saltedHashPW): bool
114  {
115  $isCorrect = false;
116  if ($this->‪isValidSalt($saltedHashPW)) {
117  $isCorrect = \password_verify($plainPW, $saltedHashPW);
118  }
119  return $isCorrect;
120  }
121 
127  public function ‪isAvailable(): bool
128  {
129  return (bool)CRYPT_BLOWFISH;
130  }
131 
139  public function ‪getHashedPassword(string $password, string $salt = null)
140  {
141  if ($salt !== null) {
142  trigger_error(static::class . ': using a custom salt is deprecated.', E_USER_DEPRECATED);
143  }
144  $saltedPW = null;
145  if (!empty($password)) {
146  if (empty($salt) || !$this->‪isValidSalt($salt)) {
147  $salt = $this->‪getGeneratedSalt();
148  }
149  $saltedPW = crypt($password, $this->‪applySettingsToSalt($salt));
150  }
151  return $saltedPW;
152  }
153 
165  public function ‪isHashUpdateNeeded(string $saltedPW): bool
166  {
167  // Check whether the iteration count used differs from the standard number.
168  $countLog2 = $this->‪getCountLog2($saltedPW);
169  return $countLog2 !== null && $countLog2 < $this->options['hash_count'];
170  }
171 
178  public function ‪isValidSaltedPW(string $saltedPW): bool
179  {
180  $isValid = !strncmp(self::PREFIX, $saltedPW, strlen(self::PREFIX));
181  if ($isValid) {
182  $isValid = $this->‪isValidSalt($saltedPW);
183  }
184  return $isValid;
185  }
186 
198  protected function ‪getGeneratedSalt(): string
199  {
200  $randomBytes = GeneralUtility::makeInstance(Random::class)->generateRandomBytes(16);
201  return $this->‪base64Encode($randomBytes, 16);
202  }
203 
210  protected function ‪applySettingsToSalt(string $salt): string
211  {
212  $saltWithSettings = $salt;
213  $reqLenBase64 = $this->‪getLengthBase64FromBytes(16);
214  // salt without setting
215  if (strlen($salt) == $reqLenBase64) {
216  $saltWithSettings = self::PREFIX . sprintf('%02u', $this->options['hash_count']) . '$' . $salt;
217  }
218  return $saltWithSettings;
219  }
220 
227  protected function ‪getCountLog2(string $setting): int
228  {
229  $countLog2 = null;
230  $setting = substr($setting, strlen(self::PREFIX));
231  $firstSplitPos = strpos($setting, '$');
232  // Hashcount existing
233  if ($firstSplitPos !== false && $firstSplitPos <= 2 && is_numeric(substr($setting, 0, $firstSplitPos))) {
234  $countLog2 = (int)substr($setting, 0, $firstSplitPos);
235  }
236  return $countLog2;
237  }
238 
244  protected function ‪getItoa64(): string
245  {
246  return './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
247  }
248 
255  protected function ‪isValidSalt(string $salt): bool
256  {
257  $isValid = ($skip = false);
258  $reqLenBase64 = $this->‪getLengthBase64FromBytes(16);
259  if (strlen($salt) >= $reqLenBase64) {
260  // Salt with prefixed setting
261  if (!strncmp('$', $salt, 1)) {
262  if (!strncmp(self::PREFIX, $salt, strlen(self::PREFIX))) {
263  $isValid = true;
264  $salt = substr($salt, strrpos($salt, '$') + 1);
265  } else {
266  $skip = true;
267  }
268  }
269  // Checking base64 characters
270  if (!$skip && strlen($salt) >= $reqLenBase64) {
271  if (preg_match('/^[' . preg_quote($this->‪getItoa64(), '/') . ']{' . $reqLenBase64 . ',' . $reqLenBase64 . '}$/', substr($salt, 0, $reqLenBase64))) {
272  $isValid = true;
273  }
274  }
275  }
276  return $isValid;
277  }
278 
286  protected function ‪base64Encode(string $input, int $count): string
287  {
288  ‪$output = '';
289  $i = 0;
290  $itoa64 = $this->‪getItoa64();
291  do {
292  $value = ord($input[$i++]);
293  ‪$output .= $itoa64[$value & 63];
294  if ($i < $count) {
295  $value |= ord($input[$i]) << 8;
296  }
297  ‪$output .= $itoa64[$value >> 6 & 63];
298  if ($i++ >= $count) {
299  break;
300  }
301  if ($i < $count) {
302  $value |= ord($input[$i]) << 16;
303  }
304  ‪$output .= $itoa64[$value >> 12 & 63];
305  if ($i++ >= $count) {
306  break;
307  }
308  ‪$output .= $itoa64[$value >> 18 & 63];
309  } while ($i < $count);
310  return ‪$output;
311  }
312 
320  protected function ‪getLengthBase64FromBytes(int $byteLength): int
321  {
322  // Calculates bytes in bits in base64
323  return (int)ceil($byteLength * 8 / 6);
324  }
325 
332  public function ‪getHashCount(): int
333  {
334  trigger_error('This method will be removed in TYPO3 v10.0.', E_USER_DEPRECATED);
335  return $this->options['hash_count'];
336  }
337 
344  public function ‪getMaxHashCount(): int
345  {
346  trigger_error('This method will be removed in TYPO3 v10.0.', E_USER_DEPRECATED);
347  return 17;
348  }
349 
356  public function ‪getMinHashCount(): int
357  {
358  trigger_error('This method will be removed in TYPO3 v10.0.', E_USER_DEPRECATED);
359  return 4;
360  }
361 
368  public function ‪getSaltLength(): int
369  {
370  trigger_error('This method will be removed in TYPO3 v10.0.', E_USER_DEPRECATED);
371  return 16;
372  }
373 
380  public function ‪getSetting(): string
381  {
382  trigger_error('This method will be removed in TYPO3 v10.0.', E_USER_DEPRECATED);
383  return ‪self::PREFIX;
384  }
385 
392  public function ‪setHashCount(int $hashCount = null)
393  {
394  trigger_error('This method will be removed in TYPO3 v10.0.', E_USER_DEPRECATED);
395  if ($hashCount >= 4 && $hashCount <= 17) {
396  $this->options['hash_count'] = $hashCount;
397  }
398  }
399 
406  public function ‪setMaxHashCount(int $maxHashCount = null)
407  {
408  trigger_error('This method will be removed in TYPO3 v10.0.', E_USER_DEPRECATED);
409  // Empty, max hash count is hard coded to 17
410  }
411 
418  public function ‪setMinHashCount(int $minHashCount = null)
419  {
420  trigger_error('This method will be removed in TYPO3 v10.0.', E_USER_DEPRECATED);
421  // Empty, min hash count is hard coded to 4
422  }
423 }
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\ITOA64
‪const ITOA64
Definition: BlowfishPasswordHash.php:57
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\__construct
‪__construct(array $options=[])
Definition: BlowfishPasswordHash.php:88
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\PREFIX
‪const PREFIX
Definition: BlowfishPasswordHash.php:43
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\MIN_HASH_COUNT
‪const MIN_HASH_COUNT
Definition: BlowfishPasswordHash.php:80
‪TYPO3\CMS\Core\Crypto\PasswordHashing
Definition: AbstractComposedSalt.php:3
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\getSaltLength
‪int getSaltLength()
Definition: BlowfishPasswordHash.php:366
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\getSetting
‪string getSetting()
Definition: BlowfishPasswordHash.php:378
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\getItoa64
‪string getItoa64()
Definition: BlowfishPasswordHash.php:242
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\isAvailable
‪bool isAvailable()
Definition: BlowfishPasswordHash.php:125
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\getHashCount
‪int getHashCount()
Definition: BlowfishPasswordHash.php:330
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\MAX_HASH_COUNT
‪const MAX_HASH_COUNT
Definition: BlowfishPasswordHash.php:72
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\getCountLog2
‪int getCountLog2(string $setting)
Definition: BlowfishPasswordHash.php:225
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\$deprecatedPublicMethods
‪array $deprecatedPublicMethods
Definition: BlowfishPasswordHash.php:35
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\getLengthBase64FromBytes
‪int getLengthBase64FromBytes(int $byteLength)
Definition: BlowfishPasswordHash.php:318
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash
Definition: BlowfishPasswordHash.php:30
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\isValidSalt
‪bool isValidSalt(string $salt)
Definition: BlowfishPasswordHash.php:253
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\setMaxHashCount
‪setMaxHashCount(int $maxHashCount=null)
Definition: BlowfishPasswordHash.php:404
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\base64Encode
‪string base64Encode(string $input, int $count)
Definition: BlowfishPasswordHash.php:284
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\checkPassword
‪bool checkPassword(string $plainPW, string $saltedHashPW)
Definition: BlowfishPasswordHash.php:111
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\getHashedPassword
‪string getHashedPassword(string $password, string $salt=null)
Definition: BlowfishPasswordHash.php:137
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\isValidSaltedPW
‪bool isValidSaltedPW(string $saltedPW)
Definition: BlowfishPasswordHash.php:176
‪TYPO3\CMS\Core\Compatibility\PublicMethodDeprecationTrait
Definition: PublicMethodDeprecationTrait.php:68
‪$output
‪$output
Definition: annotationChecker.php:113
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\getMinHashCount
‪int getMinHashCount()
Definition: BlowfishPasswordHash.php:354
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\getGeneratedSalt
‪string getGeneratedSalt()
Definition: BlowfishPasswordHash.php:196
‪TYPO3\CMS\Core\Crypto\Random
Definition: Random.php:22
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\setHashCount
‪setHashCount(int $hashCount=null)
Definition: BlowfishPasswordHash.php:390
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\isHashUpdateNeeded
‪bool isHashUpdateNeeded(string $saltedPW)
Definition: BlowfishPasswordHash.php:163
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\applySettingsToSalt
‪string applySettingsToSalt(string $salt)
Definition: BlowfishPasswordHash.php:208
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\HASH_COUNT
‪const HASH_COUNT
Definition: BlowfishPasswordHash.php:64
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Core\Crypto\PasswordHashing\PasswordHashInterface
Definition: PasswordHashInterface.php:23
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\setMinHashCount
‪setMinHashCount(int $minHashCount=null)
Definition: BlowfishPasswordHash.php:416
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\getMaxHashCount
‪int getMaxHashCount()
Definition: BlowfishPasswordHash.php:342
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\$options
‪array $options
Definition: BlowfishPasswordHash.php:47