Update to the last version of password_compat

This commit is contained in:
Frédéric Guillot
2014-03-14 21:16:37 -04:00
parent ede188815b
commit eaae8dfc0b

120
vendor/password.php vendored
View File

@@ -7,20 +7,13 @@
* @copyright 2012 The Authors * @copyright 2012 The Authors
*/ */
if (!defined('PASSWORD_BCRYPT')) { namespace {
if (!defined('PASSWORD_DEFAULT')) {
define('PASSWORD_BCRYPT', 1); define('PASSWORD_BCRYPT', 1);
define('PASSWORD_DEFAULT', PASSWORD_BCRYPT); define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);
if (version_compare(PHP_VERSION, '5.3.7', '<')) {
define('PASSWORD_PREFIX', '$2a$');
}
else {
define('PASSWORD_PREFIX', '$2y$');
}
/** /**
* Hash the password using the specified algorithm * Hash the password using the specified algorithm
* *
@@ -43,6 +36,7 @@ if (!defined('PASSWORD_BCRYPT')) {
trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING); trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING);
return null; return null;
} }
$resultLength = 0;
switch ($algo) { switch ($algo) {
case PASSWORD_BCRYPT: case PASSWORD_BCRYPT:
// Note that this is a C constant, but not exposed to PHP, so we don't define it here. // Note that this is a C constant, but not exposed to PHP, so we don't define it here.
@@ -54,13 +48,19 @@ if (!defined('PASSWORD_BCRYPT')) {
return null; return null;
} }
} }
// The length of salt to generate
$raw_salt_len = 16;
// The length required in the final serialization
$required_salt_len = 22; $required_salt_len = 22;
$hash_format = sprintf("%s%02d$", PASSWORD_PREFIX, $cost); $hash_format = sprintf("$2y$%02d$", $cost);
// The expected length of the final crypt() output
$resultLength = 60;
break; break;
default: default:
trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING); trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING);
return null; return null;
} }
$salt_requires_encoding = false;
if (isset($options['salt'])) { if (isset($options['salt'])) {
switch (gettype($options['salt'])) { switch (gettype($options['salt'])) {
case 'NULL': case 'NULL':
@@ -81,43 +81,42 @@ if (!defined('PASSWORD_BCRYPT')) {
trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING); trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING);
return null; return null;
} }
if (strlen($salt) < $required_salt_len) { if (PasswordCompat\binary\_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); 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; return null;
} elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) { } elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) {
$salt = str_replace('+', '.', base64_encode($salt)); $salt_requires_encoding = true;
} }
} else { } else {
$buffer = ''; $buffer = '';
$raw_length = (int) ($required_salt_len * 3 / 4 + 1);
$buffer_valid = false; $buffer_valid = false;
if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) { if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {
$buffer = mcrypt_create_iv($raw_length, MCRYPT_DEV_URANDOM); $buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM);
if ($buffer) { if ($buffer) {
$buffer_valid = true; $buffer_valid = true;
} }
} }
if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) { if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
$buffer = openssl_random_pseudo_bytes($raw_length); $buffer = openssl_random_pseudo_bytes($raw_salt_len);
if ($buffer) { if ($buffer) {
$buffer_valid = true; $buffer_valid = true;
} }
} }
if (!$buffer_valid && is_readable('/dev/urandom')) { if (!$buffer_valid && @is_readable('/dev/urandom')) {
$f = fopen('/dev/urandom', 'r'); $f = fopen('/dev/urandom', 'r');
$read = strlen($buffer); $read = PasswordCompat\binary\_strlen($buffer);
while ($read < $raw_length) { while ($read < $raw_salt_len) {
$buffer .= fread($f, $raw_length - $read); $buffer .= fread($f, $raw_salt_len - $read);
$read = strlen($buffer); $read = PasswordCompat\binary\_strlen($buffer);
} }
fclose($f); fclose($f);
if ($read >= $raw_length) { if ($read >= $raw_salt_len) {
$buffer_valid = true; $buffer_valid = true;
} }
} }
if (!$buffer_valid || strlen($buffer) < $raw_length) { if (!$buffer_valid || PasswordCompat\binary\_strlen($buffer) < $raw_salt_len) {
$bl = strlen($buffer); $bl = PasswordCompat\binary\_strlen($buffer);
for ($i = 0; $i < $raw_length; $i++) { for ($i = 0; $i < $raw_salt_len; $i++) {
if ($i < $bl) { if ($i < $bl) {
$buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255)); $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
} else { } else {
@@ -125,16 +124,26 @@ if (!defined('PASSWORD_BCRYPT')) {
} }
} }
} }
$salt = str_replace('+', '.', base64_encode($buffer)); $salt = $buffer;
$salt_requires_encoding = true;
} }
$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';
$base64_string = base64_encode($salt);
$salt = strtr(rtrim($base64_string, '='), $base64_digits, $bcrypt64_digits);
}
$salt = PasswordCompat\binary\_substr($salt, 0, $required_salt_len);
$hash = $hash_format . $salt; $hash = $hash_format . $salt;
$ret = crypt($password, $hash); $ret = crypt($password, $hash);
if (!is_string($ret) || strlen($ret) <= 13) { if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != $resultLength) {
return false; return false;
} }
@@ -163,10 +172,10 @@ if (!defined('PASSWORD_BCRYPT')) {
'algoName' => 'unknown', 'algoName' => 'unknown',
'options' => array(), 'options' => array(),
); );
if (substr($hash, 0, 4) == PASSWORD_PREFIX && strlen($hash) == 60) { if (PasswordCompat\binary\_substr($hash, 0, 4) == '$2y$' && PasswordCompat\binary\_strlen($hash) == 60) {
$return['algo'] = PASSWORD_BCRYPT; $return['algo'] = PASSWORD_BCRYPT;
$return['algoName'] = 'bcrypt'; $return['algoName'] = 'bcrypt';
list($cost) = sscanf($hash, PASSWORD_PREFIX."%d$"); list($cost) = sscanf($hash, "$2y$%d$");
$return['options']['cost'] = $cost; $return['options']['cost'] = $cost;
} }
return $return; return $return;
@@ -213,15 +222,58 @@ if (!defined('PASSWORD_BCRYPT')) {
return false; return false;
} }
$ret = crypt($password, $hash); $ret = crypt($password, $hash);
if (!is_string($ret) || strlen($ret) != strlen($hash) || strlen($ret) <= 13) { if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != PasswordCompat\binary\_strlen($hash) || PasswordCompat\binary\_strlen($ret) <= 13) {
return false; return false;
} }
$status = 0; $status = 0;
for ($i = 0; $i < strlen($ret); $i++) { for ($i = 0; $i < PasswordCompat\binary\_strlen($ret); $i++) {
$status |= (ord($ret[$i]) ^ ord($hash[$i])); $status |= (ord($ret[$i]) ^ ord($hash[$i]));
} }
return $status === 0; return $status === 0;
} }
} }
}
namespace PasswordCompat\binary {
/**
* 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);
}
}