Guest User

Untitled

a guest
Aug 13th, 2013
354
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 7.77 KB | None | 0 0
  1. ?php
  2. if (!defined('BASEPATH')) exit('No direct script access allowed');
  3.  
  4. Class Bcrypt {
  5.     /**
  6.      * Hash the password using the specified algorithm
  7.      *
  8.      * @param string $password The password to hash
  9.      * @param int    $algo     The algorithm to use (Defined by PASSWORD_* constants)
  10.      * @param array  $options  The options for the algorithm to use
  11.      *
  12.      * @return string|false The hashed password, or false on error.
  13.      */
  14.     function password_hash($password, $algo, array $options = array()) {
  15.         if (!function_exists('crypt')) {
  16.             trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING);
  17.             return null;
  18.         }
  19.         if (!is_string($password)) {
  20.             trigger_error("password_hash(): Password must be a string", E_USER_WARNING);
  21.             return null;
  22.         }
  23.         if (!is_int($algo)) {
  24.             trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING);
  25.             return null;
  26.         }
  27.         switch ($algo) {
  28.             case PASSWORD_BCRYPT :
  29.                 // Note that this is a C constant, but not exposed to PHP, so we don't define it here.
  30.                 $cost = 10;
  31.                 if (isset($options['cost'])) {
  32.                     $cost = $options['cost'];
  33.                     if ($cost < 4 || $cost > 31) {
  34.                         trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING);
  35.                         return null;
  36.                     }
  37.                 }
  38.                 // The length of salt to generate
  39.                 $raw_salt_len = 16;
  40.                 // The length required in the final serialization
  41.                 $required_salt_len = 22;
  42.                 $hash_format = sprintf("$2y$%02d$", $cost);
  43.                 break;
  44.             default :
  45.                 trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING);
  46.                 return null;
  47.         }
  48.         if (isset($options['salt'])) {
  49.             switch (gettype($options['salt'])) {
  50.                 case 'NULL' :
  51.                 case 'boolean' :
  52.                 case 'integer' :
  53.                 case 'double' :
  54.                 case 'string' :
  55.                     $salt = (string)$options['salt'];
  56.                     break;
  57.                 case 'object' :
  58.                     if (method_exists($options['salt'], '__tostring')) {
  59.                         $salt = (string)$options['salt'];
  60.                         break;
  61.                     }
  62.                 case 'array' :
  63.                 case 'resource' :
  64.                 default :
  65.                     trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING);
  66.                     return null;
  67.             }
  68.             if (strlen($salt) < $required_salt_len) {
  69.                 trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", strlen($salt), $required_salt_len), E_USER_WARNING);
  70.                 return null;
  71.             } elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) {
  72.                 $salt = str_replace('+', '.', base64_encode($salt));
  73.             }
  74.         } else {
  75.             $buffer = '';
  76.             $buffer_valid = false;
  77.             if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {
  78.                 $buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM);
  79.                 if ($buffer) {
  80.                     $buffer_valid = true;
  81.                 }
  82.             }
  83.             if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
  84.                 $buffer = openssl_random_pseudo_bytes($raw_salt_len);
  85.                 if ($buffer) {
  86.                     $buffer_valid = true;
  87.                 }
  88.             }
  89.             if (!$buffer_valid && is_readable('/dev/urandom')) {
  90.                 $f = fopen('/dev/urandom', 'r');
  91.                 $read = strlen($buffer);
  92.                 while ($read < $raw_salt_len) {
  93.                     $buffer .= fread($f, $raw_salt_len - $read);
  94.                     $read = strlen($buffer);
  95.                 }
  96.                 fclose($f);
  97.                 if ($read >= $raw_salt_len) {
  98.                     $buffer_valid = true;
  99.                 }
  100.             }
  101.             if (!$buffer_valid || strlen($buffer) < $raw_salt_len) {
  102.                 $bl = strlen($buffer);
  103.                 for ($i = 0; $i < $raw_salt_len; $i++) {
  104.                     if ($i < $bl) {
  105.                         $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
  106.                     } else {
  107.                         $buffer .= chr(mt_rand(0, 255));
  108.                     }
  109.                 }
  110.             }
  111.             $salt = str_replace('+', '.', base64_encode($buffer));
  112.         }
  113.         $salt = substr($salt, 0, $required_salt_len);
  114.  
  115.         $hash = $hash_format . $salt;
  116.  
  117.         $ret = crypt($password, $hash);
  118.  
  119.         if (!is_string($ret) || strlen($ret) <= 13) {
  120.             return false;
  121.         }
  122.  
  123.         return $ret;
  124.     }
  125.  
  126.     /**
  127.      * Get information about the password hash. Returns an array of the information
  128.      * that was used to generate the password hash.
  129.      *
  130.      * array(
  131.      *    'algo' => 1,
  132.      *    'algoName' => 'bcrypt',
  133.      *    'options' => array(
  134.      *        'cost' => 10,
  135.      *    ),
  136.      * )
  137.      *
  138.      * @param string $hash The password hash to extract info from
  139.      *
  140.      * @return array The array of information about the hash.
  141.      */
  142.     function password_get_info($hash) {
  143.         $return = array('algo' => 0, 'algoName' => 'unknown', 'options' => array(), );
  144.         if (substr($hash, 0, 4) == '$2y$' && strlen($hash) == 60) {
  145.             $return['algo'] = PASSWORD_BCRYPT;
  146.             $return['algoName'] = 'bcrypt';
  147.             list($cost) = sscanf($hash, "$2y$%d$");
  148.             $return['options']['cost'] = $cost;
  149.         }
  150.         return $return;
  151.     }
  152.  
  153.     /**
  154.      * Determine if the password hash needs to be rehashed according to the options provided
  155.      *
  156.      * If the answer is true, after validating the password using password_verify, rehash it.
  157.      *
  158.      * @param string $hash    The hash to test
  159.      * @param int    $algo    The algorithm used for new password hashes
  160.      * @param array  $options The options array passed to password_hash
  161.      *
  162.      * @return boolean True if the password needs to be rehashed.
  163.      */
  164.     function password_needs_rehash($hash, $algo, array $options = array()) {
  165.         $info = password_get_info($hash);
  166.         if ($info['algo'] != $algo) {
  167.             return true;
  168.         }
  169.         switch ($algo) {
  170.             case PASSWORD_BCRYPT :
  171.                 $cost = isset($options['cost']) ? $options['cost'] : 10;
  172.                 if ($cost != $info['options']['cost']) {
  173.                     return true;
  174.                 }
  175.                 break;
  176.         }
  177.         return false;
  178.     }
  179.  
  180.     /**
  181.      * Verify a password against a hash using a timing attack resistant approach
  182.      *
  183.      * @param string $password The password to verify
  184.      * @param string $hash     The hash to verify against
  185.      *
  186.      * @return boolean If the password matches the hash
  187.      */
  188.     function password_verify($password, $hash) {
  189.         if (!function_exists('crypt')) {
  190.             trigger_error("Crypt must be loaded for password_verify to function", E_USER_WARNING);
  191.             return false;
  192.         }
  193.         $ret = crypt($password, $hash);
  194.         if (!is_string($ret) || strlen($ret) != strlen($hash) || strlen($ret) <= 13) {
  195.             return false;
  196.         }
  197.  
  198.         $status = 0;
  199.         for ($i = 0; $i < strlen($ret); $i++) {
  200.             $status |= (ord($ret[$i]) ^ ord($hash[$i]));
  201.         }
  202.  
  203.         return $status === 0;
  204.     }
  205.  
  206. }
Advertisement
Add Comment
Please, Sign In to add comment