TYPO3 CMS  TYPO3_7-6
PhpassSalt.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
28 class PhpassSalt extends AbstractSalt implements SaltInterface
29 {
34  const ITOA64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
35 
39  const HASH_COUNT = 14;
40 
45  const MAX_HASH_COUNT = 24;
46 
51  const MIN_HASH_COUNT = 7;
52 
59  protected static $hashCount;
60 
67  protected static $maxHashCount;
68 
75  protected static $minHashCount;
76 
82  protected static $saltLengthPhpass = 6;
83 
89  protected static $settingPhpass = '$P$';
90 
100  protected function applySettingsToSalt($salt)
101  {
102  $saltWithSettings = $salt;
103  $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength());
104  // Salt without setting
105  if (strlen($salt) == $reqLenBase64) {
106  // We encode the final log2 iteration count in base 64.
107  $itoa64 = $this->getItoa64();
108  $saltWithSettings = $this->getSetting() . $itoa64[$this->getHashCount()];
109  $saltWithSettings .= $salt;
110  }
111  return $saltWithSettings;
112  }
113 
122  public function checkPassword($plainPW, $saltedHashPW)
123  {
124  $hash = $this->cryptPassword($plainPW, $saltedHashPW);
125  return $hash && $saltedHashPW === $hash;
126  }
127 
133  public function isAvailable()
134  {
135  return true;
136  }
137 
150  protected function cryptPassword($password, $setting)
151  {
152  $saltedPW = null;
153  $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength());
154  // Retrieving settings with salt
155  $setting = substr($setting, 0, strlen($this->getSetting()) + 1 + $reqLenBase64);
156  $count_log2 = $this->getCountLog2($setting);
157  // Hashes may be imported from elsewhere, so we allow != HASH_COUNT
158  if ($count_log2 >= $this->getMinHashCount() && $count_log2 <= $this->getMaxHashCount()) {
159  $salt = substr($setting, strlen($this->getSetting()) + 1, $reqLenBase64);
160  // We must use md5() or sha1() here since they are the only cryptographic
161  // primitives always available in PHP 5. To implement our own low-level
162  // cryptographic function in PHP would result in much worse performance and
163  // consequently in lower iteration counts and hashes that are quicker to crack
164  // (by non-PHP code).
165  $count = 1 << $count_log2;
166  $hash = md5($salt . $password, true);
167  do {
168  $hash = md5($hash . $password, true);
169  } while (--$count);
170  $saltedPW = $setting . $this->base64Encode($hash, 16);
171  // base64Encode() of a 16 byte MD5 will always be 22 characters.
172  return strlen($saltedPW) == 34 ? $saltedPW : false;
173  }
174  return $saltedPW;
175  }
176 
183  protected function getCountLog2($setting)
184  {
185  return strpos($this->getItoa64(), $setting[strlen($this->getSetting())]);
186  }
187 
199  protected function getGeneratedSalt()
200  {
202  return $this->base64Encode($randomBytes, $this->getSaltLength());
203  }
204 
213  public function getHashCount()
214  {
215  return isset(self::$hashCount) ? self::$hashCount : self::HASH_COUNT;
216  }
217 
225  public function getHashedPassword($password, $salt = null)
226  {
227  $saltedPW = null;
228  if (!empty($password)) {
229  if (empty($salt) || !$this->isValidSalt($salt)) {
230  $salt = $this->getGeneratedSalt();
231  }
232  $saltedPW = $this->cryptPassword($password, $this->applySettingsToSalt($salt));
233  }
234  return $saltedPW;
235  }
236 
242  protected function getItoa64()
243  {
244  return self::ITOA64;
245  }
246 
255  public function getMaxHashCount()
256  {
257  return isset(self::$maxHashCount) ? self::$maxHashCount : self::MAX_HASH_COUNT;
258  }
259 
268  public function getMinHashCount()
269  {
270  return isset(self::$minHashCount) ? self::$minHashCount : self::MIN_HASH_COUNT;
271  }
272 
278  public function getSaltLength()
279  {
280  return self::$saltLengthPhpass;
281  }
282 
288  public function getSetting()
289  {
290  return self::$settingPhpass;
291  }
292 
305  public function isHashUpdateNeeded($passString)
306  {
307  // Check whether this was an updated password.
308  if (strncmp($passString, '$P$', 3) || strlen($passString) != 34) {
309  return true;
310  }
311  // Check whether the iteration count used differs from the standard number.
312  return $this->getCountLog2($passString) < $this->getHashCount();
313  }
314 
321  public function isValidSalt($salt)
322  {
323  $isValid = ($skip = false);
324  $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength());
325  if (strlen($salt) >= $reqLenBase64) {
326  // Salt with prefixed setting
327  if (!strncmp('$', $salt, 1)) {
328  if (!strncmp($this->getSetting(), $salt, strlen($this->getSetting()))) {
329  $isValid = true;
330  $salt = substr($salt, strrpos($salt, '$') + 2);
331  } else {
332  $skip = true;
333  }
334  }
335  // Checking base64 characters
336  if (!$skip && strlen($salt) >= $reqLenBase64) {
337  if (preg_match('/^[' . preg_quote($this->getItoa64(), '/') . ']{' . $reqLenBase64 . ',' . $reqLenBase64 . '}$/', substr($salt, 0, $reqLenBase64))) {
338  $isValid = true;
339  }
340  }
341  }
342  return $isValid;
343  }
344 
351  public function isValidSaltedPW($saltedPW)
352  {
353  $isValid = false;
354  $isValid = !strncmp($this->getSetting(), $saltedPW, strlen($this->getSetting()));
355  if ($isValid) {
356  $isValid = $this->isValidSalt($saltedPW);
357  }
358  return $isValid;
359  }
360 
369  public function setHashCount($hashCount = null)
370  {
371  self::$hashCount = !is_null($hashCount) && is_int($hashCount) && $hashCount >= $this->getMinHashCount() && $hashCount <= $this->getMaxHashCount() ? $hashCount : self::HASH_COUNT;
372  }
373 
382  public function setMaxHashCount($maxHashCount = null)
383  {
384  self::$maxHashCount = !is_null($maxHashCount) && is_int($maxHashCount) ? $maxHashCount : self::MAX_HASH_COUNT;
385  }
386 
395  public function setMinHashCount($minHashCount = null)
396  {
397  self::$minHashCount = !is_null($minHashCount) && is_int($minHashCount) ? $minHashCount : self::MIN_HASH_COUNT;
398  }
399 }
static generateRandomBytes($bytesToReturn)
getHashedPassword($password, $salt=null)
Definition: PhpassSalt.php:225
checkPassword($plainPW, $saltedHashPW)
Definition: PhpassSalt.php:122