[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/vendor/web-auth/webauthn-lib/src/AttestationStatement/ -> FidoU2FAttestationStatementSupport.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\AttestationStatement;
  15  
  16  use Assert\Assertion;
  17  use CBOR\Decoder;
  18  use CBOR\MapObject;
  19  use CBOR\OtherObject\OtherObjectManager;
  20  use CBOR\Tag\TagObjectManager;
  21  use Cose\Key\Ec2Key;
  22  use InvalidArgumentException;
  23  use Throwable;
  24  use Webauthn\AuthenticatorData;
  25  use Webauthn\CertificateToolbox;
  26  use Webauthn\MetadataService\MetadataStatementRepository;
  27  use Webauthn\StringStream;
  28  use Webauthn\TrustPath\CertificateTrustPath;
  29  
  30  final class FidoU2FAttestationStatementSupport implements AttestationStatementSupport
  31  {
  32      /**
  33       * @var Decoder
  34       */
  35      private $decoder;
  36  
  37      /**
  38       * @var MetadataStatementRepository|null
  39       */
  40      private $metadataStatementRepository;
  41  
  42      public function __construct(?Decoder $decoder = null, ?MetadataStatementRepository $metadataStatementRepository = null)
  43      {
  44          if (null !== $decoder) {
  45              @trigger_error('The argument "$decoder" is deprecated since 2.1 and will be removed in v3.0. Set null instead', E_USER_DEPRECATED);
  46          }
  47          if (null === $metadataStatementRepository) {
  48              @trigger_error('Setting "null" for argument "$metadataStatementRepository" is deprecated since 2.1 and will be mandatory in v3.0.', E_USER_DEPRECATED);
  49          }
  50          $this->decoder = $decoder ?? new Decoder(new TagObjectManager(), new OtherObjectManager());
  51          $this->metadataStatementRepository = $metadataStatementRepository;
  52      }
  53  
  54      public function name(): string
  55      {
  56          return 'fido-u2f';
  57      }
  58  
  59      public function load(array $attestation): AttestationStatement
  60      {
  61          Assertion::keyExists($attestation, 'attStmt', 'Invalid attestation object');
  62          foreach (['sig', 'x5c'] as $key) {
  63              Assertion::keyExists($attestation['attStmt'], $key, sprintf('The attestation statement value "%s" is missing.', $key));
  64          }
  65          $certificates = $attestation['attStmt']['x5c'];
  66          Assertion::isArray($certificates, 'The attestation statement value "x5c" must be a list with one certificate.');
  67          Assertion::count($certificates, 1, 'The attestation statement value "x5c" must be a list with one certificate.');
  68          Assertion::allString($certificates, 'The attestation statement value "x5c" must be a list with one certificate.');
  69  
  70          reset($certificates);
  71          $certificates = CertificateToolbox::convertAllDERToPEM($certificates);
  72          $this->checkCertificate($certificates[0]);
  73  
  74          return AttestationStatement::createBasic($attestation['fmt'], $attestation['attStmt'], new CertificateTrustPath($certificates));
  75      }
  76  
  77      public function isValid(string $clientDataJSONHash, AttestationStatement $attestationStatement, AuthenticatorData $authenticatorData): bool
  78      {
  79          Assertion::eq(
  80              $authenticatorData->getAttestedCredentialData()->getAaguid()->toString(),
  81              '00000000-0000-0000-0000-000000000000',
  82              'Invalid AAGUID for fido-u2f attestation statement. Shall be "00000000-0000-0000-0000-000000000000"'
  83          );
  84          if (null !== $this->metadataStatementRepository) {
  85              CertificateToolbox::checkAttestationMedata(
  86                  $attestationStatement,
  87                  $authenticatorData->getAttestedCredentialData()->getAaguid()->toString(),
  88                  [],
  89                  $this->metadataStatementRepository
  90              );
  91          }
  92          $trustPath = $attestationStatement->getTrustPath();
  93          Assertion::isInstanceOf($trustPath, CertificateTrustPath::class, 'Invalid trust path');
  94          $dataToVerify = "\0";
  95          $dataToVerify .= $authenticatorData->getRpIdHash();
  96          $dataToVerify .= $clientDataJSONHash;
  97          $dataToVerify .= $authenticatorData->getAttestedCredentialData()->getCredentialId();
  98          $dataToVerify .= $this->extractPublicKey($authenticatorData->getAttestedCredentialData()->getCredentialPublicKey());
  99  
 100          return 1 === openssl_verify($dataToVerify, $attestationStatement->get('sig'), $trustPath->getCertificates()[0], OPENSSL_ALGO_SHA256);
 101      }
 102  
 103      private function extractPublicKey(?string $publicKey): string
 104      {
 105          Assertion::notNull($publicKey, 'The attested credential data does not contain a valid public key.');
 106  
 107          $publicKeyStream = new StringStream($publicKey);
 108          $coseKey = $this->decoder->decode($publicKeyStream);
 109          Assertion::true($publicKeyStream->isEOF(), 'Invalid public key. Presence of extra bytes.');
 110          $publicKeyStream->close();
 111          Assertion::isInstanceOf($coseKey, MapObject::class, 'The attested credential data does not contain a valid public key.');
 112  
 113          $coseKey = $coseKey->getNormalizedData();
 114          $ec2Key = new Ec2Key($coseKey + [Ec2Key::TYPE => 2, Ec2Key::DATA_CURVE => Ec2Key::CURVE_P256]);
 115  
 116          return "\x04".$ec2Key->x().$ec2Key->y();
 117      }
 118  
 119      private function checkCertificate(string $publicKey): void
 120      {
 121          try {
 122              $resource = openssl_pkey_get_public($publicKey);
 123              Assertion::isResource($resource, 'Unable to load the public key');
 124          } catch (Throwable $throwable) {
 125              throw new InvalidArgumentException('Invalid certificate or certificate chain', 0, $throwable);
 126          }
 127          $details = openssl_pkey_get_details($resource);
 128          Assertion::keyExists($details, 'ec', 'Invalid certificate or certificate chain');
 129          Assertion::keyExists($details['ec'], 'curve_name', 'Invalid certificate or certificate chain');
 130          Assertion::eq($details['ec']['curve_name'], 'prime256v1', 'Invalid certificate or certificate chain');
 131          Assertion::keyExists($details['ec'], 'curve_oid', 'Invalid certificate or certificate chain');
 132          Assertion::eq($details['ec']['curve_oid'], '1.2.840.10045.3.1.7', 'Invalid certificate or certificate chain');
 133      }
 134  }


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