Skip to content

Instantly share code, notes, and snippets.

@jkmickelson
Created September 6, 2012 20:40
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 jkmickelson/3660219 to your computer and use it in GitHub Desktop.
Save jkmickelson/3660219 to your computer and use it in GitHub Desktop.
PHP 5.3+ bCryptic class (bcrypt, php, drupal 7)
/**
* bCryptic by Jonathan K Mickelson <jkmickelson.com>
* This code is released in the public domain.
* (Enhanced from Bcrypt original by Marco Arment <me@marco.org>)
*
* THERE IS ABSOLUTELY NO WARRANTY.
*
* Usage example:
*
* // In a registration or password-change form:
* $hash_for_user = bCryptic::hash($_POST['password']);
*
* // In a login form:
* $is_correct = bCryptic::check($_POST['password'], $stored_hash_for_user);
*
* // In a login form when migrating entries gradually from a legacy SHA-1 hash:
* $is_correct = bCryptic::check(
* $_POST['password'],
* $stored_hash_for_user,
* function($password, $hash) { return $hash == sha1($password); }
* );
* if ($is_correct && bCryptic::legacy_hash_status($stored_hash_for_user)!==0) {
* $user->store_new_hash(bCryptic::hash($_POST['password']));
* }
*
*/
class bCryptic {
// the bcrypt algorithm declares a work factor range of 4-31.
// (one may change the minimum and maximum factors to customize
// a smaller range within those values)
const MINIMUM_WORK_FACTOR = 4;
const MAXIMUM_WORK_FACTOR = 31;
const DEFAULT_WORK_FACTOR = 8; // sysadmin/coder selectable
public static function hash($password, $work_factor = 0) {
if (version_compare(PHP_VERSION, '5.3') < 0) {
throw new Exception('bCryptic requires PHP 5.3 or above');
}
if (! function_exists('openssl_random_pseudo_bytes')) {
throw new Exception('bCryptic requires openssl PHP extension');
}
$work_factor = self::get_work_boundary($work_factor);
$salt =
'$2a$' . str_pad($work_factor, 2, '0', STR_PAD_LEFT) . '$'
. substr(
strtr(base64_encode(openssl_random_pseudo_bytes(16)), '+', '.'),
0, 22
)
;
return crypt($password, $salt);
}
public static function get_work_boundary($work_factor = 0) {
if (!$work_factor) {
$work_factor = self::DEFAULT_WORK_FACTOR;
}
if ($work_factor < self::MINIMUM_WORK_FACTOR) {
$work_factor = self::MINIMUM_WORK_FACTOR;
}
else if ($work_factor > self::MAXIMUM_WORK_FACTOR) {
$work_factor = self::MAXIMUM_WORK_FACTOR;
}
return $work_factor;
}
public static function check($password, $stored_hash, $legacy_handler = NULL) {
if (version_compare(PHP_VERSION, '5.3') < 0) {
throw new Exception('bCryptic requires PHP 5.3 or above');
}
if (self::legacy_hash_status($stored_hash) > 0) {
if ($legacy_handler) {
return call_user_func($legacy_handler, $password, $stored_hash);
}
else {
throw new Exception('Unsupported hash format');
}
}
return (crypt($password, $stored_hash) == $stored_hash);
}
public static function legacy_hash_status($hash) {
if (substr($hash, 0, 4) != '$2a$') {
return 1; // not bcrypt
}
$stored_work_factor = substr($hash, 4, 2) * 1;
if (!$stored_work_factor) {
// this should not occur unless hash is corrupted
return 2; // not valid bcrypt
}
if ($stored_work_factor != self::get_work_boundary($stored_work_factor)) {
return -1; // signal that the bcrypt work_factor is currently out-of-bounds.
}
return 0;
}
}
/**
* A contribution for Drupal 7 as bcryptic_password.inc resides here:
* http://drupal.org/node/29706 (entry #287, file attachment: bcryptic_password.inc_.txt)
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment