diff options
Diffstat (limited to 'tests/phpunit/includes/password')
4 files changed, 203 insertions, 0 deletions
diff --git a/tests/phpunit/includes/password/BcryptPasswordTest.php b/tests/phpunit/includes/password/BcryptPasswordTest.php new file mode 100644 index 00000000..8ac419ff --- /dev/null +++ b/tests/phpunit/includes/password/BcryptPasswordTest.php @@ -0,0 +1,40 @@ +<?php + +/** + * @group large + */ +class BcryptPasswordTestCase extends PasswordTestCase { + protected function getTypeConfigs() { + return array( 'bcrypt' => array( + 'class' => 'BcryptPassword', + 'cost' => 9, + ) ); + } + + public static function providePasswordTests() { + /** @codingStandardsIgnoreStart Generic.Files.LineLength.TooLong */ + return array( + // Tests from glibc bcrypt implementation + array( true, ':bcrypt:5$CCCCCCCCCCCCCCCCCCCCC.$E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW', "U*U" ), + array( true, ':bcrypt:5$CCCCCCCCCCCCCCCCCCCCC.$VGOzA784oUp/Z0DY336zx7pLYAy0lwK', "U*U*" ), + array( true, ':bcrypt:5$XXXXXXXXXXXXXXXXXXXXXO$AcXxm9kjPGEMsLznoKqmqw7tc8WCx4a', "U*U*U" ), + array( true, ':bcrypt:5$abcdefghijklmnopqrstuu$5s2v8.iXieOjg/.AySBTTZIIVFJeBui', "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789chars after 72 are ignored" ), + array( true, ':bcrypt:5$/OK.fbVrR/bpIqNJ5ianF.$CE5elHaaO4EbggVDjb8P19RukzXSM3e', "\xff\xff\xa3" ), + array( true, ':bcrypt:5$/OK.fbVrR/bpIqNJ5ianF.$Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq', "\xa3" ), + array( true, ':bcrypt:5$/OK.fbVrR/bpIqNJ5ianF.$Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq', "\xa3" ), + array( true, ':bcrypt:5$/OK.fbVrR/bpIqNJ5ianF.$o./n25XVfn6oAPaUvHe.Csk4zRfsYPi', "\xff\xa334\xff\xff\xff\xa3345" ), + array( true, ':bcrypt:5$/OK.fbVrR/bpIqNJ5ianF.$nRht2l/HRhr6zmCp9vYUvvsqynflf9e', "\xff\xa3345" ), + array( true, ':bcrypt:5$/OK.fbVrR/bpIqNJ5ianF.$nRht2l/HRhr6zmCp9vYUvvsqynflf9e', "\xff\xa3345" ), + array( true, ':bcrypt:5$/OK.fbVrR/bpIqNJ5ianF.$6IflQkJytoRVc1yuaNtHfiuq.FRlSIS', "\xa3ab" ), + array( true, ':bcrypt:5$/OK.fbVrR/bpIqNJ5ianF.$6IflQkJytoRVc1yuaNtHfiuq.FRlSIS', "\xa3ab" ), + array( true, ':bcrypt:5$/OK.fbVrR/bpIqNJ5ianF.$swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6', "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaachars after 72 are ignored as usual" ), + array( true, ':bcrypt:5$/OK.fbVrR/bpIqNJ5ianF.$R9xrDjiycxMbQE2bp.vgqlYpW5wx2yy', "\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55" ), + array( true, ':bcrypt:5$/OK.fbVrR/bpIqNJ5ianF.$9tQZzcJfm3uj2NvJ/n5xkhpqLrMpWCe', "\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff" ), + array( true, ':bcrypt:5$CCCCCCCCCCCCCCCCCCCCC.$7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy', "" ), + // One or two false sanity tests + array( false, ':bcrypt:5$CCCCCCCCCCCCCCCCCCCCC.$E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW', "UXU" ), + array( false, ':bcrypt:5$CCCCCCCCCCCCCCCCCCCCC.$E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW', "" ), + ); + /** @codingStandardsIgnoreEnd */ + } +} diff --git a/tests/phpunit/includes/password/LayeredParameterizedPasswordTest.php b/tests/phpunit/includes/password/LayeredParameterizedPasswordTest.php new file mode 100644 index 00000000..86e8270a --- /dev/null +++ b/tests/phpunit/includes/password/LayeredParameterizedPasswordTest.php @@ -0,0 +1,51 @@ +<?php + +class LayeredParameterizedPasswordTest extends PasswordTestCase { + protected function getTypeConfigs() { + return array( + 'testLargeLayeredTop' => array( + 'class' => 'LayeredParameterizedPassword', + 'types' => array( + 'testLargeLayeredBottom', + 'testLargeLayeredBottom', + 'testLargeLayeredBottom', + 'testLargeLayeredBottom', + 'testLargeLayeredFinal', + ), + ), + 'testLargeLayeredBottom' => array( + 'class' => 'Pbkdf2Password', + 'algo' => 'sha512', + 'cost' => 1024, + 'length' => 512, + ), + 'testLargeLayeredFinal' => array( + 'class' => 'BcryptPassword', + 'cost' => 5, + ) + ); + } + + public static function providePasswordTests() { + /** @codingStandardsIgnoreStart Generic.Files.LineLength.TooLong */ + return array( + array( true, ':testLargeLayeredTop:sha512:1024:512!sha512:1024:512!sha512:1024:512!sha512:1024:512!5!vnRy+2SrSA0fHt3dwhTP5g==!AVnwfZsAQjn+gULv7FSGjA==!xvHUX3WcpkeSn1lvjWcvBg==!It+OC/N9tu+d3ByHhuB0BQ==!Tb.gqUOiD.aWktVwHM.Q/O!7CcyMfXUPky5ptyATJsR2nq3vUqtnBC', 'testPassword123' ), + ); + /** @codingStandardsIgnoreEnd */ + } + + /** + * @covers LayeredParameterizedPassword::partialCrypt + */ + public function testLargeLayeredPartialUpdate() { + /** @var ParameterizedPassword $partialPassword */ + $partialPassword = $this->passwordFactory->newFromType( 'testLargeLayeredBottom' ); + $partialPassword->crypt( 'testPassword123' ); + + /** @var LayeredParameterizedPassword $totalPassword */ + $totalPassword = $this->passwordFactory->newFromType( 'testLargeLayeredTop' ); + $totalPassword->partialCrypt( $partialPassword ); + + $this->assertTrue( $totalPassword->equals( 'testPassword123' ) ); + } +} diff --git a/tests/phpunit/includes/password/PasswordTestCase.php b/tests/phpunit/includes/password/PasswordTestCase.php new file mode 100644 index 00000000..ef16f1c4 --- /dev/null +++ b/tests/phpunit/includes/password/PasswordTestCase.php @@ -0,0 +1,88 @@ +<?php +/** + * Testing framework for the password hashes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + */ + +/** + * @since 1.24 + */ +abstract class PasswordTestCase extends MediaWikiTestCase { + /** + * @var PasswordFactory + */ + protected $passwordFactory; + + protected function setUp() { + parent::setUp(); + + $this->passwordFactory = new PasswordFactory(); + foreach ( $this->getTypeConfigs() as $type => $config ) { + $this->passwordFactory->register( $type, $config ); + } + } + + /** + * Return an array of configs to be used for this class's password type. + * + * @return array[] + */ + abstract protected function getTypeConfigs(); + + /** + * An array of tests in the form of (bool, string, string), where the first + * element is whether the second parameter (a password hash) and the third + * parameter (a password) should match. + * + * @return array + */ + abstract public static function providePasswordTests(); + + /** + * @dataProvider providePasswordTests + */ + public function testHashing( $shouldMatch, $hash, $password ) { + $hash = $this->passwordFactory->newFromCiphertext( $hash ); + $password = $this->passwordFactory->newFromPlaintext( $password, $hash ); + $this->assertSame( $shouldMatch, $hash->equals( $password ) ); + } + + /** + * @dataProvider providePasswordTests + */ + public function testStringSerialization( $shouldMatch, $hash, $password ) { + $hashObj = $this->passwordFactory->newFromCiphertext( $hash ); + $serialized = $hashObj->toString(); + $unserialized = $this->passwordFactory->newFromCiphertext( $serialized ); + $this->assertTrue( $hashObj->equals( $unserialized ) ); + } + + /** + * @dataProvider providePasswordTests + * @covers InvalidPassword::equals + * @covers InvalidPassword::toString + */ + public function testInvalidUnequalNormal( $shouldMatch, $hash, $password ) { + $invalid = $this->passwordFactory->newFromCiphertext( null ); + $normal = $this->passwordFactory->newFromCiphertext( $hash ); + + $this->assertFalse( $invalid->equals( $normal ) ); + $this->assertFalse( $normal->equals( $invalid ) ); + } +} diff --git a/tests/phpunit/includes/password/Pbkdf2PasswordTest.php b/tests/phpunit/includes/password/Pbkdf2PasswordTest.php new file mode 100644 index 00000000..091853e1 --- /dev/null +++ b/tests/phpunit/includes/password/Pbkdf2PasswordTest.php @@ -0,0 +1,24 @@ +<?php + +/** + * @group large + */ +class Pbkdf2PasswordTest extends PasswordTestCase { + protected function getTypeConfigs() { + return array( 'pbkdf2' => array( + 'class' => 'Pbkdf2Password', + 'algo' => 'sha256', + 'cost' => '10000', + 'length' => '128', + ) ); + } + + public static function providePasswordTests() { + return array( + array( true, ":pbkdf2:sha1:1:20:c2FsdA==:DGDID5YfDnHzqbUkr2ASBi/gN6Y=", 'password' ), + array( true, ":pbkdf2:sha1:2:20:c2FsdA==:6mwBTcctb4zNHtkqzh1B8NjeiVc=", 'password' ), + array( true, ":pbkdf2:sha1:4096:20:c2FsdA==:SwB5AbdlSJq+rUnZJvch0GWkKcE=", 'password' ), + array( true, ":pbkdf2:sha1:4096:16:c2EAbHQ=:Vvpqp1VICZ3MN9fwNCXgww==", "pass\x00word" ), + ); + } +} |