mirror of
https://github.com/Lastorder-DC/rhymix.git
synced 2026-01-05 09:41:40 +09:00
Implement new Password class and related unit tests
This commit is contained in:
parent
90dcc4a2e8
commit
647bc7c112
4 changed files with 579 additions and 9 deletions
443
common/framework/password.php
Normal file
443
common/framework/password.php
Normal file
|
|
@ -0,0 +1,443 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework;
|
||||
|
||||
/**
|
||||
* The password class.
|
||||
*/
|
||||
class Password
|
||||
{
|
||||
/**
|
||||
* Regular expressions to detect various hashing algorithms.
|
||||
*/
|
||||
protected static $_algorithm_callbacks = array();
|
||||
protected static $_algorithm_signatures = array(
|
||||
'bcrypt' => '/^\$2[a-z]\$[0-9]{2}\$/',
|
||||
'pbkdf2' => '/^[a-z0-9]+:[0-9]+:/',
|
||||
'md5' => '/^[0-9a-f]{32}$/',
|
||||
'md5,sha1,md5' => '/^[0-9a-f]{32}$/',
|
||||
'sha1' => '/^[0-9a-f]{40}$/',
|
||||
'sha256' => '/^[0-9a-f]{64}$/',
|
||||
'sha384' => '/^[0-9a-f]{96}$/',
|
||||
'sha512' => '/^[0-9a-f]{128}$/',
|
||||
'ripemd160' => '/^[0-9a-f]{40}$/',
|
||||
'whirlpool' => '/^[0-9a-f]{128}$/',
|
||||
'mssql_pwdencrypt' => '/^0x0100[0-9A-F]{48}$/',
|
||||
'mysql_old_password' => '/^[0-9a-f]{16}$/',
|
||||
'mysql_new_password' => '/^\*[0-9A-F]{40}$/',
|
||||
'portable' => '/^\$P\$/',
|
||||
'drupal' => '/^\$S\$/',
|
||||
'joomla' => '/^[0-9a-f]{32}:[0-9a-zA-Z\.\+\/\=]{32}$/',
|
||||
'kimsqrb' => '/\$[1-4]\$[0-9]{14}$/',
|
||||
'crypt' => '/^([0-9a-zA-Z\.\/]{13}$|_[0-9a-zA-Z\.\/]{19}$|\$[156]\$)/',
|
||||
);
|
||||
|
||||
/**
|
||||
* Add a custom algorithm.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $signature
|
||||
* @param callable $callback
|
||||
* @return void
|
||||
*/
|
||||
public static function addAlgorithm($name, $signature, $callback)
|
||||
{
|
||||
self::$_algorithm_signatures[$name] = $signature;
|
||||
self::$_algorithm_callbacks[$name] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given sequence of algorithms is valid.
|
||||
*
|
||||
* @param array|string $algos
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidAlgorithm($algos)
|
||||
{
|
||||
$hash_algos = hash_algos();
|
||||
$algos = is_array($algos) ? $algos : explode(',', $algos);
|
||||
foreach ($algos as $algo)
|
||||
{
|
||||
if (array_key_exists($algo, self::$_algorithm_signatures))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (in_array($algo, $hash_algos))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of hashing algorithms supported by this server.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getSupportedAlgorithms()
|
||||
{
|
||||
$retval = array();
|
||||
if (version_compare(PHP_VERSION, '5.3.7', '>=') && defined('\CRYPT_BLOWFISH'))
|
||||
{
|
||||
$retval['bcrypt'] = 'bcrypt';
|
||||
}
|
||||
if (function_exists('hash_hmac') && in_array('sha512', hash_algos()))
|
||||
{
|
||||
$retval['pbkdf2'] = 'pbkdf2';
|
||||
}
|
||||
$retval['sha512'] = 'sha512';
|
||||
$retval['sha256'] = 'sha256';
|
||||
$retval['sha1'] = 'sha1';
|
||||
$retval['md5'] = 'md5';
|
||||
return $retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the best hashing algorithm supported by this server.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getBestSupportedAlgorithm()
|
||||
{
|
||||
$algos = self::getSupportedAlgorithms();
|
||||
return key($algos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently selected hashing algorithm.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getSelectedAlgorithm()
|
||||
{
|
||||
if (function_exists('getModel'))
|
||||
{
|
||||
$config = getModel('member')->getMemberConfig();
|
||||
$algorithm = $config->password_hashing_algorithm;
|
||||
if (strval($algorithm) === '')
|
||||
{
|
||||
$algorithm = 'md5';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$algorithm = 'md5';
|
||||
}
|
||||
return $algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently configured work factor for bcrypt and other adjustable algorithms.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getWorkFactor()
|
||||
{
|
||||
if (function_exists('getModel'))
|
||||
{
|
||||
$config = getModel('member')->getMemberConfig();
|
||||
$work_factor = $config->password_hashing_work_factor;
|
||||
if (!$work_factor || $work_factor < 4 || $work_factor > 31)
|
||||
{
|
||||
$work_factor = 9;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$work_factor = 9;
|
||||
}
|
||||
|
||||
return $work_factor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash a password.
|
||||
*
|
||||
* To use multiple algorithms in series, provide them as an array.
|
||||
* Salted algorithms such as bcrypt, pbkdf2, or portable must be used last.
|
||||
* On error, false will be returned.
|
||||
*
|
||||
* @param string $password
|
||||
* @param string|array $algos
|
||||
* @param string $salt (optional)
|
||||
* @return string|false
|
||||
*/
|
||||
public static function hashPassword($password, $algos, $salt = null)
|
||||
{
|
||||
// Initialize the chain of hashes.
|
||||
$algos = array_map('strtolower', array_map('trim', is_array($algos) ? $algos : explode(',', $algos)));
|
||||
$hashchain = preg_replace('/\\s+/', ' ', trim($password));
|
||||
|
||||
// Apply the given algorithms one by one.
|
||||
foreach ($algos as $algo)
|
||||
{
|
||||
switch ($algo)
|
||||
{
|
||||
// bcrypt (must be used last)
|
||||
case 'bcrypt':
|
||||
$hashchain = self::bcrypt($hashchain, $salt, self::getWorkFactor());
|
||||
if ($hashchain[0] === '*') return false;
|
||||
return $hashchain;
|
||||
|
||||
// PBKDF2 (must be used last)
|
||||
case 'pbkdf2':
|
||||
if ($salt === null)
|
||||
{
|
||||
$salt = Security::getRandom(12, 'alnum');
|
||||
$hash_algorithm = 'sha512';
|
||||
$iterations = pow(2, self::getWorkFactor() + 5);
|
||||
$key_length = 24;
|
||||
}
|
||||
else
|
||||
{
|
||||
$parts = explode(':', $salt);
|
||||
$salt = $parts[2];
|
||||
$hash_algorithm = $parts[0];
|
||||
$iterations = $parts[1];
|
||||
$key_length = strlen(base64_decode($parts[3]));
|
||||
}
|
||||
return self::pbkdf2($hashchain, $salt, $hash_algorithm, $iterations, $key_length);
|
||||
|
||||
// phpass portable algorithm (must be used last)
|
||||
case 'portable':
|
||||
$phpass = new \Hautelook\Phpass\PasswordHash(self::getWorkFactor(), true);
|
||||
if ($salt === null)
|
||||
{
|
||||
$hashchain = $phpass->HashPassword($hashchain);
|
||||
return $hashchain;
|
||||
}
|
||||
else
|
||||
{
|
||||
$match = $phpass->CheckPassword($hashchain, $salt);
|
||||
return $match ? $salt : false;
|
||||
}
|
||||
|
||||
// Drupal's SHA-512 based algorithm (must be used last)
|
||||
case 'drupal':
|
||||
$hashchain = \VendorPass::drupal($password, $salt);
|
||||
return $hashchain;
|
||||
|
||||
// Joomla's MD5 based algorithm (must be used last)
|
||||
case 'joomla':
|
||||
$hashchain = \VendorPass::joomla($password, $salt);
|
||||
return $hashchain;
|
||||
|
||||
// KimsQ Rb algorithms (must be used last)
|
||||
case 'kimsqrb':
|
||||
$hashchain = \VendorPass::kimsqrb($password, $salt);
|
||||
return $hashchain;
|
||||
|
||||
// crypt() function (must be used last)
|
||||
case 'crypt':
|
||||
if ($salt === null) $salt = Security::getRandom(2, 'alnum');
|
||||
$hashchain = crypt($hashchain, $salt);
|
||||
return $hashchain;
|
||||
|
||||
// MS SQL's PWDENCRYPT() function (must be used last)
|
||||
case 'mssql_pwdencrypt':
|
||||
$hashchain = \VendorPass::mssql_pwdencrypt($hashchain, $salt);
|
||||
return $hashchain;
|
||||
|
||||
// MySQL's old PASSWORD() function.
|
||||
case 'mysql_old_password':
|
||||
$hashchain = \VendorPass::mysql_old_password($hashchain);
|
||||
break;
|
||||
|
||||
// MySQL's new PASSWORD() function.
|
||||
case 'mysql_new_password':
|
||||
$hashchain = \VendorPass::mysql_new_password($hashchain);
|
||||
break;
|
||||
|
||||
// A dummy algorithm that does nothing.
|
||||
case 'null':
|
||||
break;
|
||||
|
||||
// All other algorithms will be passed to hash() or treated as a function name.
|
||||
default:
|
||||
if (isset(self::$_algorithm_callbacks[$algo]))
|
||||
{
|
||||
$callback = self::$_algorithm_callbacks[$algo];
|
||||
$hashchain = $callback($hashchain, $salt);
|
||||
}
|
||||
elseif (in_array($algo, hash_algos()))
|
||||
{
|
||||
$hashchain = hash($algo, $hashchain);
|
||||
}
|
||||
elseif (function_exists($algo))
|
||||
{
|
||||
$hashchain = $algo($hashchain, $salt);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $hashchain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a password against a hash.
|
||||
*
|
||||
* This method returns true if the password is correct, and false otherwise.
|
||||
* If the algorithm is not specified, it will be guessed from the format of the hash.
|
||||
*
|
||||
* @param string $password
|
||||
* @param string $hash
|
||||
* @param array|string $algos
|
||||
* @return bool
|
||||
*/
|
||||
public static function checkPassword($password, $hash, $algos = null)
|
||||
{
|
||||
if (!$algos)
|
||||
{
|
||||
$algos = self::checkAlgorithm($hash);
|
||||
}
|
||||
if (!is_array($algos))
|
||||
{
|
||||
$algos = explode(',', $algos);
|
||||
}
|
||||
|
||||
return Security::compareStrings($hash, self::hashPassword($password, $algos, $hash));
|
||||
}
|
||||
|
||||
/**
|
||||
* Guess which algorithm(s) were used to generate the given hash.
|
||||
*
|
||||
* If there are multiple possibilities, all of them will be returned in an array.
|
||||
*
|
||||
* @param string $hash
|
||||
* @return array
|
||||
*/
|
||||
public static function checkAlgorithm($hash)
|
||||
{
|
||||
$candidates = array();
|
||||
foreach (self::$_algorithm_signatures as $name => $signature)
|
||||
{
|
||||
if (preg_match($signature, $hash)) $candidates[] = $name;
|
||||
}
|
||||
return $candidates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the work factor of a hash.
|
||||
*
|
||||
* @param string $hash
|
||||
* @return int
|
||||
*/
|
||||
public static function checkWorkFactor($hash)
|
||||
{
|
||||
if(preg_match('/^\$2[axy]\$([0-9]{2})\$/', $hash, $matches))
|
||||
{
|
||||
return intval($matches[1], 10);
|
||||
}
|
||||
elseif(preg_match('/^sha[0-9]+:([0-9]+):/', $hash, $matches))
|
||||
{
|
||||
return max(0, round(log($matches[1], 2)) - 5);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the bcrypt hash of a string.
|
||||
*
|
||||
* @param string $password
|
||||
* @param string $salt (optional)
|
||||
* @param int $work_factor (optional)
|
||||
* @return string
|
||||
*/
|
||||
public static function bcrypt($password, $salt = null, $work_factor = 10)
|
||||
{
|
||||
if ($salt === null)
|
||||
{
|
||||
$salt = '$2y$' . sprintf('%02d', $work_factor) . '$' . Security::getRandom(22, 'alnum');
|
||||
}
|
||||
|
||||
return crypt($password, $salt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the PBKDF2 hash of a string.
|
||||
*
|
||||
* @param string $password
|
||||
* @param string $salt (optional)
|
||||
* @param string $algorithm (optional)
|
||||
* @param int $iterations (optional)
|
||||
* @param int $length (optional)
|
||||
* @return string
|
||||
*/
|
||||
public static function pbkdf2($password, $salt = null, $algorithm = 'sha512', $iterations = 16384, $length = 24)
|
||||
{
|
||||
if ($salt === null)
|
||||
{
|
||||
$salt = Security::getRandom(12, 'alnum');
|
||||
}
|
||||
|
||||
if (function_exists('hash_pbkdf2'))
|
||||
{
|
||||
$hash = hash_pbkdf2($algorithm, $password, $salt, $iterations, $length, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
$output = '';
|
||||
$block_count = ceil($length / strlen(hash($algorithm, '', true))); // key length divided by the length of one hash
|
||||
for ($i = 1; $i <= $block_count; $i++)
|
||||
{
|
||||
$last = $salt . pack('N', $i); // $i encoded as 4 bytes, big endian
|
||||
$last = $xorsum = hash_hmac($algorithm, $last, $password, true); // first iteration
|
||||
for ($j = 1; $j < $iterations; $j++) // The other $count - 1 iterations
|
||||
{
|
||||
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
|
||||
}
|
||||
$output .= $xorsum;
|
||||
}
|
||||
$hash = substr($output, 0, $length);
|
||||
}
|
||||
|
||||
return $algorithm . ':' . sprintf('%07d', $iterations) . ':' . $salt . ':' . base64_encode($hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the amount of entropy that a password contains.
|
||||
*
|
||||
* @param string $password
|
||||
* @return int
|
||||
*/
|
||||
public static function countEntropyBits($password)
|
||||
{
|
||||
// An empty string has no entropy.
|
||||
|
||||
if ($password === '') return 0;
|
||||
|
||||
// Common character sets and the number of possible mutations.
|
||||
|
||||
static $entropy_per_char = array(
|
||||
'/^[0-9]+$/' => 10,
|
||||
'/^[a-z]+$/' => 26,
|
||||
'/^[A-Z]+$/' => 26,
|
||||
'/^[a-z0-9]+$/' => 36,
|
||||
'/^[A-Z0-9]+$/' => 36,
|
||||
'/^[a-zA-Z]+$/' => 52,
|
||||
'/^[a-zA-Z0-9]+$/' => 62,
|
||||
'/^[a-zA-Z0-9_-]+$/' => 64,
|
||||
'/^[\\x20-\\x7e]+$/' => 95,
|
||||
'/^[\\x00-\\x7f]+$/' => 128,
|
||||
);
|
||||
|
||||
foreach ($entropy_per_char as $regex => $entropy)
|
||||
{
|
||||
if (preg_match($regex, $password))
|
||||
{
|
||||
return log(pow($entropy, strlen($password)), 2);
|
||||
}
|
||||
}
|
||||
|
||||
return strlen($password) * 8;
|
||||
}
|
||||
}
|
||||
|
|
@ -61,7 +61,7 @@ class VendorPass
|
|||
else
|
||||
{
|
||||
$iterations = 15;
|
||||
$salt = Password::createSecureSalt(8, 'hex');
|
||||
$salt = Rhymix\Framework\Security::getRandom(8, 'hex');
|
||||
}
|
||||
$count = 1 << $iterations;
|
||||
$hash = hash('sha512', $salt . $password, true);
|
||||
|
|
@ -104,7 +104,7 @@ class VendorPass
|
|||
}
|
||||
else
|
||||
{
|
||||
$salt = Password::createSecureSalt(32, 'hex');
|
||||
$salt = Rhymix\Framework\Security::getRandom(32, 'hex');
|
||||
}
|
||||
return md5($password . $salt) . ':' . $salt;
|
||||
}
|
||||
|
|
@ -137,7 +137,7 @@ class VendorPass
|
|||
{
|
||||
if (!isset($options['salt']) || !preg_match('/^[0-9a-zA-Z\.\/]{22,}$/', $options['salt']))
|
||||
{
|
||||
$options['salt'] = Password::createSecureSalt(22, 'alnum');
|
||||
$options['salt'] = Rhymix\Framework\Security::getRandom(22, 'alnum');
|
||||
}
|
||||
if (!isset($options['cost']) || $options['cost'] < 4 || $options['cost'] > 31)
|
||||
{
|
||||
|
|
|
|||
127
tests/unit/framework/PasswordTest.php
Normal file
127
tests/unit/framework/PasswordTest.php
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
|
||||
class PasswordTest extends \Codeception\TestCase\Test
|
||||
{
|
||||
public function testIsValidAlgorithm()
|
||||
{
|
||||
$this->assertTrue(Rhymix\Framework\Password::isValidAlgorithm('bcrypt'));
|
||||
$this->assertTrue(Rhymix\Framework\Password::isValidAlgorithm('whirlpool,pbkdf2'));
|
||||
$this->assertTrue(Rhymix\Framework\Password::isValidAlgorithm(array('md5', 'sha1', 'md5')));
|
||||
|
||||
$this->assertFalse(Rhymix\Framework\Password::isValidAlgorithm('bunga_bunga'));
|
||||
Rhymix\Framework\Password::addAlgorithm('bunga_bunga', '/bunga_bunga/', function($hash) { return 'bunga_bunga'; });
|
||||
$this->assertTrue(Rhymix\Framework\Password::isValidAlgorithm('bunga_bunga'));
|
||||
}
|
||||
|
||||
public function testGetSupportedAlgorithms()
|
||||
{
|
||||
$algos = Rhymix\Framework\Password::getSupportedAlgorithms();
|
||||
$this->assertTrue(in_array('bcrypt', $algos));
|
||||
$this->assertTrue(in_array('pbkdf2', $algos));
|
||||
$this->assertTrue(in_array('md5', $algos));
|
||||
}
|
||||
|
||||
public function testGetBestSupportedAlgorithm()
|
||||
{
|
||||
$algo = Rhymix\Framework\Password::getBestSupportedAlgorithm();
|
||||
$this->assertTrue($algo === 'bcrypt' || $algo === 'pbkdf2');
|
||||
}
|
||||
|
||||
public function testGetSelectedAlgorithm()
|
||||
{
|
||||
$algo = Rhymix\Framework\Password::getSelectedAlgorithm();
|
||||
$this->assertTrue($algo === 'bcrypt' || $algo === 'pbkdf2' || $algo === 'md5');
|
||||
}
|
||||
|
||||
public function testGetWorkFactor()
|
||||
{
|
||||
$work_factor = $algo = Rhymix\Framework\Password::getWorkFactor();
|
||||
$this->assertTrue($work_factor >= 4);
|
||||
$this->assertTrue($work_factor <= 31);
|
||||
}
|
||||
|
||||
public function testHashPassword()
|
||||
{
|
||||
$password = Rhymix\Framework\Security::getRandom(32);
|
||||
$this->assertEquals(md5($password), Rhymix\Framework\Password::hashPassword($password, 'md5'));
|
||||
$this->assertEquals(md5(sha1(md5($password))), Rhymix\Framework\Password::hashPassword($password, 'md5,sha1,md5'));
|
||||
$this->assertEquals(hash('whirlpool', $password), Rhymix\Framework\Password::hashPassword($password, 'whirlpool'));
|
||||
$this->assertEquals('5d2e19393cc5ef67', Rhymix\Framework\Password::hashPassword('password', 'mysql_old_password'));
|
||||
}
|
||||
|
||||
public function testCheckPassword()
|
||||
{
|
||||
$password = Rhymix\Framework\Security::getRandom(32);
|
||||
|
||||
$algos = array('whirlpool', 'ripemd160', 'bcrypt');
|
||||
$hash = Rhymix\Framework\Password::hashPassword($password, $algos);
|
||||
$this->assertRegExp('/^\$2y\$/', $hash);
|
||||
$this->assertEquals(60, strlen($hash));
|
||||
$this->assertTrue(Rhymix\Framework\Password::checkPassword($password, $hash, $algos));
|
||||
|
||||
$algos = array('pbkdf2');
|
||||
$hash = Rhymix\Framework\Password::hashPassword($password, $algos);
|
||||
$this->assertRegExp('/^(sha256|sha512):[0-9]+:/', $hash);
|
||||
$this->assertEquals(60, strlen($hash));
|
||||
$this->assertTrue(Rhymix\Framework\Password::checkPassword($password, $hash, $algos));
|
||||
|
||||
foreach (array('drupal', 'joomla', 'kimsqrb', 'mysql_old_password', 'mysql_new_password', 'mssql_pwdencrypt') as $algo)
|
||||
{
|
||||
$hash = Rhymix\Framework\Password::hashPassword($password, $algo);
|
||||
$this->assertTrue(Rhymix\Framework\Password::checkPassword($password, $hash, $algo));
|
||||
$this->assertFalse(Rhymix\Framework\Password::checkPassword($password, $hash . 'x', $algo));
|
||||
}
|
||||
}
|
||||
|
||||
public function testCheckAlgorithm()
|
||||
{
|
||||
$password = Rhymix\Framework\Security::getRandom(32, 'hex');
|
||||
|
||||
$this->assertEquals(array('md5', 'md5,sha1,md5'), Rhymix\Framework\Password::checkAlgorithm($password));
|
||||
$this->assertEquals(array('sha512', 'whirlpool'), Rhymix\Framework\Password::checkAlgorithm(hash('sha512', $password)));
|
||||
|
||||
$hash = '$2y$10$VkxBdEBTZ1HyLluZPjXCjuFffw0a6alZlbb733CF/zA22HDpBNsMm';
|
||||
$this->assertEquals(array('bcrypt'), Rhymix\Framework\Password::checkAlgorithm($hash));
|
||||
|
||||
$hash = 'sha512:0008192:hoXcLXQzIiIJ:ElokybdRf+i512M4/4PIdEiSDgZ8f0uL';
|
||||
$this->assertEquals(array('pbkdf2'), Rhymix\Framework\Password::checkAlgorithm($hash));
|
||||
}
|
||||
|
||||
public function testCheckWorkFactor()
|
||||
{
|
||||
$hash = '$2y$10$VkxBdEBTZ1HyLluZPjXCjuFffw0a6alZlbb733CF/zA22HDpBNsMm';
|
||||
$this->assertEquals(10, Rhymix\Framework\Password::checkWorkFactor($hash));
|
||||
|
||||
$hash = 'sha512:0008192:hoXcLXQzIiIJ:ElokybdRf+i512M4/4PIdEiSDgZ8f0uL';
|
||||
$this->assertEquals(8, Rhymix\Framework\Password::checkWorkFactor($hash));
|
||||
|
||||
$hash = '5f4dcc3b5aa765d61d8327deb882cf99';
|
||||
$this->assertEquals(0, Rhymix\Framework\Password::checkWorkFactor($hash));
|
||||
}
|
||||
|
||||
public function testBcrypt()
|
||||
{
|
||||
$password = 'password';
|
||||
$hash = '$2y$10$VkxBdEBTZ1HyLluZPjXCjuFffw0a6alZlbb733CF/zA22HDpBNsMm';
|
||||
$this->assertEquals($hash, Rhymix\Framework\Password::bcrypt($password, $hash));
|
||||
}
|
||||
|
||||
public function testPBKDF2()
|
||||
{
|
||||
$password = 'password';
|
||||
$salt = 'rtmIxdEUoWUk';
|
||||
$hash = 'sha512:0016384:rtmIxdEUoWUk:1hrwGP3ScWvxslnqNFqyhM6Ddn4iYrwf';
|
||||
$this->assertEquals($hash, Rhymix\Framework\Password::pbkdf2($password, $salt, 'sha512', 16384, 24));
|
||||
}
|
||||
|
||||
public function testCountEntropyBits()
|
||||
{
|
||||
$this->assertEquals(0, Rhymix\Framework\Password::countEntropyBits(''));
|
||||
$this->assertEquals(13, round(Rhymix\Framework\Password::countEntropyBits('1234')));
|
||||
$this->assertEquals(20, round(Rhymix\Framework\Password::countEntropyBits('123456')));
|
||||
$this->assertEquals(28, round(Rhymix\Framework\Password::countEntropyBits('rhymix')));
|
||||
$this->assertEquals(52, round(Rhymix\Framework\Password::countEntropyBits('rhymix1234')));
|
||||
$this->assertEquals(60, round(Rhymix\Framework\Password::countEntropyBits('RhymiX1234')));
|
||||
$this->assertEquals(125, round(Rhymix\Framework\Password::countEntropyBits('Rhymix_is*the%Best!')));
|
||||
}
|
||||
}
|
||||
|
|
@ -57,11 +57,11 @@ class SecurityTest extends \Codeception\TestCase\Test
|
|||
|
||||
public function testGetRandom()
|
||||
{
|
||||
$this->assertEquals(1, preg_match('/^[0-9a-zA-Z]{32}$/', Rhymix\Framework\Security::getRandom()));
|
||||
$this->assertEquals(1, preg_match('/^[0-9a-zA-Z]{256}$/', Rhymix\Framework\Security::getRandom(256)));
|
||||
$this->assertEquals(1, preg_match('/^[0-9a-zA-Z]{16}$/', Rhymix\Framework\Security::getRandom(16, 'alnum')));
|
||||
$this->assertEquals(1, preg_match('/^[0-9a-f]{16}$/', Rhymix\Framework\Security::getRandom(16, 'hex')));
|
||||
$this->assertEquals(1, preg_match('/^[\x21-\x7e]{16}$/', Rhymix\Framework\Security::getRandom(16, 'printable')));
|
||||
$this->assertRegExp('/^[0-9a-zA-Z]{32}$/', Rhymix\Framework\Security::getRandom());
|
||||
$this->assertRegExp('/^[0-9a-zA-Z]{256}$/', Rhymix\Framework\Security::getRandom(256));
|
||||
$this->assertRegExp('/^[0-9a-zA-Z]{16}$/', Rhymix\Framework\Security::getRandom(16, 'alnum'));
|
||||
$this->assertRegExp('/^[0-9a-f]{16}$/', Rhymix\Framework\Security::getRandom(16, 'hex'));
|
||||
$this->assertRegExp('/^[\x21-\x7e]{16}$/', Rhymix\Framework\Security::getRandom(16, 'printable'));
|
||||
}
|
||||
|
||||
public function testGetRandomNumber()
|
||||
|
|
@ -80,7 +80,7 @@ class SecurityTest extends \Codeception\TestCase\Test
|
|||
$regex = '/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/';
|
||||
for ($i = 0; $i < 10; $i++)
|
||||
{
|
||||
$this->assertEquals(1, preg_match($regex, Rhymix\Framework\Security::getRandomUUID()));
|
||||
$this->assertRegExp($regex, Rhymix\Framework\Security::getRandomUUID());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue