TYPO3 CMS  TYPO3_6-2
PhpassSalt.php
Go to the documentation of this file.
1 <?php
3 
31 
36  const ITOA64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
40  const HASH_COUNT = 14;
45  const MAX_HASH_COUNT = 24;
50  const MIN_HASH_COUNT = 7;
57  static protected $hashCount;
58 
65  static protected $maxHashCount;
66 
73  static protected $minHashCount;
74 
80  static protected $saltLengthPhpass = 6;
81 
87  static protected $settingPhpass = '$P$';
88 
98  protected function applySettingsToSalt($salt) {
99  $saltWithSettings = $salt;
100  $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength());
101  // Salt without setting
102  if (strlen($salt) == $reqLenBase64) {
103  // We encode the final log2 iteration count in base 64.
104  $itoa64 = $this->getItoa64();
105  $saltWithSettings = $this->getSetting() . $itoa64[$this->getHashCount()];
106  $saltWithSettings .= $salt;
107  }
108  return $saltWithSettings;
109  }
110 
119  public function checkPassword($plainPW, $saltedHashPW) {
120  $hash = $this->cryptPassword($plainPW, $saltedHashPW);
121  return $hash && $saltedHashPW === $hash;
122  }
123 
129  public function isAvailable() {
130  return TRUE;
131  }
132 
145  protected function cryptPassword($password, $setting) {
146  $saltedPW = NULL;
147  $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength());
148  // Retrieving settings with salt
149  $setting = substr($setting, 0, strlen($this->getSetting()) + 1 + $reqLenBase64);
150  $count_log2 = $this->getCountLog2($setting);
151  // Hashes may be imported from elsewhere, so we allow != HASH_COUNT
152  if ($count_log2 >= $this->getMinHashCount() && $count_log2 <= $this->getMaxHashCount()) {
153  $salt = substr($setting, strlen($this->getSetting()) + 1, $reqLenBase64);
154  // We must use md5() or sha1() here since they are the only cryptographic
155  // primitives always available in PHP 5. To implement our own low-level
156  // cryptographic function in PHP would result in much worse performance and
157  // consequently in lower iteration counts and hashes that are quicker to crack
158  // (by non-PHP code).
159  $count = 1 << $count_log2;
160  $hash = md5($salt . $password, TRUE);
161  do {
162  $hash = md5($hash . $password, TRUE);
163  } while (--$count);
164  $saltedPW = $setting . $this->base64Encode($hash, 16);
165  // base64Encode() of a 16 byte MD5 will always be 22 characters.
166  return strlen($saltedPW) == 34 ? $saltedPW : FALSE;
167  }
168  return $saltedPW;
169  }
170 
177  protected function getCountLog2($setting) {
178  return strpos($this->getItoa64(), $setting[strlen($this->getSetting())]);
179  }
180 
192  protected function getGeneratedSalt() {
194  return $this->base64Encode($randomBytes, $this->getSaltLength());
195  }
196 
205  public function getHashCount() {
206  return isset(self::$hashCount) ? self::$hashCount : self::HASH_COUNT;
207  }
208 
216  public function getHashedPassword($password, $salt = NULL) {
217  $saltedPW = NULL;
218  if (!empty($password)) {
219  if (empty($salt) || !$this->isValidSalt($salt)) {
220  $salt = $this->getGeneratedSalt();
221  }
222  $saltedPW = $this->cryptPassword($password, $this->applySettingsToSalt($salt));
223  }
224  return $saltedPW;
225  }
226 
232  protected function getItoa64() {
233  return self::ITOA64;
234  }
235 
244  public function getMaxHashCount() {
245  return isset(self::$maxHashCount) ? self::$maxHashCount : self::MAX_HASH_COUNT;
246  }
247 
256  public function getMinHashCount() {
257  return isset(self::$minHashCount) ? self::$minHashCount : self::MIN_HASH_COUNT;
258  }
259 
265  public function getSaltLength() {
266  return self::$saltLengthPhpass;
267  }
268 
274  public function getSetting() {
275  return self::$settingPhpass;
276  }
277 
290  public function isHashUpdateNeeded($passString) {
291  // Check whether this was an updated password.
292  if (strncmp($passString, '$P$', 3) || strlen($passString) != 34) {
293  return TRUE;
294  }
295  // Check whether the iteration count used differs from the standard number.
296  return $this->getCountLog2($passString) < $this->getHashCount();
297  }
298 
305  public function isValidSalt($salt) {
306  $isValid = ($skip = FALSE);
307  $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength());
308  if (strlen($salt) >= $reqLenBase64) {
309  // Salt with prefixed setting
310  if (!strncmp('$', $salt, 1)) {
311  if (!strncmp($this->getSetting(), $salt, strlen($this->getSetting()))) {
312  $isValid = TRUE;
313  $salt = substr($salt, strrpos($salt, '$') + 2);
314  } else {
315  $skip = TRUE;
316  }
317  }
318  // Checking base64 characters
319  if (!$skip && strlen($salt) >= $reqLenBase64) {
320  if (preg_match('/^[' . preg_quote($this->getItoa64(), '/') . ']{' . $reqLenBase64 . ',' . $reqLenBase64 . '}$/', substr($salt, 0, $reqLenBase64))) {
321  $isValid = TRUE;
322  }
323  }
324  }
325  return $isValid;
326  }
327 
334  public function isValidSaltedPW($saltedPW) {
335  $isValid = FALSE;
336  $isValid = !strncmp($this->getSetting(), $saltedPW, strlen($this->getSetting())) ? TRUE : FALSE;
337  if ($isValid) {
338  $isValid = $this->isValidSalt($saltedPW);
339  }
340  return $isValid;
341  }
342 
351  public function setHashCount($hashCount = NULL) {
352  self::$hashCount = !is_NULL($hashCount) && is_int($hashCount) && $hashCount >= $this->getMinHashCount() && $hashCount <= $this->getMaxHashCount() ? $hashCount : self::HASH_COUNT;
353  }
354 
363  public function setMaxHashCount($maxHashCount = NULL) {
364  self::$maxHashCount = !is_NULL($maxHashCount) && is_int($maxHashCount) ? $maxHashCount : self::MAX_HASH_COUNT;
365  }
366 
375  public function setMinHashCount($minHashCount = NULL) {
376  self::$minHashCount = !is_NULL($minHashCount) && is_int($minHashCount) ? $minHashCount : self::MIN_HASH_COUNT;
377  }
378 
379 }
getHashedPassword($password, $salt=NULL)
Definition: PhpassSalt.php:216
static generateRandomBytes($bytesToReturn)
checkPassword($plainPW, $saltedHashPW)
Definition: PhpassSalt.php:119