Another solution to the salted hash with salt included directly in the hash, while keeping the same length of the result. If you want to generate a hash, call the function without the second argument. If you want to check a password against a hash, use the hash as the second argument. In this case, the function returns the hash itself on success, or boolean false on failure. You can also specify a hash algorithm as the third argument (otherwise SHA-1 will be used).
<?php
function __hash($password, $obscured = NULL, $algorithm = "sha1")
{
$mode = in_array($algorithm, hash_algos());
$salt = uniqid(mt_rand(), true);
$salt = $mode ? hash($algorithm, $salt) : sha1($salt);
$slen = strlen($salt);
$slen = max($slen >> 3, ($slen >> 2) - strlen($password));
$salt = $obscured ? __harvest($obscured, $slen, $password) : substr($salt, 0, $slen);
$hash = $mode ? hash($algorithm, $password) : sha1($password);
$hash = __scramble($hash, $salt, $password);
$hash = $mode ? hash($algorithm, $hash) : sha1($hash);
$hash = substr($hash, $slen);
$hash = __scramble($hash, $salt, $password);
return $obscured && $obscured !== $hash ? false : $hash;
}
?>
It uses a random, variable length salt, depending on the length of the password. The functions __scramble() and __harvest() are used to place salt into the hash or pull it out respectively. You can write your own, and of course the strength of the result greatly depends on them. They can be relatively simple yet still quite secure:
<?php
function __scramble($hash, $salt, $password)
{
return substr($hash, 0, strlen($password)) . $salt . substr($hash, strlen($password));
}
function __harvest($obscured, $slen, $password)
{
return substr($obscured, min(strlen($password), strlen($obscured) - $slen), $slen);
}
?>
Or they can be ridiculously complicated (my favourite kind):
<?php
function __scramble($hash, $salt, $password)
{
$k = strlen($password); $j = $k = $k > 0 ? $k : 1; $p = 0; $index = array(); $out = ""; $m = 0;
for ($i = 0; $i < strlen($salt); $i++)
{
$c = substr($password, $p, 1);
$j = pow($j + ($c !== false ? ord($c) : 0), 2) % (strlen($hash) + strlen($salt));
while (array_key_exists($j, $index))
$j = ++$j % (strlen($hash) + strlen($salt));
$index[$j] = $i;
$p = ++$p % $k;
}
for ($i = 0; $i < strlen($hash) + strlen($salt); $i++)
$out .= array_key_exists($i, $index) ? $salt[$index[$i]] : $hash[$m++];
return $out;
}
function __harvest($obscured, $slen, $password)
{
$k = strlen($password); $j = $k = $k > 0 ? $k : 1; $p = 0; $index = array(); $out = "";
for ($i = 0; $i < $slen; $i++)
{
$c = substr($password, $p, 1);
$j = pow($j + ($c !== false ? ord($c) : 0), 2) % strlen($obscured);
while (in_array($j, $index))
$j = ++$j % strlen($obscured);
$index[$i] = $j;
$p = ++$p % $k;
}
for ($i = 0; $i < $slen; $i++)
$out .= $obscured[$index[$i]];
return $out;
}
?>