Advertisement
rfv123

PHP - Hash Class includes Password_Hash

Nov 6th, 2015
247
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 7.53 KB | None | 0 0
  1. <?php // https://en.wikipedia.org/wiki/Hash-based_message_authentication_code
  2. /**
  3.  * Based on something by Created by Chris on 9/29/2014 3:55 PM.
  4.  *
  5.  * - added password_hash functions - http://php.net/manual/en/faq.passwords.php
  6.  *   by RFV 7/2015
  7.  *
  8.  * Added HMAC (Hash-based_message_authentication_code) functions...
  9.  *
  10.  * All the faults are mine.
  11.  *
  12.  * These have been PHPUnit tested.
  13.  *
  14.  * Licence: https://opensource.org/licenses/MIT
  15.  *
  16.  * I really do not care what you use it for. All the information used here is
  17.  * in the public domain. Enjoy, change it or whatever.
  18.  *
  19.  */
  20.  
  21. namespace app\system\encryption;
  22.  
  23. use app\system\encryption\RandomData;
  24. // use app\system\IForgetState;
  25.  
  26. class Hash  /* implements IForgetState */ {
  27.  
  28.     const GEN_RANDOM       = 'random';
  29.  
  30.     const HASH_LEN         = 32;  // bytes not characters i.e CHR(0) .. CHR(255)
  31.     const SALT_LEN         = 32;  // bytes...
  32.  
  33.     const HMAC_LEN         = 32;  // bytes not characters i.e CHR(0) .. CHR(255)
  34.     const HMAC_KEY_LEN     = 32; // bytes...
  35.  
  36.     /**
  37.      * @var Hash
  38.      */
  39.     private static $instance = null;
  40.  
  41.     public static function instance()
  42.     {
  43.         if (is_null(self::$instance)) {
  44.             self::$instance = new static();
  45.         }
  46.  
  47.         return self::$instance;
  48.     }
  49.  
  50.     public static function factory($hmacKey = '')
  51.     {
  52.         $instance = new static();
  53.         if (!empty($hmacKey)) {
  54.             $instance->setHmacKey($hmacKey);
  55.         }
  56.         return $instance;
  57.     }
  58.  
  59.  
  60.     // do not allow CHR(0) as it may confuse by being treated as a terminator
  61.     public static function uniqueId() {
  62.         return hash('sha256',
  63.              uniqid() . RandomData::instance()->filteredBytes(self::SALT_LEN, 1, 255));
  64.     }
  65.  
  66.     /**
  67.      * Create a new hash from the supplied password
  68.      *
  69.      * @param type $password
  70.      */
  71.     public static function passwordHash($password)
  72.     {
  73.         return \password_hash($password, PASSWORD_DEFAULT);
  74.     }
  75.  
  76.     /**
  77.      * Verify Password matches
  78.      *
  79.      * @param type $password
  80.      * @param type $passwordHash
  81.      * @return boolean
  82.      */
  83.     public static function passwordVerify($password, $passwordHash, $throwError = true)
  84.     {
  85.         if (\password_verify($password, $passwordHash)) {
  86.             return true;
  87.         }
  88.         else {
  89.             if ($throwError) {
  90.                 throw new \UnexpectedValueException('Invalid Login Details', 500);
  91.             }
  92.             else {
  93.                 return false;
  94.             }
  95.         }
  96.     }
  97.  
  98.     /**
  99.      * HMAC - Is what you got matching either:
  100.      *        1) what you sent out?
  101.      *        2) from a reliable source?
  102.      *
  103.      * @param string $data
  104.      * @param string $digest
  105.      * @param string $sharedKey
  106.      * @param boolean $throwError
  107.      * @return boolean
  108.      * @throws \UnexpectedValueException
  109.      */
  110.     public static function hmacVerify($data, $digest, $sharedKey, $throwError = true)
  111.     {
  112.         $hash = self::factory();
  113.         $hash->setHmacKey($sharedKey);
  114.         $allOk = $hash->generateHMAC($data);
  115.  
  116.         if ($allOk && $hash->curHMAC() === $digest) {
  117.             $hash = null;
  118.             return true;
  119.         }
  120.         else {
  121.             $hash = null;
  122.             if ($throwError) {
  123.                 throw new \UnexpectedValueException('Invalid HMAC details', 500);
  124.             }
  125.             else {
  126.                 return false;
  127.             }
  128.         }
  129.     }
  130.  
  131.     // testing - next call will have re-initialize everything
  132.     public static function forget() {
  133.         self::$instance = null;
  134.     }
  135.  
  136.     /**
  137.      * unique identifier for this instance
  138.      */
  139.     private $instanceId = null;
  140.  
  141.     public function instanceId()
  142.     {
  143.         return $this->instanceId;
  144.     }
  145.  
  146.     /**
  147.      * Salt value corresponding to the curremt Hash
  148.      * @var string
  149.      */
  150.     private $curSalt = '';
  151.  
  152.     public function curSalt()          {  return $this->curSalt; }
  153.     public function curSaltAsHex()     { return bin2hex($this->curSalt); }
  154.  
  155.     public function setSalt($salt = self::GEN_RANDOM)
  156.     {
  157.         if ($salt === self::GEN_RANDOM) { // zero not allowed
  158.             $this->curSalt = RandomData::instance()->filteredBytes(self::SALT_LEN, 1, 255);
  159.         }
  160.         elseif (!empty($salt)) { // repeat the salt until required length
  161.             if (strlen($salt) < self::SALT_LEN) {
  162.                 $this->curSalt = $salt;
  163.                 while (strlen($this->curSalt) < self::SALT_LEN) {
  164.                     $this->curSalt .= $salt;
  165.                 }
  166.                 $this->curSalt = substr($this->curSalt, 0, self::SALT_LEN);
  167.              }
  168.              else {
  169.                 $this->curSalt = $salt;
  170.              }
  171.         }
  172.         else {
  173.             throw new \UnexpectedValueException('Empty salt is not allowed', 500);
  174.         }
  175.  
  176.         return !empty($this->curSalt);
  177.     }
  178.  
  179.     /**
  180.      * The hash generated using the salt
  181.      * @var string
  182.      */
  183.     private $curHash = '';
  184.  
  185.     public function curHash()          {  return $this->curHash; }
  186.     public function curHashAsHex()     { return bin2hex($this->curHash); }
  187.  
  188.     /**
  189.      * Generate a hash
  190.      * @param string $string
  191.      * @return string
  192.      */
  193.     public function generateHash($data, $salt = self::GEN_RANDOM)
  194.     {
  195.         $this->setSalt($salt);
  196.         $this->curHash = hash('sha256', $data . $this->curSalt, true);
  197.         return $this->curHash !== false;
  198.     }
  199.  
  200.     /**
  201.      * HMAC key - shared key used to verify the message is authentic
  202.      *
  203.      * @var string
  204.      */
  205.     private $curHmacKey = '';
  206.  
  207.     public function curHmacKey()          { return $this->curHmacKey; }
  208.  
  209.     public function setHmacKey($keyString = self::GEN_RANDOM)
  210.     {
  211.         if ($keyString === self::GEN_RANDOM) {  // you need to store this to use it later
  212.             $this->curHmacKey = RandomData::instance()->filteredBytes(self::HMAC_KEY_LEN, 1, 255);
  213.         }
  214.         elseif (!empty($keyString)) { // pad to required length by repeating...
  215.             if (strlen($keyString) < self::HMAC_KEY_LEN) {
  216.                 $this->curHmacKey = $keyString;
  217.                 while (strlen($this->curHmacKey) < self::HMAC_KEY_LEN) {
  218.                     $this->curHmacKey .= $keyString;
  219.                 }
  220.                 $this->curHmacKey = substr($this->curHmacKey, 0, self::HMAC_KEY_LEN);
  221.              }
  222.              else {
  223.                 $this->curHmacKey = $keyString;
  224.              }
  225.         }
  226.         else {
  227.             throw new \UnexpectedValueException('Empty HMAC key is not allowed', 500);
  228.         }
  229.  
  230.         return !empty($this->curHmacKey);
  231.     }
  232.  
  233.     /**
  234.      * The HMAC generated using the key
  235.      * @var string
  236.      */
  237.     private $curHMAC = '';
  238.  
  239.     public function curHMAC()          {  return $this->curHMAC; }
  240.     public function curHMACAsHex()     { return bin2hex($this->curHMAC); }
  241.  
  242.     public function generateHMAC($data, $key = '')
  243.     {
  244.         if (empty($data)) {
  245.             throw new UnexpectedValueException('no data provided', 500);
  246.         }
  247.  
  248.         if (empty($key)) {
  249.             if (empty($this->curHmacKey)) {
  250.                 throw new UnexpectedValueException('no key available', 500);
  251.             }
  252.         }
  253.         else {
  254.             $this->setHmacKey($key);
  255.         }
  256.  
  257.         $this->curHMAC = hash_hmac('sha256', $data, $this->curHmacKey(), true);
  258.  
  259.         return strlen($this->curHMAC) == self::HMAC_LEN;
  260.     }
  261.  
  262.     public function __construct()
  263.     {
  264.         $this->instanceId = self::uniqueId();
  265.     }
  266. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement