PHP Password Hashing Class



<?php
//usage: 
//    $hash = HashSimple::hash($password);
//    if (HashSimple::equivalent($password, $hash))...
 
class HashSimple
{
    private static $rounds_exp=14;//14 : 2^14 rounds of hashing alg
    private static $salt_length=12;//12 base64_encodes nicely 16 chars without padding, and is a multiple of 2
    private static $skip_logging=true;
 
    public static function hash($input)
    {
        $random_salt = openssl_random_pseudo_bytes(self::$salt_length, $crypto_strong=true);
        return self::saltedHash($input,$random_salt);
    }
 
    public static function equivalent($plaintext,$candidate_hash)
    {
        $candidate_hash[0]=='$' or self::error("HashError: invalid param");
        $pieces = explode("$", substr($candidate_hash,1));
        $rounds_exp = ltrim($pieces[0],".");
        $salt = base64_decode(strtr($pieces[1],".","+"));
        $generated_hash = self::saltedHash($plaintext,$salt,$rounds_exp);
        return ($candidate_hash==$generated_hash) ? true : false;
    }
 
    private static function saltedHash($input,$salt,$rounds_exp=null)
    {
        $rounds_exp = is_null($rounds_exp) ? self::$rounds_exp : $rounds_exp;
        if (!in_array('sha384', hash_algos())) { return self::log("HashError: unknown hash alg:".self::$hash_alg); }
        if (self::$salt_length%2==1) { return self::log("HashError: salt length must be multiple of 2"); }
 
        $rounds = pow(2,$rounds_exp);
        $halfway = floor(self::$salt_length/2);
        $p1 = substr($salt, 0, $halfway);
        $p2 = substr($salt, $halfway);
        $hashme = $p1.$input.$p2;
        self::$skip_logging or $start = microtime(true);
        for($i=0; $i<$rounds; $i++)
        {
            $hashme = hash('sha384', $hashme, $raw = true);
        }
        self::$skip_logging or self::log("HashLog: hashtime-".sprintf("%1.3f", microtime(true) - $start));
        self::$skip_logging or self::log("HashLog: rounds-".$rounds);
        return '$.'.$rounds_exp.'$'.strtr(base64_encode($salt),"+",".").'$'.strtr(base64_encode($hashme),"+",".");
    }
 
    private static function log($message)
    {
        $r = php_sapi_name()=='cli' ? file_put_contents('php://stderr', $message."\n") : error_log($message);
        return null;
    }
}