[!!!][TASK] Implement Salted Passwords against SaltInterface
[Packages/TYPO3.CMS.git] / typo3 / sysext / saltedpasswords / Tests / Unit / Salt / SaltFactoryTest.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 * Testcase for SaltFactory
21 */
22 class SaltFactoryTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
23 {
24 /**
25 * Keeps instance of object to test.
26 *
27 * @var \TYPO3\CMS\Saltedpasswords\Salt\SaltInterface
28 */
29 protected $objectInstance = null;
30
31 /**
32 * Sets up the fixtures for this testcase.
33 */
34 protected function setUp()
35 {
36 $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['saltedpasswords'] = [
37 'BE' => [
38 'saltedPWHashingMethod' => \TYPO3\CMS\Saltedpasswords\Salt\Pbkdf2Salt::class,
39 'forceSalted' => 0,
40 'onlyAuthService' => 0,
41 'updatePasswd' => 1,
42 ],
43 'FE' => [
44 'enabled' => 1,
45 'saltedPWHashingMethod' => \TYPO3\CMS\Saltedpasswords\Salt\Pbkdf2Salt::class,
46 'forceSalted' => 0,
47 'onlyAuthService' => 0,
48 'updatePasswd' => 1,
49 ],
50 ];
51 $this->objectInstance = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance();
52 }
53
54 /**
55 * @test
56 */
57 public function objectInstanceNotNull()
58 {
59 $this->assertNotNull($this->objectInstance);
60 }
61
62 /**
63 * @test
64 */
65 public function objectInstanceImplementsInterface()
66 {
67 $this->assertInstanceOf(\TYPO3\CMS\Saltedpasswords\Salt\SaltInterface::class, $this->objectInstance);
68 }
69
70 /**
71 * @test
72 */
73 public function abstractComposedSaltBase64EncodeReturnsProperLength()
74 {
75 // set up an instance that extends AbstractComposedSalt first
76 $saltPbkdf2 = '$pbkdf2-sha256$6400$0ZrzXitFSGltTQnBWOsdAw$Y11AchqV4b0sUisdZd0Xr97KWoymNE0LNNrnEgY4H9M';
77 $this->objectInstance = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance($saltPbkdf2);
78
79 // 3 Bytes should result in a 6 char length base64 encoded string
80 // used for MD5 and PHPass salted hashing
81 $byteLength = 3;
82 $reqLengthBase64 = (int)ceil($byteLength * 8 / 6);
83 $randomBytes = (new Random())->generateRandomBytes($byteLength);
84 $this->assertTrue(strlen($this->objectInstance->base64Encode($randomBytes, $byteLength)) == $reqLengthBase64);
85 // 16 Bytes should result in a 22 char length base64 encoded string
86 // used for Blowfish salted hashing
87 $byteLength = 16;
88 $reqLengthBase64 = (int)ceil($byteLength * 8 / 6);
89 $randomBytes = (new Random())->generateRandomBytes($byteLength);
90 $this->assertTrue(strlen($this->objectInstance->base64Encode($randomBytes, $byteLength)) == $reqLengthBase64);
91 }
92
93 /**
94 * @test
95 */
96 public function objectInstanceForMD5Salts()
97 {
98 $saltMD5 = '$1$rasmusle$rISCgZzpwk3UhDidwXvin0';
99 $this->objectInstance = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance($saltMD5);
100 $this->assertTrue(get_class($this->objectInstance) == \TYPO3\CMS\Saltedpasswords\Salt\Md5Salt::class || is_subclass_of($this->objectInstance, \TYPO3\CMS\Saltedpasswords\Salt\Md5Salt::class));
101 $this->assertInstanceOf(\TYPO3\CMS\Saltedpasswords\Salt\AbstractComposedSalt::class, $this->objectInstance);
102 }
103
104 /**
105 * @test
106 */
107 public function objectInstanceForBlowfishSalts()
108 {
109 $saltBlowfish = '$2a$07$abcdefghijklmnopqrstuuIdQV69PAxWYTgmnoGpe0Sk47GNS/9ZW';
110 $this->objectInstance = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance($saltBlowfish);
111 $this->assertTrue(get_class($this->objectInstance) == \TYPO3\CMS\Saltedpasswords\Salt\BlowfishSalt::class || is_subclass_of($this->objectInstance, \TYPO3\CMS\Saltedpasswords\Salt\BlowfishSalt::class));
112 $this->assertInstanceOf(\TYPO3\CMS\Saltedpasswords\Salt\AbstractComposedSalt::class, $this->objectInstance);
113 }
114
115 /**
116 * @test
117 */
118 public function objectInstanceForPhpassSalts()
119 {
120 $saltPhpass = '$P$CWF13LlG/0UcAQFUjnnS4LOqyRW43c.';
121 $this->objectInstance = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance($saltPhpass);
122 $this->assertTrue(get_class($this->objectInstance) == \TYPO3\CMS\Saltedpasswords\Salt\PhpassSalt::class || is_subclass_of($this->objectInstance, \TYPO3\CMS\Saltedpasswords\Salt\PhpassSalt::class));
123 $this->assertInstanceOf(\TYPO3\CMS\Saltedpasswords\Salt\AbstractComposedSalt::class, $this->objectInstance);
124 }
125
126 /**
127 * @test
128 */
129 public function objectInstanceForPbkdf2Salts()
130 {
131 $saltPbkdf2 = '$pbkdf2-sha256$6400$0ZrzXitFSGltTQnBWOsdAw$Y11AchqV4b0sUisdZd0Xr97KWoymNE0LNNrnEgY4H9M';
132 $this->objectInstance = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance($saltPbkdf2);
133 $this->assertTrue(get_class($this->objectInstance) == \TYPO3\CMS\Saltedpasswords\Salt\Pbkdf2Salt::class || is_subclass_of($this->objectInstance, \TYPO3\CMS\Saltedpasswords\Salt\Pbkdf2Salt::class));
134 $this->assertInstanceOf(\TYPO3\CMS\Saltedpasswords\Salt\AbstractComposedSalt::class, $this->objectInstance);
135 }
136
137 /**
138 * @test
139 */
140 public function resettingFactoryInstanceSucceeds()
141 {
142 $defaultClassNameToUse = \TYPO3\CMS\Saltedpasswords\Utility\SaltedPasswordsUtility::getDefaultSaltingHashingMethod();
143 if ($defaultClassNameToUse == \TYPO3\CMS\Saltedpasswords\Salt\Md5Salt::class) {
144 $saltedPW = '$P$CWF13LlG/0UcAQFUjnnS4LOqyRW43c.';
145 } else {
146 $saltedPW = '$1$rasmusle$rISCgZzpwk3UhDidwXvin0';
147 }
148 $this->objectInstance = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance($saltedPW);
149 // resetting
150 $this->objectInstance = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance(null);
151 $this->assertTrue(get_class($this->objectInstance) == $defaultClassNameToUse || is_subclass_of($this->objectInstance, $defaultClassNameToUse));
152 }
153 }