wp/wp-includes/class-phpass.php
changeset 18 be944660c56a
parent 16 a86126ab1dd4
child 21 48c4eec2b7e6
equal deleted inserted replaced
17:34716fd837a4 18:be944660c56a
     1 <?php
     1 <?php
     2 /**
     2 /**
     3  * Portable PHP password hashing framework.
     3  * Portable PHP password hashing framework.
     4  * @package phpass
     4  * @package phpass
     5  * @since 2.5.0
     5  * @since 2.5.0
     6  * @version 0.3 / WordPress
     6  * @version 0.5 / WordPress
     7  * @link https://www.openwall.com/phpass/
     7  * @link https://www.openwall.com/phpass/
     8  */
     8  */
     9 
     9 
    10 #
    10 #
       
    11 # Portable PHP password hashing framework.
       
    12 #
       
    13 # Version 0.5 / WordPress.
       
    14 #
    11 # Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
    15 # Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
    12 # the public domain.  Revised in subsequent years, still public domain.
    16 # the public domain.  Revised in subsequent years, still public domain.
    13 #
    17 #
    14 # There's absolutely no warranty.
    18 # There's absolutely no warranty.
       
    19 #
       
    20 # The homepage URL for this framework is:
       
    21 #
       
    22 #	http://www.openwall.com/phpass/
    15 #
    23 #
    16 # Please be sure to update the Version line if you edit this file in any way.
    24 # 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
    25 # 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.
    26 # your project name (after the slash) and add your own revision information.
    19 #
    27 #
    27 
    35 
    28 /**
    36 /**
    29  * Portable PHP password hashing framework.
    37  * Portable PHP password hashing framework.
    30  *
    38  *
    31  * @package phpass
    39  * @package phpass
    32  * @version 0.3 / WordPress
    40  * @version 0.5 / WordPress
    33  * @link https://www.openwall.com/phpass/
    41  * @link https://www.openwall.com/phpass/
    34  * @since 2.5.0
    42  * @since 2.5.0
    35  */
    43  */
    36 class PasswordHash {
    44 class PasswordHash {
    37 	var $itoa64;
    45 	var $itoa64;
    38 	var $iteration_count_log2;
    46 	var $iteration_count_log2;
    39 	var $portable_hashes;
    47 	var $portable_hashes;
    40 	var $random_state;
    48 	var $random_state;
    41 
    49 
    42 	/**
    50 	function __construct($iteration_count_log2, $portable_hashes)
    43 	 * PHP5 constructor.
       
    44 	 */
       
    45 	function __construct( $iteration_count_log2, $portable_hashes )
       
    46 	{
    51 	{
    47 		$this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    52 		$this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    48 
    53 
    49 		if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
    54 		if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
    50 			$iteration_count_log2 = 8;
    55 			$iteration_count_log2 = 8;
    51 		$this->iteration_count_log2 = $iteration_count_log2;
    56 		$this->iteration_count_log2 = $iteration_count_log2;
    52 
    57 
    53 		$this->portable_hashes = $portable_hashes;
    58 		$this->portable_hashes = $portable_hashes;
    54 
    59 
    55 		$this->random_state = microtime() . uniqid(rand(), TRUE); // removed getmypid() for compatibility reasons
    60 		$this->random_state = microtime();
    56 	}
    61 		if (function_exists('getmypid'))
    57 
    62 			$this->random_state .= getmypid();
    58 	/**
    63 	}
    59 	 * PHP4 constructor.
    64 
    60 	 */
    65 	function PasswordHash($iteration_count_log2, $portable_hashes)
    61 	public function PasswordHash( $iteration_count_log2, $portable_hashes ) {
    66 	{
    62 		self::__construct( $iteration_count_log2, $portable_hashes );
    67 		self::__construct($iteration_count_log2, $portable_hashes);
    63 	}
    68 	}
    64 
    69 
    65 	function get_random_bytes($count)
    70 	function get_random_bytes($count)
    66 	{
    71 	{
    67 		$output = '';
    72 		$output = '';
    68 		if ( @is_readable('/dev/urandom') &&
    73 		if (@is_readable('/dev/urandom') &&
    69 		    ($fh = @fopen('/dev/urandom', 'rb'))) {
    74 		    ($fh = @fopen('/dev/urandom', 'rb'))) {
    70 			$output = fread($fh, $count);
    75 			$output = fread($fh, $count);
    71 			fclose($fh);
    76 			fclose($fh);
    72 		}
    77 		}
    73 
    78 
    74 		if (strlen($output) < $count) {
    79 		if (strlen($output) < $count) {
    75 			$output = '';
    80 			$output = '';
    76 			for ($i = 0; $i < $count; $i += 16) {
    81 			for ($i = 0; $i < $count; $i += 16) {
    77 				$this->random_state =
    82 				$this->random_state =
    78 				    md5(microtime() . $this->random_state);
    83 				    md5(microtime() . $this->random_state);
    79 				$output .=
    84 				$output .= md5($this->random_state, TRUE);
    80 				    pack('H*', md5($this->random_state));
       
    81 			}
    85 			}
    82 			$output = substr($output, 0, $count);
    86 			$output = substr($output, 0, $count);
    83 		}
    87 		}
    84 
    88 
    85 		return $output;
    89 		return $output;
   119 	}
   123 	}
   120 
   124 
   121 	function crypt_private($password, $setting)
   125 	function crypt_private($password, $setting)
   122 	{
   126 	{
   123 		$output = '*0';
   127 		$output = '*0';
   124 		if (substr($setting, 0, 2) == $output)
   128 		if (substr($setting, 0, 2) === $output)
   125 			$output = '*1';
   129 			$output = '*1';
   126 
   130 
   127 		$id = substr($setting, 0, 3);
   131 		$id = substr($setting, 0, 3);
   128 		# We use "$P$", phpBB3 uses "$H$" for the same thing
   132 		# We use "$P$", phpBB3 uses "$H$" for the same thing
   129 		if ($id != '$P$' && $id != '$H$')
   133 		if ($id !== '$P$' && $id !== '$H$')
   130 			return $output;
   134 			return $output;
   131 
   135 
   132 		$count_log2 = strpos($this->itoa64, $setting[3]);
   136 		$count_log2 = strpos($this->itoa64, $setting[3]);
   133 		if ($count_log2 < 7 || $count_log2 > 30)
   137 		if ($count_log2 < 7 || $count_log2 > 30)
   134 			return $output;
   138 			return $output;
   135 
   139 
   136 		$count = 1 << $count_log2;
   140 		$count = 1 << $count_log2;
   137 
   141 
   138 		$salt = substr($setting, 4, 8);
   142 		$salt = substr($setting, 4, 8);
   139 		if (strlen($salt) != 8)
   143 		if (strlen($salt) !== 8)
   140 			return $output;
   144 			return $output;
   141 
   145 
   142 		# We're kind of forced to use MD5 here since it's the only
   146 		# We were kind of forced to use MD5 here since it's the only
   143 		# cryptographic primitive available in all versions of PHP
   147 		# cryptographic primitive that was available in all versions
   144 		# currently in use.  To implement our own low-level crypto
   148 		# of PHP in use.  To implement our own low-level crypto in PHP
   145 		# in PHP would result in much worse performance and
   149 		# would have resulted in much worse performance and
   146 		# consequently in lower iteration counts and hashes that are
   150 		# consequently in lower iteration counts and hashes that are
   147 		# quicker to crack (by non-PHP code).
   151 		# quicker to crack (by non-PHP code).
   148 		if (PHP_VERSION >= '5') {
   152 		$hash = md5($salt . $password, TRUE);
   149 			$hash = md5($salt . $password, TRUE);
   153 		do {
   150 			do {
   154 			$hash = md5($hash . $password, TRUE);
   151 				$hash = md5($hash . $password, TRUE);
   155 		} while (--$count);
   152 			} while (--$count);
       
   153 		} else {
       
   154 			$hash = pack('H*', md5($salt . $password));
       
   155 			do {
       
   156 				$hash = pack('H*', md5($hash . $password));
       
   157 			} while (--$count);
       
   158 		}
       
   159 
   156 
   160 		$output = substr($setting, 0, 12);
   157 		$output = substr($setting, 0, 12);
   161 		$output .= $this->encode64($hash, 16);
   158 		$output .= $this->encode64($hash, 16);
   162 
       
   163 		return $output;
       
   164 	}
       
   165 
       
   166 	function gensalt_extended($input)
       
   167 	{
       
   168 		$count_log2 = min($this->iteration_count_log2 + 8, 24);
       
   169 		# This should be odd to not reveal weak DES keys, and the
       
   170 		# maximum valid value is (2**24 - 1) which is odd anyway.
       
   171 		$count = (1 << $count_log2) - 1;
       
   172 
       
   173 		$output = '_';
       
   174 		$output .= $this->itoa64[$count & 0x3f];
       
   175 		$output .= $this->itoa64[($count >> 6) & 0x3f];
       
   176 		$output .= $this->itoa64[($count >> 12) & 0x3f];
       
   177 		$output .= $this->itoa64[($count >> 18) & 0x3f];
       
   178 
       
   179 		$output .= $this->encode64($input, 3);
       
   180 
   159 
   181 		return $output;
   160 		return $output;
   182 	}
   161 	}
   183 
   162 
   184 	function gensalt_blowfish($input)
   163 	function gensalt_blowfish($input)
   228 			return '*';
   207 			return '*';
   229 		}
   208 		}
   230 
   209 
   231 		$random = '';
   210 		$random = '';
   232 
   211 
   233 		if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) {
   212 		if (CRYPT_BLOWFISH === 1 && !$this->portable_hashes) {
   234 			$random = $this->get_random_bytes(16);
   213 			$random = $this->get_random_bytes(16);
   235 			$hash =
   214 			$hash =
   236 			    crypt($password, $this->gensalt_blowfish($random));
   215 			    crypt($password, $this->gensalt_blowfish($random));
   237 			if (strlen($hash) == 60)
   216 			if (strlen($hash) === 60)
   238 				return $hash;
       
   239 		}
       
   240 
       
   241 		if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) {
       
   242 			if (strlen($random) < 3)
       
   243 				$random = $this->get_random_bytes(3);
       
   244 			$hash =
       
   245 			    crypt($password, $this->gensalt_extended($random));
       
   246 			if (strlen($hash) == 20)
       
   247 				return $hash;
   217 				return $hash;
   248 		}
   218 		}
   249 
   219 
   250 		if (strlen($random) < 6)
   220 		if (strlen($random) < 6)
   251 			$random = $this->get_random_bytes(6);
   221 			$random = $this->get_random_bytes(6);
   252 		$hash =
   222 		$hash =
   253 		    $this->crypt_private($password,
   223 		    $this->crypt_private($password,
   254 		    $this->gensalt_private($random));
   224 		    $this->gensalt_private($random));
   255 		if (strlen($hash) == 34)
   225 		if (strlen($hash) === 34)
   256 			return $hash;
   226 			return $hash;
   257 
   227 
   258 		# Returning '*' on error is safe here, but would _not_ be safe
   228 		# Returning '*' on error is safe here, but would _not_ be safe
   259 		# in a crypt(3)-like function used _both_ for generating new
   229 		# in a crypt(3)-like function used _both_ for generating new
   260 		# hashes and for validating passwords against existing hashes.
   230 		# hashes and for validating passwords against existing hashes.
   266 		if ( strlen( $password ) > 4096 ) {
   236 		if ( strlen( $password ) > 4096 ) {
   267 			return false;
   237 			return false;
   268 		}
   238 		}
   269 
   239 
   270 		$hash = $this->crypt_private($password, $stored_hash);
   240 		$hash = $this->crypt_private($password, $stored_hash);
   271 		if ($hash[0] == '*')
   241 		if ($hash[0] === '*')
   272 			$hash = crypt($password, $stored_hash);
   242 			$hash = crypt($password, $stored_hash);
   273 
   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).
   274 		return $hash === $stored_hash;
   248 		return $hash === $stored_hash;
   275 	}
   249 	}
   276 }
   250 }