PageRenderTime 47ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/administrator/components/com_virtuemart/helpers/password_compat.php

https://bitbucket.org/isaacoheneayisi/ecommerce
PHP | 280 lines | 181 code | 19 blank | 80 comment | 52 complexity | 2162a14d24a8ca4b90bb2a61a580bb00 MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php
  2. /**
  3. * A Compatibility library with PHP 5.5's simplified password hashing API.
  4. *
  5. * @author Anthony Ferrara <ircmaxell@php.net>
  6. * @license http://www.opensource.org/licenses/mit-license.html MIT License
  7. * @copyright 2012 The Authors
  8. */
  9. namespace {
  10. if (!defined('PASSWORD_DEFAULT')) {
  11. define('PASSWORD_BCRYPT', 1);
  12. define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);
  13. /**
  14. * Hash the password using the specified algorithm
  15. *
  16. * @param string $password The password to hash
  17. * @param int $algo The algorithm to use (Defined by PASSWORD_* constants)
  18. * @param array $options The options for the algorithm to use
  19. *
  20. * @return string|false The hashed password, or false on error.
  21. */
  22. function password_hash($password, $algo, array $options = array()) {
  23. if (!function_exists('crypt')) {
  24. trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING);
  25. return null;
  26. }
  27. if (!is_string($password)) {
  28. trigger_error("password_hash(): Password must be a string", E_USER_WARNING);
  29. return null;
  30. }
  31. if (!is_int($algo)) {
  32. trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING);
  33. return null;
  34. }
  35. $resultLength = 0;
  36. switch ($algo) {
  37. case PASSWORD_BCRYPT:
  38. // Note that this is a C constant, but not exposed to PHP, so we don't define it here.
  39. $cost = 10;
  40. if (isset($options['cost'])) {
  41. $cost = $options['cost'];
  42. if ($cost < 4 || $cost > 31) {
  43. trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING);
  44. return null;
  45. }
  46. }
  47. // The length of salt to generate
  48. $raw_salt_len = 16;
  49. // The length required in the final serialization
  50. $required_salt_len = 22;
  51. $hash_format = sprintf("$2y$%02d$", $cost);
  52. // The expected length of the final crypt() output
  53. $resultLength = 60;
  54. break;
  55. default:
  56. trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING);
  57. return null;
  58. }
  59. $salt_requires_encoding = false;
  60. if (isset($options['salt'])) {
  61. switch (gettype($options['salt'])) {
  62. case 'NULL':
  63. case 'boolean':
  64. case 'integer':
  65. case 'double':
  66. case 'string':
  67. $salt = (string) $options['salt'];
  68. break;
  69. case 'object':
  70. if (method_exists($options['salt'], '__tostring')) {
  71. $salt = (string) $options['salt'];
  72. break;
  73. }
  74. case 'array':
  75. case 'resource':
  76. default:
  77. trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING);
  78. return null;
  79. }
  80. if (PasswordCompat\binary\_strlen($salt) < $required_salt_len) {
  81. trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", PasswordCompat\binary\_strlen($salt), $required_salt_len), E_USER_WARNING);
  82. return null;
  83. } elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) {
  84. $salt_requires_encoding = true;
  85. }
  86. } else {
  87. $buffer = '';
  88. $buffer_valid = false;
  89. if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {
  90. $buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM);
  91. if ($buffer) {
  92. $buffer_valid = true;
  93. }
  94. }
  95. if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
  96. $buffer = openssl_random_pseudo_bytes($raw_salt_len);
  97. if ($buffer) {
  98. $buffer_valid = true;
  99. }
  100. }
  101. if (!$buffer_valid && @is_readable('/dev/urandom')) {
  102. $f = fopen('/dev/urandom', 'r');
  103. $read = PasswordCompat\binary\_strlen($buffer);
  104. while ($read < $raw_salt_len) {
  105. $buffer .= fread($f, $raw_salt_len - $read);
  106. $read = PasswordCompat\binary\_strlen($buffer);
  107. }
  108. fclose($f);
  109. if ($read >= $raw_salt_len) {
  110. $buffer_valid = true;
  111. }
  112. }
  113. if (!$buffer_valid || PasswordCompat\binary\_strlen($buffer) < $raw_salt_len) {
  114. $bl = PasswordCompat\binary\_strlen($buffer);
  115. for ($i = 0; $i < $raw_salt_len; $i++) {
  116. if ($i < $bl) {
  117. $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
  118. } else {
  119. $buffer .= chr(mt_rand(0, 255));
  120. }
  121. }
  122. }
  123. $salt = $buffer;
  124. $salt_requires_encoding = true;
  125. }
  126. if ($salt_requires_encoding) {
  127. // encode string with the Base64 variant used by crypt
  128. $base64_digits =
  129. 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  130. $bcrypt64_digits =
  131. './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  132. $base64_string = base64_encode($salt);
  133. $salt = strtr(rtrim($base64_string, '='), $base64_digits, $bcrypt64_digits);
  134. }
  135. $salt = PasswordCompat\binary\_substr($salt, 0, $required_salt_len);
  136. $hash = $hash_format . $salt;
  137. $ret = crypt($password, $hash);
  138. if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != $resultLength) {
  139. return false;
  140. }
  141. return $ret;
  142. }
  143. /**
  144. * Get information about the password hash. Returns an array of the information
  145. * that was used to generate the password hash.
  146. *
  147. * array(
  148. * 'algo' => 1,
  149. * 'algoName' => 'bcrypt',
  150. * 'options' => array(
  151. * 'cost' => 10,
  152. * ),
  153. * )
  154. *
  155. * @param string $hash The password hash to extract info from
  156. *
  157. * @return array The array of information about the hash.
  158. */
  159. function password_get_info($hash) {
  160. $return = array(
  161. 'algo' => 0,
  162. 'algoName' => 'unknown',
  163. 'options' => array(),
  164. );
  165. if (PasswordCompat\binary\_substr($hash, 0, 4) == '$2y$' && PasswordCompat\binary\_strlen($hash) == 60) {
  166. $return['algo'] = PASSWORD_BCRYPT;
  167. $return['algoName'] = 'bcrypt';
  168. list($cost) = sscanf($hash, "$2y$%d$");
  169. $return['options']['cost'] = $cost;
  170. }
  171. return $return;
  172. }
  173. /**
  174. * Determine if the password hash needs to be rehashed according to the options provided
  175. *
  176. * If the answer is true, after validating the password using password_verify, rehash it.
  177. *
  178. * @param string $hash The hash to test
  179. * @param int $algo The algorithm used for new password hashes
  180. * @param array $options The options array passed to password_hash
  181. *
  182. * @return boolean True if the password needs to be rehashed.
  183. */
  184. function password_needs_rehash($hash, $algo, array $options = array()) {
  185. $info = password_get_info($hash);
  186. if ($info['algo'] != $algo) {
  187. return true;
  188. }
  189. switch ($algo) {
  190. case PASSWORD_BCRYPT:
  191. $cost = isset($options['cost']) ? $options['cost'] : 10;
  192. if ($cost != $info['options']['cost']) {
  193. return true;
  194. }
  195. break;
  196. }
  197. return false;
  198. }
  199. /**
  200. * Verify a password against a hash using a timing attack resistant approach
  201. *
  202. * @param string $password The password to verify
  203. * @param string $hash The hash to verify against
  204. *
  205. * @return boolean If the password matches the hash
  206. */
  207. function password_verify($password, $hash) {
  208. if (!function_exists('crypt')) {
  209. trigger_error("Crypt must be loaded for password_verify to function", E_USER_WARNING);
  210. return false;
  211. }
  212. $ret = crypt($password, $hash);
  213. if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != PasswordCompat\binary\_strlen($hash) || PasswordCompat\binary\_strlen($ret) <= 13) {
  214. return false;
  215. }
  216. $status = 0;
  217. for ($i = 0; $i < PasswordCompat\binary\_strlen($ret); $i++) {
  218. $status |= (ord($ret[$i]) ^ ord($hash[$i]));
  219. }
  220. return $status === 0;
  221. }
  222. }
  223. }
  224. namespace PasswordCompat\binary {
  225. /**
  226. * Count the number of bytes in a string
  227. *
  228. * We cannot simply use strlen() for this, because it might be overwritten by the mbstring extension.
  229. * In this case, strlen() will count the number of *characters* based on the internal encoding. A
  230. * sequence of bytes might be regarded as a single multibyte character.
  231. *
  232. * @param string $binary_string The input string
  233. *
  234. * @internal
  235. * @return int The number of bytes
  236. */
  237. function _strlen($binary_string) {
  238. if (function_exists('mb_strlen')) {
  239. return mb_strlen($binary_string, '8bit');
  240. }
  241. return strlen($binary_string);
  242. }
  243. /**
  244. * Get a substring based on byte limits
  245. *
  246. * @see _strlen()
  247. *
  248. * @param string $binary_string The input string
  249. * @param int $start
  250. * @param int $length
  251. *
  252. * @internal
  253. * @return string The substring
  254. */
  255. function _substr($binary_string, $start, $length) {
  256. if (function_exists('mb_substr')) {
  257. return mb_substr($binary_string, $start, $length, '8bit');
  258. }
  259. return substr($binary_string, $start, $length);
  260. }
  261. }