PageRenderTime 54ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/app/Plugin/Tools/Lib/Bootstrap/Password.php

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