[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/vendor/web-token/jwt-signature/ -> JWSBuilder.php (source)

   1  <?php
   2  
   3  declare(strict_types=1);
   4  
   5  /*
   6   * The MIT License (MIT)
   7   *
   8   * Copyright (c) 2014-2020 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 Jose\Component\Signature;
  15  
  16  use function array_key_exists;
  17  use Base64Url\Base64Url;
  18  use function count;
  19  use function in_array;
  20  use InvalidArgumentException;
  21  use function is_array;
  22  use Jose\Component\Core\Algorithm;
  23  use Jose\Component\Core\AlgorithmManager;
  24  use Jose\Component\Core\JWK;
  25  use Jose\Component\Core\Util\JsonConverter;
  26  use Jose\Component\Core\Util\KeyChecker;
  27  use Jose\Component\Signature\Algorithm\MacAlgorithm;
  28  use Jose\Component\Signature\Algorithm\SignatureAlgorithm;
  29  use LogicException;
  30  use RuntimeException;
  31  
  32  class JWSBuilder
  33  {
  34      /**
  35       * @var null|string
  36       */
  37      protected $payload;
  38  
  39      /**
  40       * @var bool
  41       */
  42      protected $isPayloadDetached;
  43  
  44      /**
  45       * @var array
  46       */
  47      protected $signatures = [];
  48  
  49      /**
  50       * @var null|bool
  51       */
  52      protected $isPayloadEncoded;
  53  
  54      /**
  55       * @var AlgorithmManager
  56       */
  57      private $signatureAlgorithmManager;
  58  
  59      public function __construct(AlgorithmManager $signatureAlgorithmManager)
  60      {
  61          $this->signatureAlgorithmManager = $signatureAlgorithmManager;
  62      }
  63  
  64      /**
  65       * Returns the algorithm manager associated to the builder.
  66       */
  67      public function getSignatureAlgorithmManager(): AlgorithmManager
  68      {
  69          return $this->signatureAlgorithmManager;
  70      }
  71  
  72      /**
  73       * Reset the current data.
  74       *
  75       * @return JWSBuilder
  76       */
  77      public function create(): self
  78      {
  79          $this->payload = null;
  80          $this->isPayloadDetached = false;
  81          $this->signatures = [];
  82          $this->isPayloadEncoded = null;
  83  
  84          return $this;
  85      }
  86  
  87      /**
  88       * Set the payload.
  89       * This method will return a new JWSBuilder object.
  90       *
  91       * @throws InvalidArgumentException if the payload is not UTF-8 encoded
  92       *
  93       * @return JWSBuilder
  94       */
  95      public function withPayload(string $payload, bool $isPayloadDetached = false): self
  96      {
  97          if (false === mb_detect_encoding($payload, 'UTF-8', true)) {
  98              throw new InvalidArgumentException('The payload must be encoded in UTF-8');
  99          }
 100          $clone = clone $this;
 101          $clone->payload = $payload;
 102          $clone->isPayloadDetached = $isPayloadDetached;
 103  
 104          return $clone;
 105      }
 106  
 107      /**
 108       * Adds the information needed to compute the signature.
 109       * This method will return a new JWSBuilder object.
 110       *
 111       * @throws InvalidArgumentException if the payload encoding is inconsistent
 112       *
 113       * @return JWSBuilder
 114       */
 115      public function addSignature(JWK $signatureKey, array $protectedHeader, array $header = []): self
 116      {
 117          $this->checkB64AndCriticalHeader($protectedHeader);
 118          $isPayloadEncoded = $this->checkIfPayloadIsEncoded($protectedHeader);
 119          if (null === $this->isPayloadEncoded) {
 120              $this->isPayloadEncoded = $isPayloadEncoded;
 121          } elseif ($this->isPayloadEncoded !== $isPayloadEncoded) {
 122              throw new InvalidArgumentException('Foreign payload encoding detected.');
 123          }
 124          $this->checkDuplicatedHeaderParameters($protectedHeader, $header);
 125          KeyChecker::checkKeyUsage($signatureKey, 'signature');
 126          $algorithm = $this->findSignatureAlgorithm($signatureKey, $protectedHeader, $header);
 127          KeyChecker::checkKeyAlgorithm($signatureKey, $algorithm->name());
 128          $clone = clone $this;
 129          $clone->signatures[] = [
 130              'signature_algorithm' => $algorithm,
 131              'signature_key' => $signatureKey,
 132              'protected_header' => $protectedHeader,
 133              'header' => $header,
 134          ];
 135  
 136          return $clone;
 137      }
 138  
 139      /**
 140       * Computes all signatures and return the expected JWS object.
 141       *
 142       * @throws RuntimeException if the payload is not set
 143       * @throws RuntimeException if no signature is defined
 144       */
 145      public function build(): JWS
 146      {
 147          if (null === $this->payload) {
 148              throw new RuntimeException('The payload is not set.');
 149          }
 150          if (0 === count($this->signatures)) {
 151              throw new RuntimeException('At least one signature must be set.');
 152          }
 153  
 154          $encodedPayload = false === $this->isPayloadEncoded ? $this->payload : Base64Url::encode($this->payload);
 155          $jws = new JWS($this->payload, $encodedPayload, $this->isPayloadDetached);
 156          foreach ($this->signatures as $signature) {
 157              /** @var MacAlgorithm|SignatureAlgorithm $algorithm */
 158              $algorithm = $signature['signature_algorithm'];
 159              /** @var JWK $signatureKey */
 160              $signatureKey = $signature['signature_key'];
 161              /** @var array $protectedHeader */
 162              $protectedHeader = $signature['protected_header'];
 163              /** @var array $header */
 164              $header = $signature['header'];
 165              $encodedProtectedHeader = 0 === count($protectedHeader) ? null : Base64Url::encode(JsonConverter::encode($protectedHeader));
 166              $input = sprintf('%s.%s', $encodedProtectedHeader, $encodedPayload);
 167              if ($algorithm instanceof SignatureAlgorithm) {
 168                  $s = $algorithm->sign($signatureKey, $input);
 169              } else {
 170                  $s = $algorithm->hash($signatureKey, $input);
 171              }
 172              $jws = $jws->addSignature($s, $protectedHeader, $encodedProtectedHeader, $header);
 173          }
 174  
 175          return $jws;
 176      }
 177  
 178      private function checkIfPayloadIsEncoded(array $protectedHeader): bool
 179      {
 180          return !array_key_exists('b64', $protectedHeader) || true === $protectedHeader['b64'];
 181      }
 182  
 183      /**
 184       * @throws LogicException if the header parameter "crit" is missing, invalid or does not contain "b64" when "b64" is set
 185       */
 186      private function checkB64AndCriticalHeader(array $protectedHeader): void
 187      {
 188          if (!array_key_exists('b64', $protectedHeader)) {
 189              return;
 190          }
 191          if (!array_key_exists('crit', $protectedHeader)) {
 192              throw new LogicException('The protected header parameter "crit" is mandatory when protected header parameter "b64" is set.');
 193          }
 194          if (!is_array($protectedHeader['crit'])) {
 195              throw new LogicException('The protected header parameter "crit" must be an array.');
 196          }
 197          if (!in_array('b64', $protectedHeader['crit'], true)) {
 198              throw new LogicException('The protected header parameter "crit" must contain "b64" when protected header parameter "b64" is set.');
 199          }
 200      }
 201  
 202      /**
 203       * @throws InvalidArgumentException if the header parameter "alg" is missing or the algorithm is not allowed/not supported
 204       *
 205       * @return MacAlgorithm|SignatureAlgorithm
 206       */
 207      private function findSignatureAlgorithm(JWK $key, array $protectedHeader, array $header): Algorithm
 208      {
 209          $completeHeader = array_merge($header, $protectedHeader);
 210          if (!array_key_exists('alg', $completeHeader)) {
 211              throw new InvalidArgumentException('No "alg" parameter set in the header.');
 212          }
 213          if ($key->has('alg') && $key->get('alg') !== $completeHeader['alg']) {
 214              throw new InvalidArgumentException(sprintf('The algorithm "%s" is not allowed with this key.', $completeHeader['alg']));
 215          }
 216  
 217          $algorithm = $this->signatureAlgorithmManager->get($completeHeader['alg']);
 218          if (!$algorithm instanceof SignatureAlgorithm && !$algorithm instanceof MacAlgorithm) {
 219              throw new InvalidArgumentException(sprintf('The algorithm "%s" is not supported.', $completeHeader['alg']));
 220          }
 221  
 222          return $algorithm;
 223      }
 224  
 225      /**
 226       * @throws InvalidArgumentException if the header contains duplicated entries
 227       */
 228      private function checkDuplicatedHeaderParameters(array $header1, array $header2): void
 229      {
 230          $inter = array_intersect_key($header1, $header2);
 231          if (0 !== count($inter)) {
 232              throw new InvalidArgumentException(sprintf('The header contains duplicated entries: %s.', implode(', ', array_keys($inter))));
 233          }
 234      }
 235  }


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