ulmus-user/src/Authorize/Header/DigestMethod.php
2025-03-28 13:37:38 +00:00

105 lines
3.1 KiB
PHP

<?php
namespace Ulmus\User\Authorize\Header;
use Psr\Http\Message\ServerRequestInterface;
use Ulmus\User\Entity\DigestAuthUserInterface;
use Ulmus\User\Lib\Authenticate;
class DigestMethod implements MethodInterface
{
public function __construct(
protected Authenticate $authorize,
protected DigestAuthUserInterface $user,
protected string|array $arguments
) {}
public function execute(ServerRequestInterface $request) : bool
{
$arguments = $this->parseDigestArguments($this->arguments);
if (empty($arguments['username'])) {
throw new \InvalidArgumentException("A 'username' key is required to authenticate using Digest");
}
$isSess = stripos($arguments['algorithm'] ?? "", "-SESS") !== false;
$hashMethod = $this->getDigestAlgorithmHash($arguments);
$arguments['nc'] = str_pad($arguments['nc'] ?? "1", 8, '0', STR_PAD_LEFT);
$ha1 = $this->getSecretHash();
if ($isSess) {
$ha1 = hash($hashMethod, implode(':', [
$ha1, $arguments['nonce'] , $arguments['cnonce']
]));
}
switch($arguments['qop'] ?? 'auth') {
case 'auth-int':
$ha2 = hash($hashMethod, implode(':', [
strtoupper($request->getMethod()), $arguments['uri'], hash($hashMethod, $body ?? "")
]));
break;
case 'auth':
default:
$ha2 = hash($hashMethod, implode(':', [
strtoupper($request->getMethod()), $arguments['uri']
]));
break;
}
if (isset($arguments['qop'])) {
$response = hash($hashMethod, implode(':', [
$ha1, $arguments['nonce'], $arguments['nc'], $arguments['cnonce'], $arguments['qop'], $ha2
]));
}
else {
$response = hash($hashMethod, implode(':', [
$ha1, $arguments['nonce'], $ha2
]));
}
return $response === $arguments['response'];
}
protected function parseDigestArguments(string|array $arguments) : array
{
if (is_string($arguments)) {
$keys = [ 'nonce', 'nc', 'cnonce', 'qop', 'username', 'uri', 'realm', 'response', 'opaque', 'algorithm' ];
# From https://www.php.net/manual/en/features.http-auth.php
preg_match_all('@(' . implode('|', $keys) . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $arguments, $matches, PREG_SET_ORDER);
$arguments = [];
foreach ($matches as $match) {
$arguments[$match[1]] = $match[3] ?: $match[4];
}
}
return $arguments;
}
protected function getDigestAlgorithmHash(array $arguments) : string
{
switch(strtoupper($arguments['algorithm'] ?? "")) {
case "SHA-512-256":
case "SHA-512-256-SESS":
return "sha512/256";
case "SHA-256":
case "SHA-256-SESS":
return "sha256";
default:
case 'MD5-SESS':
case 'MD5':
return "md5";
}
}
}