Created
June 29, 2011 17:01
Revisions
-
marcoarment revised this gist
Jun 29, 2011 . 1 changed file with 12 additions and 49 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -48,8 +48,19 @@ public static function hash($password, $work_factor = 0) { if (version_compare(PHP_VERSION, '5.3') < 0) throw new Exception('Bcrypt requires PHP 5.3 or above'); if (! function_exists('openssl_random_pseudo_bytes')) { throw new Exception('Bcrypt requires openssl PHP extension'); } if ($work_factor < 4 || $work_factor > 31) $work_factor = self::DEFAULT_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 check($password, $stored_hash, $legacy_handler = NULL) @@ -65,52 +76,4 @@ public static function check($password, $stored_hash, $legacy_handler = NULL) } public static function is_legacy_hash($hash) { return substr($hash, 0, 4) != '$2a$'; } } -
marcoarment revised this gist
Jun 29, 2011 . 1 changed file with 20 additions and 33 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -14,6 +14,8 @@ - Removed support for versions of PHP prior to 5.3 - Removed "portable" hash support, since it was mostly for those earlier versions of PHP - Replaced manual randomness generation with openssl_random_pseudo_bytes() (thanks, Marc Hedlund) - Added convenience features for migrations from legacy password hashes - Refactored to a more appropriate and modern structure and style for PHP 5 @@ -26,9 +28,15 @@ $is_correct = Bcrypt::check($_POST['password'], $stored_hash_for_user); // In a login form when migrating entries gradually from a legacy SHA-1 hash: $is_correct = Bcrypt::check( $_POST['password'], $stored_hash_for_user, function($password, $hash) { return $hash == sha1($password); } ); if ($is_correct && Bcrypt::is_legacy_hash($stored_hash_for_user)) { $user->store_new_hash(Bcrypt::hash($_POST['password'])); } */ @@ -41,8 +49,7 @@ public static function hash($password, $work_factor = 0) if (version_compare(PHP_VERSION, '5.3') < 0) throw new Exception('Bcrypt requires PHP 5.3 or above'); if ($work_factor < 4 || $work_factor > 31) $work_factor = self::DEFAULT_WORK_FACTOR; return crypt($password, self::generate_blowfish_salt($work_factor)); } public static function check($password, $stored_hash, $legacy_handler = NULL) @@ -59,33 +66,13 @@ public static function check($password, $stored_hash, $legacy_handler = NULL) public static function is_legacy_hash($hash) { return substr($hash, 0, 4) != '$2a$'; } private static function generate_blowfish_salt($work_factor) { if (! function_exists('openssl_random_pseudo_bytes')) { throw new Exception('Bcrypt requires openssl PHP extension'); } $random = openssl_random_pseudo_bytes(16); /* This function has been essentially copied verbatim from phpass 0.3. @@ -105,20 +92,20 @@ private static function generate_blowfish_salt($input, $work_factor) $i = 0; do { $c1 = ord($random[$i++]); $output .= $itoa64[$c1 >> 2]; $c1 = ($c1 & 0x03) << 4; if ($i >= 16) { $output .= $itoa64[$c1]; break; } $c2 = ord($random[$i++]); $c1 |= $c2 >> 4; $output .= $itoa64[$c1]; $c1 = ($c2 & 0x0f) << 2; $c2 = ord($random[$i++]); $c1 |= $c2 >> 6; $output .= $itoa64[$c1]; $output .= $itoa64[$c2 & 0x3f]; -
marcoarment revised this gist
Jun 29, 2011 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -87,6 +87,8 @@ public static function random_bytes($count) private static function generate_blowfish_salt($input, $work_factor) { /* This function has been essentially copied verbatim from phpass 0.3. The last character in our encoded string will only represent 2 bits. While two known implementations of bcrypt will happily accept and correct a salt string which -
marcoarment created this gist
Jun 29, 2011 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,127 @@ <?php /* Adaptation by Marco Arment <[email protected]>. Adapted from Portable PHP Password Hashing Framework (phpass) version 0.3: http://www.openwall.com/phpass/ phpass was by Solar Designer <solar at openwall.com> in 2004-2006 and is in the public domain. This adaptation is also in the public domain. THERE IS ABSOLUTELY NO WARRANTY. Major differences from phpass in this adaptation: - Removed support for versions of PHP prior to 5.3 - Removed "portable" hash support, since it was mostly for those earlier versions of PHP - Added convenience features for migrations from legacy password hashes - Refactored to a more appropriate and modern structure and style for PHP 5 Usage example: // In a registration or password-change form: $hash_for_user = Bcrypt::hash($_POST['password']); // In a login form: $is_correct = Bcrypt::check($_POST['password'], $stored_hash_for_user); // In a login form when migrating entries gradually from a legacy SHA-1 hash: $is_correct = Bcrypt::check($_POST['password'], function($password, $hash) { return $hash == sha1($password); }); */ class Bcrypt { const DEFAULT_WORK_FACTOR = 8; public static function hash($password, $work_factor = 0) { if (version_compare(PHP_VERSION, '5.3') < 0) throw new Exception('Bcrypt requires PHP 5.3 or above'); if ($work_factor < 4 || $work_factor > 31) $work_factor = self::DEFAULT_WORK_FACTOR; $random = self::random_bytes(16); return crypt($password, self::generate_blowfish_salt($random, $work_factor)); } public static function check($password, $stored_hash, $legacy_handler = NULL) { if (version_compare(PHP_VERSION, '5.3') < 0) throw new Exception('Bcrypt requires PHP 5.3 or above'); if (self::is_legacy_hash($stored_hash)) { 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 is_legacy_hash($hash) { return substr($hash, 0, 4) != '$2a$'; } public static function random_bytes($count) { $output = ''; try { if (is_readable('/dev/urandom') && ($fh = fopen('/dev/urandom', 'rb')) ) { $output = fread($fh, $count); fclose($fh); } } catch (Exception $e) { } if (strlen($output) < $count) { $random_state = microtime(); if (function_exists('getmypid')) $random_state .= getmypid(); $output = ''; for ($i = 0; $i < $count; $i += 16) { $random_state = md5(microtime() . $random_state, true); $output .= $random_state; } $output = substr($output, 0, $count); } return $output; } private static function generate_blowfish_salt($input, $work_factor) { /* The last character in our encoded string will only represent 2 bits. While two known implementations of bcrypt will happily accept and correct a salt string which has the 4 unused bits set to non-zero, we do not want to take chances and we also do not want to waste an additional byte of entropy. */ $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; $output = '$2a$'; $output .= chr(ord('0') + $work_factor / 10); $output .= chr(ord('0') + $work_factor % 10); $output .= '$'; $i = 0; do { $c1 = ord($input[$i++]); $output .= $itoa64[$c1 >> 2]; $c1 = ($c1 & 0x03) << 4; if ($i >= 16) { $output .= $itoa64[$c1]; break; } $c2 = ord($input[$i++]); $c1 |= $c2 >> 4; $output .= $itoa64[$c1]; $c1 = ($c2 & 0x0f) << 2; $c2 = ord($input[$i++]); $c1 |= $c2 >> 6; $output .= $itoa64[$c1]; $output .= $itoa64[$c2 & 0x3f]; } while (1); return $output; } }