PHP Classes

File: core/src/RTpassword.php

Recommend this page to a friend!
  Classes of No name   RT PHP   core/src/RTpassword.php   Download  
File: core/src/RTpassword.php
Role: Class source
Content type: text/plain
Description: Class source
Class: RT PHP
Library of general purpose PHP classes
Author: By
Last change:
Date: 7 years ago
Size: 6,343 bytes
 

Contents

Class file image Download
<?php

/**
 * @link https://github.com/rogertiongdev/RTphp RTphp GitHub project
 * @license https://rogertiongdev.github.io/MIT-License/
 *
 * @reference https://github.com/php/php-src/tree/master/ext/standard
 * @reference https://github.com/php/php-src/blob/master/ext/standard/crypt_blowfish.c
 * @reference https://github.com/php/php-src/blob/master/ext/standard/password.c
 * @reference http://php.net/manual/en/ref.password.php
 */

namespace RTdev\RTphp;

/**
 * Similar as PHP 5.5 Password functions. But this class only apply Bcrypt.
 *
 * @version 0.1
 * @author Roger Tiong RTdev
 */
class RTpassword {

    CONST
BCRYPT_LEN = 60;
    CONST
BCRYPT_NAME = 'bcrypt';
    CONST
BCRYPT_IDENTIFIER = '$2y$';
    CONST
BCRYPT_DEFAULT_COST = 10;
    CONST
BCRYPT_REQ_SALT_LEN = 22;
    CONST
BCRYPT_RAW_SALT_LEN = 16;
    CONST
BASE_64_STR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
    CONST
BF_ITOA_64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

   
/**
     * Test current PHP environment is able to run this class or NOT.
     *
     * @param string $password
     * @return boolean
     */
   
public function test($password = 'password4testing') {

        return (
$this->isCryptLoaded() && ($hash = $this->bcryptHash($password))) ? $this->verify($password, $hash) : FALSE;
    }

   
/**
     * Verifies that a password matches a hash.
     *
     * @param string $password
     * @param string $hash
     * @return boolean
     */
   
public function verify($password, $hash) {

        if (!
$this->isCryptLoaded()) {

            return
FALSE;
        }

       
$npassword = (string) $password;
       
$nhash = (string) $hash;

        if (!
$this->isCryptLoaded() || !is_string($ret = crypt($npassword, $nhash)) || $this->binStrLen($ret) != $this->binStrLen($nhash) || $this->binStrLen($ret) <= 13) {

            return
FALSE;
        }

       
$status = 0;

        for (
$i = -1; ++$i < $this->binStrLen($ret);) {

           
$status |= (ord($ret[$i]) ^ ord($nhash[$i]));
        }

        return
$status === 0;
    }

   
/**
     * Creates a password hash.
     *
     * @param string $password
     * @param string $cost
     * @return boolean
     */
   
public function bcryptHash($password, $cost = self::BCRYPT_DEFAULT_COST) {

        if (!
$this->isCryptLoaded()) {

            return
FALSE;
        }

       
$npassword = (string) $password;
       
$ncost = (int) $cost;

        if (
$ncost < 4 || $ncost > 31) {

           
trigger_error(sprintf('%s Expects cost is between 4 and 31. %d given. Default cost (10) will be apply.', __METHOD__, $ncost));
           
$ncost = self::BCRYPT_DEFAULT_COST;
        }

       
$hash = sprintf(self::BCRYPT_IDENTIFIER . '%02d$', $ncost) . $this->subBinStr(strtr(rtrim(base64_encode($this->getSalt()), '='), self::BASE_64_STR, self::BF_ITOA_64), 0, self::BCRYPT_REQ_SALT_LEN);
       
$ret = crypt($npassword, $hash);

        return (
is_string($ret) && $this->binStrLen($ret) == self::BCRYPT_LEN) ? $ret : FALSE;
    }

   
/**
     * Returns information about the given hash.
     *
     * @param string $hash
     * @return array
     */
   
public function getHashInfo($hash) {

       
$nhash = (string) $hash;

        if (
$this->subBinStr($nhash, 0, 4) == self::BCRYPT_IDENTIFIER && $this->binStrLen($nhash) == self::BCRYPT_LEN) {

            list(
$cost) = sscanf($nhash, self::BCRYPT_IDENTIFIER . '%d$');
            return array(
'algoName' => self::BCRYPT_NAME, 'cost' => $cost);
        }

        return array(
'algoName' => 'unknown', 'cost' => 0);
    }

   
/**
     * Checks if the given hash matches the given options.
     *
     * @param string $hash
     * @param integer $cost
     * @return boolean
     */
   
public function isNeedReHash($hash, $cost = self::BCRYPT_DEFAULT_COST) {

       
$info = $this->getHashInfo((string) $hash);

        return ((int)
$cost !== $info['cost']);
    }

   
/**
     * Get length of given binary string
     *
     * @param string $binStr
     * @return integer
     */
   
protected function binStrLen($binStr) {

        return (
function_exists('mb_strlen')) ? mb_strlen($binStr, '8bit') : strlen($binStr);
    }

   
/**
     * Sub binary string
     *
     * @param string $binStr Binary string
     * @param integer $start
     * @param integer $len
     * @return string
     */
   
protected function subBinStr($binStr, $start, $len) {

        return (
function_exists('mb_substr')) ? mb_substr($binStr, $start, $len, '8bit') : substr($binStr, $start, $len);
    }

   
/**
     * Get random salt
     *
     * @param integer $saltLen Salt length
     * @return string
     */
   
protected function getSalt($saltLen = self::BCRYPT_RAW_SALT_LEN) {

       
$nsalt = '';
       
$valid = FALSE;
       
$nsaltLen = (int) $saltLen;

        if (
function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {

           
$valid = ($nsalt = mcrypt_create_iv($nsaltLen, MCRYPT_DEV_URANDOM) == TRUE);
        }

        if (!
$valid && function_exists('openssl_random_pseudo_bytes')) {

           
$strong = FALSE;
           
$valid = ($nsalt = (openssl_random_pseudo_bytes($nsaltLen, $strong) && $strong) == TRUE);
        }

        if (!
$valid && @is_readable('/dev/urandom')) {

           
$file = fopen('/dev/urandom', 'r');
           
$read = 0;
           
$localBuffer = '';

            while (
$read < $nsaltLen) {

               
$read = $this->binStrLen($localBuffer .= fread($file, $nsaltLen - $read));
            }

           
fclose($file);

           
$valid = ($read >= $nsaltLen);
           
$nsalt = str_pad($nsalt, $nsaltLen, '\0') ^ str_pad($localBuffer, $nsaltLen, '\0');
        }

        if (!
$valid || $this->binStrLen($nsalt) < $nsaltLen) {

           
$max = $this->binStrLen($nsalt);

            for (
$i = -1; ++$i < $nsaltLen;) {

                (
$i < $max) ? @$nsalt[$i] = @$nsalt[$i] ^ chr(mt_rand(0, 255)) : $nsalt .= chr(mt_rand(0, 255));
            }
        }

        return
$nsalt;
    }

   
/**
     * Check is PHP function: crypt loaded
     *
     * @return boolean
     */
   
protected function isCryptLoaded() {

        if (!
function_exists('crypt')) {

           
trigger_error(sprintf('%s Expects crypt loaded.', __METHOD__));
            return
FALSE;
        }
        return
TRUE;
    }

}