[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/vendor/web-auth/webauthn-lib/src/AttestationStatement/ -> AndroidKeyAttestationStatementSupport.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\OtherObject\OtherObjectManager;
  19  use CBOR\Tag\TagObjectManager;
  20  use Cose\Algorithms;
  21  use Cose\Key\Ec2Key;
  22  use Cose\Key\Key;
  23  use Cose\Key\RsaKey;
  24  use FG\ASN1\ASNObject;
  25  use FG\ASN1\ExplicitlyTaggedObject;
  26  use FG\ASN1\Universal\OctetString;
  27  use FG\ASN1\Universal\Sequence;
  28  use Webauthn\AuthenticatorData;
  29  use Webauthn\CertificateToolbox;
  30  use Webauthn\MetadataService\MetadataStatementRepository;
  31  use Webauthn\StringStream;
  32  use Webauthn\TrustPath\CertificateTrustPath;
  33  
  34  final class AndroidKeyAttestationStatementSupport implements AttestationStatementSupport
  35  {
  36      /**
  37       * @var Decoder
  38       */
  39      private $decoder;
  40  
  41      /**
  42       * @var MetadataStatementRepository|null
  43       */
  44      private $metadataStatementRepository;
  45  
  46      public function __construct(?Decoder $decoder = null, ?MetadataStatementRepository $metadataStatementRepository = null)
  47      {
  48          if (null !== $decoder) {
  49              @trigger_error('The argument "$decoder" is deprecated since 2.1 and will be removed in v3.0. Set null instead', E_USER_DEPRECATED);
  50          }
  51          if (null === $metadataStatementRepository) {
  52              @trigger_error('Setting "null" for argument "$metadataStatementRepository" is deprecated since 2.1 and will be mandatory in v3.0.', E_USER_DEPRECATED);
  53          }
  54          $this->decoder = $decoder ?? new Decoder(new TagObjectManager(), new OtherObjectManager());
  55          $this->metadataStatementRepository = $metadataStatementRepository;
  56      }
  57  
  58      public function name(): string
  59      {
  60          return 'android-key';
  61      }
  62  
  63      public function load(array $attestation): AttestationStatement
  64      {
  65          Assertion::keyExists($attestation, 'attStmt', 'Invalid attestation object');
  66          foreach (['sig', 'x5c', 'alg'] as $key) {
  67              Assertion::keyExists($attestation['attStmt'], $key, sprintf('The attestation statement value "%s" is missing.', $key));
  68          }
  69          $certificates = $attestation['attStmt']['x5c'];
  70          Assertion::isArray($certificates, 'The attestation statement value "x5c" must be a list with at least one certificate.');
  71          Assertion::greaterThan(\count($certificates), 0, 'The attestation statement value "x5c" must be a list with at least one certificate.');
  72          Assertion::allString($certificates, 'The attestation statement value "x5c" must be a list with at least one certificate.');
  73          $certificates = CertificateToolbox::convertAllDERToPEM($certificates);
  74  
  75          return AttestationStatement::createBasic($attestation['fmt'], $attestation['attStmt'], new CertificateTrustPath($certificates));
  76      }
  77  
  78      public function isValid(string $clientDataJSONHash, AttestationStatement $attestationStatement, AuthenticatorData $authenticatorData): bool
  79      {
  80          $trustPath = $attestationStatement->getTrustPath();
  81          Assertion::isInstanceOf($trustPath, CertificateTrustPath::class, 'Invalid trust path');
  82  
  83          $certificates = $trustPath->getCertificates();
  84          if (null !== $this->metadataStatementRepository) {
  85              $certificates = CertificateToolbox::checkAttestationMedata(
  86                  $attestationStatement,
  87                  $authenticatorData->getAttestedCredentialData()->getAaguid()->toString(),
  88                  $certificates,
  89                  $this->metadataStatementRepository
  90              );
  91          }
  92  
  93          //Decode leaf attestation certificate
  94          $leaf = $certificates[0];
  95          $this->checkCertificateAndGetPublicKey($leaf, $clientDataJSONHash, $authenticatorData);
  96  
  97          $signedData = $authenticatorData->getAuthData().$clientDataJSONHash;
  98          $alg = $attestationStatement->get('alg');
  99  
 100          return 1 === openssl_verify($signedData, $attestationStatement->get('sig'), $leaf, Algorithms::getOpensslAlgorithmFor((int) $alg));
 101      }
 102  
 103      private function checkCertificateAndGetPublicKey(string $certificate, string $clientDataHash, AuthenticatorData $authenticatorData): void
 104      {
 105          $resource = openssl_pkey_get_public($certificate);
 106          Assertion::isResource($resource, 'Unable to read the certificate');
 107          $details = openssl_pkey_get_details($resource);
 108          Assertion::isArray($details, 'Unable to read the certificate');
 109  
 110          //Check that authData publicKey matches the public key in the attestation certificate
 111          $attestedCredentialData = $authenticatorData->getAttestedCredentialData();
 112          Assertion::notNull($attestedCredentialData, 'No attested credential data found');
 113          $publicKeyData = $attestedCredentialData->getCredentialPublicKey();
 114          Assertion::notNull($publicKeyData, 'No attested public key found');
 115          $publicDataStream = new StringStream($publicKeyData);
 116          $coseKey = $this->decoder->decode($publicDataStream)->getNormalizedData(false);
 117          Assertion::true($publicDataStream->isEOF(), 'Invalid public key data. Presence of extra bytes.');
 118          $publicDataStream->close();
 119          $publicKey = Key::createFromData($coseKey);
 120  
 121          Assertion::true(($publicKey instanceof Ec2Key) || ($publicKey instanceof RsaKey), 'Unsupported key type');
 122          Assertion::eq($publicKey->asPEM(), $details['key'], 'Invalid key');
 123  
 124          /*---------------------------*/
 125          $certDetails = openssl_x509_parse($certificate);
 126  
 127          //Find Android KeyStore Extension with OID “1.3.6.1.4.1.11129.2.1.17” in certificate extensions
 128          Assertion::keyExists($certDetails, 'extensions', 'The certificate has no extension');
 129          Assertion::isArray($certDetails['extensions'], 'The certificate has no extension');
 130          Assertion::keyExists($certDetails['extensions'], '1.3.6.1.4.1.11129.2.1.17', 'The certificate extension "1.3.6.1.4.1.11129.2.1.17" is missing');
 131          $extension = $certDetails['extensions']['1.3.6.1.4.1.11129.2.1.17'];
 132          $extensionAsAsn1 = ASNObject::fromBinary($extension);
 133          Assertion::isInstanceOf($extensionAsAsn1, Sequence::class, 'The certificate extension "1.3.6.1.4.1.11129.2.1.17" is invalid');
 134          $objects = $extensionAsAsn1->getChildren();
 135  
 136          //Check that attestationChallenge is set to the clientDataHash.
 137          Assertion::keyExists($objects, 4, 'The certificate extension "1.3.6.1.4.1.11129.2.1.17" is invalid');
 138          Assertion::isInstanceOf($objects[4], OctetString::class, 'The certificate extension "1.3.6.1.4.1.11129.2.1.17" is invalid');
 139          Assertion::eq($clientDataHash, hex2bin(($objects[4])->getContent()), 'The client data hash is not valid');
 140  
 141          //Check that both teeEnforced and softwareEnforced structures don’t contain allApplications(600) tag.
 142          Assertion::keyExists($objects, 6, 'The certificate extension "1.3.6.1.4.1.11129.2.1.17" is invalid');
 143          $softwareEnforcedFlags = $objects[6];
 144          Assertion::isInstanceOf($softwareEnforcedFlags, Sequence::class, 'The certificate extension "1.3.6.1.4.1.11129.2.1.17" is invalid');
 145          $this->checkAbsenceOfAllApplicationsTag($softwareEnforcedFlags);
 146  
 147          Assertion::keyExists($objects, 7, 'The certificate extension "1.3.6.1.4.1.11129.2.1.17" is invalid');
 148          $teeEnforcedFlags = $objects[6];
 149          Assertion::isInstanceOf($teeEnforcedFlags, Sequence::class, 'The certificate extension "1.3.6.1.4.1.11129.2.1.17" is invalid');
 150          $this->checkAbsenceOfAllApplicationsTag($teeEnforcedFlags);
 151      }
 152  
 153      private function checkAbsenceOfAllApplicationsTag(Sequence $sequence): void
 154      {
 155          foreach ($sequence->getChildren() as $tag) {
 156              Assertion::isInstanceOf($tag, ExplicitlyTaggedObject::class, 'Invalid tag');
 157              /* @var ExplicitlyTaggedObject $tag */
 158              Assertion::notEq(600, (int) $tag->getTag(), 'Forbidden tag 600 found');
 159          }
 160      }
 161  }


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