Skip to content

Commit

Permalink
Initial import of PasswordHash code
Browse files Browse the repository at this point in the history
  • Loading branch information
reines committed Apr 6, 2011
0 parents commit bbb124f
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 0 deletions.
20 changes: 20 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Copyright (c) 2011 FluxBB (http://fluxbb.org)
Copyright (c) 2009 David Soria Parra

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# fluxbb-password
A password class for securely hashing passwords using blowfish with fallback to repeated hashing.

License: [LGPL - GNU Lesser General Public License](http://www.gnu.org/licenses/lgpl.html)
168 changes: 168 additions & 0 deletions password.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
<?php

/**
* Copyright (C) 2011 FluxBB (http://fluxbb.org)
* License: LGPL - GNU Lesser General Public License (http://www.gnu.org/licenses/lgpl.html)
*/

class PasswordHash
{
const HASH_ALGO = 'sha256';

/**
* Fetches random data from a secure source if possible -
* /dev/urandom on UNIX systems. Falls back to mt_rand()
* if no better source is available.
*
* @param string $length
* Number of bytes of random data to generate.
*
* @param bool $raw_output
* When set to TRUE, the data is returned in raw
* binary form, otherwise the returned value is a
* ($length * 2)-character hexadecimal number.
*
* @return string
* The random data.
*/
public static function random_bytes($length, $raw_output = false)
{
$data = '';

// On a UNIX system use /dev/urandom
if (is_readable('/dev/urandom'))
{
$handle = @fopen('/dev/urandom', 'rb');
if ($handle !== false)
{
$data = fread($handle, $length);
fclose($handle);
}
}

// Fall back to using md_rand() - not cryptographically secure, but available everywhere
while (strlen($data) < $length)
$data .= pack('i', mt_rand());

if (strlen($data) > $length)
$data = substr($data, 0, $length);

// If requested return the raw output
if ($raw_output)
return $data;

// Otherwise return the data as a hex string
return current(unpack('H*', $data));
}

/**
* Encodes data in base64 using the alphabet ./0-9A-Za-z.
*
* @param string $str
* The data to encode.
*
* @return string
* The encoded data, as a string.
*/
private static function base64_encode($str)
{
$from = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
$to = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';

$str = substr(base64_encode($str), 0, -2);
return strtr($str, $from, $to);
}

/**
* Repeatedly hashes the given password according to the parameters
* in our custom salt. Salt takes the form $F$<cost>$<salt> where
* the cost is a 2 digit cost parameter, and the salt is a 22 digit
* salt using the alphabet ./0-9A-Za-z.
*
* @param string $str
* The password to hash.
*
* @param string $salt
* A salt to base the hashing on.
*
* @return string
* The hashed string, including the original salt.
*/
private static function repeated_hash($str, $salt)
{
// Check if the given salt is valid or not
if (!preg_match('%\$F\$(\d{2})\$([a-zA-Z0-9\./]{22})(.*)$%', $salt, $matches))
return null;

$workload = $matches[1] + 4; // Increase the workload since the custom hash is much faster than blowfish
$salt = $matches[2];

unset ($matches);

// Hash the input depending on the workload
$repetitions = pow(2, $workload);
for ($i = 0;$i < $repetitions;$i++)
$str = hash(self::HASH_ALGO, $salt.$str, true);

// Return the salt + hash
return '$F$'.str_pad($workload, 2, '0', STR_PAD_LEFT).'$'.$salt.self::base64_encode($str);
}

/**
* Hashes the given password, using blowfish when available, with
* fallback to repeated hashing.
*
* @param string $str
* The password to hash.
*
* @param int $workload
* A cost parameter - the base 2 logarithm of the iteration count
* for the underlying algorithm. Must be in the range 04-31.
*
* @return string
* The hashed string, including the generated salt.
*/
public static function hash($str, $workload = 8)
{
// Validate the workload is within sensible bounds
if ($workload < 4)
$workload = 4;

if ($workload > 31)
$workload = 31;

// Generate a random salt and base64 encode it
$salt = self::random_bytes(16, true);
$salt = self::base64_encode($salt);

// If we have blowfish, use it
if (CRYPT_BLOWFISH === 1)
return crypt($str, '$2a$'.str_pad($workload, 2, '0', STR_PAD_LEFT).'$'.$salt);

// Fallback to repeated hashing
return self::repeated_hash($str, '$F$'.str_pad($workload, 2, '0', STR_PAD_LEFT).'$'.$salt);
}

/**
* Checks the given input against the stored hash.
*
* @param string $str
* The user input to check.
*
* @param string $hash
* The stored salt + hash.
*
* @return bool
* TRUE if the given input matches the original
* password, otherwise FALSE.
*/
public static function validate($str, $hash)
{
// First try the fall back method, then crypt
$answer = self::repeated_hash($str, $hash);
if ($answer === null)
$answer = crypt($str, $hash);

return $answer === $hash;
}
}

0 comments on commit bbb124f

Please sign in to comment.