| 1: | <?php
|
| 2: | |
| 3: | |
| 4: | |
| 5: | |
| 6: | |
| 7: | |
| 8: |
|
| 9: |
|
| 10: | namespace {
|
| 11: |
|
| 12: | if (!defined('PASSWORD_BCRYPT')) {
|
| 13: | |
| 14: | |
| 15: | |
| 16: | |
| 17: | |
| 18: |
|
| 19: | define('PASSWORD_BCRYPT', 1);
|
| 20: | define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);
|
| 21: | define('PASSWORD_BCRYPT_DEFAULT_COST', 10);
|
| 22: | }
|
| 23: |
|
| 24: | if (!function_exists('password_hash')) {
|
| 25: |
|
| 26: | |
| 27: | |
| 28: | |
| 29: | |
| 30: | |
| 31: | |
| 32: | |
| 33: | |
| 34: |
|
| 35: | function password_hash($password, $algo, array $options = array()) {
|
| 36: | if (!function_exists('crypt')) {
|
| 37: | trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING);
|
| 38: | return null;
|
| 39: | }
|
| 40: | if (is_null($password) || is_int($password)) {
|
| 41: | $password = (string) $password;
|
| 42: | }
|
| 43: | if (!is_string($password)) {
|
| 44: | trigger_error("password_hash(): Password must be a string", E_USER_WARNING);
|
| 45: | return null;
|
| 46: | }
|
| 47: | if (!is_int($algo)) {
|
| 48: | trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING);
|
| 49: | return null;
|
| 50: | }
|
| 51: | $resultLength = 0;
|
| 52: | switch ($algo) {
|
| 53: | case PASSWORD_BCRYPT:
|
| 54: | $cost = PASSWORD_BCRYPT_DEFAULT_COST;
|
| 55: | if (isset($options['cost'])) {
|
| 56: | $cost = $options['cost'];
|
| 57: | if ($cost < 4 || $cost > 31) {
|
| 58: | trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING);
|
| 59: | return null;
|
| 60: | }
|
| 61: | }
|
| 62: |
|
| 63: | $raw_salt_len = 16;
|
| 64: |
|
| 65: | $required_salt_len = 22;
|
| 66: | $hash_format = sprintf("$2y$%02d$", $cost);
|
| 67: |
|
| 68: | $resultLength = 60;
|
| 69: | break;
|
| 70: | default:
|
| 71: | trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING);
|
| 72: | return null;
|
| 73: | }
|
| 74: | $salt_requires_encoding = false;
|
| 75: | if (isset($options['salt'])) {
|
| 76: | switch (gettype($options['salt'])) {
|
| 77: | case 'NULL':
|
| 78: | case 'boolean':
|
| 79: | case 'integer':
|
| 80: | case 'double':
|
| 81: | case 'string':
|
| 82: | $salt = (string) $options['salt'];
|
| 83: | break;
|
| 84: | case 'object':
|
| 85: | if (method_exists($options['salt'], '__tostring')) {
|
| 86: | $salt = (string) $options['salt'];
|
| 87: | break;
|
| 88: | }
|
| 89: | case 'array':
|
| 90: | case 'resource':
|
| 91: | default:
|
| 92: | trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING);
|
| 93: | return null;
|
| 94: | }
|
| 95: | if (PasswordCompat\binary\_strlen($salt) < $required_salt_len) {
|
| 96: | trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", PasswordCompat\binary\_strlen($salt), $required_salt_len), E_USER_WARNING);
|
| 97: | return null;
|
| 98: | } elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) {
|
| 99: | $salt_requires_encoding = true;
|
| 100: | }
|
| 101: | } else {
|
| 102: | $buffer = '';
|
| 103: | $buffer_valid = false;
|
| 104: | if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {
|
| 105: | $buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM);
|
| 106: | if ($buffer) {
|
| 107: | $buffer_valid = true;
|
| 108: | }
|
| 109: | }
|
| 110: | if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
|
| 111: | $buffer = openssl_random_pseudo_bytes($raw_salt_len);
|
| 112: | if ($buffer) {
|
| 113: | $buffer_valid = true;
|
| 114: | }
|
| 115: | }
|
| 116: | if (!$buffer_valid && @is_readable('/dev/urandom')) {
|
| 117: | $f = fopen('/dev/urandom', 'r');
|
| 118: | $read = PasswordCompat\binary\_strlen($buffer);
|
| 119: | while ($read < $raw_salt_len) {
|
| 120: | $buffer .= fread($f, $raw_salt_len - $read);
|
| 121: | $read = PasswordCompat\binary\_strlen($buffer);
|
| 122: | }
|
| 123: | fclose($f);
|
| 124: | if ($read >= $raw_salt_len) {
|
| 125: | $buffer_valid = true;
|
| 126: | }
|
| 127: | }
|
| 128: | if (!$buffer_valid || PasswordCompat\binary\_strlen($buffer) < $raw_salt_len) {
|
| 129: | $bl = PasswordCompat\binary\_strlen($buffer);
|
| 130: | for ($i = 0; $i < $raw_salt_len; $i++) {
|
| 131: | if ($i < $bl) {
|
| 132: | $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
|
| 133: | } else {
|
| 134: | $buffer .= chr(mt_rand(0, 255));
|
| 135: | }
|
| 136: | }
|
| 137: | }
|
| 138: | $salt = $buffer;
|
| 139: | $salt_requires_encoding = true;
|
| 140: | }
|
| 141: | if ($salt_requires_encoding) {
|
| 142: |
|
| 143: | $base64_digits =
|
| 144: | 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
| 145: | $bcrypt64_digits =
|
| 146: | './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
| 147: |
|
| 148: | $base64_string = base64_encode($salt);
|
| 149: | $salt = strtr(rtrim($base64_string, '='), $base64_digits, $bcrypt64_digits);
|
| 150: | }
|
| 151: | $salt = PasswordCompat\binary\_substr($salt, 0, $required_salt_len);
|
| 152: |
|
| 153: | $hash = $hash_format . $salt;
|
| 154: |
|
| 155: | $ret = crypt($password, $hash);
|
| 156: |
|
| 157: | if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != $resultLength) {
|
| 158: | return false;
|
| 159: | }
|
| 160: |
|
| 161: | return $ret;
|
| 162: | }
|
| 163: |
|
| 164: | |
| 165: | |
| 166: | |
| 167: | |
| 168: | |
| 169: | |
| 170: | |
| 171: | |
| 172: | |
| 173: | |
| 174: | |
| 175: | |
| 176: | |
| 177: | |
| 178: | |
| 179: |
|
| 180: | function password_get_info($hash) {
|
| 181: | $return = array(
|
| 182: | 'algo' => 0,
|
| 183: | 'algoName' => 'unknown',
|
| 184: | 'options' => array(),
|
| 185: | );
|
| 186: | if (PasswordCompat\binary\_substr($hash, 0, 4) == '$2y$' && PasswordCompat\binary\_strlen($hash) == 60) {
|
| 187: | $return['algo'] = PASSWORD_BCRYPT;
|
| 188: | $return['algoName'] = 'bcrypt';
|
| 189: | list($cost) = sscanf($hash, "$2y$%d$");
|
| 190: | $return['options']['cost'] = $cost;
|
| 191: | }
|
| 192: | return $return;
|
| 193: | }
|
| 194: |
|
| 195: | |
| 196: | |
| 197: | |
| 198: | |
| 199: | |
| 200: | |
| 201: | |
| 202: | |
| 203: | |
| 204: | |
| 205: |
|
| 206: | function password_needs_rehash($hash, $algo, array $options = array()) {
|
| 207: | $info = password_get_info($hash);
|
| 208: | if ($info['algo'] != $algo) {
|
| 209: | return true;
|
| 210: | }
|
| 211: | switch ($algo) {
|
| 212: | case PASSWORD_BCRYPT:
|
| 213: | $cost = isset($options['cost']) ? $options['cost'] : PASSWORD_BCRYPT_DEFAULT_COST;
|
| 214: | if ($cost != $info['options']['cost']) {
|
| 215: | return true;
|
| 216: | }
|
| 217: | break;
|
| 218: | }
|
| 219: | return false;
|
| 220: | }
|
| 221: |
|
| 222: | |
| 223: | |
| 224: | |
| 225: | |
| 226: | |
| 227: | |
| 228: | |
| 229: |
|
| 230: | function password_verify($password, $hash) {
|
| 231: | if (!function_exists('crypt')) {
|
| 232: | trigger_error("Crypt must be loaded for password_verify to function", E_USER_WARNING);
|
| 233: | return false;
|
| 234: | }
|
| 235: | $ret = crypt($password, $hash);
|
| 236: | if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != PasswordCompat\binary\_strlen($hash) || PasswordCompat\binary\_strlen($ret) <= 13) {
|
| 237: | return false;
|
| 238: | }
|
| 239: |
|
| 240: | $status = 0;
|
| 241: | for ($i = 0; $i < PasswordCompat\binary\_strlen($ret); $i++) {
|
| 242: | $status |= (ord($ret[$i]) ^ ord($hash[$i]));
|
| 243: | }
|
| 244: |
|
| 245: | return $status === 0;
|
| 246: | }
|
| 247: | }
|
| 248: |
|
| 249: | }
|
| 250: |
|
| 251: | namespace PasswordCompat\binary {
|
| 252: |
|
| 253: | if (!function_exists('PasswordCompat\\binary\\_strlen')) {
|
| 254: |
|
| 255: | |
| 256: | |
| 257: | |
| 258: | |
| 259: | |
| 260: | |
| 261: | |
| 262: | |
| 263: | |
| 264: | |
| 265: | |
| 266: |
|
| 267: | function _strlen($binary_string) {
|
| 268: | if (function_exists('mb_strlen')) {
|
| 269: | return mb_strlen($binary_string, '8bit');
|
| 270: | }
|
| 271: | return strlen($binary_string);
|
| 272: | }
|
| 273: |
|
| 274: | |
| 275: | |
| 276: | |
| 277: | |
| 278: | |
| 279: | |
| 280: | |
| 281: | |
| 282: | |
| 283: | |
| 284: | |
| 285: |
|
| 286: | function _substr($binary_string, $start, $length) {
|
| 287: | if (function_exists('mb_substr')) {
|
| 288: | return mb_substr($binary_string, $start, $length, '8bit');
|
| 289: | }
|
| 290: | return substr($binary_string, $start, $length);
|
| 291: | }
|
| 292: |
|
| 293: | |
| 294: | |
| 295: | |
| 296: | |
| 297: |
|
| 298: | function check() {
|
| 299: | static $pass = NULL;
|
| 300: |
|
| 301: | if (is_null($pass)) {
|
| 302: | if (function_exists('crypt')) {
|
| 303: | $hash = '$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG';
|
| 304: | $test = crypt("password", $hash);
|
| 305: | $pass = $test == $hash;
|
| 306: | } else {
|
| 307: | $pass = false;
|
| 308: | }
|
| 309: | }
|
| 310: | return $pass;
|
| 311: | }
|
| 312: |
|
| 313: | }
|
| 314: | } |