[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/vendor/web-auth/webauthn-lib/src/ -> AuthenticatorAssertionResponseValidator.php (source)

   1  <?php
   2  
   3  declare(strict_types=1);
   4  
   5  /*
   6   * The MIT License (MIT)
   7   *
   8   * Copyright (c) 2014-2019 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 Webauthn;
  15  
  16  use Assert\Assertion;
  17  use CBOR\Decoder;
  18  use CBOR\OtherObject\OtherObjectManager;
  19  use CBOR\Tag\TagObjectManager;
  20  use Cose\Algorithm\Manager;
  21  use Cose\Algorithm\Signature\Signature;
  22  use Cose\Key\Key;
  23  use Psr\Http\Message\ServerRequestInterface;
  24  use Webauthn\AuthenticationExtensions\AuthenticationExtensionsClientInputs;
  25  use Webauthn\AuthenticationExtensions\AuthenticationExtensionsClientOutputs;
  26  use Webauthn\AuthenticationExtensions\ExtensionOutputCheckerHandler;
  27  use Webauthn\TokenBinding\TokenBindingHandler;
  28  use Webauthn\Util\CoseSignatureFixer;
  29  
  30  class AuthenticatorAssertionResponseValidator
  31  {
  32      /**
  33       * @var PublicKeyCredentialSourceRepository
  34       */
  35      private $publicKeyCredentialSourceRepository;
  36  
  37      /**
  38       * @var Decoder
  39       */
  40      private $decoder;
  41  
  42      /**
  43       * @var TokenBindingHandler
  44       */
  45      private $tokenBindingHandler;
  46  
  47      /**
  48       * @var ExtensionOutputCheckerHandler
  49       */
  50      private $extensionOutputCheckerHandler;
  51  
  52      /**
  53       * @var Manager|null
  54       */
  55      private $algorithmManager;
  56  
  57      public function __construct(PublicKeyCredentialSourceRepository $publicKeyCredentialSourceRepository, ?Decoder $decoder, TokenBindingHandler $tokenBindingHandler, ExtensionOutputCheckerHandler $extensionOutputCheckerHandler, Manager $algorithmManager)
  58      {
  59          if (null !== $decoder) {
  60              @trigger_error('The argument "$decoder" is deprecated since 2.1 and will be removed in v3.0. Set null instead', E_USER_DEPRECATED);
  61          }
  62          $this->publicKeyCredentialSourceRepository = $publicKeyCredentialSourceRepository;
  63          $this->decoder = $decoder ?? new Decoder(new TagObjectManager(), new OtherObjectManager());
  64          $this->tokenBindingHandler = $tokenBindingHandler;
  65          $this->extensionOutputCheckerHandler = $extensionOutputCheckerHandler;
  66          $this->algorithmManager = $algorithmManager;
  67      }
  68  
  69      /**
  70       * @see https://www.w3.org/TR/webauthn/#verifying-assertion
  71       */
  72      public function check(string $credentialId, AuthenticatorAssertionResponse $authenticatorAssertionResponse, PublicKeyCredentialRequestOptions $publicKeyCredentialRequestOptions, ServerRequestInterface $request, ?string $userHandle): PublicKeyCredentialSource
  73      {
  74          /* @see 7.2.1 */
  75          if (0 !== \count($publicKeyCredentialRequestOptions->getAllowCredentials())) {
  76              Assertion::true($this->isCredentialIdAllowed($credentialId, $publicKeyCredentialRequestOptions->getAllowCredentials()), 'The credential ID is not allowed.');
  77          }
  78  
  79          /* @see 7.2.2 */
  80          $publicKeyCredentialSource = $this->publicKeyCredentialSourceRepository->findOneByCredentialId($credentialId);
  81          Assertion::notNull($publicKeyCredentialSource, 'The credential ID is invalid.');
  82  
  83          /* @see 7.2.3 */
  84          $attestedCredentialData = $publicKeyCredentialSource->getAttestedCredentialData();
  85          $credentialUserHandle = $publicKeyCredentialSource->getUserHandle();
  86          $responseUserHandle = $authenticatorAssertionResponse->getUserHandle();
  87  
  88          /* @see 7.2.2 User Handle*/
  89          if (null !== $userHandle) { //If the user was identified before the authentication ceremony was initiated,
  90              Assertion::eq($credentialUserHandle, $userHandle, 'Invalid user handle');
  91              if (null !== $responseUserHandle && '' !== $responseUserHandle) {
  92                  Assertion::eq($credentialUserHandle, $responseUserHandle, 'Invalid user handle');
  93              }
  94          } else {
  95              Assertion::notEmpty($responseUserHandle, 'User handle is mandatory');
  96              Assertion::eq($credentialUserHandle, $responseUserHandle, 'Invalid user handle');
  97          }
  98  
  99          $credentialPublicKey = $attestedCredentialData->getCredentialPublicKey();
 100          Assertion::notNull($credentialPublicKey, 'No public key available.');
 101          $stream = new StringStream($credentialPublicKey);
 102          $credentialPublicKeyStream = $this->decoder->decode($stream);
 103          Assertion::true($stream->isEOF(), 'Invalid key. Presence of extra bytes.');
 104          $stream->close();
 105  
 106          /** @see 7.2.4 */
 107          /** @see 7.2.5 */
 108          //Nothing to do. Use of objects directly
 109  
 110          /** @see 7.2.6 */
 111          $C = $authenticatorAssertionResponse->getClientDataJSON();
 112  
 113          /* @see 7.2.7 */
 114          Assertion::eq('webauthn.get', $C->getType(), 'The client data type is not "webauthn.get".');
 115  
 116          /* @see 7.2.8 */
 117          Assertion::true(hash_equals($publicKeyCredentialRequestOptions->getChallenge(), $C->getChallenge()), 'Invalid challenge.');
 118  
 119          /** @see 7.2.9 */
 120          $rpId = $publicKeyCredentialRequestOptions->getRpId() ?? $request->getUri()->getHost();
 121          $rpIdLength = mb_strlen($rpId);
 122          $parsedRelyingPartyId = parse_url($C->getOrigin());
 123          Assertion::isArray($parsedRelyingPartyId, 'Invalid origin');
 124          $scheme = $parsedRelyingPartyId['scheme'] ?? '';
 125          Assertion::eq('https', $scheme, 'Invalid scheme. HTTPS required.');
 126          $clientDataRpId = $parsedRelyingPartyId['host'] ?? '';
 127          Assertion::notEmpty($clientDataRpId, 'Invalid origin rpId.');
 128          Assertion::eq(mb_substr($clientDataRpId, -$rpIdLength), $rpId, 'rpId mismatch.');
 129  
 130          /* @see 7.2.10 */
 131          if (null !== $C->getTokenBinding()) {
 132              $this->tokenBindingHandler->check($C->getTokenBinding(), $request);
 133          }
 134  
 135          /** @see 7.2.11 */
 136          $facetId = $this->getFacetId($rpId, $publicKeyCredentialRequestOptions->getExtensions(), $authenticatorAssertionResponse->getAuthenticatorData()->getExtensions());
 137          $rpIdHash = hash('sha256', $rpId, true);
 138          Assertion::true(hash_equals($rpIdHash, $authenticatorAssertionResponse->getAuthenticatorData()->getRpIdHash()), 'rpId hash mismatch.');
 139  
 140          /* @see 7.2.12 */
 141          /* @see 7.2.13 */
 142          if (AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED === $publicKeyCredentialRequestOptions->getUserVerification()) {
 143              Assertion::true($authenticatorAssertionResponse->getAuthenticatorData()->isUserPresent(), 'User was not present');
 144              Assertion::true($authenticatorAssertionResponse->getAuthenticatorData()->isUserVerified(), 'User authentication required.');
 145          }
 146  
 147          /* @see 7.2.14 */
 148          $extensions = $authenticatorAssertionResponse->getAuthenticatorData()->getExtensions();
 149          if (null !== $extensions) {
 150              $this->extensionOutputCheckerHandler->check($extensions);
 151          }
 152  
 153          /** @see 7.2.15 */
 154          $getClientDataJSONHash = hash('sha256', $authenticatorAssertionResponse->getClientDataJSON()->getRawData(), true);
 155  
 156          /* @see 7.2.16 */
 157          $dataToVerify = $authenticatorAssertionResponse->getAuthenticatorData()->getAuthData().$getClientDataJSONHash;
 158          $signature = $authenticatorAssertionResponse->getSignature();
 159          $coseKey = new Key($credentialPublicKeyStream->getNormalizedData());
 160          $algorithm = $this->algorithmManager->get($coseKey->alg());
 161          Assertion::isInstanceOf($algorithm, Signature::class, 'Invalid algorithm identifier. Should refer to a signature algorithm');
 162          $signature = CoseSignatureFixer::fix($signature, $algorithm);
 163          Assertion::true($algorithm->verify($dataToVerify, $coseKey, $signature), 'Invalid signature.');
 164  
 165          /* @see 7.2.17 */
 166          $storedCounter = $publicKeyCredentialSource->getCounter();
 167          $currentCounter = $authenticatorAssertionResponse->getAuthenticatorData()->getSignCount();
 168          if (0 !== $currentCounter || 0 !== $storedCounter) {
 169              Assertion::greaterThan($currentCounter, $storedCounter, 'Invalid counter.');
 170          }
 171          $publicKeyCredentialSource->setCounter($currentCounter);
 172          $this->publicKeyCredentialSourceRepository->saveCredentialSource($publicKeyCredentialSource);
 173  
 174          /* @see 7.2.18 */
 175          //All good. We can continue.
 176          return $publicKeyCredentialSource;
 177      }
 178  
 179      private function isCredentialIdAllowed(string $credentialId, array $allowedCredentials): bool
 180      {
 181          foreach ($allowedCredentials as $allowedCredential) {
 182              if (hash_equals($allowedCredential->getId(), $credentialId)) {
 183                  return true;
 184              }
 185          }
 186  
 187          return false;
 188      }
 189  
 190      private function getFacetId(string $rpId, AuthenticationExtensionsClientInputs $authenticationExtensionsClientInputs, ?AuthenticationExtensionsClientOutputs $authenticationExtensionsClientOutputs): string
 191      {
 192          switch (true) {
 193              case !$authenticationExtensionsClientInputs->has('appid'):
 194                  return $rpId;
 195              case null === $authenticationExtensionsClientOutputs:
 196                  return $rpId;
 197              case !$authenticationExtensionsClientOutputs->has('appid'):
 198                  return $rpId;
 199              case true !== $authenticationExtensionsClientOutputs->get('appid'):
 200                  return $rpId;
 201              default:
 202                  return $authenticationExtensionsClientInputs->get('appid');
 203          }
 204      }
 205  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer