hydra-sql/src/Security/Hasher/PasswordEncoder.php

120 lines
4.0 KiB
PHP
Raw Normal View History

<?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);
}
/**
2023-01-05 11:31:29 +01:00
* 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) {
2023-01-11 10:30:24 +01:00
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();
}
}
2022-12-16 15:39:42 +01:00
$completedPlainPassword = '';
foreach ($this->securityPattern as $term) {
2022-12-16 15:39:42 +01:00
$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;
}
}