[TASk] Remove dummy files from ext:saltedpasswords
[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 /**
31 * Class that implements Blowfish salted hashing based on PHP's
32 * crypt() function.
33 *
34 * Warning: Blowfish salted hashing with PHP's crypt() is not available
35 * on every system.
36 *
37 * @author Marcus Krause <marcus#exp2009@t3sec.info>
38 */
39 class BlowfishSalt extends \TYPO3\CMS\Saltedpasswords\Salt\Md5Salt {
40
41 /**
42 * The default log2 number of iterations for password stretching.
43 */
44 const HASH_COUNT = 7;
45 /**
46 * The default maximum allowed log2 number of iterations for
47 * password stretching.
48 */
49 const MAX_HASH_COUNT = 17;
50 /**
51 * The default minimum allowed log2 number of iterations for
52 * password stretching.
53 */
54 const MIN_HASH_COUNT = 4;
55 /**
56 * Keeps log2 number
57 * of iterations for password stretching.
58 *
59 * @var integer
60 */
61 static protected $hashCount;
62
63 /**
64 * Keeps maximum allowed log2 number
65 * of iterations for password stretching.
66 *
67 * @var integer
68 */
69 static protected $maxHashCount;
70
71 /**
72 * Keeps minimum allowed log2 number
73 * of iterations for password stretching.
74 *
75 * @var integer
76 */
77 static protected $minHashCount;
78
79 /**
80 * Keeps length of a Blowfish salt in bytes.
81 *
82 * @var integer
83 */
84 static protected $saltLengthBlowfish = 16;
85
86 /**
87 * Setting string to indicate type of hashing method (blowfish).
88 *
89 * @var string
90 */
91 static protected $settingBlowfish = '$2a$';
92
93 /**
94 * Method applies settings (prefix, hash count) to a salt.
95 *
96 * Overwrites {@link Md5Salt::applySettingsToSalt()}
97 * with Blowfish specifics.
98 *
99 * @param string $salt A salt to apply setting to
100 * @return string Salt with setting
101 */
102 protected function applySettingsToSalt($salt) {
103 $saltWithSettings = $salt;
104 $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength());
105 // salt without setting
106 if (strlen($salt) == $reqLenBase64) {
107 $saltWithSettings = $this->getSetting() . sprintf('%02u', $this->getHashCount()) . '$' . $salt;
108 }
109 return $saltWithSettings;
110 }
111
112 /**
113 * Parses the log2 iteration count from a stored hash or setting string.
114 *
115 * @param string $setting Complete hash or a hash's setting string or to get log2 iteration count from
116 * @return integer Used hashcount for given hash string
117 */
118 protected function getCountLog2($setting) {
119 $countLog2 = NULL;
120 $setting = substr($setting, strlen($this->getSetting()));
121 $firstSplitPos = strpos($setting, '$');
122 // Hashcount existing
123 if ($firstSplitPos !== FALSE && $firstSplitPos <= 2 && is_numeric(substr($setting, 0, $firstSplitPos))) {
124 $countLog2 = intval(substr($setting, 0, $firstSplitPos));
125 }
126 return $countLog2;
127 }
128
129 /**
130 * Method returns log2 number of iterations for password stretching.
131 *
132 * @return integer log2 number of iterations for password stretching
133 * @see HASH_COUNT
134 * @see $hashCount
135 * @see setHashCount()
136 */
137 public function getHashCount() {
138 return isset(self::$hashCount) ? self::$hashCount : self::HASH_COUNT;
139 }
140
141 /**
142 * Method returns maximum allowed log2 number of iterations for password stretching.
143 *
144 * @return integer Maximum allowed log2 number of iterations for password stretching
145 * @see MAX_HASH_COUNT
146 * @see $maxHashCount
147 * @see setMaxHashCount()
148 */
149 public function getMaxHashCount() {
150 return isset(self::$maxHashCount) ? self::$maxHashCount : self::MAX_HASH_COUNT;
151 }
152
153 /**
154 * Returns wether all prequesites for the hashing methods are matched
155 *
156 * @return boolean Method available
157 */
158 public function isAvailable() {
159 return CRYPT_BLOWFISH;
160 }
161
162 /**
163 * Method returns minimum allowed log2 number of iterations for password stretching.
164 *
165 * @return integer Minimum allowed log2 number of iterations for password stretching
166 * @see MIN_HASH_COUNT
167 * @see $minHashCount
168 * @see setMinHashCount()
169 */
170 public function getMinHashCount() {
171 return isset(self::$minHashCount) ? self::$minHashCount : self::MIN_HASH_COUNT;
172 }
173
174 /**
175 * Returns length of a Blowfish salt in bytes.
176 *
177 * Overwrites {@link Md5Salt::getSaltLength()}
178 * with Blowfish specifics.
179 *
180 * @return integer Length of a Blowfish salt in bytes
181 */
182 public function getSaltLength() {
183 return self::$saltLengthBlowfish;
184 }
185
186 /**
187 * Returns setting string of Blowfish salted hashes.
188 *
189 * Overwrites {@link Md5Salt::getSetting()}
190 * with Blowfish specifics.
191 *
192 * @return string Setting string of Blowfish salted hashes
193 */
194 public function getSetting() {
195 return self::$settingBlowfish;
196 }
197
198 /**
199 * Checks whether a user's hashed password needs to be replaced with a new hash.
200 *
201 * This is typically called during the login process when the plain text
202 * password is available. A new hash is needed when the desired iteration
203 * count has changed through a change in the variable $hashCount or
204 * HASH_COUNT.
205 *
206 * @param string $saltedPW Salted hash to check if it needs an update
207 * @return boolean TRUE if salted hash needs an update, otherwise FALSE
208 */
209 public function isHashUpdateNeeded($saltedPW) {
210 // Check whether this was an updated password.
211 if (strncmp($saltedPW, '$2', 2) || !$this->isValidSalt($saltedPW)) {
212 return TRUE;
213 }
214 // Check whether the iteration count used differs from the standard number.
215 $countLog2 = $this->getCountLog2($saltedPW);
216 return !is_NULL($countLog2) && $countLog2 < $this->getHashCount();
217 }
218
219 /**
220 * Method determines if a given string is a valid salt.
221 *
222 * Overwrites {@link Md5Salt::isValidSalt()} with
223 * Blowfish specifics.
224 *
225 * @param string $salt String to check
226 * @return boolean TRUE if it's valid salt, otherwise FALSE
227 */
228 public function isValidSalt($salt) {
229 $isValid = ($skip = FALSE);
230 $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength());
231 if (strlen($salt) >= $reqLenBase64) {
232 // Salt with prefixed setting
233 if (!strncmp('$', $salt, 1)) {
234 if (!strncmp($this->getSetting(), $salt, strlen($this->getSetting()))) {
235 $isValid = TRUE;
236 $salt = substr($salt, strrpos($salt, '$') + 1);
237 } else {
238 $skip = TRUE;
239 }
240 }
241 // Checking base64 characters
242 if (!$skip && strlen($salt) >= $reqLenBase64) {
243 if (preg_match('/^[' . preg_quote($this->getItoa64(), '/') . ']{' . $reqLenBase64 . ',' . $reqLenBase64 . '}$/', substr($salt, 0, $reqLenBase64))) {
244 $isValid = TRUE;
245 }
246 }
247 }
248 return $isValid;
249 }
250
251 /**
252 * Method determines if a given string is a valid salted hashed password.
253 *
254 * @param string $saltedPW String to check
255 * @return boolean TRUE if it's valid salted hashed password, otherwise FALSE
256 */
257 public function isValidSaltedPW($saltedPW) {
258 $isValid = FALSE;
259 $isValid = !strncmp($this->getSetting(), $saltedPW, strlen($this->getSetting())) ? TRUE : FALSE;
260 if ($isValid) {
261 $isValid = $this->isValidSalt($saltedPW);
262 }
263 return $isValid;
264 }
265
266 /**
267 * Method sets log2 number of iterations for password stretching.
268 *
269 * @param integer $hashCount log2 number of iterations for password stretching to set
270 * @see HASH_COUNT
271 * @see $hashCount
272 * @see getHashCount()
273 */
274 public function setHashCount($hashCount = NULL) {
275 self::$hashCount = !is_NULL($hashCount) && is_int($hashCount) && $hashCount >= $this->getMinHashCount() && $hashCount <= $this->getMaxHashCount() ? $hashCount : self::HASH_COUNT;
276 }
277
278 /**
279 * Method sets maximum allowed log2 number of iterations for password stretching.
280 *
281 * @param integer $maxHashCount Maximum allowed log2 number of iterations for password stretching to set
282 * @see MAX_HASH_COUNT
283 * @see $maxHashCount
284 * @see getMaxHashCount()
285 */
286 public function setMaxHashCount($maxHashCount = NULL) {
287 self::$maxHashCount = !is_NULL($maxHashCount) && is_int($maxHashCount) ? $maxHashCount : self::MAX_HASH_COUNT;
288 }
289
290 /**
291 * Method sets minimum allowed log2 number of iterations for password stretching.
292 *
293 * @param integer $minHashCount Minimum allowed log2 number of iterations for password stretching to set
294 * @see MIN_HASH_COUNT
295 * @see $minHashCount
296 * @see getMinHashCount()
297 */
298 public function setMinHashCount($minHashCount = NULL) {
299 self::$minHashCount = !is_NULL($minHashCount) && is_int($minHashCount) ? $minHashCount : self::MIN_HASH_COUNT;
300 }
301
302 }
303
304
305 ?>