22 if(function_exists(
'hash_hmac') && in_array(
'sha256', hash_algos()))
24 $retval[
'pbkdf2'] =
'pbkdf2';
26 if(version_compare(PHP_VERSION,
'5.3.7',
'>=') && defined(
'CRYPT_BLOWFISH'))
28 $retval[
'bcrypt'] =
'bcrypt';
30 $retval[
'md5'] =
'md5';
50 if(function_exists(
'getModel'))
52 $config =
getModel(
'member')->getMemberConfig();
53 $algorithm = $config->password_hashing_algorithm;
54 if(strval($algorithm) ===
'')
72 if(function_exists(
'getModel'))
74 $config =
getModel(
'member')->getMemberConfig();
75 $work_factor = $config->password_hashing_work_factor;
76 if(!$work_factor || $work_factor < 4 || $work_factor > 31)
96 if($algorithm === null)
105 $password = trim($password);
110 return md5($password);
115 $hash = base64_encode($this->
pbkdf2($password, $salt,
'sha256', $iterations, 24));
116 return 'sha256:'.sprintf(
'%07d', $iterations).
':'.$salt.
':'.$hash;
119 return $this->
bcrypt($password);
135 if($algorithm === null)
140 $password = trim($password);
145 return md5($password) === $hash || md5(sha1(md5($password))) === $hash;
147 case 'mysql_old_password':
151 case 'mysql_password':
152 return $hash[0] ===
'*' && substr($hash, 1) === strtoupper(sha1(sha1($password,
true)));
155 $hash = explode(
':', $hash);
156 $hash[3] = base64_decode($hash[3]);
157 $hash_to_compare = $this->
pbkdf2($password, $hash[2], $hash[0], intval($hash[1], 10), strlen($hash[3]));
161 $hash_to_compare = $this->
bcrypt($password, $hash);
176 if(preg_match(
'/^\$2[axy]\$([0-9]{2})\$/', $hash, $matches))
180 elseif(preg_match(
'/^sha[0-9]+:([0-9]+):/', $hash, $matches))
184 elseif(strlen($hash) === 32 && ctype_xdigit($hash))
188 elseif(strlen($hash) === 16 && ctype_xdigit($hash))
190 return 'mysql_old_password';
192 elseif(strlen($hash) === 41 && $hash[0] ===
'*')
194 return 'mysql_password';
209 if(preg_match(
'/^\$2[axy]\$([0-9]{2})\$/', $hash, $matches))
211 return intval($matches[1], 10);
213 elseif(preg_match(
'/^sha[0-9]+:([0-9]+):/', $hash, $matches))
215 return max(0, round(log($matches[1], 2)) - 5);
235 $entropy_required_bytes = ceil($length / 2);
239 $entropy_required_bytes = ceil($length * 3 / 4);
242 $entropy_required_bytes = $length;
246 $entropy_capped_bytes = min(32, $entropy_required_bytes);
249 $is_windows = (defined(
'PHP_OS') && strtoupper(substr(PHP_OS, 0, 3)) ===
'WIN');
250 if(function_exists(
'openssl_random_pseudo_bytes') && (!$is_windows || version_compare(PHP_VERSION,
'5.4',
'>=')))
252 $entropy = openssl_random_pseudo_bytes($entropy_capped_bytes);
254 elseif(function_exists(
'mcrypt_create_iv') && (!$is_windows || version_compare(PHP_VERSION,
'5.3.7',
'>=')))
256 $entropy = mcrypt_create_iv($entropy_capped_bytes, MCRYPT_DEV_URANDOM);
258 elseif(function_exists(
'mcrypt_create_iv') && $is_windows)
260 $entropy = mcrypt_create_iv($entropy_capped_bytes, MCRYPT_RAND);
262 elseif(!$is_windows && @is_readable(
'/dev/urandom'))
264 $fp = fopen(
'/dev/urandom',
'rb');
265 $entropy = fread($fp, $entropy_capped_bytes);
271 for($i = 0; $i < $entropy_capped_bytes; $i += 2)
273 $entropy .= pack(
'S', rand(0, 65536) ^ mt_rand(0, 65535));
279 for($i = 0; $i < $entropy_required_bytes; $i += 32)
281 $output .= hash(
'sha256', $entropy . $i . rand(),
true);
288 return substr(bin2hex(
$output), 0, $length);
290 return substr(
$output, 0, $length);
293 for($i = 0; $i < $length; $i++)
295 $salt .= chr(33 + (crc32(sha1($i .
$output)) % 94));
300 $salt = substr(base64_encode(
$output), 0, $length);
301 $replacements = chr(rand(65, 90)) . chr(rand(97, 122)) . rand(0, 9);
302 return strtr($salt,
'+/=', $replacements);
316 $source = strtr($source,
'iIoOjl10/',
'@#$%&*-!?');
317 $source_length = strlen($source);
318 for($i = 0; $i < $source_length - $length; $i++)
320 $candidate = substr($source, $i, $length);
321 if(preg_match(
'/[a-z]/', $candidate) && preg_match(
'/[A-Z]/', $candidate) &&
322 preg_match(
'/[0-9]/', $candidate) && preg_match(
'/[^a-zA-Z0-9]/', $candidate))
337 $key = self::getSecretKey();
338 $salt = self::createSecureSalt(8,
'alnum');
339 $hash = substr(base64_encode(hash_hmac(
'sha256', hash_hmac(
'sha256', $string, $salt), $key,
true)), 0, 32);
340 return $salt . strtr($hash,
'+/',
'-_');
351 if(strlen($signature) !== 40)
356 $key = self::getSecretKey();
357 $salt = substr($signature, 0, 8);
358 $hash = substr(base64_encode(hash_hmac(
'sha256', hash_hmac(
'sha256', $string, $salt), $key,
true)), 0, 32);
359 return self::strcmpConstantTime(substr($signature, 8), strtr($hash,
'+/',
'-_'));
369 $db_info = Context::getDbInfo();
370 if(!isset($db_info->secret_key))
372 $db_info->secret_key = self::createSecureSalt(48,
'alnum');
376 return $db_info->secret_key;
388 public function pbkdf2($password, $salt, $algorithm =
'sha256', $iterations = 8192, $length = 24)
390 if(function_exists(
'hash_pbkdf2'))
392 return hash_pbkdf2($algorithm, $password, $salt, $iterations, $length,
true);
397 $block_count = ceil($length / strlen(hash($algorithm,
'',
true)));
398 for($i = 1; $i <= $block_count; $i++)
400 $last = $salt . pack(
'N', $i);
401 $last = $xorsum = hash_hmac($algorithm, $last, $password,
true);
402 for($j = 1; $j < $iterations; $j++)
404 $xorsum ^= ($last = hash_hmac($algorithm, $last, $password,
true));
408 return substr(
$output, 0, $length);
418 public function bcrypt($password, $salt = null)
424 return crypt($password, $salt);
435 $diff = strlen($a) ^ strlen($b);
436 $maxlen = min(strlen($a), strlen($b));
437 for($i = 0; $i < $maxlen; $i++)
439 $diff |= ord($a[$i]) ^ ord($b[$i]);
getController($module_name)
static getSecretKey()
Get the secret key for this site.
bcrypt($password, $salt=null)
Generate the bcrypt hash of a string using a salt.
static checkSignature($string, $signature)
Check whether a signature is valid.
createTemporaryPassword($length=16)
Generate a temporary password using the secure salt generator.
getCurrentlySelectedAlgorithm()
Return the currently selected hashing algorithm.
checkAlgorithm($hash)
Check the algorithm used to create a hash.
getSupportedAlgorithms()
Return the list of hashing algorithms supported by this server.
createSecureSalt($length, $format= 'hex')
Generate a cryptographically secure random string to use as a salt.
getInstance($db_type=NULL)
pbkdf2($password, $salt, $algorithm= 'sha256', $iterations=8192, $length=24)
Generate the PBKDF2 hash of a string using a salt.
strcmpConstantTime($a, $b)
Compare two strings in constant time.
checkWorkFactor($hash)
Check the work factor of a hash.
static createSignature($string)
Create a digital signature to verify the authenticity of a string.
getWorkFactor()
Return the currently configured work factor for bcrypt and other adjustable algorithms.
createHash($password, $algorithm=null)
Create a hash using the specified algorithm.
getBestAlgorithm()
Return the best hashing algorithm supported by this server.
checkPassword($password, $hash, $algorithm=null)
Check if a password matches a hash.