KXStudio Website https://kx.studio/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

237 lines
7.3KB

  1. <?php
  2. #
  3. # Portable PHP password hashing framework.
  4. #
  5. # Version 0.3 / Chyrp.
  6. #
  7. # Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
  8. # the public domain. Revised in subsequent years, still public domain.
  9. #
  10. # The homepage URL for this framework is: http://www.openwall.com/phpass/
  11. #
  12. class PasswordHash {
  13. var $itoa64;
  14. var $iteration_count_log2;
  15. var $portable_hashes;
  16. var $random_state;
  17. function PasswordHash($iteration_count_log2, $portable_hashes)
  18. {
  19. $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
  20. if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
  21. $iteration_count_log2 = 8;
  22. $this->iteration_count_log2 = $iteration_count_log2;
  23. $this->portable_hashes = $portable_hashes;
  24. $this->random_state = microtime() . uniqid(rand(), TRUE); // removed getmypid() for compability reasons;
  25. }
  26. function get_random_bytes($count)
  27. {
  28. $output = '';
  29. if (@is_readable('/dev/urandom') &&
  30. ($fh = @fopen('/dev/urandom', 'rb'))) {
  31. $output = fread($fh, $count);
  32. fclose($fh);
  33. }
  34. if (strlen($output) < $count) {
  35. $output = '';
  36. for ($i = 0; $i < $count; $i += 16) {
  37. $this->random_state =
  38. md5(microtime() . $this->random_state);
  39. $output .=
  40. pack('H*', md5($this->random_state));
  41. }
  42. $output = substr($output, 0, $count);
  43. }
  44. return $output;
  45. }
  46. function encode64($input, $count)
  47. {
  48. $output = '';
  49. $i = 0;
  50. do {
  51. $value = ord($input[$i++]);
  52. $output .= $this->itoa64[$value & 0x3f];
  53. if ($i < $count)
  54. $value |= ord($input[$i]) << 8;
  55. $output .= $this->itoa64[($value >> 6) & 0x3f];
  56. if ($i++ >= $count)
  57. break;
  58. if ($i < $count)
  59. $value |= ord($input[$i]) << 16;
  60. $output .= $this->itoa64[($value >> 12) & 0x3f];
  61. if ($i++ >= $count)
  62. break;
  63. $output .= $this->itoa64[($value >> 18) & 0x3f];
  64. } while ($i < $count);
  65. return $output;
  66. }
  67. function gensalt_private($input)
  68. {
  69. $output = '$P$';
  70. $output .= $this->itoa64[min($this->iteration_count_log2 +
  71. ((PHP_VERSION >= '5') ? 5 : 3), 30)];
  72. $output .= $this->encode64($input, 6);
  73. return $output;
  74. }
  75. function crypt_private($password, $setting)
  76. {
  77. $output = '*0';
  78. if (substr($setting, 0, 2) == $output)
  79. $output = '*1';
  80. $id = substr($setting, 0, 3);
  81. # We use "$P$", phpBB3 uses "$H$" for the same thing
  82. if ($id != '$P$' && $id != '$H$')
  83. return $output;
  84. $count_log2 = strpos($this->itoa64, $setting[3]);
  85. if ($count_log2 < 7 || $count_log2 > 30)
  86. return $output;
  87. $count = 1 << $count_log2;
  88. $salt = substr($setting, 4, 8);
  89. if (strlen($salt) != 8)
  90. return $output;
  91. # We're kind of forced to use MD5 here since it's the only
  92. # cryptographic primitive available in all versions of PHP
  93. # currently in use. To implement our own low-level crypto
  94. # in PHP would result in much worse performance and
  95. # consequently in lower iteration counts and hashes that are
  96. # quicker to crack (by non-PHP code).
  97. if (PHP_VERSION >= '5') {
  98. $hash = md5($salt . $password, TRUE);
  99. do {
  100. $hash = md5($hash . $password, TRUE);
  101. } while (--$count);
  102. } else {
  103. $hash = pack('H*', md5($salt . $password));
  104. do {
  105. $hash = pack('H*', md5($hash . $password));
  106. } while (--$count);
  107. }
  108. $output = substr($setting, 0, 12);
  109. $output .= $this->encode64($hash, 16);
  110. return $output;
  111. }
  112. function gensalt_extended($input)
  113. {
  114. $count_log2 = min($this->iteration_count_log2 + 8, 24);
  115. # This should be odd to not reveal weak DES keys, and the
  116. # maximum valid value is (2**24 - 1) which is odd anyway.
  117. $count = (1 << $count_log2) - 1;
  118. $output = '_';
  119. $output .= $this->itoa64[$count & 0x3f];
  120. $output .= $this->itoa64[($count >> 6) & 0x3f];
  121. $output .= $this->itoa64[($count >> 12) & 0x3f];
  122. $output .= $this->itoa64[($count >> 18) & 0x3f];
  123. $output .= $this->encode64($input, 3);
  124. return $output;
  125. }
  126. function gensalt_blowfish($input)
  127. {
  128. # This one needs to use a different order of characters and a
  129. # different encoding scheme from the one in encode64() above.
  130. # We care because the last character in our encoded string will
  131. # only represent 2 bits. While two known implementations of
  132. # bcrypt will happily accept and correct a salt string which
  133. # has the 4 unused bits set to non-zero, we do not want to take
  134. # chances and we also do not want to waste an additional byte
  135. # of entropy.
  136. $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  137. $output = '$2a$';
  138. $output .= chr(ord('0') + $this->iteration_count_log2 / 10);
  139. $output .= chr(ord('0') + $this->iteration_count_log2 % 10);
  140. $output .= '$';
  141. $i = 0;
  142. do {
  143. $c1 = ord($input[$i++]);
  144. $output .= $itoa64[$c1 >> 2];
  145. $c1 = ($c1 & 0x03) << 4;
  146. if ($i >= 16) {
  147. $output .= $itoa64[$c1];
  148. break;
  149. }
  150. $c2 = ord($input[$i++]);
  151. $c1 |= $c2 >> 4;
  152. $output .= $itoa64[$c1];
  153. $c1 = ($c2 & 0x0f) << 2;
  154. $c2 = ord($input[$i++]);
  155. $c1 |= $c2 >> 6;
  156. $output .= $itoa64[$c1];
  157. $output .= $itoa64[$c2 & 0x3f];
  158. } while (1);
  159. return $output;
  160. }
  161. function HashPassword($password)
  162. {
  163. $random = '';
  164. if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) {
  165. $random = $this->get_random_bytes(16);
  166. $hash =
  167. crypt($password, $this->gensalt_blowfish($random));
  168. if (strlen($hash) == 60)
  169. return $hash;
  170. }
  171. if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) {
  172. if (strlen($random) < 3)
  173. $random = $this->get_random_bytes(3);
  174. $hash =
  175. crypt($password, $this->gensalt_extended($random));
  176. if (strlen($hash) == 20)
  177. return $hash;
  178. }
  179. if (strlen($random) < 6)
  180. $random = $this->get_random_bytes(6);
  181. $hash =
  182. $this->crypt_private($password,
  183. $this->gensalt_private($random));
  184. if (strlen($hash) == 34)
  185. return $hash;
  186. # Returning '*' on error is safe here, but would _not_ be safe
  187. # in a crypt(3)-like function used _both_ for generating new
  188. # hashes and for validating passwords against existing hashes.
  189. return '*';
  190. }
  191. function CheckPassword($password, $stored_hash)
  192. {
  193. $hash = $this->crypt_private($password, $stored_hash);
  194. if ($hash[0] == '*')
  195. $hash = crypt($password, $stored_hash);
  196. return $hash == $stored_hash;
  197. }
  198. }
  199. ?>