git-svn-id: https://svn.fournier38.fr/svn/ProgSVN/trunk@5978 bf3deb0d-5f1a-0410-827f-c0cc1f45334c
236 lines
8.2 KiB
PHP
236 lines
8.2 KiB
PHP
<?php
|
|
/** A class to manage the password hashing, password generation
|
|
*/
|
|
class password
|
|
{
|
|
////////////////////
|
|
// PROPERTIES //
|
|
////////////////////
|
|
/** List all the allowed hashing methods, sort from weak to strong encryption
|
|
*/
|
|
private $methods = array (
|
|
// {{{
|
|
"MYSQL" => array (
|
|
"hash" => "", "size" => "", "pre" => "", "post" => ""),
|
|
"CRYPT_STD_DES" => array (
|
|
"hash" => CRYPT_STD_DES, "size" => 2, "pre" => "", "post" => "" ),
|
|
"CRYPT_EXT_DES" => array (
|
|
"hash" => CRYPT_EXT_DES, "size" => 9, "pre" => "", "post" => "" ),
|
|
"CRYPT_MD5" => array (
|
|
"hash" => CRYPT_MD5, "size" => 12, "pre" => "$1$", "post" => "$" ),
|
|
"CRYPT_BLOWFISH" => array (
|
|
"hash" => CRYPT_BLOWFISH, "size" => 16, "pre" => "$2y$11$",
|
|
"post" => "$" ),
|
|
"CRYPT_SHA256" => array (
|
|
"hash" => CRYPT_SHA256, "size" => 16, "pre" => "$5\$rounds=5000$",
|
|
"post" => "$" ),
|
|
"CRYPT_SHA512" => array (
|
|
"hash" => CRYPT_SHA512, "size" => 16, "pre" => "$6\$rounds=5000$",
|
|
"post" => "$" ),
|
|
"PASSWORD_BCRYPT" => array (
|
|
"hash" => PASSWORD_BCRYPT, "size" => "", "pre" => "", "post" => ""),
|
|
"PASSWORD_ARGON2I" => array (
|
|
"hash" => PASSWORD_ARGON2I, "size" => "", "pre" => "", "post" => ""),
|
|
"PASSWORD_ARGON2ID" => array (
|
|
"hash" => PASSWORD_ARGON2ID, "size" => "", "pre" => "", "post" => ""),
|
|
);
|
|
// }}}
|
|
|
|
/////////////////
|
|
// METHODS //
|
|
/////////////////
|
|
/** List all the allowed password encrytion methods, sorted from weak to
|
|
* strong encryption
|
|
* @return array ("PASSWORD_ARGON2ID" => PASSWORD_ARGON2ID);
|
|
*/
|
|
public function listMethods ()
|
|
// {{{
|
|
{
|
|
$res = array ();
|
|
foreach ($this->methods as $key => $params)
|
|
{
|
|
if ($key !== "MYSQL" && (! defined ($key) || $params["hash"] === 0))
|
|
continue;
|
|
if (substr ($key, 0, 9) === "PASSWORD_" &&
|
|
! function_exists ("password_hash"))
|
|
continue;
|
|
$res[$key] = $params["hash"];
|
|
}
|
|
return $res;
|
|
}
|
|
// }}}
|
|
|
|
/** Create a salt, based on openssl_random_pseudo_bytes function
|
|
* @return a string salt
|
|
*/
|
|
public function salt ()
|
|
// {{{
|
|
{
|
|
if (function_exists ("openssl_random_pseudo_bytes"))
|
|
$salt = substr (base64_encode (openssl_random_pseudo_bytes (17)), 0, 22);
|
|
elseif (function_exists ("random_bytes"))
|
|
$salt = substr (base64_encode(random_bytes (22)), 0, 22);
|
|
else
|
|
throw new \Exception (dgettext ("domframework",
|
|
"Password : no PHP support for random numbers (OpenSSL...)"), 500);
|
|
$salt = str_replace ("+",".",$salt);
|
|
return $salt;
|
|
}
|
|
// }}}
|
|
|
|
/** Crypt the provided password with the wanted crypt method
|
|
* @param string $password The password to crypt
|
|
* @param string|null $method The method to use. If null, use the strongest
|
|
* one available
|
|
* @return string The hashed password
|
|
*/
|
|
public function cryptPassword ($password, $method = null)
|
|
// {{{
|
|
{
|
|
if (! is_string ($password) && ! is_integer ($password))
|
|
throw new \Exception (dgettext ("domframework",
|
|
"Invalid clear password provided to be crypted : not a string"), 403);
|
|
$methods = $this->listMethods ();
|
|
if ($method === null)
|
|
{
|
|
end ($methods);
|
|
$method = key ($methods);
|
|
}
|
|
if (! key_exists ($method, $methods))
|
|
throw new \Exception (sprintf (dgettext ("domframework",
|
|
"Password : cryptPassword method not allowed : %s"), $method), 500);
|
|
$params = $this->methods[$method];
|
|
if (substr ($method, 0, 9) === "PASSWORD_")
|
|
return password_hash ($password, $params["hash"]);
|
|
if ($method === "MYSQL")
|
|
return "*" . strtoupper (sha1 (sha1 ($password, true)));
|
|
if (substr ($method, 0, 6) === "CRYPT_")
|
|
{
|
|
$salt = $this->salt ();
|
|
$tmpSalt = $params["pre"].substr ($salt, 0, $params["size"]).
|
|
$params["post"];
|
|
return crypt ($password, $tmpSalt);
|
|
}
|
|
// Will never match here
|
|
throw new \Exception (sprintf (dgettext ("domframework",
|
|
"Password : Unknown method to crypt requested : %s"), $method), 500);
|
|
}
|
|
// }}}
|
|
|
|
/** Crypt the password with the best algorithm available
|
|
* @param string $password The password to crypt
|
|
* @return string The hashed password
|
|
*/
|
|
static public function cryptPasswd ($password)
|
|
// {{{
|
|
{
|
|
$passwd = new password ();
|
|
return $passwd->cryptPassword ($password);
|
|
}
|
|
// }}}
|
|
|
|
/** Check if the clear password is valid against the hashed one
|
|
* @param string $clear The clear password
|
|
* @param string $hashed The hashed password
|
|
* @return boolean true if the password correspond to the hash
|
|
*/
|
|
static public function checkPassword ($clear, $hashed)
|
|
// {{{
|
|
{
|
|
if (! is_string ($clear))
|
|
throw new \Exception (dgettext ("domframework",
|
|
"Invalid clear password provided to be checked : not a string"), 403);
|
|
if (! is_string ($hashed))
|
|
throw new \Exception (dgettext ("domframework",
|
|
"Invalid hashed password provided to be checked : not a string"), 403);
|
|
if (function_exists ("password_verify"))
|
|
return password_verify ($clear, $hashed);
|
|
// Crypt do not work with Argon2. But If Argon2 exists, password_verify
|
|
// should exists...
|
|
if (crypt ($clear, $hashed) === $hashed)
|
|
return true;
|
|
return false;
|
|
}
|
|
// }}}
|
|
|
|
/** Create a random password with $nbChars chars, ASCII chars (with special
|
|
* chars).
|
|
* A maximum of 20% for special chars in the size
|
|
* @param integer|null $nbChar The number of chars (12 by default)
|
|
* @return The random password
|
|
*/
|
|
static public function generateASCII ($nbChars = 12)
|
|
// {{{
|
|
{
|
|
if (! is_int ($nbChars) || $nbChars < 1)
|
|
throw new \Exception (dgettext ("domframework",
|
|
"Password : generateASCII : invalid nbChars provided : not an int or ".
|
|
"negative or null"));
|
|
if ($nbChars > 72)
|
|
throw new \Exception (dgettext ("domframework",
|
|
"Password : generateASCII : Can't generate more than 72 chars"));
|
|
$password = array ();
|
|
$chars = array_merge (range (97, 122), range (65, 90), range (48, 57));
|
|
$special = range (33, 47);
|
|
$rand1 = array_rand ($chars, floor ($nbChars * 0.80));
|
|
foreach ($rand1 as $chr)
|
|
$password[] = chr($chars[$chr]);
|
|
$rand2 = array_rand ($special, ceil ($nbChars * 0.2));
|
|
foreach ($rand2 as $chr)
|
|
$password[] = chr($special[$chr]);
|
|
shuffle ($password);
|
|
return implode ($password);
|
|
}
|
|
// }}}
|
|
|
|
/** Create a random password with $nbChars chars, Alphanumericals chars
|
|
* (without special chars)
|
|
* @param integer|null $nbChar The number of chars (12 by default)
|
|
* @return The random password
|
|
*/
|
|
static public function generateAlphanum ($nbChars = 12)
|
|
// {{{
|
|
{
|
|
if (! is_int ($nbChars) || $nbChars < 1)
|
|
throw new \Exception (dgettext ("domframework",
|
|
"Password : generateAlphanum : invalid nbChars provided : not an int or ".
|
|
"negative or null"));
|
|
if ($nbChars > 72)
|
|
throw new \Exception (dgettext ("domframework",
|
|
"Password : generateASCII : Can't generate more than 72 chars"));
|
|
$password = array ();
|
|
$chars = array_merge (range (97, 122), range (65, 90), range (48, 57));
|
|
$rand1 = array_rand ($chars, $nbChars);
|
|
foreach ($rand1 as $chr)
|
|
$password[] = chr($chars[$chr]);
|
|
shuffle ($password);
|
|
return implode ($password);
|
|
}
|
|
// }}}
|
|
|
|
/** Create a random password with $nbChars chars, Alphabeticals chars
|
|
* (without special chars, neither numbers)
|
|
* @param integer|null $nbChar The number of chars (12 by default)
|
|
* @return The random password
|
|
*/
|
|
static public function generateAlphabetical ($nbChars = 12)
|
|
// {{{
|
|
{
|
|
if (! is_int ($nbChars) || $nbChars < 1)
|
|
throw new \Exception (dgettext ("domframework",
|
|
"Password : generateAlphanum : invalid nbChars provided : not an int or ".
|
|
"negative or null"));
|
|
if ($nbChars > 72)
|
|
throw new \Exception (dgettext ("domframework",
|
|
"Password : generateASCII : Can't generate more than 72 chars"));
|
|
$password = array ();
|
|
$chars = array_merge (range (97, 122), range (65, 90));
|
|
$rand1 = array_rand ($chars, $nbChars);
|
|
foreach ($rand1 as $chr)
|
|
$password[] = chr($chars[$chr]);
|
|
shuffle ($password);
|
|
return implode ($password);
|
|
}
|
|
// }}}
|
|
}
|