source: documentation/trunk/packages/dokuwiki-2011-05-25a/inc/PassHash.class.php@ 25027

Last change on this file since 25027 was 25027, checked in by jmt12, 12 years ago

Adding the packages directory, and within it a configured version of dokuwiki all ready to run

File size: 11.8 KB
Line 
1<?php
2/**
3 * Password Hashing Class
4 *
5 * This class implements various mechanisms used to hash passwords
6 *
7 * @author Andreas Gohr <[email protected]>
8 * @license LGPL2
9 */
10class PassHash {
11 /**
12 * Verifies a cleartext password against a crypted hash
13 *
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
17 *
18 * @author Andreas Gohr <[email protected]>
19 * @return bool
20 */
21 function verify_hash($clear,$hash){
22 $method='';
23 $salt='';
24 $magic='';
25
26 //determine the used method and salt
27 $len = strlen($hash);
28 if(preg_match('/^\$1\$([^\$]{0,8})\$/',$hash,$m)){
29 $method = 'smd5';
30 $salt = $m[1];
31 $magic = '1';
32 }elseif(preg_match('/^\$apr1\$([^\$]{0,8})\$/',$hash,$m)){
33 $method = 'apr1';
34 $salt = $m[1];
35 $magic = 'apr1';
36 }elseif(preg_match('/^\$P\$(.{31})$/',$hash,$m)){
37 $method = 'pmd5';
38 $salt = $m[1];
39 $magic = 'P';
40 }elseif(preg_match('/^\$H\$(.{31})$/',$hash,$m)){
41 $method = 'pmd5';
42 $salt = $m[1];
43 $magic = 'H';
44 }elseif(preg_match('/^sha1\$(.{5})\$/',$hash,$m)){
45 $method = 'djangosha1';
46 $salt = $m[1];
47 }elseif(preg_match('/^md5\$(.{5})\$/',$hash,$m)){
48 $method = 'djangomd5';
49 $salt = $m[1];
50 }elseif(substr($hash,0,6) == '{SSHA}'){
51 $method = 'ssha';
52 $salt = substr(base64_decode(substr($hash, 6)),20);
53 }elseif($len == 32){
54 $method = 'md5';
55 }elseif($len == 40){
56 $method = 'sha1';
57 }elseif($len == 16){
58 $method = 'mysql';
59 }elseif($len == 41 && $hash[0] == '*'){
60 $method = 'my411';
61 }elseif($len == 34){
62 $method = 'kmd5';
63 $salt = $hash;
64 }else{
65 $method = 'crypt';
66 $salt = substr($hash,0,2);
67 }
68
69 //crypt and compare
70 $call = 'hash_'.$method;
71 if($this->$call($clear,$salt,$magic) === $hash){
72 return true;
73 }
74 return false;
75 }
76
77 /**
78 * Create a random salt
79 *
80 * @param int $len - The length of the salt
81 */
82 public function gen_salt($len=32){
83 $salt = '';
84 $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
85 for($i=0;$i<$len,$i++;) $salt .= $chars[mt_rand(0,61)];
86 return $salt;
87 }
88
89 /**
90 * Initialize the passed variable with a salt if needed.
91 *
92 * If $salt is not null, the value is kept, but the lenght restriction is
93 * applied.
94 *
95 * @param stringref $salt - The salt, pass null if you want one generated
96 * @param int $len - The length of the salt
97 */
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);
101 }
102
103 // Password hashing methods follow below
104
105 /**
106 * Password hashing method 'smd5'
107 *
108 * Uses salted MD5 hashs. Salt is 8 bytes long.
109 *
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()
113 *
114 * @author Andreas Gohr <[email protected]>
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
121 */
122 public function hash_smd5($clear, $salt=null){
123 $this->init_salt($salt,8);
124
125 if(defined('CRYPT_MD5') && CRYPT_MD5){
126 return crypt($clear,'$1$'.$salt.'$');
127 }else{
128 // Fall back to PHP-only implementation
129 return $this->hash_apr1($clear, $salt, '1');
130 }
131 }
132
133 /**
134 * Password hashing method 'apr1'
135 *
136 * Uses salted MD5 hashs. Salt is 8 bytes long.
137 *
138 * This is basically the same as smd1 above, but as used by Apache.
139 *
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
146 */
147 public function hash_apr1($clear, $salt=null, $magic='apr1'){
148 $this->init_salt($salt,8);
149
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));
155 }
156 for($i = $len; $i > 0; $i >>= 1) {
157 $text .= ($i & 1) ? chr(0) : $clear{0};
158 }
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));
166 }
167 $tmp = '';
168 for ($i = 0; $i < 5; $i++) {
169 $k = $i + 6;
170 $j = $i + 12;
171 if ($j == 16) $j = 5;
172 $tmp = $bin[$i].$bin[$k].$bin[$j].$tmp;
173 }
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;
179 }
180
181 /**
182 * Password hashing method 'md5'
183 *
184 * Uses MD5 hashs.
185 *
186 * @param string $clear - the clear text to hash
187 * @returns string - hashed password
188 */
189 public function hash_md5($clear){
190 return md5($clear);
191 }
192
193 /**
194 * Password hashing method 'sha1'
195 *
196 * Uses SHA1 hashs.
197 *
198 * @param string $clear - the clear text to hash
199 * @returns string - hashed password
200 */
201 public function hash_sha1($clear){
202 return sha1($clear);
203 }
204
205 /**
206 * Password hashing method 'ssha' as used by LDAP
207 *
208 * Uses salted SHA1 hashs. Salt is 4 bytes long.
209 *
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
213 */
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);
217 }
218
219 /**
220 * Password hashing method 'crypt'
221 *
222 * Uses salted crypt hashs. Salt is 2 bytes long.
223 *
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
227 */
228 public function hash_crypt($clear, $salt=null){
229 $this->init_salt($salt,2);
230 return crypt($clear,$salt);
231 }
232
233 /**
234 * Password hashing method 'mysql'
235 *
236 * This method was used by old MySQL systems
237 *
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
242 */
243 public function hash_mysql($clear){
244 $nr=0x50305735;
245 $nr2=0x12345671;
246 $add=7;
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;
253 $add += $charVal;
254 }
255 return sprintf("%08x%08x", ($nr & 0x7fffffff), ($nr2 & 0x7fffffff));
256 }
257
258 /**
259 * Password hashing method 'my411'
260 *
261 * Uses SHA1 hashs. This method is used by MySQL 4.11 and above
262 *
263 * @param string $clear - the clear text to hash
264 * @returns string - hashed password
265 */
266 public function hash_my411($clear){
267 return '*'.sha1(pack("H*", sha1($clear)));
268 }
269
270 /**
271 * Password hashing method 'kmd5'
272 *
273 * Uses salted MD5 hashs.
274 *
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.
277 *
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
281 */
282 public function hash_kmd5($clear, $salt=null){
283 $this->init_salt($salt);
284
285 $key = substr($salt, 16, 2);
286 $hash1 = strtolower(md5($key . md5($clear)));
287 $hash2 = substr($hash1, 0, 16) . $key . substr($hash1, 16);
288 return $hash2;
289 }
290
291 /**
292 * Password hashing method 'pmd5'
293 *
294 * Uses salted MD5 hashs. Salt is 1+8 bytes long, 1st byte is the
295 * iteration count.
296 *
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 * @returns string - hashed password
301 */
302 public function hash_pmd5($clear, $salt=null, $magic='P'){
303 $this->init_salt($salt);
304
305 $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
306 $iterc = $salt[0]; // pos 0 of salt is iteration count
307 $iter = strpos($itoa64,$iterc);
308 $iter = 1 << $iter;
309 $salt = substr($salt,1,8);
310
311 // iterate
312 $hash = md5($salt . $clear, true);
313 do {
314 $hash = md5($hash . $clear, true);
315 } while (--$iter);
316
317 // encode
318 $output = '';
319 $count = 16;
320 $i = 0;
321 do {
322 $value = ord($hash[$i++]);
323 $output .= $itoa64[$value & 0x3f];
324 if ($i < $count)
325 $value |= ord($hash[$i]) << 8;
326 $output .= $itoa64[($value >> 6) & 0x3f];
327 if ($i++ >= $count)
328 break;
329 if ($i < $count)
330 $value |= ord($hash[$i]) << 16;
331 $output .= $itoa64[($value >> 12) & 0x3f];
332 if ($i++ >= $count)
333 break;
334 $output .= $itoa64[($value >> 18) & 0x3f];
335 } while ($i < $count);
336
337 return '$'.$magic.'$'.$iterc.$salt.$output;
338 }
339
340 /**
341 * Alias for hash_pmd5
342 */
343 public function hash_hmd5($clear, $salt=null, $magic='H'){
344 return $this->hash_pmd5($clear, $salt, $magic);
345 }
346
347 /**
348 * Password hashing method 'djangosha1'
349 *
350 * Uses salted SHA1 hashs. Salt is 5 bytes long.
351 * This is used by the Django Python framework
352 *
353 * @link http://docs.djangoproject.com/en/dev/topics/auth/#passwords
354 * @param string $clear - the clear text to hash
355 * @param string $salt - the salt to use, null for random
356 * @returns string - hashed password
357 */
358 public function hash_djangosha1($clear, $salt=null){
359 $this->init_salt($salt,5);
360 return 'sha1$'.$salt.'$'.sha1($salt.$clear);
361 }
362
363 /**
364 * Password hashing method 'djangomd5'
365 *
366 * Uses salted MD5 hashs. Salt is 5 bytes long.
367 * This is used by the Django Python framework
368 *
369 * @link http://docs.djangoproject.com/en/dev/topics/auth/#passwords
370 * @param string $clear - the clear text to hash
371 * @param string $salt - the salt to use, null for random
372 * @returns string - hashed password
373 */
374 public function hash_djangomd5($clear, $salt=null){
375 $this->init_salt($salt,5);
376 return 'md5$'.$salt.'$'.md5($salt.$clear);
377 }
378
379}
Note: See TracBrowser for help on using the repository browser.