Skip to content

Instantly share code, notes, and snippets.

@rk
Created August 10, 2009 18:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rk/165342 to your computer and use it in GitHub Desktop.
Save rk/165342 to your computer and use it in GitHub Desktop.
A variant upon crypt() and phpass that isn't interested in portability, so much as customized security for PHP 5.1.2 or higher implementations.
<?php
/*******************************************************************************
PHP Crypt 0.1
=============
Original Source: http://gist.github.com/165342
Designed for password protection. Provides some extent of customization over
crypt, bcrypt, and phpass but shares similar methods. PCrypt is not compatible
with those implementations.
Placed in the public domain by Robert Kosek on August 10th, 2009. When you edit
this, please change the version number.
Notes:
------
SHA512 / WHIRLPOOL @ 500 rounds average 0.03 seconds to hash a given password on
a system with: 4gb RAM & an Intel Core2 Duo @ 2GHz
This limits attacks to 33/per-second. Increasing rounds to 5,000 involves a
0.124 average time and potentially 5 tests per second.
Requirements:
-------------
* PHP 5.1.2 or higher, w/ HASH message digest framework
*******************************************************************************/
abstract class pcrypt {
// Used for the rotating salt value; should still be a strong algorithm, but
// not sha512 or whirlpool.
static $salt_algorithm = 'gost';
// Used for the password _only_, should be the strongest algorithm available
// like sha512 or whirlpool.
static $hash_algorithm = 'sha512';
// When the number of rounds is a valid multiple of this number ($i % $n == 0)
// the below callback is called; the callback must take 1 number and return
// something castable into a string.
static $multiple_count = 13;
static $multiple_callback = 'sqrt';
// Hashes a password with a random salt value, returns in the format:
// salt$base64-password-hash
static public function hash_password($password, $rounds = 4000) {
return self::crypt($password, hash(self::$salt_algorithm, uniqid('', true)),
$rounds);
}
// Verifies a plaintext password against the stored hash previously generated,
// returns boolean.
static public function verify_password($password, $stored, $rounds = 4000) {
$parts = explode('$', $stored);
$salt = $parts[0];
$test = self::crypt($password, $salt, $rounds);
return strcmp($stored, $test) === 0;
}
static private function crypt($password, $beginning_salt, $rounds) {
$func = self::$multiple_callback;
$salt = $beginning_salt;
$result = hash_hmac(self::$hash_algorithm, $password, $salt, true);
do {
if($rounds % self::$multiple_count === 0) {
$salt = hash(self::$salt_algorithm, $func($rounds) . $salt, true);
} else {
$salt = hash(self::$salt_algorithm, $salt . $beginning_salt, true);
}
$result = hash_hmac(self::$hash_algorithm, $result, $salt, true);
} while($rounds-- > 0);
return $beginning_salt . '$' . base64_encode($result);
}
}
if(!defined('ENVIRONMENT') || ENVIRONMENT !== 'production') {
echo '<pre style="font: 10pt consolas;">';
echo "<u>testing hashing (default)</u>\n";
echo "testing password: <b>admin</b>\n";
$start = microtime(true);
$hash = pcrypt::hash_password('admin');
$time = round(microtime(true) - $start, 7);
echo "resulting hash: <b>$hash</b>\n";
echo "elapsed time: <b>$time</b>\n";
echo 'resulting length: <b>', strlen($hash), "</b>\n";
echo 'verifies: <b>';
echo pcrypt::verify_password('admin', $hash) ? 'true' : 'false', "</b>\n";
// test with customized & pseudo-random methods...
function __my_really_special_func($i) {
static $base_seed;
if(empty($base_seed)) { $base_seed = time() + log($i, 8); }
mt_srand($base_seed + $i);
return mt_rand($i, mt_getrandmax());
}
echo "\n<u>testing hashing (custom)</u>\n";
echo "testing password: <b>admin</b>\n";
pcrypt::$salt_algorithm = 'tiger192,4';
pcrypt::$multiple_callback = '__my_really_special_func';
$hash = pcrypt::hash_password('admin');
echo "resulting hash: <b>$hash</b>\n";
echo 'resulting length: <b>', strlen($hash), "</b>\n";
echo 'verifies: <b>';
echo pcrypt::verify_password('admin', $hash) ? 'true' : 'false', "</b>\n";
echo '</pre>';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment