‪TYPO3CMS  ‪main
BlowfishPasswordHash.php
Go to the documentation of this file.
1 <?php
2 
3 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 
19 
22 
31 {
35  protected const ‪PREFIX = '$2a$';
36 
40  protected ‪$options = [
41  'hash_count' => 7,
42  ];
43 
49  public function ‪__construct(array ‪$options = [])
50  {
51  $newOptions = ‪$this->options;
52  if (isset(‪$options['hash_count'])) {
53  if ((int)‪$options['hash_count'] < 4 || (int)‪$options['hash_count'] > 17) {
54  throw new \InvalidArgumentException(
55  'hash_count must not be lower than 4 or bigger than 17',
56  1533903545
57  );
58  }
59  $newOptions['hash_count'] = (int)‪$options['hash_count'];
60  }
61  $this->options = $newOptions;
62  }
63 
72  public function ‪checkPassword(string $plainPW, string $saltedHashPW): bool
73  {
74  $isCorrect = false;
75  if ($this->‪isValidSalt($saltedHashPW)) {
76  $isCorrect = \password_verify($plainPW, $saltedHashPW);
77  }
78  return $isCorrect;
79  }
80 
86  public function ‪isAvailable(): bool
87  {
88  return (bool)CRYPT_BLOWFISH;
89  }
90 
91  public function ‪getHashedPassword(string $password): ?string
92  {
93  $saltedPW = null;
94  if (!empty($password)) {
95  $salt = $this->‪getGeneratedSalt();
96  $saltedPW = crypt($password, $this->‪applySettingsToSalt($salt));
97  }
98  return $saltedPW;
99  }
100 
112  public function ‪isHashUpdateNeeded(string $saltedPW): bool
113  {
114  // Check whether the iteration count used differs from the standard number.
115  $countLog2 = $this->‪getCountLog2($saltedPW);
116  return $countLog2 !== null && $countLog2 < $this->options['hash_count'];
117  }
118 
125  public function ‪isValidSaltedPW(string $saltedPW): bool
126  {
127  $isValid = !strncmp(self::PREFIX, $saltedPW, strlen(self::PREFIX));
128  if ($isValid) {
129  $isValid = $this->‪isValidSalt($saltedPW);
130  }
131  return $isValid;
132  }
133 
145  protected function ‪getGeneratedSalt(): string
146  {
147  $randomBytes = GeneralUtility::makeInstance(Random::class)->generateRandomBytes(16);
148  return $this->‪base64Encode($randomBytes, 16);
149  }
150 
157  protected function ‪applySettingsToSalt(string $salt): string
158  {
159  $saltWithSettings = $salt;
160  $reqLenBase64 = $this->‪getLengthBase64FromBytes(16);
161  // salt without setting
162  if (strlen($salt) == $reqLenBase64) {
163  $saltWithSettings = self::PREFIX . sprintf('%02u', $this->options['hash_count']) . '$' . $salt;
164  }
165  return $saltWithSettings;
166  }
167 
174  protected function ‪getCountLog2(string $setting): int
175  {
176  $countLog2 = null;
177  $setting = substr($setting, strlen(self::PREFIX));
178  $firstSplitPos = strpos($setting, '$');
179  // Hashcount existing
180  if ($firstSplitPos !== false && $firstSplitPos <= 2 && is_numeric(substr($setting, 0, $firstSplitPos))) {
181  $countLog2 = (int)substr($setting, 0, $firstSplitPos);
182  }
183  return $countLog2;
184  }
185 
191  protected function ‪getItoa64(): string
192  {
193  return './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
194  }
195 
202  protected function ‪isValidSalt(string $salt): bool
203  {
204  $isValid = ($skip = false);
205  $reqLenBase64 = $this->‪getLengthBase64FromBytes(16);
206  if (strlen($salt) >= $reqLenBase64) {
207  // Salt with prefixed setting
208  if (!strncmp('$', $salt, 1)) {
209  if (!strncmp(self::PREFIX, $salt, strlen(self::PREFIX))) {
210  $isValid = true;
211  $salt = substr($salt, (int)strrpos($salt, '$') + 1);
212  } else {
213  $skip = true;
214  }
215  }
216  // Checking base64 characters
217  if (!$skip && strlen($salt) >= $reqLenBase64) {
218  if (preg_match('/^[' . preg_quote($this->‪getItoa64(), '/') . ']{' . $reqLenBase64 . ',' . $reqLenBase64 . '}$/', substr($salt, 0, $reqLenBase64))) {
219  $isValid = true;
220  }
221  }
222  }
223  return $isValid;
224  }
225 
233  protected function ‪base64Encode(string $input, int $count): string
234  {
235  ‪$output = '';
236  $i = 0;
237  $itoa64 = $this->‪getItoa64();
238  do {
239  $value = ord($input[$i++]);
240  ‪$output .= $itoa64[$value & 63];
241  if ($i < $count) {
242  $value |= ord($input[$i]) << 8;
243  }
244  ‪$output .= $itoa64[$value >> 6 & 63];
245  if ($i++ >= $count) {
246  break;
247  }
248  if ($i < $count) {
249  $value |= ord($input[$i]) << 16;
250  }
251  ‪$output .= $itoa64[$value >> 12 & 63];
252  if ($i++ >= $count) {
253  break;
254  }
255  ‪$output .= $itoa64[$value >> 18 & 63];
256  } while ($i < $count);
257  return ‪$output;
258  }
259 
267  protected function ‪getLengthBase64FromBytes(int $byteLength): int
268  {
269  // Calculates bytes in bits in base64
270  return (int)ceil($byteLength * 8 / 6);
271  }
272 }
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\__construct
‪__construct(array $options=[])
Definition: BlowfishPasswordHash.php:48
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\PREFIX
‪const PREFIX
Definition: BlowfishPasswordHash.php:35
‪TYPO3\CMS\Core\Crypto\PasswordHashing
Definition: AbstractArgon2PasswordHash.php:18
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\getItoa64
‪string getItoa64()
Definition: BlowfishPasswordHash.php:190
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\isAvailable
‪bool isAvailable()
Definition: BlowfishPasswordHash.php:85
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\getCountLog2
‪int getCountLog2(string $setting)
Definition: BlowfishPasswordHash.php:173
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\getLengthBase64FromBytes
‪int getLengthBase64FromBytes(int $byteLength)
Definition: BlowfishPasswordHash.php:266
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash
Definition: BlowfishPasswordHash.php:31
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\isValidSalt
‪bool isValidSalt(string $salt)
Definition: BlowfishPasswordHash.php:201
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\base64Encode
‪string base64Encode(string $input, int $count)
Definition: BlowfishPasswordHash.php:232
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\checkPassword
‪bool checkPassword(string $plainPW, string $saltedHashPW)
Definition: BlowfishPasswordHash.php:71
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\isValidSaltedPW
‪bool isValidSaltedPW(string $saltedPW)
Definition: BlowfishPasswordHash.php:124
‪$output
‪$output
Definition: annotationChecker.php:114
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\getHashedPassword
‪getHashedPassword(string $password)
Definition: BlowfishPasswordHash.php:90
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\getGeneratedSalt
‪string getGeneratedSalt()
Definition: BlowfishPasswordHash.php:144
‪TYPO3\CMS\Core\Crypto\Random
Definition: Random.php:27
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\isHashUpdateNeeded
‪bool isHashUpdateNeeded(string $saltedPW)
Definition: BlowfishPasswordHash.php:111
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\applySettingsToSalt
‪string applySettingsToSalt(string $salt)
Definition: BlowfishPasswordHash.php:156
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Crypto\PasswordHashing\PasswordHashInterface
Definition: PasswordHashInterface.php:25
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash\$options
‪array $options
Definition: BlowfishPasswordHash.php:39