PageRenderTime 55ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/dokuwiki-2011-05-25a/inc/PassHash.class.php

#
PHP | 379 lines | 184 code | 26 blank | 169 comment | 35 complexity | b0bce8118337e47809c24cfb9cae62d9 MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. /**
  3. * Password Hashing Class
  4. *
  5. * This class implements various mechanisms used to hash passwords
  6. *
  7. * @author Andreas Gohr <andi@splitbrain.org>
  8. * @license LGPL2
  9. */
  10. class 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 <andi@splitbrain.org>
  19. * @return bool
  20. */
  21. function verify_hash($clear,$hash){
  22. $method='';
  23. $salt='';
  24. $magic='';
  25. //determine the used method and salt
  26. $len = strlen($hash);
  27. if(preg_match('/^\$1\$([^\$]{0,8})\$/',$hash,$m)){
  28. $method = 'smd5';
  29. $salt = $m[1];
  30. $magic = '1';
  31. }elseif(preg_match('/^\$apr1\$([^\$]{0,8})\$/',$hash,$m)){
  32. $method = 'apr1';
  33. $salt = $m[1];
  34. $magic = 'apr1';
  35. }elseif(preg_match('/^\$P\$(.{31})$/',$hash,$m)){
  36. $method = 'pmd5';
  37. $salt = $m[1];
  38. $magic = 'P';
  39. }elseif(preg_match('/^\$H\$(.{31})$/',$hash,$m)){
  40. $method = 'pmd5';
  41. $salt = $m[1];
  42. $magic = 'H';
  43. }elseif(preg_match('/^sha1\$(.{5})\$/',$hash,$m)){
  44. $method = 'djangosha1';
  45. $salt = $m[1];
  46. }elseif(preg_match('/^md5\$(.{5})\$/',$hash,$m)){
  47. $method = 'djangomd5';
  48. $salt = $m[1];
  49. }elseif(substr($hash,0,6) == '{SSHA}'){
  50. $method = 'ssha';
  51. $salt = substr(base64_decode(substr($hash, 6)),20);
  52. }elseif($len == 32){
  53. $method = 'md5';
  54. }elseif($len == 40){
  55. $method = 'sha1';
  56. }elseif($len == 16){
  57. $method = 'mysql';
  58. }elseif($len == 41 && $hash[0] == '*'){
  59. $method = 'my411';
  60. }elseif($len == 34){
  61. $method = 'kmd5';
  62. $salt = $hash;
  63. }else{
  64. $method = 'crypt';
  65. $salt = substr($hash,0,2);
  66. }
  67. //crypt and compare
  68. $call = 'hash_'.$method;
  69. if($this->$call($clear,$salt,$magic) === $hash){
  70. return true;
  71. }
  72. return false;
  73. }
  74. /**
  75. * Create a random salt
  76. *
  77. * @param int $len - The length of the salt
  78. */
  79. public function gen_salt($len=32){
  80. $salt = '';
  81. $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  82. for($i=0;$i<$len,$i++;) $salt .= $chars[mt_rand(0,61)];
  83. return $salt;
  84. }
  85. /**
  86. * Initialize the passed variable with a salt if needed.
  87. *
  88. * If $salt is not null, the value is kept, but the lenght restriction is
  89. * applied.
  90. *
  91. * @param stringref $salt - The salt, pass null if you want one generated
  92. * @param int $len - The length of the salt
  93. */
  94. public function init_salt(&$salt,$len=32){
  95. if(is_null($salt)) $salt = $this->gen_salt($len);
  96. if(strlen($salt) > $len) $salt = substr($salt,0,$len);
  97. }
  98. // Password hashing methods follow below
  99. /**
  100. * Password hashing method 'smd5'
  101. *
  102. * Uses salted MD5 hashs. Salt is 8 bytes long.
  103. *
  104. * The same mechanism is used by Apache's 'apr1' method. This will
  105. * fallback to a implementation in pure PHP if MD5 support is not
  106. * available in crypt()
  107. *
  108. * @author Andreas Gohr <andi@splitbrain.org>
  109. * @author <mikey_nich at hotmail dot com>
  110. * @link http://de.php.net/manual/en/function.crypt.php#73619
  111. * @param string $clear - the clear text to hash
  112. * @param string $salt - the salt to use, null for random
  113. * @param string $magic - the hash identifier (apr1 or 1)
  114. * @returns string - hashed password
  115. */
  116. public function hash_smd5($clear, $salt=null){
  117. $this->init_salt($salt,8);
  118. if(defined('CRYPT_MD5') && CRYPT_MD5){
  119. return crypt($clear,'$1$'.$salt.'$');
  120. }else{
  121. // Fall back to PHP-only implementation
  122. return $this->hash_apr1($clear, $salt, '1');
  123. }
  124. }
  125. /**
  126. * Password hashing method 'apr1'
  127. *
  128. * Uses salted MD5 hashs. Salt is 8 bytes long.
  129. *
  130. * This is basically the same as smd1 above, but as used by Apache.
  131. *
  132. * @author <mikey_nich at hotmail dot com>
  133. * @link http://de.php.net/manual/en/function.crypt.php#73619
  134. * @param string $clear - the clear text to hash
  135. * @param string $salt - the salt to use, null for random
  136. * @param string $magic - the hash identifier (apr1 or 1)
  137. * @returns string - hashed password
  138. */
  139. public function hash_apr1($clear, $salt=null, $magic='apr1'){
  140. $this->init_salt($salt,8);
  141. $len = strlen($clear);
  142. $text = $clear.'$'.$magic.'$'.$salt;
  143. $bin = pack("H32", md5($clear.$salt.$clear));
  144. for($i = $len; $i > 0; $i -= 16) {
  145. $text .= substr($bin, 0, min(16, $i));
  146. }
  147. for($i = $len; $i > 0; $i >>= 1) {
  148. $text .= ($i & 1) ? chr(0) : $clear{0};
  149. }
  150. $bin = pack("H32", md5($text));
  151. for($i = 0; $i < 1000; $i++) {
  152. $new = ($i & 1) ? $clear : $bin;
  153. if ($i % 3) $new .= $salt;
  154. if ($i % 7) $new .= $clear;
  155. $new .= ($i & 1) ? $bin : $clear;
  156. $bin = pack("H32", md5($new));
  157. }
  158. $tmp = '';
  159. for ($i = 0; $i < 5; $i++) {
  160. $k = $i + 6;
  161. $j = $i + 12;
  162. if ($j == 16) $j = 5;
  163. $tmp = $bin[$i].$bin[$k].$bin[$j].$tmp;
  164. }
  165. $tmp = chr(0).chr(0).$bin[11].$tmp;
  166. $tmp = strtr(strrev(substr(base64_encode($tmp), 2)),
  167. "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
  168. "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
  169. return '$'.$magic.'$'.$salt.'$'.$tmp;
  170. }
  171. /**
  172. * Password hashing method 'md5'
  173. *
  174. * Uses MD5 hashs.
  175. *
  176. * @param string $clear - the clear text to hash
  177. * @returns string - hashed password
  178. */
  179. public function hash_md5($clear){
  180. return md5($clear);
  181. }
  182. /**
  183. * Password hashing method 'sha1'
  184. *
  185. * Uses SHA1 hashs.
  186. *
  187. * @param string $clear - the clear text to hash
  188. * @returns string - hashed password
  189. */
  190. public function hash_sha1($clear){
  191. return sha1($clear);
  192. }
  193. /**
  194. * Password hashing method 'ssha' as used by LDAP
  195. *
  196. * Uses salted SHA1 hashs. Salt is 4 bytes long.
  197. *
  198. * @param string $clear - the clear text to hash
  199. * @param string $salt - the salt to use, null for random
  200. * @returns string - hashed password
  201. */
  202. public function hash_ssha($clear, $salt=null){
  203. $this->init_salt($salt,4);
  204. return '{SSHA}'.base64_encode(pack("H*", sha1($clear.$salt)).$salt);
  205. }
  206. /**
  207. * Password hashing method 'crypt'
  208. *
  209. * Uses salted crypt hashs. Salt is 2 bytes long.
  210. *
  211. * @param string $clear - the clear text to hash
  212. * @param string $salt - the salt to use, null for random
  213. * @returns string - hashed password
  214. */
  215. public function hash_crypt($clear, $salt=null){
  216. $this->init_salt($salt,2);
  217. return crypt($clear,$salt);
  218. }
  219. /**
  220. * Password hashing method 'mysql'
  221. *
  222. * This method was used by old MySQL systems
  223. *
  224. * @link http://www.php.net/mysql
  225. * @author <soren at byu dot edu>
  226. * @param string $clear - the clear text to hash
  227. * @returns string - hashed password
  228. */
  229. public function hash_mysql($clear){
  230. $nr=0x50305735;
  231. $nr2=0x12345671;
  232. $add=7;
  233. $charArr = preg_split("//", $clear);
  234. foreach ($charArr as $char) {
  235. if (($char == '') || ($char == ' ') || ($char == '\t')) continue;
  236. $charVal = ord($char);
  237. $nr ^= ((($nr & 63) + $add) * $charVal) + ($nr << 8);
  238. $nr2 += ($nr2 << 8) ^ $nr;
  239. $add += $charVal;
  240. }
  241. return sprintf("%08x%08x", ($nr & 0x7fffffff), ($nr2 & 0x7fffffff));
  242. }
  243. /**
  244. * Password hashing method 'my411'
  245. *
  246. * Uses SHA1 hashs. This method is used by MySQL 4.11 and above
  247. *
  248. * @param string $clear - the clear text to hash
  249. * @returns string - hashed password
  250. */
  251. public function hash_my411($clear){
  252. return '*'.sha1(pack("H*", sha1($clear)));
  253. }
  254. /**
  255. * Password hashing method 'kmd5'
  256. *
  257. * Uses salted MD5 hashs.
  258. *
  259. * Salt is 2 bytes long, but stored at position 16, so you need to pass at
  260. * least 18 bytes. You can pass the crypted hash as salt.
  261. *
  262. * @param string $clear - the clear text to hash
  263. * @param string $salt - the salt to use, null for random
  264. * @returns string - hashed password
  265. */
  266. public function hash_kmd5($clear, $salt=null){
  267. $this->init_salt($salt);
  268. $key = substr($salt, 16, 2);
  269. $hash1 = strtolower(md5($key . md5($clear)));
  270. $hash2 = substr($hash1, 0, 16) . $key . substr($hash1, 16);
  271. return $hash2;
  272. }
  273. /**
  274. * Password hashing method 'pmd5'
  275. *
  276. * Uses salted MD5 hashs. Salt is 1+8 bytes long, 1st byte is the
  277. * iteration count.
  278. *
  279. * @param string $clear - the clear text to hash
  280. * @param string $salt - the salt to use, null for random
  281. * @param string $magic - the hash identifier (P or H)
  282. * @returns string - hashed password
  283. */
  284. public function hash_pmd5($clear, $salt=null, $magic='P'){
  285. $this->init_salt($salt);
  286. $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
  287. $iterc = $salt[0]; // pos 0 of salt is iteration count
  288. $iter = strpos($itoa64,$iterc);
  289. $iter = 1 << $iter;
  290. $salt = substr($salt,1,8);
  291. // iterate
  292. $hash = md5($salt . $clear, true);
  293. do {
  294. $hash = md5($hash . $clear, true);
  295. } while (--$iter);
  296. // encode
  297. $output = '';
  298. $count = 16;
  299. $i = 0;
  300. do {
  301. $value = ord($hash[$i++]);
  302. $output .= $itoa64[$value & 0x3f];
  303. if ($i < $count)
  304. $value |= ord($hash[$i]) << 8;
  305. $output .= $itoa64[($value >> 6) & 0x3f];
  306. if ($i++ >= $count)
  307. break;
  308. if ($i < $count)
  309. $value |= ord($hash[$i]) << 16;
  310. $output .= $itoa64[($value >> 12) & 0x3f];
  311. if ($i++ >= $count)
  312. break;
  313. $output .= $itoa64[($value >> 18) & 0x3f];
  314. } while ($i < $count);
  315. return '$'.$magic.'$'.$iterc.$salt.$output;
  316. }
  317. /**
  318. * Alias for hash_pmd5
  319. */
  320. public function hash_hmd5($clear, $salt=null, $magic='H'){
  321. return $this->hash_pmd5($clear, $salt, $magic);
  322. }
  323. /**
  324. * Password hashing method 'djangosha1'
  325. *
  326. * Uses salted SHA1 hashs. Salt is 5 bytes long.
  327. * This is used by the Django Python framework
  328. *
  329. * @link http://docs.djangoproject.com/en/dev/topics/auth/#passwords
  330. * @param string $clear - the clear text to hash
  331. * @param string $salt - the salt to use, null for random
  332. * @returns string - hashed password
  333. */
  334. public function hash_djangosha1($clear, $salt=null){
  335. $this->init_salt($salt,5);
  336. return 'sha1$'.$salt.'$'.sha1($salt.$clear);
  337. }
  338. /**
  339. * Password hashing method 'djangomd5'
  340. *
  341. * Uses salted MD5 hashs. Salt is 5 bytes long.
  342. * This is used by the Django Python framework
  343. *
  344. * @link http://docs.djangoproject.com/en/dev/topics/auth/#passwords
  345. * @param string $clear - the clear text to hash
  346. * @param string $salt - the salt to use, null for random
  347. * @returns string - hashed password
  348. */
  349. public function hash_djangomd5($clear, $salt=null){
  350. $this->init_salt($salt,5);
  351. return 'md5$'.$salt.'$'.md5($salt.$clear);
  352. }
  353. }