angelovcom.net

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

class-phpass.php (6699B)


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