PageRenderTime 50ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 1ms

/libs/php/pw-hashing/passwordLibClass.php

https://gitlab.com/Raymon/QualityCaps_PHP
PHP | 354 lines | 171 code | 50 blank | 133 comment | 26 complexity | 7ae581005f32ec5591d4b9ba1eda2994 MD5 | raw file
  1. <?php
  2. /**
  3. * PHP 5.5-like password hashing functions
  4. *
  5. * Provides a password_hash() and password_verify() function as appeared in PHP 5.5.0
  6. *
  7. * See: http://php.net/password_hash and http://php.net/password_verify
  8. *
  9. * @link https://github.com/Antnee/phpPasswordHashingLib
  10. */
  11. namespace Antnee\PhpPasswordLib;
  12. if (!defined('PASSWORD_BCRYPT')) define('PASSWORD_BCRYPT', 1);
  13. // Note that SHA hashes are not implemented in password_hash() or password_verify() in PHP 5.5
  14. // and are not recommended for use. Recommend only the default BCrypt option
  15. if (!defined('PASSWORD_SHA256')) define('PASSWORD_SHA256', -1);
  16. if (!defined('PASSWORD_SHA512')) define('PASSWORD_SHA512', -2);
  17. if (!defined('PASSWORD_DEFAULT')) define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);
  18. class PhpPasswordLib{
  19. CONST BLOWFISH_CHAR_RANGE = './0123456789ABCDEFGHIJKLMONPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
  20. CONST BLOWFISH_CRYPT_SETTING = '$2a$';
  21. CONST BLOWFISH_CRYPT_SETTING_ALT = '$2y$'; // Available from PHP 5.3.7
  22. CONST BLOWFISH_ROUNDS = 10;
  23. CONST BLOWFISH_NAME = 'bcrypt';
  24. // Note that SHA hashes are not implemented in password_hash() or password_verify() in PHP 5.5
  25. // and are not recommended for use. Recommend only the default BCrypt option
  26. CONST SHA256_CHAR_RANGE = './0123456789ABCDEFGHIJKLMONPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
  27. CONST SHA256_CRYPT_SETTING = '$5$';
  28. CONST SHA256_ROUNDS = 5000;
  29. CONST SHA256_NAME = 'sha256';
  30. CONST SHA512_CHAR_RANGE = './0123456789ABCDEFGHIJKLMONPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
  31. CONST SHA512_CRYPT_SETTING = '$6$';
  32. CONST SHA512_ROUNDS = 5000;
  33. CONST SHA512_NAME = 'sha512';
  34. /**
  35. * Default Crypt Algorithm
  36. *
  37. * @var INT
  38. */
  39. private $algorithm = PASSWORD_BCRYPT;
  40. /**
  41. * Name of the current algorithm
  42. *
  43. * @var STRING
  44. */
  45. private $algoName;
  46. /**
  47. * Setting for PHP Crypt function, defines algorithm
  48. *
  49. * Default setting is '$2a$' : BCrypt
  50. *
  51. * @var STRING
  52. */
  53. protected $cryptSetting;
  54. /**
  55. * Setting for PHP Crypt function, defines processing cost
  56. *
  57. * Default setting is '08$' for BCrypt rounds
  58. *
  59. * @var INT
  60. */
  61. protected $rounds;
  62. /**
  63. * Salt Character Count for Crypt Functions
  64. *
  65. * @var INT
  66. */
  67. protected $addSaltChars;
  68. /**
  69. * Salt Character Range for Crypt Functions
  70. *
  71. * @var STRING
  72. */
  73. protected $saltCharRange;
  74. /**
  75. * Class Constructor
  76. */
  77. public function __construct(){
  78. // Initialise default algorithm
  79. $this->setAlgorithm($this->algorithm);
  80. }
  81. /**
  82. * Generate Crypt Password
  83. *
  84. * @param STRING $password The password to encode
  85. * @param ARRAY $options Cost value, and Salt if required
  86. * @param BOOL $debug If true will return time to calculate hash
  87. * @return STRING The encoded password
  88. */
  89. public function generateCryptPassword($password, $options = array(), $debug = FALSE){
  90. $startTime = microtime(TRUE);
  91. if (isset($options['cost'])) $this->setCost($options['cost']);
  92. $salt = $this->cryptSalt(@$options['salt']);
  93. $crypt = crypt($password, $salt);
  94. $endTime = microtime(TRUE);
  95. if ($debug){
  96. $calcTime = $endTime - $startTime;
  97. return $calcTime;
  98. }
  99. return $crypt;
  100. }
  101. /**
  102. * Generate Crypt Salt
  103. *
  104. * Generates a salt suitable for Crypt using the defined crypt settings
  105. *
  106. * @param STRING $salt Override random salt with predefined value
  107. * @return STRING
  108. */
  109. public function cryptSalt($salt=NULL){
  110. if (empty($salt)){
  111. for ($i = 0; $i<$this->addSaltChars; $i++){
  112. $salt .= $this->saltCharRange[rand(0,(strlen($this->saltCharRange)-1))];
  113. }
  114. }
  115. $salt = $this->cryptSetting.$this->rounds.$salt.'$';
  116. return $salt;
  117. }
  118. /**
  119. * Set Crypt Setting
  120. *
  121. * @param type $setting
  122. * @return \Antnee\PhpPasswordLib\PhpPasswordLib
  123. */
  124. public function cryptSetting($setting){
  125. $this->cryptSetting = $setting;
  126. return $this;
  127. }
  128. /**
  129. * Salt Character Count
  130. *
  131. * @param INT $count Number of characters to set
  132. * @return \Antnee\PhpPasswordLib\PhpPasswordLib|boolean
  133. */
  134. public function addSaltChars($count){
  135. if (is_int($count)){
  136. $this->addSaltChars = $count;
  137. return $this;
  138. } else {
  139. return FALSE;
  140. }
  141. }
  142. /**
  143. * Salt Character Range
  144. *
  145. * @param STRING $chars
  146. * @return \Antnee\PhpPasswordLib\PhpPasswordLib|boolean
  147. */
  148. public function saltCharRange($chars){
  149. if (is_string($chars)){
  150. $this->saltCharRange = $chars;
  151. return $this;
  152. } else {
  153. return FALSE;
  154. }
  155. }
  156. /**
  157. * Set Crypt Algorithm
  158. *
  159. * @param INT $algo
  160. * @return \Antnee\PhpPasswordLib\PhpPasswordLib
  161. */
  162. public function setAlgorithm($algo=NULL){
  163. switch ($algo){
  164. case PASSWORD_SHA256:
  165. $this->algorithm = PASSWORD_SHA256;
  166. $this->cryptSetting(self::SHA256_CRYPT_SETTING);
  167. $this->setCost(self::SHA256_ROUNDS);
  168. $this->addSaltChars(16);
  169. $this->saltCharRange(self::SHA256_CHAR_RANGE);
  170. $this->algoName = self::SHA256_NAME;
  171. break;
  172. case PASSWORD_SHA512:
  173. $this->algorithm = PASSWORD_SHA512;
  174. $this->cryptSetting(self::SHA512_CRYPT_SETTING);
  175. $this->setCost(self::SHA512_ROUNDS);
  176. $this->addSaltChars(16);
  177. $this->saltCharRange(self::SHA512_CHAR_RANGE);
  178. $this->algoName = self::SHA512_NAME;
  179. break;
  180. case PASSWORD_BCRYPT:
  181. default:
  182. $this->algorithm = PASSWORD_BCRYPT;
  183. if (version_compare(PHP_VERSION, '5.3.7') >= 1){
  184. // Use improved Blowfish algorithm if supported
  185. $this->cryptSetting(self::BLOWFISH_CRYPT_SETTING_ALT);
  186. } else {
  187. $this->cryptSetting(self::BLOWFISH_CRYPT_SETTING);
  188. }
  189. $this->setCost(self::BLOWFISH_ROUNDS);
  190. $this->addSaltChars(22);
  191. $this->saltCharRange(self::BLOWFISH_CHAR_RANGE);
  192. $this->algoName = self::BLOWFISH_NAME;
  193. break;
  194. }
  195. return $this;
  196. }
  197. /**
  198. * Set Cost
  199. *
  200. * @todo implement
  201. *
  202. * @return \Antnee\PhpPasswordLib\PhpPasswordLib
  203. */
  204. public function setCost($rounds){
  205. switch ($this->algorithm){
  206. case PASSWORD_BCRYPT:
  207. $this->rounds = $this->setBlowfishCost($rounds);
  208. break;
  209. case PASSWORD_SHA256:
  210. case PASSWORD_SHA512:
  211. $this->rounds = $this->setShaCost($rounds);
  212. break;
  213. }
  214. return $this;
  215. }
  216. /**
  217. * Set Blowfish hash cost
  218. *
  219. * Minimum 4, maximum 31. Value is base-2 log of actual number of rounds, so
  220. * 4 = 16, 8 = 256, 16 = 65,536 and 31 = 2,147,483,648
  221. * Defaults to 8 if value is out of range or incorrect type
  222. *
  223. * @param int $rounds
  224. * @return STRING
  225. */
  226. private function setBlowfishCost($rounds){
  227. if (!is_int($rounds) || $rounds < 4 || $rounds > 31){
  228. $rounds = $rounds = self::BLOWFISH_ROUNDS;
  229. }
  230. return sprintf("%02d", $rounds)."$";
  231. }
  232. /**
  233. * Set SHA hash cost
  234. *
  235. * Minimum 1000, maximum 999,999,999
  236. * Defaults to 5000 if value is out of range or incorrect type
  237. *
  238. * @param INT $rounds
  239. * @return STRING
  240. */
  241. private function setShaCost($rounds){
  242. if (!is_int($rounds) || $rounds < 1000 || $rounds > 999999999){
  243. switch ($this->algorithm){
  244. case PASSWORD_SHA256:
  245. $rounds = self::SHA256_ROUNDS;
  246. case PASSWORD_SHA512:
  247. default:
  248. $rounds = self::SHA512_ROUNDS;
  249. }
  250. }
  251. return "rounds=" . $rounds ."$";
  252. }
  253. /**
  254. * Get hash info
  255. *
  256. * @param STRING $hash
  257. * @return ARRAY
  258. */
  259. public function getInfo($hash){
  260. $params = explode("$", $hash);
  261. if (count($params) < 4) return FALSE;
  262. switch ($params['1']){
  263. case '2a':
  264. case '2y':
  265. case '2x':
  266. $algo = PASSWORD_BCRYPT;
  267. $algoName = self::BLOWFISH_NAME;
  268. break;
  269. case '5':
  270. $algo = PASSWORD_SHA256;
  271. $algoName = self::SHA256_NAME;
  272. break;
  273. case '6':
  274. $algo = PASSWORD_SHA512;
  275. $algoName = self::SHA512_NAME;
  276. break;
  277. default:
  278. return FALSE;
  279. }
  280. $cost = preg_replace("/[^0-9,.]/", "", $params['2']);
  281. return array(
  282. 'algo' => $algo,
  283. 'algoName' => $algoName,
  284. 'options' => array(
  285. 'cost' => $cost
  286. ),
  287. );
  288. }
  289. /**
  290. * Verify Crypt Setting
  291. *
  292. * Checks that the hash provided is encrypted at the current settings or not,
  293. * returning BOOL accordingly
  294. *
  295. * @param STRING $hash
  296. * @return BOOL
  297. */
  298. public function verifyCryptSetting($hash, $algo, $options=array()){
  299. $this->setAlgorithm($algo);
  300. if (isset($options['cost'])) $this->setCost($options['cost']);
  301. $setting = $this->cryptSetting.$this->rounds;
  302. return (substr($hash, 0, strlen($setting)) === $setting);
  303. }
  304. }