|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 2009-2011 Marcus Krause <marcus#exp2009@t3sec.info> 00006 * All rights reserved 00007 * 00008 * This script is part of the TYPO3 project. The TYPO3 project is 00009 * free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2 of the License, or 00012 * (at your option) any later version. 00013 * 00014 * The GNU General Public License can be found at 00015 * http://www.gnu.org/copyleft/gpl.html. 00016 * A copy is found in the textfile GPL.txt and important notices to the license 00017 * from the author is found in LICENSE.txt distributed with these scripts. 00018 * 00019 * 00020 * This script is distributed in the hope that it will be useful, 00021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00023 * GNU General Public License for more details. 00024 * 00025 * This copyright notice MUST APPEAR in all copies of the script! 00026 ***************************************************************/ 00027 /** 00028 * Contains class "tx_saltedpasswords_salts_phpass" 00029 * that provides PHPass salted hashing. 00030 * 00031 * Derived from Drupal CMS 00032 * original license: GNU General Public License (GPL) 00033 * @see http://drupal.org/node/29706/ 00034 * 00035 * Based on the Portable PHP password hashing framework 00036 * original license: Public Domain 00037 * @see http://www.openwall.com/phpass/ 00038 * 00039 * $Id: class.tx_saltedpasswords_salts_phpass.php 10120 2011-01-18 20:03:36Z ohader $ 00040 */ 00041 00042 00043 /** 00044 * Class that implements PHPass salted hashing based on Drupal's 00045 * modified Openwall implementation. 00046 * 00047 * PHPass should work on every system. 00048 * 00049 * @author Marcus Krause <marcus#exp2009@t3sec.info> 00050 * 00051 * @since 2009-09-06 00052 * @package TYPO3 00053 * @subpackage tx_saltedpasswords 00054 */ 00055 class tx_saltedpasswords_salts_phpass extends tx_saltedpasswords_abstract_salts implements tx_saltedpasswords_salts { 00056 /** 00057 * Keeps a string for mapping an int to the corresponding 00058 * base 64 character. 00059 */ 00060 const ITOA64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; 00061 00062 /** 00063 * The default log2 number of iterations for password stretching. 00064 */ 00065 const HASH_COUNT = 14; 00066 00067 /** 00068 * The default maximum allowed log2 number of iterations for 00069 * password stretching. 00070 */ 00071 const MAX_HASH_COUNT = 24; 00072 00073 /** 00074 * The default minimum allowed log2 number of iterations for 00075 * password stretching. 00076 */ 00077 const MIN_HASH_COUNT = 7; 00078 00079 00080 /** 00081 * Keeps log2 number 00082 * of iterations for password stretching. 00083 * 00084 * @var integer 00085 */ 00086 static protected $hashCount; 00087 00088 /** 00089 * Keeps maximum allowed log2 number 00090 * of iterations for password stretching. 00091 * 00092 * @var integer 00093 */ 00094 static protected $maxHashCount; 00095 00096 /** 00097 * Keeps minimum allowed log2 number 00098 * of iterations for password stretching. 00099 * 00100 * @var integer 00101 */ 00102 static protected $minHashCount; 00103 00104 /** 00105 * Keeps length of a PHPass salt in bytes. 00106 * 00107 * @var integer 00108 */ 00109 static protected $saltLengthPhpass = 6; 00110 00111 /** 00112 * Setting string to indicate type of hashing method (PHPass). 00113 * 00114 * @var string 00115 */ 00116 static protected $settingPhpass = '$P$'; 00117 00118 00119 /** 00120 * Method applies settings (prefix, hash count) to a salt. 00121 * 00122 * Overwrites {@link tx_saltedpasswords_salts_md5::applySettingsToSalt()} 00123 * with Blowfish specifics. 00124 * 00125 * @param string $salt: a salt to apply setting to 00126 * @return string salt with setting 00127 */ 00128 protected function applySettingsToSalt($salt) { 00129 $saltWithSettings = $salt; 00130 00131 $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength()); 00132 00133 // salt without setting 00134 if (strlen($salt) == $reqLenBase64) { 00135 // We encode the final log2 iteration count in base 64. 00136 $itoa64 = $this->getItoa64(); 00137 $saltWithSettings = $this->getSetting() . $itoa64[$this->getHashCount()]; 00138 00139 $saltWithSettings .= $salt; 00140 } 00141 00142 return $saltWithSettings; 00143 } 00144 00145 /** 00146 * Method checks if a given plaintext password is correct by comparing it with 00147 * a given salted hashed password. 00148 * 00149 * @param string $plainPW: plain-text password to compare with salted hash 00150 * @param string $saltedHashPW: salted hash to compare plain-text password with 00151 * @return boolean TRUE, if plain-text password matches the salted hash, otherwise FALSE 00152 */ 00153 public function checkPassword($plainPW, $saltedHashPW) { 00154 $hash = $this->cryptPassword($plainPW, $saltedHashPW); 00155 00156 return ($hash && $saltedHashPW === $hash); 00157 } 00158 00159 /** 00160 * Returns wether all prequesites for the hashing methods are matched 00161 * 00162 * @return boolean method available 00163 */ 00164 public function isAvailable() { 00165 return TRUE; 00166 } 00167 00168 /** 00169 * Hashes a password using a secure stretched hash. 00170 * 00171 * By using a salt and repeated hashing the password is "stretched". Its 00172 * security is increased because it becomes much more computationally costly 00173 * for an attacker to try to break the hash by brute-force computation of the 00174 * hashes of a large number of plain-text words or strings to find a match. 00175 * 00176 * @param string $password: plain-text password to hash 00177 * @param string $setting: an existing hash or the output of getGeneratedSalt() 00178 * @return mixed a string containing the hashed password (and salt) 00179 * or boolean FALSE on failure. 00180 */ 00181 protected function cryptPassword($password, $setting) { 00182 $saltedPW = NULL; 00183 00184 $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength()); 00185 00186 // Retrieving settings with salt 00187 $setting = substr($setting, 0, strlen($this->getSetting()) + 1 + $reqLenBase64); 00188 00189 $count_log2 = $this->getCountLog2($setting); 00190 00191 // Hashes may be imported from elsewhere, so we allow != HASH_COUNT 00192 if ($count_log2 >= $this->getMinHashCount() && $count_log2 <= $this->getMaxHashCount()) { 00193 00194 $salt = substr($setting, strlen($this->getSetting()) + 1, $reqLenBase64); 00195 00196 // We must use md5() or sha1() here since they are the only cryptographic 00197 // primitives always available in PHP 5. To implement our own low-level 00198 // cryptographic function in PHP would result in much worse performance and 00199 // consequently in lower iteration counts and hashes that are quicker to crack 00200 // (by non-PHP code). 00201 $count = 1 << $count_log2; 00202 00203 $hash = md5($salt . $password, TRUE); 00204 do { 00205 $hash = md5($hash . $password, TRUE); 00206 } while (--$count); 00207 00208 $saltedPW = $setting . $this->base64Encode($hash, 16); 00209 00210 // base64Encode() of a 16 byte MD5 will always be 22 characters. 00211 return (strlen($saltedPW) == 34) ? $saltedPW : FALSE; 00212 } 00213 00214 return $saltedPW; 00215 } 00216 00217 /** 00218 * Parses the log2 iteration count from a stored hash or setting string. 00219 * 00220 * @param string $setting: complete hash or a hash's setting string or to get log2 iteration count from 00221 * @return int used hashcount for given hash string 00222 */ 00223 protected function getCountLog2($setting) { 00224 return strpos( 00225 $this->getItoa64(), 00226 $setting[strlen($this->getSetting())] 00227 ); 00228 } 00229 00230 /** 00231 * Generates a random base 64-encoded salt prefixed and suffixed with settings for the hash. 00232 * 00233 * Proper use of salts may defeat a number of attacks, including: 00234 * - The ability to try candidate passwords against multiple hashes at once. 00235 * - The ability to use pre-hashed lists of candidate passwords. 00236 * - The ability to determine whether two users have the same (or different) 00237 * password without actually having to guess one of the passwords. 00238 * 00239 * @return string a character string containing settings and a random salt 00240 */ 00241 protected function getGeneratedSalt() { 00242 $randomBytes = t3lib_div::generateRandomBytes($this->getSaltLength()); 00243 00244 return $this->base64Encode($randomBytes, $this->getSaltLength()); 00245 } 00246 00247 /** 00248 * Method returns log2 number of iterations for password stretching. 00249 * 00250 * @return integer log2 number of iterations for password stretching 00251 * @see HASH_COUNT 00252 * @see $hashCount 00253 * @see setHashCount() 00254 */ 00255 public function getHashCount() { 00256 return isset(self::$hashCount) ? self::$hashCount : self::HASH_COUNT; 00257 } 00258 00259 /** 00260 * Method creates a salted hash for a given plaintext password 00261 * 00262 * @param string $password: plaintext password to create a salted hash from 00263 * @param string $salt: optional custom salt with setting to use 00264 * @return string salted hashed password 00265 */ 00266 public function getHashedPassword($password, $salt = NULL) { 00267 $saltedPW = NULL; 00268 00269 if (!empty($password)) { 00270 if (empty($salt) || !$this->isValidSalt($salt)) { 00271 $salt = $this->getGeneratedSalt(); 00272 } 00273 $saltedPW = $this->cryptPassword($password, $this->applySettingsToSalt($salt)); 00274 } 00275 00276 return $saltedPW; 00277 } 00278 00279 /** 00280 * Returns a string for mapping an int to the corresponding base 64 character. 00281 * 00282 * @return string string for mapping an int to the corresponding base 64 character 00283 */ 00284 protected function getItoa64() { 00285 return self::ITOA64; 00286 } 00287 00288 /** 00289 * Method returns maximum allowed log2 number of iterations for password stretching. 00290 * 00291 * @return integer maximum allowed log2 number of iterations for password stretching 00292 * @see MAX_HASH_COUNT 00293 * @see $maxHashCount 00294 * @see setMaxHashCount() 00295 */ 00296 public function getMaxHashCount() { 00297 return isset(self::$maxHashCount) ? self::$maxHashCount : self::MAX_HASH_COUNT; 00298 } 00299 00300 /** 00301 * Method returns minimum allowed log2 number of iterations for password stretching. 00302 * 00303 * @return integer minimum allowed log2 number of iterations for password stretching 00304 * @see MIN_HASH_COUNT 00305 * @see $minHashCount 00306 * @see setMinHashCount() 00307 */ 00308 public function getMinHashCount() { 00309 return isset(self::$minHashCount) ? self::$minHashCount : self::MIN_HASH_COUNT; 00310 } 00311 00312 /** 00313 * Returns length of a Blowfish salt in bytes. 00314 * 00315 * @return integer length of a Blowfish salt in bytes 00316 */ 00317 public function getSaltLength() { 00318 return self::$saltLengthPhpass; 00319 } 00320 00321 /** 00322 * Returns setting string of PHPass salted hashes. 00323 * 00324 * @return string setting string of PHPass salted hashes 00325 */ 00326 public function getSetting() { 00327 return self::$settingPhpass; 00328 } 00329 00330 /** 00331 * Checks whether a user's hashed password needs to be replaced with a new hash. 00332 * 00333 * This is typically called during the login process when the plain text 00334 * password is available. A new hash is needed when the desired iteration 00335 * count has changed through a change in the variable $hashCount or 00336 * HASH_COUNT or if the user's password hash was generated in an bulk update 00337 * with class ext_update. 00338 * 00339 * @param string $passString salted hash to check if it needs an update 00340 * @return boolean TRUE if salted hash needs an update, otherwise FALSE 00341 */ 00342 public function isHashUpdateNeeded($passString) { 00343 // Check whether this was an updated password. 00344 if ((strncmp($passString, '$P$', 3)) || (strlen($passString) != 34)) { 00345 return TRUE; 00346 } 00347 // Check whether the iteration count used differs from the standard number. 00348 return ($this->getCountLog2($passString) < $this->getHashCount()); 00349 } 00350 00351 /** 00352 * Method determines if a given string is a valid salt. 00353 * 00354 * @param string $salt: string to check 00355 * @return boolean TRUE if it's valid salt, otherwise FALSE 00356 */ 00357 public function isValidSalt($salt) { 00358 $isValid = $skip = FALSE; 00359 00360 $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength()); 00361 00362 if (strlen($salt) >= $reqLenBase64) { 00363 // salt with prefixed setting 00364 if (!strncmp('$', $salt, 1)) { 00365 if (!strncmp($this->getSetting(), $salt, strlen($this->getSetting()))) { 00366 $isValid = TRUE; 00367 $salt = substr($salt, strrpos($salt, '$') + 2); 00368 } else { 00369 $skip = TRUE; 00370 } 00371 } 00372 00373 // checking base64 characters 00374 if (!$skip && (strlen($salt) >= $reqLenBase64)) { 00375 if (preg_match('/^[' . preg_quote($this->getItoa64(),'/') . ']{' . $reqLenBase64 . ',' . $reqLenBase64 . '}$/', substr($salt, 0, $reqLenBase64))) { 00376 $isValid = TRUE; 00377 } 00378 } 00379 } 00380 00381 return $isValid; 00382 } 00383 00384 /** 00385 * Method determines if a given string is a valid salted hashed password. 00386 * 00387 * @param string $saltedPW: string to check 00388 * @return boolean TRUE if it's valid salted hashed password, otherwise FALSE 00389 */ 00390 public function isValidSaltedPW($saltedPW) { 00391 $isValid = FALSE; 00392 00393 $isValid = (!strncmp($this->getSetting(), $saltedPW, strlen($this->getSetting()))) ? TRUE : FALSE; 00394 if ($isValid) { 00395 $isValid = $this->isValidSalt($saltedPW); 00396 } 00397 00398 return $isValid; 00399 } 00400 00401 /** 00402 * Method sets log2 number of iterations for password stretching. 00403 * 00404 * @param integer $hashCount: log2 number of iterations for password stretching to set 00405 * @see HASH_COUNT 00406 * @see $hashCount 00407 * @see getHashCount() 00408 */ 00409 public function setHashCount($hashCount = NULL) { 00410 self::$hashCount = !is_NULL($hashCount) && is_int($hashCount) && $hashCount >= $this->getMinHashCount() && $hashCount <= $this->getMaxHashCount() ? $hashCount : self::HASH_COUNT; 00411 } 00412 00413 /** 00414 * Method sets maximum allowed log2 number of iterations for password stretching. 00415 * 00416 * @param integer $maxHashCount: maximum allowed log2 number of iterations for password stretching to set 00417 * @see MAX_HASH_COUNT 00418 * @see $maxHashCount 00419 * @see getMaxHashCount() 00420 */ 00421 public function setMaxHashCount($maxHashCount = NULL) { 00422 self::$maxHashCount = !is_NULL($maxHashCount) && is_int($maxHashCount) ? $maxHashCount : self::MAX_HASH_COUNT; 00423 } 00424 00425 /** 00426 * Method sets minimum allowed log2 number of iterations for password stretching. 00427 * 00428 * @param integer $minHashCount minimum allowed log2 number of iterations for password stretching to set 00429 * @see MIN_HASH_COUNT 00430 * @see $minHashCount 00431 * @see getMinHashCount() 00432 */ 00433 public function setMinHashCount($minHashCount = NULL) { 00434 self::$minHashCount = !is_NULL($minHashCount) && is_int($minHashCount) ? $minHashCount : self::MIN_HASH_COUNT; 00435 } 00436 } 00437 00438 00439 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_phpass.php'])) { 00440 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_phpass.php']); 00441 } 00442 ?>
1.8.0