120 lines
4.0 KiB
PHP
120 lines
4.0 KiB
PHP
<?php
|
|
|
|
namespace App\Security\Hasher;
|
|
|
|
use App\SQLLogin\Exception\InvalidSQLPasswordException;
|
|
use App\SQLLogin\Exception\SecurityPatternConfigurationException;
|
|
use Psr\Log\LoggerInterface;
|
|
use Symfony\Component\PasswordHasher\Exception\InvalidPasswordException;
|
|
use Symfony\Component\PasswordHasher\Hasher\CheckPasswordLengthTrait;
|
|
use Symfony\Component\PasswordHasher\LegacyPasswordHasherInterface;
|
|
|
|
class PasswordEncoder implements LegacyPasswordHasherInterface
|
|
{
|
|
use CheckPasswordLengthTrait;
|
|
public const PASSWORD_PATTERN = 'password';
|
|
public const SALT_PATTERN = 'salt';
|
|
public const PEPPER_PATTERN = 'pepper';
|
|
|
|
protected ?string $pepper;
|
|
protected array $hashAlgoLegacy;
|
|
protected array $securityPattern;
|
|
|
|
public function __construct(?string $pepper, string $hashAlgoLegacy, string $securityPattern, private LoggerInterface $loggerInterface)
|
|
{
|
|
$this->pepper = $pepper;
|
|
$this->hashAlgoLegacy = explode(',', $hashAlgoLegacy);
|
|
$this->securityPattern = explode(',', $securityPattern);
|
|
}
|
|
|
|
/**
|
|
* Pas utilisé, mais on doit le garder pour le implements
|
|
*/
|
|
public function hash(string $plainPassword, string $salt = null): string
|
|
{
|
|
if ($this->isPasswordTooLong($plainPassword)) {
|
|
throw new InvalidPasswordException();
|
|
}
|
|
|
|
return hash($plainPassword.$salt, $this->hashAlgoLegacy[0]);
|
|
}
|
|
|
|
public function verify(string $hashedPassword, string $plainPassword, string $salt = null): bool
|
|
{
|
|
if ('' === $plainPassword || $this->isPasswordTooLong($plainPassword)) {
|
|
return false;
|
|
}
|
|
|
|
$completedPassword = $this->getPasswordToHash($plainPassword, $salt);
|
|
|
|
foreach ($this->hashAlgoLegacy as $algo) {
|
|
if ('ssha' === $algo && $this->compareSsha($hashedPassword, $completedPassword)) {
|
|
return true;
|
|
}
|
|
if ($this->isObsoleteAlgo($algo)) {
|
|
if (hash_equals(hash($algo, $completedPassword), $hashedPassword)) {
|
|
return true;
|
|
}
|
|
} else {
|
|
if (password_verify($completedPassword, $hashedPassword)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
throw new InvalidSQLPasswordException();
|
|
}
|
|
|
|
public function needsRehash(string $hashedPassword): bool
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public function isObsoleteAlgo($algo): bool
|
|
{
|
|
return in_array($algo, hash_algos());
|
|
}
|
|
|
|
/**
|
|
* Retourne la string à hasher en fonction du pattern indiqué
|
|
*
|
|
* @param mixed $plainTextPassword
|
|
* @param mixed $salt
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function getPasswordToHash($plainTextPassword, $salt)
|
|
{
|
|
$arrayRef = [
|
|
self::PASSWORD_PATTERN => $plainTextPassword,
|
|
self::SALT_PATTERN => $salt,
|
|
self::PEPPER_PATTERN => $this->pepper,
|
|
];
|
|
|
|
foreach ($this->securityPattern as $term) {
|
|
if (self::PEPPER_PATTERN !== $term && self::PASSWORD_PATTERN !== $term && self::SALT_PATTERN !== $term) {
|
|
$this->loggerInterface->critical('La configuration du security pattern est invalide, les termes autorisés sont : '.self::PASSWORD_PATTERN.', '.self::SALT_PATTERN.' et '.self::PEPPER_PATTERN);
|
|
throw new SecurityPatternConfigurationException();
|
|
}
|
|
}
|
|
$completedPlainPassword = '';
|
|
foreach ($this->securityPattern as $term) {
|
|
$completedPlainPassword .= $arrayRef[$term];
|
|
}
|
|
|
|
return $completedPlainPassword;
|
|
}
|
|
|
|
protected function compareSsha($hashPassword, $plainPassword)
|
|
{
|
|
$base_64_hash_with_salt = substr($hashPassword, 6);
|
|
$hash_with_salt = base64_decode($base_64_hash_with_salt);
|
|
$hash = substr($hash_with_salt, 0, 20);
|
|
$salt = substr($hash_with_salt, 20);
|
|
|
|
// hash given password
|
|
$hash_given = sha1($plainPassword.$salt, true);
|
|
|
|
return $hash == $hash_given;
|
|
}
|
|
}
|