Skip to content

Instantly share code, notes, and snippets.

@rk
Last active January 4, 2018 13:15
Show Gist options
  • Save rk/1070401 to your computer and use it in GitHub Desktop.
Save rk/1070401 to your computer and use it in GitHub Desktop.
Simple bcrypt object to wrap crypt() with.
<?php
// Originally by Andrew Moore
// Src: http://stackoverflow.com/questions/4795385/how-do-you-use-bcrypt-for-hashing-passwords-in-php/6337021#6337021
//
// Heavily modified by Robert Kosek, from data at php.net/crypt
class Bcrypt {
private $rounds;
private $prefix;
private $salt_prefix;
public function __construct($prefix = '', $rounds = 12) {
if(CRYPT_BLOWFISH != 1) {
throw new Exception("bcrypt not supported in this installation. See http://php.net/crypt");
}
$this->rounds = $rounds;
$this->prefix = $prefix;
// Determine if this version of PHP has the Bcrypt fix
$this->salt_prefix = version_compare(PHP_VERSION, '5.3.7') >= 0 ? '$2y' : '$2a';
}
public function hash($input) {
$hash = crypt($input, $this->getSalt());
if(strlen($hash) > 13)
return $hash;
return false;
}
public function verify($input, $existingHash) {
$hash = crypt($input, $existingHash);
return $hash === $existingHash;
}
private function getSalt() {
// the base64 function uses +'s and ending ='s; translate the first, and cut out the latter
return sprintf('%s$%02d$%s', $this->salt_prefix, $this->rounds, substr(strtr(base64_encode($this->getBytes()), '+', '.'), 0, 22));
}
private function getBytes() {
$bytes = '';
if(function_exists('openssl_random_pseudo_bytes') &&
(strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')) { // OpenSSL slow on Win
$bytes = openssl_random_pseudo_bytes(18);
}
if($bytes === '' && is_readable('/dev/urandom') &&
($hRand = @fopen('/dev/urandom', 'rb')) !== FALSE) {
$bytes = fread($hRand, 18);
fclose($hRand);
}
if($bytes === '') {
$key = uniqid($this->prefix, true);
// 12 rounds of HMAC must be reproduced / created verbatim, no known shortcuts.
// Changed the hash algorithm from salsa20, which has been removed from PHP 5.4.
for($i = 0; $i < 12; $i++) {
$bytes = hash_hmac('snefru256', microtime() . $bytes, $key, true);
usleep(10);
}
}
return $bytes;
}
}
?>
@rk
Copy link
Author

rk commented Aug 18, 2011

My previous response was discussing the derivation of the final hash.

What you're saying is correct though, only 1 iteration is strictly required because they will know the first 11 bytes of the result anyway. And that's all that matters for bcrypt.

@rk
Copy link
Author

rk commented Jun 20, 2012

I've updated this gist by switching from salsa20 to snefru256 as the fallback method for generating random bytes.

@rk
Copy link
Author

rk commented May 30, 2013

I've updated the gist to check for the PHP 5.3.7 or higher, and use the fixed bcrypt implementation.

@Topener
Copy link

Topener commented Aug 6, 2014

You should probably remove the last line of this script. It should not include the closing tag of php

@gplcart
Copy link

gplcart commented Jan 4, 2018

unpack() returns an array, the part mt_srand(unpack('N1', uniqid())); is wrong

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment