[BUGFIX] Add missing namespace parts
[Packages/TYPO3.CMS.git] / typo3 / sysext / saltedpasswords / Tests / Unit / Salt / Md5SaltTest.php
1 <?php
2 namespace TYPO3\CMS\Saltedpasswords\Tests\Unit\Salt;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Crypto\Random;
18
19 /**
20 * Testcases for Md5Salt
21 */
22 class Md5SaltTest extends \TYPO3\Components\TestingFramework\Core\Unit\UnitTestCase
23 {
24 /**
25 * Keeps instance of object to test.
26 *
27 * @var \TYPO3\CMS\Saltedpasswords\Salt\Md5Salt
28 */
29 protected $objectInstance = null;
30
31 /**
32 * Sets up the fixtures for this testcase.
33 *
34 * @return void
35 */
36 protected function setUp()
37 {
38 $this->objectInstance = $this->getMockBuilder(\TYPO3\CMS\Saltedpasswords\Salt\Md5Salt::class)
39 ->setMethods(['dummy'])
40 ->getMock();
41 }
42
43 /**
44 * Prepares a message to be shown when a salted hashing is not supported.
45 *
46 * @return string Empty string if salted hashing method is available, otherwise an according warning
47 */
48 protected function getWarningWhenMethodUnavailable()
49 {
50 $warningMsg = '';
51 if (!CRYPT_MD5) {
52 $warningMsg = 'MD5 is not supported on your platform. ' . 'Then, some of the md5 tests will fail.';
53 }
54 return $warningMsg;
55 }
56
57 /**
58 * @test
59 */
60 public function hasCorrectBaseClass()
61 {
62 $hasCorrectBaseClass = get_class($this->objectInstance) === \TYPO3\CMS\Saltedpasswords\Salt\Md5Salt::class;
63 // XCLASS ?
64 if (!$hasCorrectBaseClass && false != get_parent_class($this->objectInstance)) {
65 $hasCorrectBaseClass = is_subclass_of($this->objectInstance, \TYPO3\CMS\Saltedpasswords\Salt\Md5Salt::class);
66 }
67 $this->assertTrue($hasCorrectBaseClass);
68 }
69
70 /**
71 * @test
72 */
73 public function nonZeroSaltLength()
74 {
75 $this->assertTrue($this->objectInstance->getSaltLength() > 0);
76 }
77
78 /**
79 * @test
80 */
81 public function emptyPasswordResultsInNullSaltedPassword()
82 {
83 $password = '';
84 $this->assertNull($this->objectInstance->getHashedPassword($password));
85 }
86
87 /**
88 * @test
89 */
90 public function nonEmptyPasswordResultsInNonNullSaltedPassword()
91 {
92 $password = 'a';
93 $this->assertNotNull($this->objectInstance->getHashedPassword($password), $this->getWarningWhenMethodUnavailable());
94 }
95
96 /**
97 * @test
98 */
99 public function createdSaltedHashOfProperStructure()
100 {
101 $password = 'password';
102 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
103 $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPassword), $this->getWarningWhenMethodUnavailable());
104 }
105
106 /**
107 * @test
108 */
109 public function createdSaltedHashOfProperStructureForCustomSaltWithoutSetting()
110 {
111 $password = 'password';
112 // custom salt without setting
113 $randomBytes = (new Random())->generateRandomBytes($this->objectInstance->getSaltLength());
114 $salt = $this->objectInstance->base64Encode($randomBytes, $this->objectInstance->getSaltLength());
115 $this->assertTrue($this->objectInstance->isValidSalt($salt), $this->getWarningWhenMethodUnavailable());
116 $saltedHashPassword = $this->objectInstance->getHashedPassword($password, $salt);
117 $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPassword), $this->getWarningWhenMethodUnavailable());
118 }
119
120 /**
121 * Tests authentication procedure with alphabet characters.
122 *
123 * Checks if a "plain-text password" is every time mapped to the
124 * same "salted password hash" when using the same salt.
125 *
126 * @test
127 */
128 public function authenticationWithValidAlphaCharClassPassword()
129 {
130 $password = 'aEjOtY';
131 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
132 $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPassword), $this->getWarningWhenMethodUnavailable());
133 }
134
135 /**
136 * Tests authentication procedure with numeric characters.
137 *
138 * Checks if a "plain-text password" is every time mapped to the
139 * same "salted password hash" when using the same salt.
140 *
141 * @test
142 */
143 public function authenticationWithValidNumericCharClassPassword()
144 {
145 $password = '01369';
146 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
147 $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPassword), $this->getWarningWhenMethodUnavailable());
148 }
149
150 /**
151 * Tests authentication procedure with US-ASCII special characters.
152 *
153 * Checks if a "plain-text password" is every time mapped to the
154 * same "salted password hash" when using the same salt.
155 *
156 * @test
157 */
158 public function authenticationWithValidAsciiSpecialCharClassPassword()
159 {
160 $password = ' !"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~';
161 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
162 $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPassword), $this->getWarningWhenMethodUnavailable());
163 }
164
165 /**
166 * Tests authentication procedure with latin1 special characters.
167 *
168 * Checks if a "plain-text password" is every time mapped to the
169 * same "salted password hash" when using the same salt.
170 *
171 * @test
172 */
173 public function authenticationWithValidLatin1SpecialCharClassPassword()
174 {
175 $password = '';
176 for ($i = 160; $i <= 191; $i++) {
177 $password .= chr($i);
178 }
179 $password .= chr(215) . chr(247);
180 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
181 $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPassword), $this->getWarningWhenMethodUnavailable());
182 }
183
184 /**
185 * Tests authentication procedure with latin1 umlauts.
186 *
187 * Checks if a "plain-text password" is every time mapped to the
188 * same "salted password hash" when using the same salt.
189 *
190 * @test
191 */
192 public function authenticationWithValidLatin1UmlautCharClassPassword()
193 {
194 $password = '';
195 for ($i = 192; $i <= 214; $i++) {
196 $password .= chr($i);
197 }
198 for ($i = 216; $i <= 246; $i++) {
199 $password .= chr($i);
200 }
201 for ($i = 248; $i <= 255; $i++) {
202 $password .= chr($i);
203 }
204 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
205 $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPassword), $this->getWarningWhenMethodUnavailable());
206 }
207
208 /**
209 * @test
210 */
211 public function authenticationWithNonValidPassword()
212 {
213 $password = 'password';
214 $password1 = $password . 'INVALID';
215 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
216 $this->assertFalse($this->objectInstance->checkPassword($password1, $saltedHashPassword), $this->getWarningWhenMethodUnavailable());
217 }
218
219 /**
220 * @test
221 */
222 public function passwordVariationsResultInDifferentHashes()
223 {
224 $pad = 'a';
225 $criticalPwLength = 0;
226 // We're using a constant salt.
227 $saltedHashPasswordCurrent = $salt = $this->objectInstance->getHashedPassword($pad);
228 for ($i = 0; $i <= 128; $i += 8) {
229 $password = str_repeat($pad, max($i, 1));
230 $saltedHashPasswordPrevious = $saltedHashPasswordCurrent;
231 $saltedHashPasswordCurrent = $this->objectInstance->getHashedPassword($password, $salt);
232 if ($i > 0 && $saltedHashPasswordPrevious === $saltedHashPasswordCurrent) {
233 $criticalPwLength = $i;
234 break;
235 }
236 }
237 $this->assertTrue($criticalPwLength == 0 || $criticalPwLength > 32, $this->getWarningWhenMethodUnavailable() . 'Duplicates of hashed passwords with plaintext password of length ' . $criticalPwLength . '+.');
238 }
239
240 /**
241 * @test
242 */
243 public function noUpdateNecessityForMd5()
244 {
245 $password = 'password';
246 $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
247 $this->assertFalse($this->objectInstance->isHashUpdateNeeded($saltedHashPassword));
248 }
249 }