76aa4d56182a225656545b5d34dc4981255ed74a
[Packages/TYPO3.CMS.git] / typo3 / sysext / saltedpasswords / Classes / Salt / BlowfishSalt.php
1 <?php
2 namespace TYPO3\CMS\Saltedpasswords\Salt;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2009-2013 Marcus Krause <marcus#exp2009@t3sec.info>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the textfile GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29 /**
30 * Contains class "tx_saltedpasswords_salts_blowfish"
31 * that provides Blowfish salted hashing.
32 */
33 /**
34 * Class that implements Blowfish salted hashing based on PHP's
35 * crypt() function.
36 *
37 * Warning: Blowfish salted hashing with PHP's crypt() is not available
38 * on every system.
39 *
40 * @author Marcus Krause <marcus#exp2009@t3sec.info>
41 * @since 2009-09-06
42 */
43 class BlowfishSalt extends \TYPO3\CMS\Saltedpasswords\Salt\Md5Salt {
44
45 /**
46 * The default log2 number of iterations for password stretching.
47 */
48 const HASH_COUNT = 7;
49 /**
50 * The default maximum allowed log2 number of iterations for
51 * password stretching.
52 */
53 const MAX_HASH_COUNT = 17;
54 /**
55 * The default minimum allowed log2 number of iterations for
56 * password stretching.
57 */
58 const MIN_HASH_COUNT = 4;
59 /**
60 * Keeps log2 number
61 * of iterations for password stretching.
62 *
63 * @var integer
64 */
65 static protected $hashCount;
66
67 /**
68 * Keeps maximum allowed log2 number
69 * of iterations for password stretching.
70 *
71 * @var integer
72 */
73 static protected $maxHashCount;
74
75 /**
76 * Keeps minimum allowed log2 number
77 * of iterations for password stretching.
78 *
79 * @var integer
80 */
81 static protected $minHashCount;
82
83 /**
84 * Keeps length of a Blowfish salt in bytes.
85 *
86 * @var integer
87 */
88 static protected $saltLengthBlowfish = 16;
89
90 /**
91 * Setting string to indicate type of hashing method (blowfish).
92 *
93 * @var string
94 */
95 static protected $settingBlowfish = '$2a$';
96
97 /**
98 * Method applies settings (prefix, hash count) to a salt.
99 *
100 * Overwrites {@link tx_saltedpasswords_salts_md5::applySettingsToSalt()}
101 * with Blowfish specifics.
102 *
103 * @param string $salt A salt to apply setting to
104 * @return string Salt with setting
105 */
106 protected function applySettingsToSalt($salt) {
107 $saltWithSettings = $salt;
108 $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength());
109 // salt without setting
110 if (strlen($salt) == $reqLenBase64) {
111 $saltWithSettings = $this->getSetting() . sprintf('%02u', $this->getHashCount()) . '$' . $salt;
112 }
113 return $saltWithSettings;
114 }
115
116 /**
117 * Parses the log2 iteration count from a stored hash or setting string.
118 *
119 * @param string $setting Complete hash or a hash's setting string or to get log2 iteration count from
120 * @return integer Used hashcount for given hash string
121 */
122 protected function getCountLog2($setting) {
123 $countLog2 = NULL;
124 $setting = substr($setting, strlen($this->getSetting()));
125 $firstSplitPos = strpos($setting, '$');
126 // Hashcount existing
127 if ($firstSplitPos !== FALSE && $firstSplitPos <= 2 && is_numeric(substr($setting, 0, $firstSplitPos))) {
128 $countLog2 = intval(substr($setting, 0, $firstSplitPos));
129 }
130 return $countLog2;
131 }
132
133 /**
134 * Method returns log2 number of iterations for password stretching.
135 *
136 * @return integer log2 number of iterations for password stretching
137 * @see HASH_COUNT
138 * @see $hashCount
139 * @see setHashCount()
140 */
141 public function getHashCount() {
142 return isset(self::$hashCount) ? self::$hashCount : self::HASH_COUNT;
143 }
144
145 /**
146 * Method returns maximum allowed log2 number of iterations for password stretching.
147 *
148 * @return integer Maximum allowed log2 number of iterations for password stretching
149 * @see MAX_HASH_COUNT
150 * @see $maxHashCount
151 * @see setMaxHashCount()
152 */
153 public function getMaxHashCount() {
154 return isset(self::$maxHashCount) ? self::$maxHashCount : self::MAX_HASH_COUNT;
155 }
156
157 /**
158 * Returns wether all prequesites for the hashing methods are matched
159 *
160 * @return boolean Method available
161 */
162 public function isAvailable() {
163 return CRYPT_BLOWFISH;
164 }
165
166 /**
167 * Method returns minimum allowed log2 number of iterations for password stretching.
168 *
169 * @return integer Minimum allowed log2 number of iterations for password stretching
170 * @see MIN_HASH_COUNT
171 * @see $minHashCount
172 * @see setMinHashCount()
173 */
174 public function getMinHashCount() {
175 return isset(self::$minHashCount) ? self::$minHashCount : self::MIN_HASH_COUNT;
176 }
177
178 /**
179 * Returns length of a Blowfish salt in bytes.
180 *
181 * Overwrites {@link tx_saltedpasswords_salts_md5::getSaltLength()}
182 * with Blowfish specifics.
183 *
184 * @return integer Length of a Blowfish salt in bytes
185 */
186 public function getSaltLength() {
187 return self::$saltLengthBlowfish;
188 }
189
190 /**
191 * Returns setting string of Blowfish salted hashes.
192 *
193 * Overwrites {@link tx_saltedpasswords_salts_md5::getSetting()}
194 * with Blowfish specifics.
195 *
196 * @return string Setting string of Blowfish salted hashes
197 */
198 public function getSetting() {
199 return self::$settingBlowfish;
200 }
201
202 /**
203 * Checks whether a user's hashed password needs to be replaced with a new hash.
204 *
205 * This is typically called during the login process when the plain text
206 * password is available. A new hash is needed when the desired iteration
207 * count has changed through a change in the variable $hashCount or
208 * HASH_COUNT.
209 *
210 * @param string $saltedPW Salted hash to check if it needs an update
211 * @return boolean TRUE if salted hash needs an update, otherwise FALSE
212 */
213 public function isHashUpdateNeeded($saltedPW) {
214 // Check whether this was an updated password.
215 if (strncmp($saltedPW, '$2', 2) || !$this->isValidSalt($saltedPW)) {
216 return TRUE;
217 }
218 // Check whether the iteration count used differs from the standard number.
219 $countLog2 = $this->getCountLog2($saltedPW);
220 return !is_NULL($countLog2) && $countLog2 < $this->getHashCount();
221 }
222
223 /**
224 * Method determines if a given string is a valid salt.
225 *
226 * Overwrites {@link tx_saltedpasswords_salts_md5::isValidSalt()} with
227 * Blowfish specifics.
228 *
229 * @param string $salt String to check
230 * @return boolean TRUE if it's valid salt, otherwise FALSE
231 */
232 public function isValidSalt($salt) {
233 $isValid = ($skip = FALSE);
234 $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength());
235 if (strlen($salt) >= $reqLenBase64) {
236 // Salt with prefixed setting
237 if (!strncmp('$', $salt, 1)) {
238 if (!strncmp($this->getSetting(), $salt, strlen($this->getSetting()))) {
239 $isValid = TRUE;
240 $salt = substr($salt, strrpos($salt, '$') + 1);
241 } else {
242 $skip = TRUE;
243 }
244 }
245 // Checking base64 characters
246 if (!$skip && strlen($salt) >= $reqLenBase64) {
247 if (preg_match('/^[' . preg_quote($this->getItoa64(), '/') . ']{' . $reqLenBase64 . ',' . $reqLenBase64 . '}$/', substr($salt, 0, $reqLenBase64))) {
248 $isValid = TRUE;
249 }
250 }
251 }
252 return $isValid;
253 }
254
255 /**
256 * Method determines if a given string is a valid salted hashed password.
257 *
258 * @param string $saltedPW String to check
259 * @return boolean TRUE if it's valid salted hashed password, otherwise FALSE
260 */
261 public function isValidSaltedPW($saltedPW) {
262 $isValid = FALSE;
263 $isValid = !strncmp($this->getSetting(), $saltedPW, strlen($this->getSetting())) ? TRUE : FALSE;
264 if ($isValid) {
265 $isValid = $this->isValidSalt($saltedPW);
266 }
267 return $isValid;
268 }
269
270 /**
271 * Method sets log2 number of iterations for password stretching.
272 *
273 * @param integer $hashCount log2 number of iterations for password stretching to set
274 * @see HASH_COUNT
275 * @see $hashCount
276 * @see getHashCount()
277 */
278 public function setHashCount($hashCount = NULL) {
279 self::$hashCount = !is_NULL($hashCount) && is_int($hashCount) && $hashCount >= $this->getMinHashCount() && $hashCount <= $this->getMaxHashCount() ? $hashCount : self::HASH_COUNT;
280 }
281
282 /**
283 * Method sets maximum allowed log2 number of iterations for password stretching.
284 *
285 * @param integer $maxHashCount Maximum allowed log2 number of iterations for password stretching to set
286 * @see MAX_HASH_COUNT
287 * @see $maxHashCount
288 * @see getMaxHashCount()
289 */
290 public function setMaxHashCount($maxHashCount = NULL) {
291 self::$maxHashCount = !is_NULL($maxHashCount) && is_int($maxHashCount) ? $maxHashCount : self::MAX_HASH_COUNT;
292 }
293
294 /**
295 * Method sets minimum allowed log2 number of iterations for password stretching.
296 *
297 * @param integer $minHashCount Minimum allowed log2 number of iterations for password stretching to set
298 * @see MIN_HASH_COUNT
299 * @see $minHashCount
300 * @see getMinHashCount()
301 */
302 public function setMinHashCount($minHashCount = NULL) {
303 self::$minHashCount = !is_NULL($minHashCount) && is_int($minHashCount) ? $minHashCount : self::MIN_HASH_COUNT;
304 }
305
306 }
307
308
309 ?>