Fujiwaranosai

Php scrypt equivalent to ruby scrypt

Aug 28th, 2015
111
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 7.34 KB | None | 0 0
  1. <?php
  2.  
  3. /**
  4.  * This file contains an example helper classes for the php-scrypt extension.
  5.  *
  6.  * As with all cryptographic code; it is recommended that you use a tried and
  7.  * tested library which uses this library; rather than rolling your own.
  8.  *
  9.  * PHP version 5
  10.  *
  11.  * @category Security
  12.  * @package  Scrypt
  13.  * @author   Dominic Black <thephenix@gmail.com>
  14.  * @license  http://www.opensource.org/licenses/BSD-2-Clause BSD 2-Clause License
  15.  * @link     http://github.com/DomBlack/php-scrypt
  16.  */
  17.  
  18. /**
  19.  * This class abstracts away from scrypt module, allowing for easy use.
  20.  *
  21.  * You can create a new hash for a password by calling Password::hash($password)
  22.  *
  23.  * You can check a password by calling Password::check($password, $hash)
  24.  *
  25.  * @category Security
  26.  * @package  Scrypt
  27.  * @author   Dominic Black <thephenix@gmail.com>
  28.  * @license  http://www.opensource.org/licenses/BSD-2-Clause BSD 2-Clause License
  29.  * @link     http://github.com/DomBlack/php-scrypt
  30.  */
  31. class Password
  32. {
  33.  
  34.     /**
  35.      *
  36.      * @var int The key length
  37.      */
  38.     private static $_keyLength = 32;
  39.  
  40.     /**
  41.      * Get the byte-length of the given string
  42.      *
  43.      * @param string $str Input string
  44.      *
  45.      * @return int
  46.      */
  47.     protected static function strlen( $str ) {
  48.         static $isShadowed = null;
  49.  
  50.         if ($isShadowed === null) {
  51.             $isShadowed = extension_loaded('mbstring') &&
  52.                 ini_get('mbstring.func_overload') & 2;
  53.         }
  54.  
  55.         if ($isShadowed) {
  56.             return mb_strlen($str, '8bit');
  57.         } else {
  58.             return strlen($str);
  59.         }
  60.     }
  61.  
  62.     /**
  63.      * Generates a random salt
  64.      *
  65.      * @param int $length The length of the salt
  66.      *
  67.      * @return string The salt
  68.      */
  69.     public static function generateSalt($length = 8)
  70.     {
  71.         $buffer = '';
  72.         $buffer_valid = false;
  73.         if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {
  74.             $buffer = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
  75.             if ($buffer) {
  76.                 $buffer_valid = true;
  77.             }
  78.         }
  79.         if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
  80.             $cryptoStrong = false;
  81.             $buffer = openssl_random_pseudo_bytes($length, $cryptoStrong);
  82.             if ($buffer && $cryptoStrong) {
  83.                 $buffer_valid = true;
  84.             }
  85.         }
  86.         if (!$buffer_valid && is_readable('/dev/urandom')) {
  87.             $f = fopen('/dev/urandom', 'r');
  88.             $read = static::strlen($buffer);
  89.             while ($read < $length) {
  90.                 $buffer .= fread($f, $length - $read);
  91.                 $read = static::strlen($buffer);
  92.             }
  93.             fclose($f);
  94.             if ($read >= $length) {
  95.                 $buffer_valid = true;
  96.             }
  97.         }
  98.         if (!$buffer_valid || static::strlen($buffer) < $length) {
  99.             $bl = static::strlen($buffer);
  100.             for ($i = 0; $i < $length; $i++) {
  101.                 if ($i < $bl) {
  102.                     $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
  103.                 } else {
  104.                     $buffer .= chr(mt_rand(0, 255));
  105.                 }
  106.             }
  107.         }
  108.         $salt = str_replace(array('+', '$'), array('.', ''), base64_encode($buffer));
  109.  
  110.         return $salt;
  111.     }
  112.  
  113.     /**
  114.      * Create a password hash
  115.      *
  116.      * @param string $password The clear text password
  117.      * @param string $salt     The salt to use, or null to generate a random one
  118.      * @param int    $N        The CPU difficultly (must be a power of 2, > 1)
  119.      * @param int    $r        The memory difficultly
  120.      * @param int    $p        The parallel difficultly
  121.      *
  122.      * @return string The hashed password
  123.      */
  124.     public static function hash($password, $salt = false, $N = 16384, $r = 8, $p = 1)
  125.     {
  126.         /*if ($N == 0 || ($N & ($N - 1)) != 0) {
  127.             throw new \InvalidArgumentException("N must be > 0 and a power of 2");
  128.         }*/
  129.  
  130.         if ($N > PHP_INT_MAX / 128 / $r) {
  131.             throw new \InvalidArgumentException("Parameter N is too large");
  132.         }
  133.  
  134.         if ($r > PHP_INT_MAX / 128 / $p) {
  135.             throw new \InvalidArgumentException("Parameter r is too large");
  136.         }
  137.  
  138.         if ($salt === false) {
  139.             $salt = self::generateSalt();
  140.         } else {
  141.             // Remove dollar signs from the salt, as we use that as a separator.
  142.             //$salt = str_replace(array('+', '$'), array('.', ''), base64_encode($salt));
  143.         }
  144.  
  145.         $hash = scrypt($password, $salt, $N, $r, $p, self::$_keyLength);
  146.  
  147.         //return $N . '$' . $r . '$' . $p . '$' . $salt . '$' . $hash;
  148.         return $hash;
  149.     }
  150.  
  151.     /**
  152.      * Check a clear text password against a hash
  153.      *
  154.      * @param string $password The clear text password
  155.      * @param string $hash     The hashed password
  156.      *
  157.      * @return boolean If the clear text matches
  158.      */
  159.     public static function check($password, $hash)
  160.     {
  161.         // Is there actually a hash?
  162.         if (!$hash) {
  163.             return false;
  164.         }
  165.  
  166.         list ($N, $r, $p, $salt, $hash) = explode('$', $hash);
  167.        
  168.         $N = hexdec($N);
  169.         $r = hexdec($r);
  170.         $p = hexdec($p);
  171.         $keyLength = strlen($hash) / 2;
  172.  
  173.         // No empty fields?
  174.         if (empty($N) or empty($r) or empty($p) or empty($salt) or empty($hash)) {
  175.             return false;
  176.         }
  177.  
  178.         // Are numeric values numeric?
  179.         if (!is_numeric($N) or !is_numeric($r) or !is_numeric($p)) {
  180.             return false;
  181.         }
  182.        
  183.         $salt = call_user_func_array('pack', array_merge(['H*'], [$salt]));
  184.  
  185.         $calculated = scrypt($password, $salt, $N, $r, $p, $keyLength);
  186.         var_dump($calculated, $hash);
  187.  
  188.         // Use compareStrings to avoid timeing attacks
  189.         return self::compareStrings($hash, $calculated);
  190.     }
  191.  
  192.     /**
  193.      * Zend Framework (http://framework.zend.com/)
  194.      *
  195.      * @link      http://github.com/zendframework/zf2 for the canonical source repository
  196.      * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
  197.      * @license   http://framework.zend.com/license/new-bsd New BSD License
  198.      *
  199.      * Compare two strings to avoid timing attacks
  200.      *
  201.      * C function memcmp() internally used by PHP, exits as soon as a difference
  202.      * is found in the two buffers. That makes possible of leaking
  203.      * timing information useful to an attacker attempting to iteratively guess
  204.      * the unknown string (e.g. password).
  205.      *
  206.      * @param string $expected
  207.      * @param string $actual
  208.      *
  209.      * @return boolean If the two strings match.
  210.      */
  211.     public static function compareStrings($expected, $actual)
  212.     {
  213.         $expected    = (string) $expected;
  214.         $actual      = (string) $actual;
  215.         $lenExpected = static::strlen($expected);
  216.         $lenActual   = static::strlen($actual);
  217.         $len         = min($lenExpected, $lenActual);
  218.  
  219.         $result = 0;
  220.         for ($i = 0; $i < $len; $i ++) {
  221.             $result |= ord($expected[$i]) ^ ord($actual[$i]);
  222.         }
  223.         $result |= $lenExpected ^ $lenActual;
  224.  
  225.         return ($result === 0);
  226.     }
  227. }
  228.  
  229. $e = '400$8$2c$1600ca91c4c30d8a$3fca8393a52965ba12f4b6bd7351e8e7f85974c29e0c95ae91673caff9e5480d';
  230. $c = Password::check('pkcSS9915ofUfGMno1E2viJNqpwtv', $e);
  231. var_dump($c);
Add Comment
Please, Sign In to add comment