Update code_sniffer build.xml file to be executable on our system
[phpbb.git] / phpBB / includes / core / security.php
blobf5aca65e8d9202e6f967d3d7719de7ea7af282cf
1 <?php
2 /**
4 * @package core
5 * @version $Id$
6 * @copyright (c) 2008 phpBB Group
7 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
9 */
11 /**
12 * @ignore
14 if (!defined('IN_PHPBB'))
16 exit();
18 /**
19 * Class for generating random numbers, unique ids, unique keys, seeds, hashes...
20 * @package core
22 class phpbb_security extends phpbb_plugin_support
24 /**
25 * @var array required phpBB objects
27 public $phpbb_required = array();
29 /**
30 * @var array Optional phpBB objects
32 public $phpbb_optional = array('config');
34 /**
35 * @var string Used hash type. The default type is $P$, phpBB uses a different one.
37 public $hash_type = '$H$';
39 /**
40 * @var bool Is true if random seed got updated.
42 private $dss_seeded = false;
44 /**
45 * Constructor
46 * @access public
48 public function __construct() {}
50 /**
51 * Generates an alphanumeric random string of given length
53 * @param int $num_chars Number of characters to return
54 * @return string Random string of $num_chars characters.
55 * @access public
57 public function gen_rand_string($num_chars = 8)
59 $rand_str = $this->unique_id();
60 $rand_str = str_replace('0', 'Z', strtoupper(base_convert($rand_str, 16, 35)));
62 return substr($rand_str, 0, $num_chars);
65 /**
66 * Return unique id
68 * @param string $extra Additional entropy
69 * @return string Unique id
70 * @access public
72 public function unique_id($extra = 'c')
74 if (!isset(phpbb::$config['rand_seed']))
76 $val = md5(md5($extra) . microtime());
77 $val = md5(md5($extra) . $val . $extra);
78 return substr($val, 4, 16);
82 $val = phpbb::$config['rand_seed'] . microtime();
83 $val = md5($val);
84 phpbb::$config['rand_seed'] = md5(phpbb::$config['rand_seed'] . $val . $extra);
86 if (!$this->dss_seeded && phpbb::$config['rand_seed_last_update'] < time() - rand(1, 10))
88 set_config('rand_seed', phpbb::$config['rand_seed'], true);
89 set_config('rand_seed_last_update', time(), true);
91 $this->dss_seeded = true;
94 return substr($val, 4, 16);
97 /**
98 * Hash passwords
100 * @version Version 0.1
102 * Portable PHP password hashing framework.
104 * Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
105 * the public domain.
107 * There's absolutely no warranty.
109 * The homepage URL for this framework is:
111 * http://www.openwall.com/phpass/
113 * Please be sure to update the Version line if you edit this file in any way.
114 * It is suggested that you leave the main version number intact, but indicate
115 * your project name (after the slash) and add your own revision information.
117 * Please do not change the "private" password hashing method implemented in
118 * here, thereby making your hashes incompatible. However, if you must, please
119 * change the hash type identifier (the "$P$") to something different.
121 * Obviously, since this code is in the public domain, the above are not
122 * requirements (there can be none), but merely suggestions.
124 * @param string $password Password to hash
125 * @return string Hashed password
126 * @access public
128 public function hash_password($password)
130 $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
132 $random_state = $this->unique_id();
133 $random = '';
134 $count = 6;
136 if (($fh = @fopen('/dev/urandom', 'rb')))
138 $random = fread($fh, $count);
139 fclose($fh);
142 if (strlen($random) < $count)
144 $random = '';
146 for ($i = 0; $i < $count; $i += 16)
148 $random_state = md5($this->unique_id() . $random_state);
149 $random .= pack('H*', md5($random_state));
151 $random = substr($random, 0, $count);
154 $hash = $this->_hash_crypt_private($password, $this->_hash_gensalt_private($random, $itoa64), $itoa64);
155 $result = (strlen($hash) == 34) ? $hash : md5($password);
157 return $result;
161 * Check for correct password
163 * If the hash length is != 34, then a md5($password) === $hash comparison is done. The correct hash length is 34.
165 * @param string $password The password in plain text
166 * @param string $hash The stored password hash
168 * @return bool Returns true if the password is correct, false if not.
169 * @access public
171 public function check_password($password, $hash)
173 $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
174 if (strlen($hash) == 34)
176 $result = ($this->_hash_crypt_private($password, $hash, $itoa64) === $hash) ? true : false;
178 else
180 $result = (md5($password) === $hash) ? true : false;
183 return $result;
187 * Generate salt for hash generation
188 * @access private
190 private function _hash_gensalt_private($input, &$itoa64, $iteration_count_log2 = 6)
192 if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
194 $iteration_count_log2 = 8;
197 $output = $this->hash_type;
198 $output .= $itoa64[min($iteration_count_log2 + 5, 30)];
199 $output .= $this->_hash_encode64($input, 6, $itoa64);
201 return $output;
205 * Encode hash
206 * @access private
208 private function _hash_encode64($input, $count, &$itoa64)
210 $output = '';
211 $i = 0;
215 $value = ord($input[$i++]);
216 $output .= $itoa64[$value & 0x3f];
218 if ($i < $count)
220 $value |= ord($input[$i]) << 8;
223 $output .= $itoa64[($value >> 6) & 0x3f];
225 if ($i++ >= $count)
227 break;
230 if ($i < $count)
232 $value |= ord($input[$i]) << 16;
235 $output .= $itoa64[($value >> 12) & 0x3f];
237 if ($i++ >= $count)
239 break;
242 $output .= $itoa64[($value >> 18) & 0x3f];
244 while ($i < $count);
246 return $output;
250 * The crypt function/replacement
251 * @access private
253 private function _hash_crypt_private($password, $setting, &$itoa64)
255 $output = '*';
257 // Check for correct hash
258 if (substr($setting, 0, 3) != $this->hash_type)
260 return $output;
263 $count_log2 = strpos($itoa64, $setting[3]);
265 if ($count_log2 < 7 || $count_log2 > 30)
267 return $output;
270 $count = 1 << $count_log2;
271 $salt = substr($setting, 4, 8);
273 if (strlen($salt) != 8)
275 return $output;
279 * We're kind of forced to use MD5 here since it's the only
280 * cryptographic primitive available in all versions of PHP
281 * currently in use. To implement our own low-level crypto
282 * in PHP would result in much worse performance and
283 * consequently in lower iteration counts and hashes that are
284 * quicker to crack (by non-PHP code).
286 $hash = md5($salt . $password, true);
289 $hash = md5($hash . $password, true);
291 while (--$count);
293 $output = substr($setting, 0, 12);
294 $output .= $this->_hash_encode64($hash, 16, $itoa64);
296 return $output;