[44] | 1 | <?php
|
---|
| 2 | /**
|
---|
| 3 | * Portable PHP password hashing framework.
|
---|
| 4 | * @package phpass
|
---|
| 5 | * @since 2.5
|
---|
| 6 | * @version 0.1
|
---|
| 7 | * @link http://www.openwall.com/phpass/
|
---|
| 8 | */
|
---|
| 9 |
|
---|
| 10 | #
|
---|
| 11 | # Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
|
---|
| 12 | # the public domain.
|
---|
| 13 | #
|
---|
| 14 | # There's absolutely no warranty.
|
---|
| 15 | #
|
---|
| 16 | # Please be sure to update the Version line if you edit this file in any way.
|
---|
| 17 | # It is suggested that you leave the main version number intact, but indicate
|
---|
| 18 | # your project name (after the slash) and add your own revision information.
|
---|
| 19 | #
|
---|
| 20 | # Please do not change the "private" password hashing method implemented in
|
---|
| 21 | # here, thereby making your hashes incompatible. However, if you must, please
|
---|
| 22 | # change the hash type identifier (the "$P$") to something different.
|
---|
| 23 | #
|
---|
| 24 | # Obviously, since this code is in the public domain, the above are not
|
---|
| 25 | # requirements (there can be none), but merely suggestions.
|
---|
| 26 | #
|
---|
| 27 |
|
---|
| 28 | /**
|
---|
| 29 | * Portable PHP password hashing framework.
|
---|
| 30 | *
|
---|
| 31 | * @package phpass
|
---|
| 32 | * @version 0.1 / genuine
|
---|
| 33 | * @link http://www.openwall.com/phpass/
|
---|
| 34 | * @since 2.5
|
---|
| 35 | */
|
---|
| 36 | class PasswordHash {
|
---|
| 37 | var $itoa64;
|
---|
| 38 | var $iteration_count_log2;
|
---|
| 39 | var $portable_hashes;
|
---|
| 40 | var $random_state;
|
---|
| 41 |
|
---|
| 42 | function PasswordHash($iteration_count_log2, $portable_hashes)
|
---|
| 43 | {
|
---|
| 44 | $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
---|
| 45 |
|
---|
| 46 | if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
|
---|
| 47 | $iteration_count_log2 = 8;
|
---|
| 48 | $this->iteration_count_log2 = $iteration_count_log2;
|
---|
| 49 |
|
---|
| 50 | $this->portable_hashes = $portable_hashes;
|
---|
| 51 |
|
---|
| 52 | $this->random_state = microtime() . (function_exists('getmypid') ? getmypid() : '') . uniqid(rand(), TRUE);
|
---|
| 53 |
|
---|
| 54 | }
|
---|
| 55 |
|
---|
| 56 | function get_random_bytes($count)
|
---|
| 57 | {
|
---|
| 58 | $output = '';
|
---|
| 59 | if (($fh = @fopen('/dev/urandom', 'rb'))) {
|
---|
| 60 | $output = fread($fh, $count);
|
---|
| 61 | fclose($fh);
|
---|
| 62 | }
|
---|
| 63 |
|
---|
| 64 | if (strlen($output) < $count) {
|
---|
| 65 | $output = '';
|
---|
| 66 | for ($i = 0; $i < $count; $i += 16) {
|
---|
| 67 | $this->random_state =
|
---|
| 68 | md5(microtime() . $this->random_state);
|
---|
| 69 | $output .=
|
---|
| 70 | pack('H*', md5($this->random_state));
|
---|
| 71 | }
|
---|
| 72 | $output = substr($output, 0, $count);
|
---|
| 73 | }
|
---|
| 74 |
|
---|
| 75 | return $output;
|
---|
| 76 | }
|
---|
| 77 |
|
---|
| 78 | function encode64($input, $count)
|
---|
| 79 | {
|
---|
| 80 | $output = '';
|
---|
| 81 | $i = 0;
|
---|
| 82 | do {
|
---|
| 83 | $value = ord($input[$i++]);
|
---|
| 84 | $output .= $this->itoa64[$value & 0x3f];
|
---|
| 85 | if ($i < $count)
|
---|
| 86 | $value |= ord($input[$i]) << 8;
|
---|
| 87 | $output .= $this->itoa64[($value >> 6) & 0x3f];
|
---|
| 88 | if ($i++ >= $count)
|
---|
| 89 | break;
|
---|
| 90 | if ($i < $count)
|
---|
| 91 | $value |= ord($input[$i]) << 16;
|
---|
| 92 | $output .= $this->itoa64[($value >> 12) & 0x3f];
|
---|
| 93 | if ($i++ >= $count)
|
---|
| 94 | break;
|
---|
| 95 | $output .= $this->itoa64[($value >> 18) & 0x3f];
|
---|
| 96 | } while ($i < $count);
|
---|
| 97 |
|
---|
| 98 | return $output;
|
---|
| 99 | }
|
---|
| 100 |
|
---|
| 101 | function gensalt_private($input)
|
---|
| 102 | {
|
---|
| 103 | $output = '$P$';
|
---|
| 104 | $output .= $this->itoa64[min($this->iteration_count_log2 +
|
---|
| 105 | ((PHP_VERSION >= '5') ? 5 : 3), 30)];
|
---|
| 106 | $output .= $this->encode64($input, 6);
|
---|
| 107 |
|
---|
| 108 | return $output;
|
---|
| 109 | }
|
---|
| 110 |
|
---|
| 111 | function crypt_private($password, $setting)
|
---|
| 112 | {
|
---|
| 113 | $output = '*0';
|
---|
| 114 | if (substr($setting, 0, 2) == $output)
|
---|
| 115 | $output = '*1';
|
---|
| 116 |
|
---|
| 117 | if (substr($setting, 0, 3) != '$P$')
|
---|
| 118 | return $output;
|
---|
| 119 |
|
---|
| 120 | $count_log2 = strpos($this->itoa64, $setting[3]);
|
---|
| 121 | if ($count_log2 < 7 || $count_log2 > 30)
|
---|
| 122 | return $output;
|
---|
| 123 |
|
---|
| 124 | $count = 1 << $count_log2;
|
---|
| 125 |
|
---|
| 126 | $salt = substr($setting, 4, 8);
|
---|
| 127 | if (strlen($salt) != 8)
|
---|
| 128 | return $output;
|
---|
| 129 |
|
---|
| 130 | # We're kind of forced to use MD5 here since it's the only
|
---|
| 131 | # cryptographic primitive available in all versions of PHP
|
---|
| 132 | # currently in use. To implement our own low-level crypto
|
---|
| 133 | # in PHP would result in much worse performance and
|
---|
| 134 | # consequently in lower iteration counts and hashes that are
|
---|
| 135 | # quicker to crack (by non-PHP code).
|
---|
| 136 | if (PHP_VERSION >= '5') {
|
---|
| 137 | $hash = md5($salt . $password, TRUE);
|
---|
| 138 | do {
|
---|
| 139 | $hash = md5($hash . $password, TRUE);
|
---|
| 140 | } while (--$count);
|
---|
| 141 | } else {
|
---|
| 142 | $hash = pack('H*', md5($salt . $password));
|
---|
| 143 | do {
|
---|
| 144 | $hash = pack('H*', md5($hash . $password));
|
---|
| 145 | } while (--$count);
|
---|
| 146 | }
|
---|
| 147 |
|
---|
| 148 | $output = substr($setting, 0, 12);
|
---|
| 149 | $output .= $this->encode64($hash, 16);
|
---|
| 150 |
|
---|
| 151 | return $output;
|
---|
| 152 | }
|
---|
| 153 |
|
---|
| 154 | function gensalt_extended($input)
|
---|
| 155 | {
|
---|
| 156 | $count_log2 = min($this->iteration_count_log2 + 8, 24);
|
---|
| 157 | # This should be odd to not reveal weak DES keys, and the
|
---|
| 158 | # maximum valid value is (2**24 - 1) which is odd anyway.
|
---|
| 159 | $count = (1 << $count_log2) - 1;
|
---|
| 160 |
|
---|
| 161 | $output = '_';
|
---|
| 162 | $output .= $this->itoa64[$count & 0x3f];
|
---|
| 163 | $output .= $this->itoa64[($count >> 6) & 0x3f];
|
---|
| 164 | $output .= $this->itoa64[($count >> 12) & 0x3f];
|
---|
| 165 | $output .= $this->itoa64[($count >> 18) & 0x3f];
|
---|
| 166 |
|
---|
| 167 | $output .= $this->encode64($input, 3);
|
---|
| 168 |
|
---|
| 169 | return $output;
|
---|
| 170 | }
|
---|
| 171 |
|
---|
| 172 | function gensalt_blowfish($input)
|
---|
| 173 | {
|
---|
| 174 | # This one needs to use a different order of characters and a
|
---|
| 175 | # different encoding scheme from the one in encode64() above.
|
---|
| 176 | # We care because the last character in our encoded string will
|
---|
| 177 | # only represent 2 bits. While two known implementations of
|
---|
| 178 | # bcrypt will happily accept and correct a salt string which
|
---|
| 179 | # has the 4 unused bits set to non-zero, we do not want to take
|
---|
| 180 | # chances and we also do not want to waste an additional byte
|
---|
| 181 | # of entropy.
|
---|
| 182 | $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
---|
| 183 |
|
---|
| 184 | $output = '$2a$';
|
---|
| 185 | $output .= chr(ord('0') + $this->iteration_count_log2 / 10);
|
---|
| 186 | $output .= chr(ord('0') + $this->iteration_count_log2 % 10);
|
---|
| 187 | $output .= '$';
|
---|
| 188 |
|
---|
| 189 | $i = 0;
|
---|
| 190 | do {
|
---|
| 191 | $c1 = ord($input[$i++]);
|
---|
| 192 | $output .= $itoa64[$c1 >> 2];
|
---|
| 193 | $c1 = ($c1 & 0x03) << 4;
|
---|
| 194 | if ($i >= 16) {
|
---|
| 195 | $output .= $itoa64[$c1];
|
---|
| 196 | break;
|
---|
| 197 | }
|
---|
| 198 |
|
---|
| 199 | $c2 = ord($input[$i++]);
|
---|
| 200 | $c1 |= $c2 >> 4;
|
---|
| 201 | $output .= $itoa64[$c1];
|
---|
| 202 | $c1 = ($c2 & 0x0f) << 2;
|
---|
| 203 |
|
---|
| 204 | $c2 = ord($input[$i++]);
|
---|
| 205 | $c1 |= $c2 >> 6;
|
---|
| 206 | $output .= $itoa64[$c1];
|
---|
| 207 | $output .= $itoa64[$c2 & 0x3f];
|
---|
| 208 | } while (1);
|
---|
| 209 |
|
---|
| 210 | return $output;
|
---|
| 211 | }
|
---|
| 212 |
|
---|
| 213 | function HashPassword($password)
|
---|
| 214 | {
|
---|
| 215 | $random = '';
|
---|
| 216 |
|
---|
| 217 | if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) {
|
---|
| 218 | $random = $this->get_random_bytes(16);
|
---|
| 219 | $hash =
|
---|
| 220 | crypt($password, $this->gensalt_blowfish($random));
|
---|
| 221 | if (strlen($hash) == 60)
|
---|
| 222 | return $hash;
|
---|
| 223 | }
|
---|
| 224 |
|
---|
| 225 | if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) {
|
---|
| 226 | if (strlen($random) < 3)
|
---|
| 227 | $random = $this->get_random_bytes(3);
|
---|
| 228 | $hash =
|
---|
| 229 | crypt($password, $this->gensalt_extended($random));
|
---|
| 230 | if (strlen($hash) == 20)
|
---|
| 231 | return $hash;
|
---|
| 232 | }
|
---|
| 233 |
|
---|
| 234 | if (strlen($random) < 6)
|
---|
| 235 | $random = $this->get_random_bytes(6);
|
---|
| 236 | $hash =
|
---|
| 237 | $this->crypt_private($password,
|
---|
| 238 | $this->gensalt_private($random));
|
---|
| 239 | if (strlen($hash) == 34)
|
---|
| 240 | return $hash;
|
---|
| 241 |
|
---|
| 242 | # Returning '*' on error is safe here, but would _not_ be safe
|
---|
| 243 | # in a crypt(3)-like function used _both_ for generating new
|
---|
| 244 | # hashes and for validating passwords against existing hashes.
|
---|
| 245 | return '*';
|
---|
| 246 | }
|
---|
| 247 |
|
---|
| 248 | function CheckPassword($password, $stored_hash)
|
---|
| 249 | {
|
---|
| 250 | $hash = $this->crypt_private($password, $stored_hash);
|
---|
| 251 | if ($hash[0] == '*')
|
---|
| 252 | $hash = crypt($password, $stored_hash);
|
---|
| 253 |
|
---|
| 254 | return $hash == $stored_hash;
|
---|
| 255 | }
|
---|
| 256 | }
|
---|
| 257 |
|
---|
| 258 | ?>
|
---|