bcrypt.php 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. <?php
  2. /*
  3. Copyright (c) 2009-2014 F3::Factory/Bong Cosca, All rights reserved.
  4. This file is part of the Fat-Free Framework (http://fatfree.sf.net).
  5. THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF
  6. ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  7. IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  8. PURPOSE.
  9. Please see the license.txt file for more information.
  10. */
  11. //! Lightweight password hashing library
  12. class Bcrypt extends Prefab {
  13. //@{ Error messages
  14. const
  15. E_CostArg='Invalid cost parameter',
  16. E_SaltArg='Salt must be at least 22 alphanumeric characters';
  17. //@}
  18. //! Default cost
  19. const
  20. COST=10;
  21. /**
  22. * Generate bcrypt hash of string
  23. * @return string|FALSE
  24. * @param $pw string
  25. * @param $salt string
  26. * @param $cost int
  27. **/
  28. function hash($pw,$salt=NULL,$cost=self::COST) {
  29. if ($cost<4 || $cost>31)
  30. user_error(self::E_CostArg);
  31. $len=22;
  32. if ($salt) {
  33. if (!preg_match('/^[[:alnum:]\.\/]{'.$len.',}$/',$salt))
  34. user_error(self::E_SaltArg);
  35. }
  36. else {
  37. $raw=16;
  38. $iv='';
  39. if (extension_loaded('mcrypt'))
  40. $iv=mcrypt_create_iv($raw,MCRYPT_DEV_URANDOM);
  41. if (!$iv && extension_loaded('openssl'))
  42. $iv=openssl_random_pseudo_bytes($raw);
  43. if (!$iv)
  44. for ($i=0;$i<$raw;$i++)
  45. $iv.=chr(mt_rand(0,255));
  46. $salt=str_replace('+','.',base64_encode($iv));
  47. }
  48. $salt=substr($salt,0,$len);
  49. $hash=crypt($pw,sprintf('$2y$%02d$',$cost).$salt);
  50. return strlen($hash)>13?$hash:FALSE;
  51. }
  52. /**
  53. * Check if password is still strong enough
  54. * @return bool
  55. * @param $hash string
  56. * @param $cost int
  57. **/
  58. function needs_rehash($hash,$cost=self::COST) {
  59. list($pwcost)=sscanf($hash,"$2y$%d$");
  60. return $pwcost<$cost;
  61. }
  62. /**
  63. * Verify password against hash using timing attack resistant approach
  64. * @return bool
  65. * @param $pw string
  66. * @param $hash string
  67. **/
  68. function verify($pw,$hash) {
  69. $val=crypt($pw,$hash);
  70. $len=strlen($val);
  71. if ($len!=strlen($hash) || $len<14)
  72. return FALSE;
  73. $out=0;
  74. for ($i=0;$i<$len;$i++)
  75. $out|=(ord($val[$i])^ord($hash[$i]));
  76. return $out===0;
  77. }
  78. }