Problem
As I understand it, blowfish (actually eksblowfish, as it’s used one way) is generally seen a secure hashing algorithm, even for enterprise use (correct me if I’m wrong). Because of this, I created functions to create and check secure password hashes using this algorithm, and using the (also deemed cryptographically secure) openssl_random_pseudo_bytes function to generate the salt.
<?php
/*
* Generate a secure hash for a given password. The cost is passed
* to the blowfish algorithm. Check the PHP manual page for crypt to
* find more information about this setting. Returns null on failure.
*/
function generate_hash($password, $cost=11){
/* To generate the salt, first generate enough random bytes. Because
* base64 returns one character for each 6 bits, the we should generate
* at least 22*6/8=16.5 bytes, so we generate 17. Then we get the first
* 22 base64 characters
*/
$salt=substr(base64_encode(openssl_random_pseudo_bytes(17,$secure)),0,22);
/* If the random generation was not secure enough, do not continue
*/
if(!$secure) return null;
/* As blowfish takes a salt with the alphabet ./A-Za-z0-9 we have to
* replace any '+' in the base64 string with '.'. We don't have to do
* anything about the '=', as this only occurs when the b64 string is
* padded, which is always after the first 22 characters.
*/
$salt=str_replace("+",".",$salt);
/* Next, create a string that will be passed to crypt, containing all
* of the settings, separated by dollar signs
*/
$param='$'.implode('$',array(
"2y", //select the most secure version of blowfish (>=PHP 5.3.7)
str_pad($cost,2,"0",STR_PAD_LEFT), //add the cost in two digits
$salt //add the salt
));
/* Now do the actual hashing
*/
$hash=crypt($password,$param);
if(strlen($hash)!=60) return null;
return $hash;
}
/*
* Check the password against a hash generated by the generate_hash
* function.
*/
function validate_pw($password, $hash){
/* Regenerating the with an available hash as the options parameter should
* produce the same hash if the same password is passed.
*/
return crypt($password, $hash)===$hash;
}
?>
Solution
I would have to agree with @EliasVanOotegem. Consider using password_hash()
and password_verify()
. All you’ve done is recreate something, which we know shouldn’t be done.