Cleanup: Updated copyright comments
[Packages/TYPO3.CMS.git] / typo3 / sysext / saltedpasswords / tests / tx_saltedpasswords_salts_blowfishTest.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2009-2011 Marcus Krause <marcus#exp2009@t3sec.info>
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /**
28 * Contains class "tx_saltedpasswords_salts_blowfish"
29 * that provides Blowfish salted hashing.
30 *
31 * $Id$
32 */
33
34 /**
35 * Testcases for class tx_saltedpasswords_salts_blowfish.
36 *
37 * @author Marcus Krause <marcus#exp2009@t3sec.info>
38 * @package TYPO3
39 * @subpackage tx_saltedpasswords
40 */
41 class tx_saltedpasswords_salts_blowfishTest extends tx_phpunit_testcase {
42
43
44 /**
45 * Keeps instance of object to test.
46 *
47 * @var tx_saltedpasswords_salts_blowfish
48 */
49 protected $objectInstance = NULL;
50
51
52 /**
53 * Sets up the fixtures for this testcase.
54 *
55 * @return void
56 */
57 public function setUp() {
58 $this->objectInstance = t3lib_div::makeInstance('tx_saltedpasswords_salts_blowfish');
59 }
60
61 /**
62 * Tears down objects and settings created in this testcase.
63 *
64 * @return void
65 */
66 public function tearDown() {
67 unset($this->objectInstance);
68 }
69
70 /**
71 * Marks tests as skipped if the blowfish method is not available.
72 *
73 * @return void
74 */
75 protected function skipTestIfBlowfishIsNotAvailable() {
76 if (!CRYPT_BLOWFISH) {
77 $this->markTestSkipped('Blowfish is not supported on your platform.');
78 }
79 }
80
81 /**
82 * @test
83 */
84 public function hasCorrectBaseClass() {
85
86 $hasCorrectBaseClass = (0 === strcmp('tx_saltedpasswords_salts_blowfish', get_class($this->objectInstance))) ? TRUE : FALSE;
87
88 // XCLASS ?
89 if (!$hasCorrectBaseClass && FALSE != get_parent_class($this->objectInstance)) {
90 $hasCorrectBaseClass = is_subclass_of($this->objectInstance, 'tx_saltedpasswords_salts_blowfish');
91 }
92
93 $this->assertTrue($hasCorrectBaseClass);
94 }
95
96 /**
97 * @test
98 */
99 public function nonZeroSaltLength() {
100 $this->assertTrue($this->objectInstance->getSaltLength() > 0);
101 }
102
103 /**
104 * @test
105 */
106 public function emptyPasswordResultsInNullSaltedPassword() {
107 $password = '';
108 $this->assertNull($this->objectInstance->getHashedPassword($password));
109 }
110
111 /**
112 * @test
113 */
114 public function nonEmptyPasswordResultsInNonNullSaltedPassword() {
115 $this->skipTestIfBlowfishIsNotAvailable();
116
117 $password = 'a';
118 $this->assertNotNull($this->objectInstance->getHashedPassword($password));
119 }
120
121 /**
122 * @test
123 */
124 public function createdSaltedHashOfProperStructure() {
125 $this->skipTestIfBlowfishIsNotAvailable();
126
127 $password = 'password';
128 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
129 $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPassword));
130 }
131
132 /**
133 * @test
134 */
135 public function createdSaltedHashOfProperStructureForCustomSaltWithoutSetting() {
136 $this->skipTestIfBlowfishIsNotAvailable();
137
138 $password = 'password';
139
140 // custom salt without setting
141 $randomBytes = t3lib_div::generateRandomBytes($this->objectInstance->getSaltLength());
142 $salt = $this->objectInstance->base64Encode($randomBytes, $this->objectInstance->getSaltLength());
143 $this->assertTrue($this->objectInstance->isValidSalt($salt));
144
145 $saltedHashPassword = $this->objectInstance->getHashedPassword($password, $salt);
146 $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPassword));
147 }
148
149 /**
150 * @test
151 */
152 public function createdSaltedHashOfProperStructureForMaximumHashCount() {
153 $this->skipTestIfBlowfishIsNotAvailable();
154
155 $password = 'password';
156 $maxHashCount = $this->objectInstance->getMaxHashCount();
157 $this->objectInstance->setHashCount($maxHashCount);
158 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
159 $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPassword));
160 // reset hashcount
161 $this->objectInstance->setHashCount(NULL);
162 }
163
164 /**
165 * @test
166 */
167 public function createdSaltedHashOfProperStructureForMinimumHashCount() {
168 $this->skipTestIfBlowfishIsNotAvailable();
169
170 $password = 'password';
171 $minHashCount = $this->objectInstance->getMinHashCount();
172 $this->objectInstance->setHashCount($minHashCount);
173 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
174 $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPassword));
175 // reset hashcount
176 $this->objectInstance->setHashCount(NULL);
177 }
178
179 /**
180 * Tests authentication procedure with alphabet characters.
181 *
182 * Checks if a "plain-text password" is everytime mapped to the
183 * same "salted password hash" when using the same salt.
184 *
185 * @test
186 */
187 public function authenticationWithValidAlphaCharClassPassword() {
188 $this->skipTestIfBlowfishIsNotAvailable();
189
190 $password = 'aEjOtY';
191
192 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
193 $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPassword));
194 }
195
196 /**
197 * Tests authentication procedure with numeric characters.
198 *
199 * Checks if a "plain-text password" is everytime mapped to the
200 * same "salted password hash" when using the same salt.
201 *
202 * @test
203 */
204 public function authenticationWithValidNumericCharClassPassword() {
205 $this->skipTestIfBlowfishIsNotAvailable();
206
207 $password = '01369';
208
209 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
210 $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPassword));
211 }
212
213 /**
214 * Tests authentication procedure with US-ASCII special characters.
215 *
216 * Checks if a "plain-text password" is everytime mapped to the
217 * same "salted password hash" when using the same salt.
218 *
219 * @test
220 */
221 public function authenticationWithValidAsciiSpecialCharClassPassword() {
222 $this->skipTestIfBlowfishIsNotAvailable();
223
224 $password = ' !"#$%&\'()*+,-./:;<=>?@[\]^_`{|}~';
225
226 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
227 $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPassword));
228 }
229
230 /**
231 * Tests authentication procedure with latin1 special characters.
232 *
233 * Checks if a "plain-text password" is everytime mapped to the
234 * same "salted password hash" when using the same salt.
235 *
236 * @test
237 */
238 public function authenticationWithValidLatin1SpecialCharClassPassword() {
239 $this->skipTestIfBlowfishIsNotAvailable();
240
241 $password = '';
242 for ($i = 160; $i <= 191; $i++) {
243 $password .= chr($i);
244 }
245 $password .= chr(215) . chr(247);
246
247 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
248 $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPassword));
249 }
250
251 /**
252 * Tests authentication procedure with latin1 umlauts.
253 *
254 * Checks if a "plain-text password" is everytime mapped to the
255 * same "salted password hash" when using the same salt.
256 *
257 * @test
258 */
259 public function authenticationWithValidLatin1UmlautCharClassPassword() {
260 $this->skipTestIfBlowfishIsNotAvailable();
261
262 $password = '';
263 for ($i = 192; $i <= 214; $i++) {
264 $password .= chr($i);
265 }
266 for ($i = 216; $i <= 246; $i++) {
267 $password .= chr($i);
268 }
269 for ($i = 248; $i <= 255; $i++) {
270 $password .= chr($i);
271 }
272
273 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
274 $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPassword));
275 }
276
277 /**
278 * @test
279 */
280 public function authenticationWithNonValidPassword() {
281 $this->skipTestIfBlowfishIsNotAvailable();
282
283 $password = 'password';
284 $password1 = $password . 'INVALID';
285 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
286 $this->assertFalse($this->objectInstance->checkPassword($password1, $saltedHashPassword));
287 }
288
289 /**
290 * @test
291 */
292 public function passwordVariationsResultInDifferentHashes() {
293 $this->skipTestIfBlowfishIsNotAvailable();
294
295 $pad = 'a';
296 $password = '';
297 $criticalPwLength = 0;
298 // We're using a constant salt.
299 $saltedHashPasswordPrevious = $saltedHashPasswordCurrent = $salt = $this->objectInstance->getHashedPassword($pad);
300
301 for ($i = 0; $i <= 128; $i += 8) {
302 $password = str_repeat($pad, max($i, 1));
303 $saltedHashPasswordPrevious = $saltedHashPasswordCurrent;
304 $saltedHashPasswordCurrent = $this->objectInstance->getHashedPassword($password, $salt);
305 if ($i > 0 && 0 == strcmp($saltedHashPasswordPrevious, $saltedHashPasswordCurrent)) {
306 $criticalPwLength = $i;
307 break;
308 }
309 }
310 $this->assertTrue(($criticalPwLength == 0) || ($criticalPwLength > 32), 'Duplicates of hashed passwords with plaintext password of length ' . $criticalPwLength . '+.');
311 }
312
313 /**
314 * @test
315 */
316 public function modifiedMinHashCount() {
317 $minHashCount = $this->objectInstance->getMinHashCount();
318 $this->objectInstance->setMinHashCount($minHashCount - 1);
319 $this->assertTrue($this->objectInstance->getMinHashCount() < $minHashCount);
320 $this->objectInstance->setMinHashCount($minHashCount + 1);
321 $this->assertTrue($this->objectInstance->getMinHashCount() > $minHashCount);
322 }
323
324 /**
325 * @test
326 */
327 public function modifiedMaxHashCount() {
328 $maxHashCount = $this->objectInstance->getMaxHashCount();
329 $this->objectInstance->setMaxHashCount($maxHashCount + 1);
330 $this->assertTrue($this->objectInstance->getMaxHashCount() > $maxHashCount);
331 $this->objectInstance->setMaxHashCount($maxHashCount - 1);
332 $this->assertTrue($this->objectInstance->getMaxHashCount() < $maxHashCount);
333 }
334
335 /**
336 * @test
337 */
338 public function modifiedHashCount() {
339 $hashCount = $this->objectInstance->getHashCount();
340 $this->objectInstance->setMaxHashCount($hashCount + 1);
341 $this->objectInstance->setHashCount($hashCount + 1);
342 $this->assertTrue($this->objectInstance->getHashCount() > $hashCount);
343 $this->objectInstance->setMinHashCount($hashCount - 1);
344 $this->objectInstance->setHashCount($hashCount - 1);
345 $this->assertTrue($this->objectInstance->getHashCount() < $hashCount);
346 // reset hashcount
347 $this->objectInstance->setHashCount(NULL);
348 }
349
350 /**
351 * @test
352 */
353 public function updateNecessityForValidSaltedPassword() {
354 $this->skipTestIfBlowfishIsNotAvailable();
355
356 $password = 'password';
357 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
358 $this->assertFalse($this->objectInstance->isHashUpdateNeeded($saltedHashPassword));
359 }
360
361 /**
362 * @test
363 */
364 public function updateNecessityForIncreasedHashcount() {
365 $password = 'password';
366 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
367 $increasedHashCount = $this->objectInstance->getHashCount() + 1;
368 $this->objectInstance->setMaxHashCount($increasedHashCount);
369 $this->objectInstance->setHashCount($increasedHashCount);
370 $this->assertTrue($this->objectInstance->isHashUpdateNeeded($saltedHashPassword));
371 // reset hashcount
372 $this->objectInstance->setHashCount(NULL);
373 }
374
375 /**
376 * @test
377 */
378 public function updateNecessityForDecreasedHashcount() {
379 $this->skipTestIfBlowfishIsNotAvailable();
380
381 $password = 'password';
382 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
383 $decreasedHashCount = $this->objectInstance->getHashCount() - 1;
384 $this->objectInstance->setMinHashCount($decreasedHashCount);
385 $this->objectInstance->setHashCount($decreasedHashCount);
386 $this->assertFalse($this->objectInstance->isHashUpdateNeeded($saltedHashPassword));
387 // reset hashcount
388 $this->objectInstance->setHashCount(NULL);
389 }
390 }
391 ?>