Very Secure Password Hashing using unknown salts.
Posted: Wed Jan 19, 2011 2:20 pm
This code creates a variable length salt to the left and right of a fixed length password hash using the password length to determine the characters allocated for the salt. This makes it impossible to determine what part of the hash is the the salt vs what part of the hash is the actual hash.
Just posting this in case anyone finds it useful. Modify to suit.
Usage:
Just posting this in case anyone finds it useful. Modify to suit.
Code: Select all
<?php
/**
* Class asPasswordHash
*
* A very secure password hashing scheme using the whirlpool algorythm
* and creating an impossible to find salt without knowing the actual
* length of the password.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @package
* @author Benjamin Lewis
* @version 1.0
*/
class asPasswordHash
{
/**
* Contains the algorythm used for hashing passwords
*
* @var string
* @access private
*/
private $_algorythm = 'whirlpool';
/**
*
* @param string $algo The hashing algorythm to use
* @access public
*/
public function __construct($algo = null) {
$this->_algorythm = $algo === null ? $this->_algorythm : $algo;
}
/**
* Sets the algorythm to use when creating password hashes
*
* @var string The algorythm to use
* @return object An instance of this class
* @access public
*/
public function setAlgo($algo) {
$this->_algorythm = $algo;
return $this;
}
/**
* Gets the algorythm currently being used to create hashes
*
* @return string The current alogrythm in use
*/
public function getAlgo() {
return $this->_algorythm;
}
/**
* Creates a salted password hash with the salt embedded in the actual
* hash.
*
* @param string $password The actual password
* @param string $hash The hash to check a password against
* @return string The hashed password
*/
public function get($password, $hash = null) {
if (!in_array($this->_algorythm, hash_algos())) {
trigger_error(sprintf("%s is not an available hashing algorythm", $this->_algorythm), E_USER_ERROR);
}
/*
* If a hash is not specified, one is created by hashing a random
* 256 byte string.
*/
if ($hash === null) {
$salt = '';
for ($i = 0; $i <= 256; $i++) {
$salt .= chr(mt_rand(0, 255));
}
$hashedSalt = hash($this->_algorythm, $salt);
} else {
$hashedSalt = $hash;
}
/*
* The length of the salt to use is set by dividing the length
* of the generated hash for this algorythm by 6 and rounding
* down.
*/
$saltLen = floor(strlen($hashedSalt) / 6);
/*
* Save the length of the password for later use.
*/
$passwdLen = strlen($password);
/*
* While the length of the password is greater than the salt length,
* multiply the length of the password by the value of the last digit
* of the length divided by 10.
*
* Example:
* Password Length = 1009
* (9 / 10) = 0.9
* (1009 * 0.9) = 908.1
* New Password Length = 908.1
*/
while ($passwdLen > $saltLen) {
$passwdLen = $passwdLen * (substr($passwdLen, -1) / 10);
}
/*
* We need $passwdLen to be an absolute integer value
*/
$passwdLen = (integer) abs($passwdLen);
/*
* Subtracting the length of the password from the length of the
* salt that we will be using gives us the number of salt characters
* to use in the final hash
*/
$saltLen -= $passwdLen;
/*
* Populate the left and right salt values from the hashed salt using
* the values we calculated earlier
*/
$leftSalt = substr($hashedSalt, 0, $passwdLen);
$rightSalt = substr($hashedSalt, -$saltLen, $saltLen);
/*
* Generate the password hash
*/
$passwdHash = hash($this->_algorythm, ($leftSalt . $password . $rightSalt));
/*
* Cut off left and right portions of the hashed password. These will
* be replaced by the left and right salts.
*/
if ($saltLen == 0) {
$passwdHash = substr($passwdHash, $passwdLen);
} else {
$passwdHash = substr($passwdHash, $passwdLen, -$saltLen);
}
/*
* Return the left salt, password hash and right salt
*/
return $leftSalt . $passwdHash . $rightSalt;
}
}Code: Select all
public static function isUserPassword($password, $passwordHash) {
$pHash = new asPasswordHash();
return $passwordHash == $pHash->get($password, $passwordHash);
}
public function setPassword($password) {
/*
* Every time this method is called a new password hash will be
* generated, even if the password has not been changed. This
* check will stop the password update if it has not been changed
* by comparing the password hash with the password posted.
*/
$pHash = new asPasswordHash();
if ($this->password == $pHash->get($password, $this->password)) {
return $this;
} else {
return parent::setPassword($pHash->get($password));
}
}Code: Select all
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_name` varchar(25) NOT NULL,
`email` varchar(64) NOT NULL,
`password` varchar(128) NOT NULL,
`reg_ip_lan` int(11) DEFAULT NULL,
`reg_ip_wan` int(11) DEFAULT NULL,
`last_login` datetime DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_U_1` (`user_name`),
UNIQUE KEY `user_U_2` (`email`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;