TYPO3 API  SVNRelease
class.tx_saltedpasswords_salts_blowfish.php
Go to the documentation of this file.
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_blowfish"
00029  * that provides Blowfish salted hashing.
00030  *
00031  * $Id: class.tx_saltedpasswords_salts_blowfish.php 10120 2011-01-18 20:03:36Z ohader $
00032  */
00033 
00034 
00035 /**
00036  * Class that implements Blowfish salted hashing based on PHP's
00037  * crypt() function.
00038  *
00039  * Warning: Blowfish salted hashing with PHP's crypt() is not available
00040  * on every system.
00041  *
00042  * @author      Marcus Krause <marcus#exp2009@t3sec.info>
00043  *
00044  * @since       2009-09-06
00045  * @package     TYPO3
00046  * @subpackage  tx_saltedpasswords
00047  */
00048 class tx_saltedpasswords_salts_blowfish extends tx_saltedpasswords_salts_md5 {
00049     /**
00050      * The default log2 number of iterations for password stretching.
00051      */
00052     const HASH_COUNT = 7;
00053 
00054     /**
00055      * The default maximum allowed log2 number of iterations for
00056      * password stretching.
00057      */
00058     const MAX_HASH_COUNT = 17;
00059 
00060     /**
00061      * The default minimum allowed log2 number of iterations for
00062      * password stretching.
00063      */
00064     const MIN_HASH_COUNT = 4;
00065 
00066 
00067     /**
00068      * Keeps log2 number
00069      * of iterations for password stretching.
00070      *
00071      * @var integer
00072      */
00073     static protected $hashCount;
00074 
00075     /**
00076      * Keeps maximum allowed log2 number
00077      * of iterations for password stretching.
00078      *
00079      * @var integer
00080      */
00081     static protected $maxHashCount;
00082 
00083     /**
00084      * Keeps minimum allowed log2 number
00085      * of iterations for password stretching.
00086      *
00087      * @var integer
00088      */
00089     static protected $minHashCount;
00090 
00091     /**
00092      * Keeps length of a Blowfish salt in bytes.
00093      *
00094      * @var integer
00095      */
00096     static protected $saltLengthBlowfish = 16;
00097 
00098     /**
00099      * Setting string to indicate type of hashing method (blowfish).
00100      *
00101      * @var string
00102      */
00103     static protected $settingBlowfish = '$2a$';
00104 
00105 
00106     /**
00107      * Method applies settings (prefix, hash count) to a salt.
00108      *
00109      * Overwrites {@link tx_saltedpasswords_salts_md5::applySettingsToSalt()}
00110      * with Blowfish specifics.
00111      *
00112      * @param   string      $salt:  a salt to apply setting to
00113      * @return  string      salt with setting
00114      */
00115     protected function applySettingsToSalt($salt) {
00116         $saltWithSettings = $salt;
00117 
00118         $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength());
00119 
00120                     // salt without setting
00121         if (strlen($salt) == $reqLenBase64) {
00122             $saltWithSettings = $this->getSetting()
00123                 . sprintf('%02u', $this->getHashCount()) . '$'
00124                 . $salt;
00125         }
00126 
00127         return $saltWithSettings;
00128     }
00129 
00130     /**
00131      * Parses the log2 iteration count from a stored hash or setting string.
00132      *
00133      * @param   string      $setting: complete hash or a hash's setting string or to get log2 iteration count from
00134      * @return  integer     used hashcount for given hash string
00135      */
00136     protected function getCountLog2($setting) {
00137         $countLog2 = NULL;
00138         $setting = substr($setting, strlen($this->getSetting()));
00139         $firstSplitPos = strpos($setting, '$');
00140 
00141             // hashcount existing
00142         if (($firstSplitPos !== FALSE)
00143             && ($firstSplitPos <= 2) && is_numeric(substr($setting, 0, $firstSplitPos))) {
00144             $countLog2 = intval(substr($setting, 0, $firstSplitPos));
00145         }
00146 
00147         return $countLog2;
00148     }
00149 
00150     /**
00151      * Method returns log2 number of iterations for password stretching.
00152      *
00153      * @return  integer     log2 number of iterations for password stretching
00154      * @see     HASH_COUNT
00155      * @see     $hashCount
00156      * @see     setHashCount()
00157      */
00158     public function getHashCount() {
00159         return isset(self::$hashCount) ? self::$hashCount : self::HASH_COUNT;
00160     }
00161 
00162     /**
00163      * Method returns maximum allowed log2 number of iterations for password stretching.
00164      *
00165      * @return  integer     maximum allowed log2 number of iterations for password stretching
00166      * @see     MAX_HASH_COUNT
00167      * @see     $maxHashCount
00168      * @see     setMaxHashCount()
00169      */
00170     public function getMaxHashCount() {
00171         return isset(self::$maxHashCount) ? self::$maxHashCount : self::MAX_HASH_COUNT;
00172     }
00173 
00174     /**
00175      * Returns wether all prequesites for the hashing methods are matched
00176      *
00177      * @return  boolean     method available
00178      */
00179     public function isAvailable() {
00180         return CRYPT_BLOWFISH;
00181     }
00182 
00183     /**
00184      * Method returns minimum allowed log2 number of iterations for password stretching.
00185      *
00186      * @return  integer     minimum allowed log2 number of iterations for password stretching
00187      * @see     MIN_HASH_COUNT
00188      * @see     $minHashCount
00189      * @see     setMinHashCount()
00190      */
00191     public function getMinHashCount() {
00192         return isset(self::$minHashCount) ? self::$minHashCount : self::MIN_HASH_COUNT;
00193     }
00194 
00195     /**
00196      * Returns length of a Blowfish salt in bytes.
00197      *
00198      * Overwrites {@link tx_saltedpasswords_salts_md5::getSaltLength()}
00199      * with Blowfish specifics.
00200      *
00201      * @return  integer     length of a Blowfish salt in bytes
00202      */
00203     public function getSaltLength() {
00204         return self::$saltLengthBlowfish;
00205     }
00206 
00207     /**
00208      * Returns setting string of Blowfish salted hashes.
00209      *
00210      * Overwrites {@link tx_saltedpasswords_salts_md5::getSetting()}
00211      * with Blowfish specifics.
00212      *
00213      * @return  string      setting string of Blowfish salted hashes
00214      */
00215     public function getSetting() {
00216         return self::$settingBlowfish;
00217     }
00218 
00219     /**
00220      * Checks whether a user's hashed password needs to be replaced with a new hash.
00221      *
00222      * This is typically called during the login process when the plain text
00223      * password is available.  A new hash is needed when the desired iteration
00224      * count has changed through a change in the variable $hashCount or
00225      * HASH_COUNT.
00226      *
00227      * @param   string      $saltedPW: salted hash to check if it needs an update
00228      * @return  boolean     TRUE if salted hash needs an update, otherwise FALSE
00229      */
00230     public function isHashUpdateNeeded($saltedPW) {
00231             // Check whether this was an updated password.
00232         if ((strncmp($saltedPW, '$2', 2)) || !$this->isValidSalt($saltedPW)) {
00233             return TRUE;
00234         }
00235             // Check whether the iteration count used differs from the standard number.
00236         $countLog2 = $this->getCountLog2($saltedPW);
00237 
00238         return (!is_NULL($countLog2) && ($countLog2 < $this->getHashCount()));
00239     }
00240 
00241     /**
00242      * Method determines if a given string is a valid salt.
00243      *
00244      * Overwrites {@link tx_saltedpasswords_salts_md5::isValidSalt()} with
00245      * Blowfish specifics.
00246      *
00247      * @param   string      $salt: string to check
00248      * @return  boolean     TRUE if it's valid salt, otherwise FALSE
00249      */
00250     public function isValidSalt($salt) {
00251         $isValid = $skip = FALSE;
00252 
00253         $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength());
00254 
00255         if (strlen($salt) >= $reqLenBase64) {
00256                 // salt with prefixed setting
00257             if (!strncmp('$', $salt, 1)) {
00258                 if (!strncmp($this->getSetting(), $salt, strlen($this->getSetting()))) {
00259                     $isValid = TRUE;
00260                     $salt = substr($salt, strrpos($salt, '$') + 1);
00261                 } else {
00262                     $skip = TRUE;
00263                 }
00264             }
00265 
00266                 // checking base64 characters
00267             if (!$skip && (strlen($salt) >= $reqLenBase64)) {
00268                 if (preg_match('/^[' . preg_quote($this->getItoa64(),'/') . ']{' . $reqLenBase64 . ',' . $reqLenBase64 . '}$/', substr($salt, 0, $reqLenBase64))) {
00269                     $isValid = TRUE;
00270                 }
00271             }
00272         }
00273 
00274         return $isValid;
00275     }
00276 
00277     /**
00278      * Method determines if a given string is a valid salted hashed password.
00279      *
00280      * @param   string      $saltedPW: string to check
00281      * @return  boolean     TRUE if it's valid salted hashed password, otherwise FALSE
00282      */
00283     public function isValidSaltedPW($saltedPW) {
00284         $isValid = FALSE;
00285 
00286         $isValid = (!strncmp($this->getSetting(), $saltedPW, strlen($this->getSetting()))) ? TRUE : FALSE;
00287 
00288         if ($isValid) {
00289             $isValid = $this->isValidSalt($saltedPW);
00290         }
00291 
00292         return $isValid;
00293     }
00294 
00295     /**
00296      * Method sets log2 number of iterations for password stretching.
00297      *
00298      * @param   integer     $hashCount: log2 number of iterations for password stretching to set
00299      * @see     HASH_COUNT
00300      * @see     $hashCount
00301      * @see     getHashCount()
00302      */
00303     public function setHashCount($hashCount = NULL) {
00304         self::$hashCount = !is_NULL($hashCount) && is_int($hashCount) && $hashCount >= $this->getMinHashCount() && $hashCount <= $this->getMaxHashCount() ? $hashCount : self::HASH_COUNT;
00305     }
00306 
00307     /**
00308      * Method sets maximum allowed log2 number of iterations for password stretching.
00309      *
00310      * @param   integer     $maxHashCount: maximum allowed log2 number of iterations for password stretching to set
00311      * @see     MAX_HASH_COUNT
00312      * @see     $maxHashCount
00313      * @see     getMaxHashCount()
00314      */
00315     public function setMaxHashCount($maxHashCount = NULL) {
00316         self::$maxHashCount = !is_NULL($maxHashCount) && is_int($maxHashCount) ? $maxHashCount : self::MAX_HASH_COUNT;
00317     }
00318 
00319     /**
00320      * Method sets minimum allowed log2 number of iterations for password stretching.
00321      *
00322      * @param   integer     $minHashCount: minimum allowed log2 number of iterations for password stretching to set
00323      * @see     MIN_HASH_COUNT
00324      * @see     $minHashCount
00325      * @see     getMinHashCount()
00326      */
00327     public function setMinHashCount($minHashCount = NULL) {
00328         self::$minHashCount = !is_NULL($minHashCount) && is_int($minHashCount) ? $minHashCount : self::MIN_HASH_COUNT;
00329     }
00330 }
00331 
00332 
00333 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_blowfish.php'])) {
00334     include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_blowfish.php']);
00335 }
00336 ?>