From 3fd1b384273b7b6d56950bbad3e1fac18f5f82e4 Mon Sep 17 00:00:00 2001 From: Andrey Andreev Date: Thu, 13 Feb 2014 03:01:31 +0200 Subject: Introducing compatibility layers - Limited support for mbstring (mb_strlen(), mb_strpos(), mb_substr() only) via iconv. Falls back to regular strlen(), strpos(), substr() if iconv is not available. - Password hashing, dependant on CRYPT_BLOWFISH (2y version, available since PHP 5.3.7) availability. --- tests/codeigniter/core/compat/mbstring_test.php | 54 ++++++++ tests/codeigniter/core/compat/password_test.php | 158 ++++++++++++++++++++++++ 2 files changed, 212 insertions(+) create mode 100644 tests/codeigniter/core/compat/mbstring_test.php create mode 100644 tests/codeigniter/core/compat/password_test.php (limited to 'tests/codeigniter/core/compat') diff --git a/tests/codeigniter/core/compat/mbstring_test.php b/tests/codeigniter/core/compat/mbstring_test.php new file mode 100644 index 000000000..415222446 --- /dev/null +++ b/tests/codeigniter/core/compat/mbstring_test.php @@ -0,0 +1,54 @@ +markTestSkipped('ext/mbstring is loaded'); + } + + $this->assertTrue(function_exists('mb_strlen')); + $this->assertTrue(function_exists('mb_substr')); + } + + // ------------------------------------------------------------------------ + + /** + * @depends test_bootstrap + */ + public function test_mb_strlen() + { + $this->assertEquals(ICONV_ENABLED ? 4 : 8, mb_strlen('тест')); + $this->assertEquals(ICONV_ENABLED ? 4 : 8, mb_strlen('тест', 'UTF-8')); + } + + // ------------------------------------------------------------------------ + + /** + * @depends test_boostrap + */ + public function test_mb_strpos() + { + $this->assertEquals(ICONV_ENABLED ? 3 : 6, mb_strpos('тест', 'с')); + $this->assertFalse(mb_strpos('тест', 'с', 3)); + $this->assertEquals(ICONV_ENABLED ? 3 : 6, mb_strpos('тест', 'с', 1, 'UTF-8')); + } + + // ------------------------------------------------------------------------ + + /** + * @depends test_boostrap + */ + public function test_mb_substr() + { + $this->assertEquals(ICONV_ENABLED ? 'стинг' : 'естинг', mb_substr('тестинг', 2)); + $this->assertEquals(ICONV_ENABLED ? 'нг' : 'г', mb_substr('тестинг', -2)); + $this->assertEquals(ICONV_ENABLED ? 'ст' : 'е', mb_substr('тестинг', 2, 2)); + $this->assertEquals(ICONV_ENABLED ? 'стинг' : 'естинг', mb_substr('тестинг', 2, 'UTF-8')); + $this->assertEquals(ICONV_ENABLED ? 'нг' : 'г', mb_substr('тестинг', -2, 'UTF-8')); + $this->assertEquals(ICONV_ENABLED ? 'ст' : 'е', mb_substr('тестинг', 2, 2, 'UTF-8')); + } + +} \ No newline at end of file diff --git a/tests/codeigniter/core/compat/password_test.php b/tests/codeigniter/core/compat/password_test.php new file mode 100644 index 000000000..4014e7415 --- /dev/null +++ b/tests/codeigniter/core/compat/password_test.php @@ -0,0 +1,158 @@ +markTestSkipped('ext/standard/password is available on PHP 5.5'); + } + elseif ( ! is_php('5.3.7')) + { + $this->assertFalse(defined('PASSWORD_BCRYPT')); + return $this->markTestSkipped("PHP versions prior to 5.3.7 don't have the '2y' Blowfish version"); + } + elseif ( ! defined('CRYPT_BLOWFISH') OR CRYPT_BLOWFISH !== 1) + { + $this->assertFalse(defined('PASSWORD_BCRYPT')); + return $this->markTestSkipped('CRYPT_BLOWFISH is not available'); + } + + $this->assertTrue(defined('PASSWORD_BCRYPT')); + $this->assertTrue(defined('PASSWORD_DEFAULT')); + $this->assertEquals(1, PASSWORD_BCRYPT); + $this->assertEquals(PASSWORD_BCRYPT, PASSWORD_DEFAULT); + $this->assertTrue(function_exists('password_get_info')); + $this->assertTrue(function_exists('password_hash')); + $this->assertTrue(function_exists('password_needs_rehash')); + $this->assertTrue(function_exists('password_verify')); + } + + // ------------------------------------------------------------------------ + + /** + * password_get_info() test + * + * Borrowed from PHP's own tests + * + * @depends test_bootstrap + */ + public function test_password_get_info() + { + $expected = array( + 'algo' => 1, + 'algoName' => 'bcrypt', + 'options' => array('cost' => 10) + ); + + // default + $this->assertEquals($expected, password_get_info('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y')); + + $expected['options']['cost'] = 11; + + // cost + $this->assertEquals($expected, password_get_info('$2y$11$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y')); + + $expected = array( + 'algo' => 0, + 'algoName' => 'unknown', + 'options' => array() + ); + + // invalid length + $this->assertEquals($expected, password_get_info('$2y$11$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100')); + + // non-bcrypt + $this->assertEquals($expected, password_get_info('$1$rasmusle$rISCgZzpwk3UhDidwXvin0')); + } + + // ------------------------------------------------------------------------ + + /** + * password_hash() test + * + * Borrowed from PHP's own tests + * + * @depends test_bootstrap + */ + public function test_password_hash() + { + // FALSE is returned if no CSPRNG source is available + if ( ! defined('MCRYPT_DEV_URANDOM') && ! function_exists('openssl_random_pseudo_bytes') + && (DIRECTORY_SEPARATOR !== '/' OR ! is_readable('/dev/arandom') OR ! is_readable('/dev/urandom')) + ) + { + $this->assertFalse(password_hash('foo', PASSWORD_BCRYPT)); + } + else + { + $this->assertEquals(60, strlen(password_hash('foo', PASSWORD_BCRYPT))); + $this->assertTrue(($hash = password_hash('foo', PASSWORD_BCRYPT)) === crypt('foo', $hash)); + } + + $this->assertEquals( + '$2y$07$usesomesillystringfore2uDLvp1Ii2e./U9C8sBjqp8I90dH6hi', + password_hash('rasmuslerdorf', PASSWORD_BCRYPT, array('cost' => 7, 'salt' => 'usesomesillystringforsalt')) + ); + + $this->assertEquals( + '$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', + password_hash('test', PASSWORD_BCRYPT, array('salt' => '123456789012345678901'.chr(0))) + ); + } + + // ------------------------------------------------------------------------ + + /** + * password_needs_rehash() test + * + * Borrowed from PHP's own tests + * + * @depends test_password_get_info + */ + public function test_password_needs_rehash() + { + // invalid hash: always rehash + $this->assertTrue(password_needs_rehash('', PASSWORD_BCRYPT)); + + // valid, because it's an unknown algorithm + $this->assertFalse(password_needs_rehash('', 0)); + + // valid with same cost + $this->assertFalse(password_needs_rehash('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT, array('cost' => 10))); + + // valid with same cost and additional parameters + $this->assertFalse(password_needs_rehash('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT, array('cost' => 10, 'foo' => 3))); + + // invalid: different (lower) cost + $this->assertTrue(password_needs_rehash('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT, array('cost' => 09))); + + // invalid: different (higher) cost + $this->assertTrue(password_needs_rehash('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT, array('cost' => 11))); + + // valid with default cost + $this->assertFalse(password_needs_rehash('$2y$'.str_pad(10, 2, '0', STR_PAD_LEFT).'$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT)); + + // invalid: 'foo' is cast to 0 + $this->assertTrue(password_needs_rehash('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT, array('cost' => 'foo'))); + } + + // ------------------------------------------------------------------------ + + /** + * password_verify() test + * + * Borrowed from PHP's own tests + * + * @depends test_bootstrap + */ + public function test_password_verify() + { + $this->assertFalse(password_verify(123, 123)); + $this->assertFalse(password_verify('foo', '$2a$07$usesomesillystringforsalt$')); + $this->assertFalse(password_verify('rasmusler', '$2a$07$usesomesillystringfore2uDLvp1Ii2e./U9C8sBjqp8I90dH6hi')); + $this->assertTrue(password_verify('rasmuslerdorf', '$2a$07$usesomesillystringfore2uDLvp1Ii2e./U9C8sBjqp8I90dH6hi')); + } + +} \ No newline at end of file -- cgit v1.2.3-24-g4f1b