| 1 |
<?php |
| 2 |
|
| 3 |
declare(strict_types=1); |
| 4 |
|
| 5 |
/* |
| 6 |
* The MIT License (MIT) |
| 7 |
* |
| 8 |
* Copyright (c) 2014-2021 Spomky-Labs |
| 9 |
* |
| 10 |
* This software may be modified and distributed under the terms |
| 11 |
* of the MIT license. See the LICENSE file for details. |
| 12 |
*/ |
| 13 |
|
| 14 |
namespace WP_DEFENDER_VENDOR\Webauthn\MetadataService; |
| 15 |
|
| 16 |
use WP_DEFENDER_VENDOR\Assert\Assertion; |
| 17 |
use WP_DEFENDER_VENDOR\Base64Url\Base64Url; |
| 18 |
use Jose\Component\KeyManagement\JWKFactory; |
| 19 |
use Jose\Component\Signature\Algorithm\ES256; |
| 20 |
use Jose\Component\Signature\Serializer\CompactSerializer; |
| 21 |
use WP_DEFENDER_VENDOR\Psr\Http\Client\ClientInterface; |
| 22 |
use WP_DEFENDER_VENDOR\Psr\Http\Message\RequestFactoryInterface; |
| 23 |
use function WP_DEFENDER_VENDOR\Safe\json_decode; |
| 24 |
use function WP_DEFENDER_VENDOR\Safe\sprintf; |
| 25 |
|
| 26 |
/** |
| 27 |
* @deprecated This class is deprecated since v3.3 and will be removed in v4.0 |
| 28 |
*/ |
| 29 |
class MetadataStatementFetcher |
| 30 |
{ |
| 31 |
public static function fetchTableOfContent(string $uri, ClientInterface $client, RequestFactoryInterface $requestFactory, array $additionalHeaders = []): MetadataTOCPayload |
| 32 |
{ |
| 33 |
$content = self::fetch($uri, $client, $requestFactory, $additionalHeaders); |
| 34 |
$payload = self::getJwsPayload($content); |
| 35 |
$data = json_decode($payload, true); |
| 36 |
|
| 37 |
return MetadataTOCPayload::createFromArray($data); |
| 38 |
} |
| 39 |
|
| 40 |
public static function fetchMetadataStatement(string $uri, bool $isBase64UrlEncoded, ClientInterface $client, RequestFactoryInterface $requestFactory, array $additionalHeaders = [], string $hash = '', string $hashingFunction = 'sha256'): MetadataStatement |
| 41 |
{ |
| 42 |
$payload = self::fetch($uri, $client, $requestFactory, $additionalHeaders); |
| 43 |
if ('' !== $hash) { |
| 44 |
Assertion::true(hash_equals($hash, hash($hashingFunction, $payload, true)), 'The hash cannot be verified. The metadata statement shall be rejected'); |
| 45 |
} |
| 46 |
$json = $isBase64UrlEncoded ? Base64Url::decode($payload) : $payload; |
| 47 |
$data = json_decode($json, true); |
| 48 |
|
| 49 |
return MetadataStatement::createFromArray($data); |
| 50 |
} |
| 51 |
|
| 52 |
private static function fetch(string $uri, ClientInterface $client, RequestFactoryInterface $requestFactory, array $additionalHeaders = []): string |
| 53 |
{ |
| 54 |
$request = $requestFactory->createRequest('GET', $uri); |
| 55 |
foreach ($additionalHeaders as $k => $v) { |
| 56 |
$request = $request->withHeader($k, $v); |
| 57 |
} |
| 58 |
$response = $client->sendRequest($request); |
| 59 |
Assertion::eq(200, $response->getStatusCode(), sprintf('Unable to contact the server. Response code is %d', $response->getStatusCode())); |
| 60 |
$content = $response->getBody()->getContents(); |
| 61 |
Assertion::notEmpty($content, 'Unable to contact the server. The response has no content'); |
| 62 |
|
| 63 |
return $content; |
| 64 |
} |
| 65 |
|
| 66 |
private static function getJwsPayload(string $token): string |
| 67 |
{ |
| 68 |
$jws = (new CompactSerializer())->unserialize($token); |
| 69 |
Assertion::eq(1, $jws->countSignatures(), 'Invalid response from the metadata service. Only one signature shall be present.'); |
| 70 |
$signature = $jws->getSignature(0); |
| 71 |
$payload = $jws->getPayload(); |
| 72 |
Assertion::notEmpty($payload, 'Invalid response from the metadata service. The token payload is empty.'); |
| 73 |
$header = $signature->getProtectedHeader(); |
| 74 |
Assertion::keyExists($header, 'alg', 'The "alg" parameter is missing.'); |
| 75 |
Assertion::eq($header['alg'], 'ES256', 'The expected "alg" parameter value should be "ES256".'); |
| 76 |
Assertion::keyExists($header, 'x5c', 'The "x5c" parameter is missing.'); |
| 77 |
Assertion::isArray($header['x5c'], 'The "x5c" parameter should be an array.'); |
| 78 |
$key = JWKFactory::createFromX5C($header['x5c']); |
| 79 |
$algorithm = new ES256(); |
| 80 |
$isValid = $algorithm->verify($key, $signature->getEncodedProtectedHeader().'.'.$jws->getEncodedPayload(), $signature->getSignature()); |
| 81 |
Assertion::true($isValid, 'Invalid response from the metadata service. The token signature is invalid.'); |
| 82 |
|
| 83 |
return $jws->getPayload(); |
| 84 |
} |
| 85 |
} |
| 86 |
|