3 * Password Hashing Class
5 * This class implements various mechanisms used to hash passwords
7 * @author Andreas Gohr <andi@splitbrain.org>
12 * Verifies a cleartext password against a crypted hash
14 * The method and salt used for the crypted hash is determined automatically,
15 * then the clear text password is crypted using the same method. If both hashs
16 * match true is is returned else false
18 * @author Andreas Gohr <andi@splitbrain.org>
21 function verify_hash($clear,$hash){
26 //determine the used method and salt
28 if(preg_match('/^\$1\$([^\$]{0,8})\$/',$hash,$m)){
32 }elseif(preg_match('/^\$apr1\$([^\$]{0,8})\$/',$hash,$m)){
36 }elseif(preg_match('/^\$P\$(.{31})$/',$hash,$m)){
40 }elseif(preg_match('/^\$H\$(.{31})$/',$hash,$m)){
44 }elseif(preg_match('/^sha1\$(.{5})\$/',$hash,$m)){
45 $method = 'djangosha1';
47 }elseif(preg_match('/^md5\$(.{5})\$/',$hash,$m)){
48 $method = 'djangomd5';
50 }elseif(substr($hash,0,6) == '{SSHA}'){
52 $salt = substr(base64_decode(substr($hash, 6)),20);
59 }elseif($len == 41 && $hash[0] == '*'){
66 $salt = substr($hash,0,2);
70 $call = 'hash_'.$method;
71 if($this->$call($clear,$salt,$magic) === $hash){
78 * Create a random salt
80 * @param int $len - The length of the salt
82 public function gen_salt($len=32){
84 $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
85 for($i=0;$i<$len;$i++) $salt .= $chars[mt_rand(0,61)];
90 * Initialize the passed variable with a salt if needed.
92 * If $salt is not null, the value is kept, but the lenght restriction is
95 * @param stringref $salt - The salt, pass null if you want one generated
96 * @param int $len - The length of the salt
98 public function init_salt(&$salt,$len=32){
99 if(is_null($salt)) $salt = $this->gen_salt($len);
100 if(strlen($salt) > $len) $salt = substr($salt,0,$len);
103 // Password hashing methods follow below
106 * Password hashing method 'smd5'
108 * Uses salted MD5 hashs. Salt is 8 bytes long.
110 * The same mechanism is used by Apache's 'apr1' method. This will
111 * fallback to a implementation in pure PHP if MD5 support is not
112 * available in crypt()
114 * @author Andreas Gohr <andi@splitbrain.org>
115 * @author <mikey_nich at hotmail dot com>
116 * @link http://de.php.net/manual/en/function.crypt.php#73619
117 * @param string $clear - the clear text to hash
118 * @param string $salt - the salt to use, null for random
119 * @param string $magic - the hash identifier (apr1 or 1)
120 * @returns string - hashed password
122 public function hash_smd5($clear, $salt=null){
123 $this->init_salt($salt,8);
125 if(defined('CRYPT_MD5') && CRYPT_MD5){
126 return crypt($clear,'$1$'.$salt.'$');
128 // Fall back to PHP-only implementation
129 return $this->hash_apr1($clear, $salt, '1');
134 * Password hashing method 'apr1'
136 * Uses salted MD5 hashs. Salt is 8 bytes long.
138 * This is basically the same as smd1 above, but as used by Apache.
140 * @author <mikey_nich at hotmail dot com>
141 * @link http://de.php.net/manual/en/function.crypt.php#73619
142 * @param string $clear - the clear text to hash
143 * @param string $salt - the salt to use, null for random
144 * @param string $magic - the hash identifier (apr1 or 1)
145 * @returns string - hashed password
147 public function hash_apr1($clear, $salt=null, $magic='apr1'){
148 $this->init_salt($salt,8);
150 $len = strlen($clear);
151 $text = $clear.'$'.$magic.'$'.$salt;
152 $bin = pack("H32", md5($clear.$salt.$clear));
153 for($i = $len; $i > 0; $i -= 16) {
154 $text .= substr($bin, 0, min(16, $i));
156 for($i = $len; $i > 0; $i >>= 1) {
157 $text .= ($i & 1) ? chr(0) : $clear{0};
159 $bin = pack("H32", md5($text));
160 for($i = 0; $i < 1000; $i++) {
161 $new = ($i & 1) ? $clear : $bin;
162 if ($i % 3) $new .= $salt;
163 if ($i % 7) $new .= $clear;
164 $new .= ($i & 1) ? $bin : $clear;
165 $bin = pack("H32", md5($new));
168 for ($i = 0; $i < 5; $i++) {
171 if ($j == 16) $j = 5;
172 $tmp = $bin[$i].$bin[$k].$bin[$j].$tmp;
174 $tmp = chr(0).chr(0).$bin[11].$tmp;
175 $tmp = strtr(strrev(substr(base64_encode($tmp), 2)),
176 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
177 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
178 return '$'.$magic.'$'.$salt.'$'.$tmp;
182 * Password hashing method 'md5'
186 * @param string $clear - the clear text to hash
187 * @returns string - hashed password
189 public function hash_md5($clear){
194 * Password hashing method 'sha1'
198 * @param string $clear - the clear text to hash
199 * @returns string - hashed password
201 public function hash_sha1($clear){
206 * Password hashing method 'ssha' as used by LDAP
208 * Uses salted SHA1 hashs. Salt is 4 bytes long.
210 * @param string $clear - the clear text to hash
211 * @param string $salt - the salt to use, null for random
212 * @returns string - hashed password
214 public function hash_ssha($clear, $salt=null){
215 $this->init_salt($salt,4);
216 return '{SSHA}'.base64_encode(pack("H*", sha1($clear.$salt)).$salt);
220 * Password hashing method 'crypt'
222 * Uses salted crypt hashs. Salt is 2 bytes long.
224 * @param string $clear - the clear text to hash
225 * @param string $salt - the salt to use, null for random
226 * @returns string - hashed password
228 public function hash_crypt($clear, $salt=null){
229 $this->init_salt($salt,2);
230 return crypt($clear,$salt);
234 * Password hashing method 'mysql'
236 * This method was used by old MySQL systems
238 * @link http://www.php.net/mysql
239 * @author <soren at byu dot edu>
240 * @param string $clear - the clear text to hash
241 * @returns string - hashed password
243 public function hash_mysql($clear){
247 $charArr = preg_split("//", $clear);
248 foreach ($charArr as $char) {
249 if (($char == '') || ($char == ' ') || ($char == '\t')) continue;
250 $charVal = ord($char);
251 $nr ^= ((($nr & 63) + $add) * $charVal) + ($nr << 8);
252 $nr2 += ($nr2 << 8) ^ $nr;
255 return sprintf("%08x%08x", ($nr & 0x7fffffff), ($nr2 & 0x7fffffff));
259 * Password hashing method 'my411'
261 * Uses SHA1 hashs. This method is used by MySQL 4.11 and above
263 * @param string $clear - the clear text to hash
264 * @returns string - hashed password
266 public function hash_my411($clear){
267 return '*'.sha1(pack("H*", sha1($clear)));
271 * Password hashing method 'kmd5'
273 * Uses salted MD5 hashs.
275 * Salt is 2 bytes long, but stored at position 16, so you need to pass at
276 * least 18 bytes. You can pass the crypted hash as salt.
278 * @param string $clear - the clear text to hash
279 * @param string $salt - the salt to use, null for random
280 * @returns string - hashed password
282 public function hash_kmd5($clear, $salt=null){
283 $this->init_salt($salt);
285 $key = substr($salt, 16, 2);
286 $hash1 = strtolower(md5($key . md5($clear)));
287 $hash2 = substr($hash1, 0, 16) . $key . substr($hash1, 16);
292 * Password hashing method 'pmd5'
294 * Uses salted MD5 hashs. Salt is 1+8 bytes long, 1st byte is the
295 * iteration count when given, for null salts $compute is used.
297 * @param string $clear - the clear text to hash
298 * @param string $salt - the salt to use, null for random
299 * @param string $magic - the hash identifier (P or H)
300 * @param int $compute - the iteration count for new passwords
301 * @returns string - hashed password
303 public function hash_pmd5($clear, $salt=null, $magic='P',$compute=8){
304 $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
306 $this->init_salt($salt);
307 $salt = $itoa64[$compute].$salt; // prefix iteration count
309 $iterc = $salt[0]; // pos 0 of salt is iteration count
310 $iter = strpos($itoa64,$iterc);
312 $salt = substr($salt,1,8);
315 $hash = md5($salt . $clear, true);
317 $hash = md5($hash . $clear, true);
325 $value = ord($hash[$i++]);
326 $output .= $itoa64[$value & 0x3f];
328 $value |= ord($hash[$i]) << 8;
329 $output .= $itoa64[($value >> 6) & 0x3f];
333 $value |= ord($hash[$i]) << 16;
334 $output .= $itoa64[($value >> 12) & 0x3f];
337 $output .= $itoa64[($value >> 18) & 0x3f];
338 } while ($i < $count);
340 return '$'.$magic.'$'.$iterc.$salt.$output;
344 * Alias for hash_pmd5
346 public function hash_hmd5($clear, $salt=null, $magic='H', $compute=8){
347 return $this->hash_pmd5($clear, $salt, $magic, $compute);
351 * Password hashing method 'djangosha1'
353 * Uses salted SHA1 hashs. Salt is 5 bytes long.
354 * This is used by the Django Python framework
356 * @link http://docs.djangoproject.com/en/dev/topics/auth/#passwords
357 * @param string $clear - the clear text to hash
358 * @param string $salt - the salt to use, null for random
359 * @returns string - hashed password
361 public function hash_djangosha1($clear, $salt=null){
362 $this->init_salt($salt,5);
363 return 'sha1$'.$salt.'$'.sha1($salt.$clear);
367 * Password hashing method 'djangomd5'
369 * Uses salted MD5 hashs. Salt is 5 bytes long.
370 * This is used by the Django Python framework
372 * @link http://docs.djangoproject.com/en/dev/topics/auth/#passwords
373 * @param string $clear - the clear text to hash
374 * @param string $salt - the salt to use, null for random
375 * @returns string - hashed password
377 public function hash_djangomd5($clear, $salt=null){
378 $this->init_salt($salt,5);
379 return 'md5$'.$salt.'$'.md5($salt.$clear);