5ac6b459a73b2702090478ae3523994d0a8191f4
[Packages/TYPO3.CMS.git] / typo3 / sysext / saltedpasswords / Tests / Unit / Salt / PhpassSaltTest.php
1 <?php
2 namespace TYPO3\CMS\Saltedpasswords\Tests\Unit\Salt;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2009-2011 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 * Testcase for PhpassSalt
32 *
33 * @author Marcus Krause <marcus#exp2009@t3sec.info>
34 * @package TYPO3
35 * @subpackage tx_saltedpasswords
36 */
37 class PhpassSaltTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
38
39 /**
40 * Keeps instance of object to test.
41 *
42 * @var \TYPO3\CMS\Saltedpasswords\Salt\PhpassSalt
43 */
44 protected $objectInstance = NULL;
45
46 /**
47 * Sets up the fixtures for this testcase.
48 *
49 * @return void
50 */
51 public function setUp() {
52 $this->objectInstance = $this->getMock('TYPO3\\CMS\\Saltedpasswords\\Salt\\PhpassSalt', array('dummy'));
53 }
54
55 /**
56 * Tears down objects and settings created in this testcase.
57 *
58 * @return void
59 */
60 public function tearDown() {
61 $this->objectInstance = NULL;
62 }
63
64 /**
65 * @test
66 */
67 public function hasCorrectBaseClass() {
68 $hasCorrectBaseClass = 0 === strcmp('TYPO3\\CMS\\Saltedpasswords\\Salt\\PhpassSalt', get_class($this->objectInstance)) ? TRUE : FALSE;
69 // XCLASS ?
70 if (!$hasCorrectBaseClass && FALSE != get_parent_class($this->objectInstance)) {
71 $hasCorrectBaseClass = is_subclass_of($this->objectInstance, 'TYPO3\\CMS\\Saltedpasswords\\Salt\\PhpassSalt');
72 }
73 $this->assertTrue($hasCorrectBaseClass);
74 }
75
76 /**
77 * @test
78 */
79 public function nonZeroSaltLength() {
80 $this->assertTrue($this->objectInstance->getSaltLength() > 0);
81 }
82
83 /**
84 * @test
85 */
86 public function emptyPasswordResultsInNullSaltedPassword() {
87 $password = '';
88 $this->assertNull($this->objectInstance->getHashedPassword($password));
89 }
90
91 /**
92 * @test
93 */
94 public function nonEmptyPasswordResultsInNonNullSaltedPassword() {
95 $password = 'a';
96 $this->assertNotNull($this->objectInstance->getHashedPassword($password));
97 }
98
99 /**
100 * @test
101 */
102 public function createdSaltedHashOfProperStructure() {
103 $password = 'password';
104 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
105 $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPassword));
106 }
107
108 /**
109 * @test
110 */
111 public function createdSaltedHashOfProperStructureForCustomSaltWithoutSetting() {
112 $password = 'password';
113 // custom salt without setting
114 $randomBytes = \TYPO3\CMS\Core\Utility\GeneralUtility::generateRandomBytes($this->objectInstance->getSaltLength());
115 $salt = $this->objectInstance->base64Encode($randomBytes, $this->objectInstance->getSaltLength());
116 $this->assertTrue($this->objectInstance->isValidSalt($salt));
117 $saltedHashPassword = $this->objectInstance->getHashedPassword($password, $salt);
118 $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPassword));
119 }
120
121 /**
122 * @test
123 */
124 public function createdSaltedHashOfProperStructureForMaximumHashCount() {
125 $password = 'password';
126 $maxHashCount = $this->objectInstance->getMaxHashCount();
127 $this->objectInstance->setHashCount($maxHashCount);
128 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
129 $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPassword));
130 // reset hashcount
131 $this->objectInstance->setHashCount(NULL);
132 }
133
134 /**
135 * @test
136 */
137 public function createdSaltedHashOfProperStructureForMinimumHashCount() {
138 $password = 'password';
139 $minHashCount = $this->objectInstance->getMinHashCount();
140 $this->objectInstance->setHashCount($minHashCount);
141 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
142 $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPassword));
143 // reset hashcount
144 $this->objectInstance->setHashCount(NULL);
145 }
146
147 /**
148 * Tests authentication procedure with alphabet characters.
149 *
150 * Checks if a "plain-text password" is everytime mapped to the
151 * same "salted password hash" when using the same salt.
152 *
153 * @test
154 */
155 public function authenticationWithValidAlphaCharClassPassword() {
156 $password = 'aEjOtY';
157 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
158 $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPassword));
159 }
160
161 /**
162 * Tests authentication procedure with numeric characters.
163 *
164 * Checks if a "plain-text password" is everytime mapped to the
165 * same "salted password hash" when using the same salt.
166 *
167 * @test
168 */
169 public function authenticationWithValidNumericCharClassPassword() {
170 $password = '01369';
171 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
172 $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPassword));
173 }
174
175 /**
176 * Tests authentication procedure with US-ASCII special characters.
177 *
178 * Checks if a "plain-text password" is everytime mapped to the
179 * same "salted password hash" when using the same salt.
180 *
181 * @test
182 */
183 public function authenticationWithValidAsciiSpecialCharClassPassword() {
184 $password = ' !"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~';
185 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
186 $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPassword));
187 }
188
189 /**
190 * Tests authentication procedure with latin1 special characters.
191 *
192 * Checks if a "plain-text password" is everytime mapped to the
193 * same "salted password hash" when using the same salt.
194 *
195 * @test
196 */
197 public function authenticationWithValidLatin1SpecialCharClassPassword() {
198 $password = '';
199 for ($i = 160; $i <= 191; $i++) {
200 $password .= chr($i);
201 }
202 $password .= chr(215) . chr(247);
203 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
204 $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPassword));
205 }
206
207 /**
208 * Tests authentication procedure with latin1 umlauts.
209 *
210 * Checks if a "plain-text password" is everytime mapped to the
211 * same "salted password hash" when using the same salt.
212 *
213 * @test
214 */
215 public function authenticationWithValidLatin1UmlautCharClassPassword() {
216 $password = '';
217 for ($i = 192; $i <= 214; $i++) {
218 $password .= chr($i);
219 }
220 for ($i = 216; $i <= 246; $i++) {
221 $password .= chr($i);
222 }
223 for ($i = 248; $i <= 255; $i++) {
224 $password .= chr($i);
225 }
226 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
227 $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPassword));
228 }
229
230 /**
231 * @test
232 */
233 public function authenticationWithNonValidPassword() {
234 $password = 'password';
235 $password1 = $password . 'INVALID';
236 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
237 $this->assertFalse($this->objectInstance->checkPassword($password1, $saltedHashPassword));
238 }
239
240 /**
241 * @test
242 */
243 public function passwordVariationsResultInDifferentHashes() {
244 $pad = 'a';
245 $criticalPwLength = 0;
246 // We're using a constant salt.
247 $saltedHashPasswordCurrent = $salt = $this->objectInstance->getHashedPassword($pad);
248 for ($i = 0; $i <= 128; $i += 8) {
249 $password = str_repeat($pad, max($i, 1));
250 $saltedHashPasswordPrevious = $saltedHashPasswordCurrent;
251 $saltedHashPasswordCurrent = $this->objectInstance->getHashedPassword($password, $salt);
252 if ($i > 0 && 0 == strcmp($saltedHashPasswordPrevious, $saltedHashPasswordCurrent)) {
253 $criticalPwLength = $i;
254 break;
255 }
256 }
257 $this->assertTrue($criticalPwLength == 0 || $criticalPwLength > 32, 'Duplicates of hashed passwords with plaintext password of length ' . $criticalPwLength . '+.');
258 }
259
260 /**
261 * @test
262 */
263 public function modifiedMinHashCount() {
264 $minHashCount = $this->objectInstance->getMinHashCount();
265 $this->objectInstance->setMinHashCount($minHashCount - 1);
266 $this->assertTrue($this->objectInstance->getMinHashCount() < $minHashCount);
267 $this->objectInstance->setMinHashCount($minHashCount + 1);
268 $this->assertTrue($this->objectInstance->getMinHashCount() > $minHashCount);
269 }
270
271 /**
272 * @test
273 */
274 public function modifiedMaxHashCount() {
275 $maxHashCount = $this->objectInstance->getMaxHashCount();
276 $this->objectInstance->setMaxHashCount($maxHashCount + 1);
277 $this->assertTrue($this->objectInstance->getMaxHashCount() > $maxHashCount);
278 $this->objectInstance->setMaxHashCount($maxHashCount - 1);
279 $this->assertTrue($this->objectInstance->getMaxHashCount() < $maxHashCount);
280 }
281
282 /**
283 * @test
284 */
285 public function modifiedHashCount() {
286 $hashCount = $this->objectInstance->getHashCount();
287 $this->objectInstance->setMaxHashCount($hashCount + 1);
288 $this->objectInstance->setHashCount($hashCount + 1);
289 $this->assertTrue($this->objectInstance->getHashCount() > $hashCount);
290 $this->objectInstance->setMinHashCount($hashCount - 1);
291 $this->objectInstance->setHashCount($hashCount - 1);
292 $this->assertTrue($this->objectInstance->getHashCount() < $hashCount);
293 // reset hashcount
294 $this->objectInstance->setHashCount(NULL);
295 }
296
297 /**
298 * @test
299 */
300 public function updateNecessityForValidSaltedPassword() {
301 $password = 'password';
302 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
303 $this->assertFalse($this->objectInstance->isHashUpdateNeeded($saltedHashPassword));
304 }
305
306 /**
307 * @test
308 */
309 public function updateNecessityForIncreasedHashcount() {
310 $password = 'password';
311 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
312 $increasedHashCount = $this->objectInstance->getHashCount() + 1;
313 $this->objectInstance->setMaxHashCount($increasedHashCount);
314 $this->objectInstance->setHashCount($increasedHashCount);
315 $this->assertTrue($this->objectInstance->isHashUpdateNeeded($saltedHashPassword));
316 // reset hashcount
317 $this->objectInstance->setHashCount(NULL);
318 }
319
320 /**
321 * @test
322 */
323 public function updateNecessityForDecreasedHashcount() {
324 $password = 'password';
325 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
326 $decreasedHashCount = $this->objectInstance->getHashCount() - 1;
327 $this->objectInstance->setMinHashCount($decreasedHashCount);
328 $this->objectInstance->setHashCount($decreasedHashCount);
329 $this->assertFalse($this->objectInstance->isHashUpdateNeeded($saltedHashPassword));
330 // reset hashcount
331 $this->objectInstance->setHashCount(NULL);
332 }
333
334 }
335
336 ?>