summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnthony Ferrara <ircmaxell@gmail.com>2014-11-20 11:49:30 -0500
committerAnthony Ferrara <ircmaxell@gmail.com>2014-11-20 11:49:30 -0500
commit5c5cde8822a69545767f7c7f3058cb15ff84614c (patch)
tree34946d79143dfe2221879ebaf33ea491106d875d
parent1fc1521b5e9794ea77e4eca30717be9635f1d4f4 (diff)
parent4bedaa1ccdfb333ef837f796ab6b382895e23d8f (diff)
downloadpassword_compat-5c5cde8822a69545767f7c7f3058cb15ff84614c.zip
password_compat-5c5cde8822a69545767f7c7f3058cb15ff84614c.tar.gz
password_compat-5c5cde8822a69545767f7c7f3058cb15ff84614c.tar.bz2
Merge branch 'master' into v1.0v1.0.4
Conflicts: composer.json
-rw-r--r--.gitattributes8
-rw-r--r--.gitignore3
-rw-r--r--.travis.yml17
-rw-r--r--README.md27
-rw-r--r--composer.json36
-rw-r--r--lib/password.php458
-rw-r--r--phpunit.xml.dist22
-rw-r--r--test/Unit/PasswordHashTest.php18
-rw-r--r--version-test.php6
9 files changed, 363 insertions, 232 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..f80340e
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,8 @@
+* text=auto
+
+/test export-ignore
+/.gitattributes export-ignore
+/.gitignore export-ignore
+/.travis.yml export-ignore
+/phpunit.xml.dist export-ignore
+/README.md export-ignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..81b9258
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+composer.lock
+phpunit.xml
+vendor
diff --git a/.travis.yml b/.travis.yml
index a960061..6dd21b2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,19 @@
language: php
php:
- - 5.5
- - 5.4
- 5.3
+ - 5.4
+ - 5.5
+ - 5.6
+ - hhvm
+
+install:
+ - travis_retry composer install --no-interaction --prefer-source
+
+script:
+ - vendor/bin/phpunit
-script: phpunit --configuration phpunit.xml.dist \ No newline at end of file
+matrix:
+ allow_failures:
+ - php: hhvm
+ fast_finish: true
diff --git a/README.md b/README.md
index e158f7d..6bfd6cc 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@ password_compat
[![Build Status](https://travis-ci.org/ircmaxell/password_compat.png?branch=master)](https://travis-ci.org/ircmaxell/password_compat)
-This library is intended to provide forward compatibility with the password_* functions being worked on for PHP 5.5.
+This library is intended to provide forward compatibility with the [password_*](http://php.net/password) functions being worked on for PHP 5.5.
See [the RFC](https://wiki.php.net/rfc/password_hash) for more detailed information.
@@ -11,13 +11,13 @@ See [the RFC](https://wiki.php.net/rfc/password_hash) for more detailed informat
Requirements
============
-This library requires `PHP >= 5.3.7` OR a version that has the `$2y` fix backported into it (such as Debian provides).
+This library requires `PHP >= 5.3.7` OR a version that has the `$2y` fix backported into it (such as RedHat provides). Note that Debian's 5.3.3 version is **NOT** supported.
The runtime checks have been removed due to this version issue. To see if password_compat is available for your system, run the included `version-test.php`. If it outputs "Pass", you can safely use the library. If not, you cannot.
If you attempt to use password-compat on an unsupported version, attempts to create or verify hashes will return `false`. You have been warned!
-The reason for this is that PHP prior to 5.3.7 contains a security issue with its BCRYPT implementation. Therefore, it's highly recommended that you upgrade to a newer version of PHP prior to using this layer.
+The reason for this is that PHP prior to 5.3.7 contains a [security issue with its BCRYPT implementation](http://php.net/security/crypt_blowfish.php). Therefore, it's highly recommended that you upgrade to a newer version of PHP prior to using this layer.
Installation
============
@@ -32,21 +32,21 @@ Usage
**Creating Password Hashes**
To create a password hash from a password, simply use the `password_hash` function.
-
+````PHP
$hash = password_hash($password, PASSWORD_BCRYPT);
-
+````
Note that the algorithm that we chose is `PASSWORD_BCRYPT`. That's the current strongest algorithm supported. This is the `BCRYPT` crypt algorithm. It produces a 60 character hash as the result.
`BCRYPT` also allows for you to define a `cost` parameter in the options array. This allows for you to change the CPU cost of the algorithm:
-
- $hash = password_hash($password, PASSWORD_BCRYPT, ["cost" => 10]);
-
+````PHP
+ $hash = password_hash($password, PASSWORD_BCRYPT, array("cost" => 10));
+````
That's the same as the default. The cost can range from `4` to `31`. I would suggest that you use the highest cost that you can, while keeping response time reasonable (I target between 0.1 and 0.5 seconds for a hash, depending on use-case).
Another algorithm name is supported:
-
+````PHP
PASSWORD_DEFAULT
-
+````
This will use the strongest algorithm available to PHP at the current time. Presently, this is the same as specifying `PASSWORD_BCRYPT`. But in future versions of PHP, it may be updated to use a stronger algorithm if one is introduced. It can also be changed if a problem is identified with the BCRYPT algorithm. Note that if you use this option, you are **strongly** encouraged to store it in a `VARCHAR(255)` column to avoid truncation issues if a future algorithm increases the length of the generated hash.
It is very important that you should check the return value of `password_hash` prior to storing it, because a `false` may be returned if it encountered an error.
@@ -54,22 +54,23 @@ It is very important that you should check the return value of `password_hash` p
**Verifying Password Hashes**
To verify a hash created by `password_hash`, simply call:
-
+````PHP
if (password_verify($password, $hash)) {
/* Valid */
} else {
/* Invalid */
}
-
+````
That's all there is to it.
**Rehashing Passwords**
From time to time you may update your hashing parameters (algorithm, cost, etc). So a function to determine if rehashing is necessary is available:
-
+````PHP
if (password_verify($password, $hash)) {
if (password_needs_rehash($hash, $algorithm, $options)) {
$hash = password_hash($password, $algorithm, $options);
/* Store new hash in db */
}
}
+````
diff --git a/composer.json b/composer.json
index e0d4f14..822fd1f 100644
--- a/composer.json
+++ b/composer.json
@@ -1,18 +1,20 @@
{
- "name": "ircmaxell/password-compat",
- "description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash",
- "version": "1.0.3",
- "keywords": ["password", "hashing"],
- "homepage": "https://github.com/ircmaxell/password_compat",
- "license": "MIT",
- "authors": [
- {
- "name": "Anthony Ferrara",
- "email": "ircmaxell@php.net",
- "homepage": "http://blog.ircmaxell.com"
- }
- ],
- "autoload": {
- "files": ["lib/password.php"]
- }
-} \ No newline at end of file
+ "name": "ircmaxell/password-compat",
+ "description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash",
+ "keywords": ["password", "hashing"],
+ "homepage": "https://github.com/ircmaxell/password_compat",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Anthony Ferrara",
+ "email": "ircmaxell@php.net",
+ "homepage": "http://blog.ircmaxell.com"
+ }
+ ],
+ "require-dev": {
+ "phpunit/phpunit": "4.*"
+ },
+ "autoload": {
+ "files": ["lib/password.php"]
+ }
+}
diff --git a/lib/password.php b/lib/password.php
index 4d0e8b7..cc6896c 100644
--- a/lib/password.php
+++ b/lib/password.php
@@ -7,216 +7,308 @@
* @copyright 2012 The Authors
*/
-if (!defined('PASSWORD_BCRYPT')) {
-
- define('PASSWORD_BCRYPT', 1);
- define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);
-
- /**
- * Hash the password using the specified algorithm
- *
- * @param string $password The password to hash
- * @param int $algo The algorithm to use (Defined by PASSWORD_* constants)
- * @param array $options The options for the algorithm to use
- *
- * @return string|false The hashed password, or false on error.
- */
- function password_hash($password, $algo, array $options = array()) {
- if (!function_exists('crypt')) {
- trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING);
- return null;
- }
- if (!is_string($password)) {
- trigger_error("password_hash(): Password must be a string", E_USER_WARNING);
- return null;
- }
- if (!is_int($algo)) {
- trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING);
- return null;
- }
- switch ($algo) {
- case PASSWORD_BCRYPT:
- // Note that this is a C constant, but not exposed to PHP, so we don't define it here.
- $cost = 10;
- if (isset($options['cost'])) {
- $cost = $options['cost'];
- if ($cost < 4 || $cost > 31) {
- trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING);
- return null;
- }
- }
- // The length of salt to generate
- $raw_salt_len = 16;
- // The length required in the final serialization
- $required_salt_len = 22;
- $hash_format = sprintf("$2y$%02d$", $cost);
- break;
- default:
- trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING);
+namespace {
+
+ if (!defined('PASSWORD_BCRYPT')) {
+ /**
+ * PHPUnit Process isolation caches constants, but not function declarations.
+ * So we need to check if the constants are defined separately from
+ * the functions to enable supporting process isolation in userland
+ * code.
+ */
+ define('PASSWORD_BCRYPT', 1);
+ define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);
+ define('PASSWORD_BCRYPT_DEFAULT_COST', 10);
+ }
+
+ if (!function_exists('password_hash')) {
+
+ /**
+ * Hash the password using the specified algorithm
+ *
+ * @param string $password The password to hash
+ * @param int $algo The algorithm to use (Defined by PASSWORD_* constants)
+ * @param array $options The options for the algorithm to use
+ *
+ * @return string|false The hashed password, or false on error.
+ */
+ function password_hash($password, $algo, array $options = array()) {
+ if (!function_exists('crypt')) {
+ trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING);
return null;
- }
- if (isset($options['salt'])) {
- switch (gettype($options['salt'])) {
- case 'NULL':
- case 'boolean':
- case 'integer':
- case 'double':
- case 'string':
- $salt = (string) $options['salt'];
- break;
- case 'object':
- if (method_exists($options['salt'], '__tostring')) {
- $salt = (string) $options['salt'];
- break;
+ }
+ if (is_null($password) || is_int($password)) {
+ $password = (string) $password;
+ }
+ if (!is_string($password)) {
+ trigger_error("password_hash(): Password must be a string", E_USER_WARNING);
+ return null;
+ }
+ if (!is_int($algo)) {
+ trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING);
+ return null;
+ }
+ $resultLength = 0;
+ switch ($algo) {
+ case PASSWORD_BCRYPT:
+ $cost = PASSWORD_BCRYPT_DEFAULT_COST;
+ if (isset($options['cost'])) {
+ $cost = $options['cost'];
+ if ($cost < 4 || $cost > 31) {
+ trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING);
+ return null;
+ }
}
- case 'array':
- case 'resource':
+ // The length of salt to generate
+ $raw_salt_len = 16;
+ // The length required in the final serialization
+ $required_salt_len = 22;
+ $hash_format = sprintf("$2y$%02d$", $cost);
+ // The expected length of the final crypt() output
+ $resultLength = 60;
+ break;
default:
- trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING);
+ trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING);
return null;
}
- if (strlen($salt) < $required_salt_len) {
- trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", strlen($salt), $required_salt_len), E_USER_WARNING);
- return null;
- } elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) {
- $salt = str_replace('+', '.', base64_encode($salt));
- }
- } else {
- $buffer = '';
- $buffer_valid = false;
- if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {
- $buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM);
- if ($buffer) {
- $buffer_valid = true;
+ $salt_requires_encoding = false;
+ if (isset($options['salt'])) {
+ switch (gettype($options['salt'])) {
+ case 'NULL':
+ case 'boolean':
+ case 'integer':
+ case 'double':
+ case 'string':
+ $salt = (string) $options['salt'];
+ break;
+ case 'object':
+ if (method_exists($options['salt'], '__tostring')) {
+ $salt = (string) $options['salt'];
+ break;
+ }
+ case 'array':
+ case 'resource':
+ default:
+ trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING);
+ return null;
}
- }
- if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
- $buffer = openssl_random_pseudo_bytes($raw_salt_len);
- if ($buffer) {
- $buffer_valid = true;
+ if (PasswordCompat\binary\_strlen($salt) < $required_salt_len) {
+ trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", PasswordCompat\binary\_strlen($salt), $required_salt_len), E_USER_WARNING);
+ return null;
+ } elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) {
+ $salt_requires_encoding = true;
}
- }
- if (!$buffer_valid && is_readable('/dev/urandom')) {
- $f = fopen('/dev/urandom', 'r');
- $read = strlen($buffer);
- while ($read < $raw_salt_len) {
- $buffer .= fread($f, $raw_salt_len - $read);
- $read = strlen($buffer);
+ } else {
+ $buffer = '';
+ $buffer_valid = false;
+ if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {
+ $buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM);
+ if ($buffer) {
+ $buffer_valid = true;
+ }
}
- fclose($f);
- if ($read >= $raw_salt_len) {
- $buffer_valid = true;
+ if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
+ $buffer = openssl_random_pseudo_bytes($raw_salt_len);
+ if ($buffer) {
+ $buffer_valid = true;
+ }
}
- }
- if (!$buffer_valid || strlen($buffer) < $raw_salt_len) {
- $bl = strlen($buffer);
- for ($i = 0; $i < $raw_salt_len; $i++) {
- if ($i < $bl) {
- $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
- } else {
- $buffer .= chr(mt_rand(0, 255));
+ if (!$buffer_valid && @is_readable('/dev/urandom')) {
+ $f = fopen('/dev/urandom', 'r');
+ $read = PasswordCompat\binary\_strlen($buffer);
+ while ($read < $raw_salt_len) {
+ $buffer .= fread($f, $raw_salt_len - $read);
+ $read = PasswordCompat\binary\_strlen($buffer);
+ }
+ fclose($f);
+ if ($read >= $raw_salt_len) {
+ $buffer_valid = true;
+ }
+ }
+ if (!$buffer_valid || PasswordCompat\binary\_strlen($buffer) < $raw_salt_len) {
+ $bl = PasswordCompat\binary\_strlen($buffer);
+ for ($i = 0; $i < $raw_salt_len; $i++) {
+ if ($i < $bl) {
+ $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
+ } else {
+ $buffer .= chr(mt_rand(0, 255));
+ }
}
}
+ $salt = $buffer;
+ $salt_requires_encoding = true;
}
- $salt = str_replace('+', '.', base64_encode($buffer));
- }
- $salt = substr($salt, 0, $required_salt_len);
+ if ($salt_requires_encoding) {
+ // encode string with the Base64 variant used by crypt
+ $base64_digits =
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+ $bcrypt64_digits =
+ './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
- $hash = $hash_format . $salt;
+ $base64_string = base64_encode($salt);
+ $salt = strtr(rtrim($base64_string, '='), $base64_digits, $bcrypt64_digits);
+ }
+ $salt = PasswordCompat\binary\_substr($salt, 0, $required_salt_len);
- $ret = crypt($password, $hash);
+ $hash = $hash_format . $salt;
- if (!is_string($ret) || strlen($ret) <= 13) {
- return false;
- }
+ $ret = crypt($password, $hash);
- return $ret;
- }
+ if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != $resultLength) {
+ return false;
+ }
- /**
- * Get information about the password hash. Returns an array of the information
- * that was used to generate the password hash.
- *
- * array(
- * 'algo' => 1,
- * 'algoName' => 'bcrypt',
- * 'options' => array(
- * 'cost' => 10,
- * ),
- * )
- *
- * @param string $hash The password hash to extract info from
- *
- * @return array The array of information about the hash.
- */
- function password_get_info($hash) {
- $return = array(
- 'algo' => 0,
- 'algoName' => 'unknown',
- 'options' => array(),
- );
- if (substr($hash, 0, 4) == '$2y$' && strlen($hash) == 60) {
- $return['algo'] = PASSWORD_BCRYPT;
- $return['algoName'] = 'bcrypt';
- list($cost) = sscanf($hash, "$2y$%d$");
- $return['options']['cost'] = $cost;
+ return $ret;
}
- return $return;
- }
- /**
- * Determine if the password hash needs to be rehashed according to the options provided
- *
- * If the answer is true, after validating the password using password_verify, rehash it.
- *
- * @param string $hash The hash to test
- * @param int $algo The algorithm used for new password hashes
- * @param array $options The options array passed to password_hash
- *
- * @return boolean True if the password needs to be rehashed.
- */
- function password_needs_rehash($hash, $algo, array $options = array()) {
- $info = password_get_info($hash);
- if ($info['algo'] != $algo) {
- return true;
- }
- switch ($algo) {
- case PASSWORD_BCRYPT:
- $cost = isset($options['cost']) ? $options['cost'] : 10;
- if ($cost != $info['options']['cost']) {
- return true;
- }
- break;
+ /**
+ * Get information about the password hash. Returns an array of the information
+ * that was used to generate the password hash.
+ *
+ * array(
+ * 'algo' => 1,
+ * 'algoName' => 'bcrypt',
+ * 'options' => array(
+ * 'cost' => PASSWORD_BCRYPT_DEFAULT_COST,
+ * ),
+ * )
+ *
+ * @param string $hash The password hash to extract info from
+ *
+ * @return array The array of information about the hash.
+ */
+ function password_get_info($hash) {
+ $return = array(
+ 'algo' => 0,
+ 'algoName' => 'unknown',
+ 'options' => array(),
+ );
+ if (PasswordCompat\binary\_substr($hash, 0, 4) == '$2y$' && PasswordCompat\binary\_strlen($hash) == 60) {
+ $return['algo'] = PASSWORD_BCRYPT;
+ $return['algoName'] = 'bcrypt';
+ list($cost) = sscanf($hash, "$2y$%d$");
+ $return['options']['cost'] = $cost;
+ }
+ return $return;
}
- return false;
- }
- /**
- * Verify a password against a hash using a timing attack resistant approach
- *
- * @param string $password The password to verify
- * @param string $hash The hash to verify against
- *
- * @return boolean If the password matches the hash
- */
- function password_verify($password, $hash) {
- if (!function_exists('crypt')) {
- trigger_error("Crypt must be loaded for password_verify to function", E_USER_WARNING);
- return false;
- }
- $ret = crypt($password, $hash);
- if (!is_string($ret) || strlen($ret) != strlen($hash) || strlen($ret) <= 13) {
+ /**
+ * Determine if the password hash needs to be rehashed according to the options provided
+ *
+ * If the answer is true, after validating the password using password_verify, rehash it.
+ *
+ * @param string $hash The hash to test
+ * @param int $algo The algorithm used for new password hashes
+ * @param array $options The options array passed to password_hash
+ *
+ * @return boolean True if the password needs to be rehashed.
+ */
+ function password_needs_rehash($hash, $algo, array $options = array()) {
+ $info = password_get_info($hash);
+ if ($info['algo'] != $algo) {
+ return true;
+ }
+ switch ($algo) {
+ case PASSWORD_BCRYPT:
+ $cost = isset($options['cost']) ? $options['cost'] : PASSWORD_BCRYPT_DEFAULT_COST;
+ if ($cost != $info['options']['cost']) {
+ return true;
+ }
+ break;
+ }
return false;
}
- $status = 0;
- for ($i = 0; $i < strlen($ret); $i++) {
- $status |= (ord($ret[$i]) ^ ord($hash[$i]));
- }
+ /**
+ * Verify a password against a hash using a timing attack resistant approach
+ *
+ * @param string $password The password to verify
+ * @param string $hash The hash to verify against
+ *
+ * @return boolean If the password matches the hash
+ */
+ function password_verify($password, $hash) {
+ if (!function_exists('crypt')) {
+ trigger_error("Crypt must be loaded for password_verify to function", E_USER_WARNING);
+ return false;
+ }
+ $ret = crypt($password, $hash);
+ if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != PasswordCompat\binary\_strlen($hash) || PasswordCompat\binary\_strlen($ret) <= 13) {
+ return false;
+ }
+
+ $status = 0;
+ for ($i = 0; $i < PasswordCompat\binary\_strlen($ret); $i++) {
+ $status |= (ord($ret[$i]) ^ ord($hash[$i]));
+ }
- return $status === 0;
+ return $status === 0;
+ }
}
+
}
+namespace PasswordCompat\binary {
+ if (!function_exists('PasswordCompat\\binary\\_strlen')) {
+ /**
+ * Count the number of bytes in a string
+ *
+ * We cannot simply use strlen() for this, because it might be overwritten by the mbstring extension.
+ * In this case, strlen() will count the number of *characters* based on the internal encoding. A
+ * sequence of bytes might be regarded as a single multibyte character.
+ *
+ * @param string $binary_string The input string
+ *
+ * @internal
+ * @return int The number of bytes
+ */
+ function _strlen($binary_string) {
+ if (function_exists('mb_strlen')) {
+ return mb_strlen($binary_string, '8bit');
+ }
+ return strlen($binary_string);
+ }
+
+ /**
+ * Get a substring based on byte limits
+ *
+ * @see _strlen()
+ *
+ * @param string $binary_string The input string
+ * @param int $start
+ * @param int $length
+ *
+ * @internal
+ * @return string The substring
+ */
+ function _substr($binary_string, $start, $length) {
+ if (function_exists('mb_substr')) {
+ return mb_substr($binary_string, $start, $length, '8bit');
+ }
+ return substr($binary_string, $start, $length);
+ }
+
+ /**
+ * Check if current PHP version is compatible with the library
+ *
+ * @return boolean the check result
+ */
+ function check() {
+ static $pass = NULL;
+
+ if (is_null($pass)) {
+ if (function_exists('crypt')) {
+ $hash = '$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG';
+ $test = crypt("password", $hash);
+ $pass = $test == $hash;
+ } else {
+ $pass = false;
+ }
+ }
+ return $pass;
+ }
+
+ }
+} \ No newline at end of file
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index b2b3afb..33f37a1 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -2,28 +2,30 @@
<phpunit backupGlobals="true"
backupStaticAttributes="false"
bootstrap="lib/password.php"
- colors="false"
+ colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
- forceCoversAnnotation="false"
- mapTestClassNameToCoveredClassName="false"
processIsolation="false"
stopOnError="false"
stopOnFailure="false"
- stopOnIncomplete="false"
- stopOnSkipped="false"
- testSuiteLoaderClass="PHPUnit_Runner_StandardTestSuiteLoader"
- strict="false"
- verbose="false">
+ syntaxCheck="true"
+ strict="true"
+ verbose="true"
+>
<testsuites>
<testsuite name="Unit">
<directory>test/Unit</directory>
</testsuite>
</testsuites>
+ <testsuites>
+ <testsuite name="Password Compat Test Suite">
+ <directory suffix="Test.php">./test</directory>
+ </testsuite>
+ </testsuites>
<filter>
- <whitelist>
- <directory suffix=".php">lib/</directory>
+ <whitelist processUncoveredFilesFromWhitelist="true">
+ <directory suffix=".php">./lib</directory>
</whitelist>
</filter>
</phpunit>
diff --git a/test/Unit/PasswordHashTest.php b/test/Unit/PasswordHashTest.php
index 9e5e9ec..abe9109 100644
--- a/test/Unit/PasswordHashTest.php
+++ b/test/Unit/PasswordHashTest.php
@@ -22,9 +22,23 @@ class PasswordHashTest extends PHPUnit_Framework_TestCase {
public function testRawSalt() {
$hash = password_hash("test", PASSWORD_BCRYPT, array("salt" => "123456789012345678901" . chr(0)));
- $this->assertEquals('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', $hash);
+ if (version_compare(PHP_VERSION, '5.5.0', '<')) {
+ $this->assertEquals('$2y$10$KRGxLBS0Lxe3KBCwKxOzLexLDeu0ZfqJAKTubOfy7O/yL2hjimw3u', $hash);
+ } else {
+ $this->assertEquals('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', $hash);
+ }
}
+ public function testNullBehavior() {
+ $hash = password_hash(null, PASSWORD_BCRYPT, array("salt" => "1234567890123456789012345678901234567890"));
+ $this->assertEquals('$2y$10$123456789012345678901uhihPb9QpE2n03zMu9TDdvO34jDn6mO.', $hash);
+ }
+
+ public function testIntegerBehavior() {
+ $hash = password_hash(12345, PASSWORD_BCRYPT, array("salt" => "1234567890123456789012345678901234567890"));
+ $this->assertEquals('$2y$10$123456789012345678901ujczD5TiARVFtc68bZCAlbEg1fCIexfO', $hash);
+ }
+
/**
* @expectedException PHPUnit_Framework_Error
*/
@@ -81,4 +95,4 @@ class PasswordHashTest extends PHPUnit_Framework_TestCase {
password_hash('foo', PASSWORD_BCRYPT, array('salt' => 'abc'));
}
-} \ No newline at end of file
+}
diff --git a/version-test.php b/version-test.php
index f527e30..96f60ca 100644
--- a/version-test.php
+++ b/version-test.php
@@ -1,8 +1,6 @@
<?php
-$hash = '$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG';
-$test = crypt("password", $hash);
-$pass = $test == $hash;
+require "lib/password.php";
-echo "Test for functionality of compat library: " . ($pass ? "Pass" : "Fail");
+echo "Test for functionality of compat library: " . (PasswordCompat\binary\check() ? "Pass" : "Fail");
echo "\n"; \ No newline at end of file