[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/ -> XML.php (source)

   1  <?php
   2  
   3  /**
   4   * XML Formatted EC Key Handler
   5   *
   6   * More info:
   7   *
   8   * https://www.w3.org/TR/xmldsig-core/#sec-ECKeyValue
   9   * http://en.wikipedia.org/wiki/XML_Signature
  10   *
  11   * PHP version 5
  12   *
  13   * @category  Crypt
  14   * @package   EC
  15   * @author    Jim Wigginton <[email protected]>
  16   * @copyright 2015 Jim Wigginton
  17   * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  18   * @link      http://phpseclib.sourceforge.net
  19   */
  20  
  21  namespace phpseclib3\Crypt\EC\Formats\Keys;
  22  
  23  use ParagonIE\ConstantTime\Base64;
  24  use phpseclib3\Common\Functions\Strings;
  25  use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
  26  use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
  27  use phpseclib3\Crypt\EC\BaseCurves\Prime as PrimeCurve;
  28  use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
  29  use phpseclib3\Exception\BadConfigurationException;
  30  use phpseclib3\Exception\UnsupportedCurveException;
  31  use phpseclib3\Math\BigInteger;
  32  
  33  /**
  34   * XML Formatted EC Key Handler
  35   *
  36   * @package EC
  37   * @author  Jim Wigginton <[email protected]>
  38   * @access  public
  39   */
  40  abstract class XML
  41  {
  42      use Common;
  43  
  44      /**
  45       * Default namespace
  46       *
  47       * @var string
  48       */
  49      private static $namespace;
  50  
  51      /**
  52       * Flag for using RFC4050 syntax
  53       *
  54       * @var bool
  55       */
  56      private static $rfc4050 = false;
  57  
  58      /**
  59       * Break a public or private key down into its constituent components
  60       *
  61       * @access public
  62       * @param string $key
  63       * @param string $password optional
  64       * @return array
  65       */
  66      public static function load($key, $password = '')
  67      {
  68          self::initialize_static_variables();
  69  
  70          if (!Strings::is_stringable($key)) {
  71              throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
  72          }
  73  
  74          if (!class_exists('DOMDocument')) {
  75              throw new BadConfigurationException('The dom extension is not setup correctly on this system');
  76          }
  77  
  78          $use_errors = libxml_use_internal_errors(true);
  79  
  80          $temp = self::isolateNamespace($key, 'http://www.w3.org/2009/xmldsig11#');
  81          if ($temp) {
  82              $key = $temp;
  83          }
  84  
  85          $temp = self::isolateNamespace($key, 'http://www.w3.org/2001/04/xmldsig-more#');
  86          if ($temp) {
  87              $key = $temp;
  88          }
  89  
  90          $dom = new \DOMDocument();
  91          if (substr($key, 0, 5) != '<?xml') {
  92              $key = '<xml>' . $key . '</xml>';
  93          }
  94  
  95          if (!$dom->loadXML($key)) {
  96              libxml_use_internal_errors($use_errors);
  97              throw new \UnexpectedValueException('Key does not appear to contain XML');
  98          }
  99          $xpath = new \DOMXPath($dom);
 100          libxml_use_internal_errors($use_errors);
 101          $curve = self::loadCurveByParam($xpath);
 102  
 103          $pubkey = self::query($xpath, 'publickey', 'Public Key is not present');
 104  
 105          $QA = self::query($xpath, 'ecdsakeyvalue')->length ?
 106              self::extractPointRFC4050($xpath, $curve) :
 107              self::extractPoint("\0" . $pubkey, $curve);
 108  
 109          libxml_use_internal_errors($use_errors);
 110  
 111          return compact('curve', 'QA');
 112      }
 113  
 114      /**
 115       * Case-insensitive xpath query
 116       *
 117       * @param \DOMXPath $xpath
 118       * @param string $name
 119       * @param string $error optional
 120       * @param bool $decode optional
 121       * @return \DOMNodeList
 122       */
 123      private static function query($xpath, $name, $error = null, $decode = true)
 124      {
 125          $query = '/';
 126          $names = explode('/', $name);
 127          foreach ($names as $name) {
 128              $query .= "/*[translate(local-name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='$name']";
 129          }
 130          $result = $xpath->query($query);
 131          if (!isset($error)) {
 132              return $result;
 133          }
 134  
 135          if (!$result->length) {
 136              throw new \RuntimeException($error);
 137          }
 138          return $decode ? self::decodeValue($result->item(0)->textContent) : $result->item(0)->textContent;
 139      }
 140  
 141      /**
 142       * Finds the first element in the relevant namespace, strips the namespacing and returns the XML for that element.
 143       *
 144       * @param string $xml
 145       * @param string $ns
 146       */
 147      private static function isolateNamespace($xml, $ns)
 148      {
 149          $dom = new \DOMDocument();
 150          if (!$dom->loadXML($xml)) {
 151              return false;
 152          }
 153          $xpath = new \DOMXPath($dom);
 154          $nodes = $xpath->query("//*[namespace::*[.='$ns'] and not(../namespace::*[.='$ns'])]");
 155          if (!$nodes->length) {
 156              return false;
 157          }
 158          $node = $nodes->item(0);
 159          $ns_name = $node->lookupPrefix($ns);
 160          if ($ns_name) {
 161              $node->removeAttributeNS($ns, $ns_name);
 162          }
 163          return $dom->saveXML($node);
 164      }
 165  
 166      /**
 167       * Decodes the value
 168       *
 169       * @param string $value
 170       */
 171      private static function decodeValue($value)
 172      {
 173          return Base64::decode(str_replace(["\r", "\n", ' ', "\t"], '', $value));
 174      }
 175  
 176      /**
 177       * Extract points from an XML document
 178       *
 179       * @param \DOMXPath $xpath
 180       * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
 181       * @return object[]
 182       */
 183      private static function extractPointRFC4050(\DOMXPath $xpath, BaseCurve $curve)
 184      {
 185          $x = self::query($xpath, 'publickey/x');
 186          $y = self::query($xpath, 'publickey/y');
 187          if (!$x->length || !$x->item(0)->hasAttribute('Value')) {
 188              throw new \RuntimeException('Public Key / X coordinate not found');
 189          }
 190          if (!$y->length || !$y->item(0)->hasAttribute('Value')) {
 191              throw new \RuntimeException('Public Key / Y coordinate not found');
 192          }
 193          $point = [
 194              $curve->convertInteger(new BigInteger($x->item(0)->getAttribute('Value'))),
 195              $curve->convertInteger(new BigInteger($y->item(0)->getAttribute('Value')))
 196          ];
 197          if (!$curve->verifyPoint($point)) {
 198              throw new \RuntimeException('Unable to verify that point exists on curve');
 199          }
 200          return $point;
 201      }
 202  
 203      /**
 204       * Returns an instance of \phpseclib3\Crypt\EC\BaseCurves\Base based
 205       * on the curve parameters
 206       *
 207       * @param \DomXPath $xpath
 208       * @return \phpseclib3\Crypt\EC\BaseCurves\Base|false
 209       */
 210      private static function loadCurveByParam(\DOMXPath $xpath)
 211      {
 212          $namedCurve = self::query($xpath, 'namedcurve');
 213          if ($namedCurve->length == 1) {
 214              $oid = $namedCurve->item(0)->getAttribute('URN');
 215              $oid = preg_replace('#[^\d.]#', '', $oid);
 216              $name = array_search($oid, self::$curveOIDs);
 217              if ($name === false) {
 218                  throw new UnsupportedCurveException('Curve with OID of ' . $oid . ' is not supported');
 219              }
 220  
 221              $curve = '\phpseclib3\Crypt\EC\Curves\\' . $name;
 222              if (!class_exists($curve)) {
 223                  throw new UnsupportedCurveException('Named Curve of ' . $name . ' is not supported');
 224              }
 225              return new $curve();
 226          }
 227  
 228          $params = self::query($xpath, 'explicitparams');
 229          if ($params->length) {
 230              return self::loadCurveByParamRFC4050($xpath);
 231          }
 232  
 233          $params = self::query($xpath, 'ecparameters');
 234          if (!$params->length) {
 235              throw new \RuntimeException('No parameters are present');
 236          }
 237  
 238          $fieldTypes = [
 239              'prime-field' => ['fieldid/prime/p'],
 240              'gnb' => ['fieldid/gnb/m'],
 241              'tnb' => ['fieldid/tnb/k'],
 242              'pnb' => ['fieldid/pnb/k1', 'fieldid/pnb/k2', 'fieldid/pnb/k3'],
 243              'unknown' => []
 244          ];
 245  
 246          foreach ($fieldTypes as $type => $queries) {
 247              foreach ($queries as $query) {
 248                  $result = self::query($xpath, $query);
 249                  if (!$result->length) {
 250                      continue 2;
 251                  }
 252                  $param = preg_replace('#.*/#', '', $query);
 253                  $$param = self::decodeValue($result->item(0)->textContent);
 254              }
 255              break;
 256          }
 257  
 258          $a = self::query($xpath, 'curve/a', 'A coefficient is not present');
 259          $b = self::query($xpath, 'curve/b', 'B coefficient is not present');
 260          $base = self::query($xpath, 'base', 'Base point is not present');
 261          $order = self::query($xpath, 'order', 'Order is not present');
 262  
 263          switch ($type) {
 264              case 'prime-field':
 265                  $curve = new PrimeCurve();
 266                  $curve->setModulo(new BigInteger($p, 256));
 267                  $curve->setCoefficients(
 268                      new BigInteger($a, 256),
 269                      new BigInteger($b, 256)
 270                  );
 271                  $point = self::extractPoint("\0" . $base, $curve);
 272                  $curve->setBasePoint(...$point);
 273                  $curve->setOrder(new BigInteger($order, 256));
 274                  return $curve;
 275              case 'gnb':
 276              case 'tnb':
 277              case 'pnb':
 278              default:
 279                  throw new UnsupportedCurveException('Field Type of ' . $type . ' is not supported');
 280          }
 281      }
 282  
 283      /**
 284       * Returns an instance of \phpseclib3\Crypt\EC\BaseCurves\Base based
 285       * on the curve parameters
 286       *
 287       * @param \DomXPath $xpath
 288       * @return \phpseclib3\Crypt\EC\BaseCurves\Base|false
 289       */
 290      private static function loadCurveByParamRFC4050(\DOMXPath $xpath)
 291      {
 292          $fieldTypes = [
 293              'prime-field' => ['primefieldparamstype/p'],
 294              'unknown' => []
 295          ];
 296  
 297          foreach ($fieldTypes as $type => $queries) {
 298              foreach ($queries as $query) {
 299                  $result = self::query($xpath, $query);
 300                  if (!$result->length) {
 301                      continue 2;
 302                  }
 303                  $param = preg_replace('#.*/#', '', $query);
 304                  $$param = $result->item(0)->textContent;
 305              }
 306              break;
 307          }
 308  
 309          $a = self::query($xpath, 'curveparamstype/a', 'A coefficient is not present', false);
 310          $b = self::query($xpath, 'curveparamstype/b', 'B coefficient is not present', false);
 311          $x = self::query($xpath, 'basepointparams/basepoint/ecpointtype/x', 'Base Point X is not present', false);
 312          $y = self::query($xpath, 'basepointparams/basepoint/ecpointtype/y', 'Base Point Y is not present', false);
 313          $order = self::query($xpath, 'order', 'Order is not present', false);
 314  
 315          switch ($type) {
 316              case 'prime-field':
 317                  $curve = new PrimeCurve();
 318  
 319                  $p = str_replace(["\r", "\n", ' ', "\t"], '', $p);
 320                  $curve->setModulo(new BigInteger($p));
 321  
 322                  $a = str_replace(["\r", "\n", ' ', "\t"], '', $a);
 323                  $b = str_replace(["\r", "\n", ' ', "\t"], '', $b);
 324                  $curve->setCoefficients(
 325                      new BigInteger($a),
 326                      new BigInteger($b)
 327                  );
 328  
 329                  $x = str_replace(["\r", "\n", ' ', "\t"], '', $x);
 330                  $y = str_replace(["\r", "\n", ' ', "\t"], '', $y);
 331                  $curve->setBasePoint(
 332                      new BigInteger($x),
 333                      new BigInteger($y)
 334                  );
 335  
 336                  $order = str_replace(["\r", "\n", ' ', "\t"], '', $order);
 337                  $curve->setOrder(new BigInteger($order));
 338                  return $curve;
 339              default:
 340                  throw new UnsupportedCurveException('Field Type of ' . $type . ' is not supported');
 341          }
 342      }
 343  
 344      /**
 345       * Sets the namespace. dsig11 is the most common one.
 346       *
 347       * Set to null to unset. Used only for creating public keys.
 348       *
 349       * @param string $namespace
 350       */
 351      public static function setNamespace($namespace)
 352      {
 353          self::$namespace = $namespace;
 354      }
 355  
 356      /**
 357       * Uses the XML syntax specified in https://tools.ietf.org/html/rfc4050
 358       */
 359      public static function enableRFC4050Syntax()
 360      {
 361          self::$rfc4050 = true;
 362      }
 363  
 364      /**
 365       * Uses the XML syntax specified in https://www.w3.org/TR/xmldsig-core/#sec-ECParameters
 366       */
 367      public static function disableRFC4050Syntax()
 368      {
 369          self::$rfc4050 = false;
 370      }
 371  
 372      /**
 373       * Convert a public key to the appropriate format
 374       *
 375       * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
 376       * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
 377       * @param array $options optional
 378       * @return string
 379       */
 380      public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = [])
 381      {
 382          self::initialize_static_variables();
 383  
 384          if ($curve instanceof TwistedEdwardsCurve || $curve instanceof MontgomeryCurve) {
 385              throw new UnsupportedCurveException('TwistedEdwards and Montgomery Curves are not supported');
 386          }
 387  
 388          if (empty(static::$namespace)) {
 389              $pre = $post = '';
 390          } else {
 391              $pre = static::$namespace . ':';
 392              $post = ':' . static::$namespace;
 393          }
 394  
 395          if (self::$rfc4050) {
 396              return '<' . $pre . 'ECDSAKeyValue xmlns' . $post . '="http://www.w3.org/2001/04/xmldsig-more#">' . "\r\n" .
 397                     self::encodeXMLParameters($curve, $pre, $options) . "\r\n" .
 398                     '<' . $pre . 'PublicKey>' . "\r\n" .
 399                     '<' . $pre . 'X Value="' . $publicKey[0] . '" />' . "\r\n" .
 400                     '<' . $pre . 'Y Value="' . $publicKey[1] . '" />' . "\r\n" .
 401                     '</' . $pre . 'PublicKey>' . "\r\n" .
 402                     '</' . $pre . 'ECDSAKeyValue>';
 403          }
 404  
 405          $publicKey = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();
 406  
 407          return '<' . $pre . 'ECDSAKeyValue xmlns' . $post . '="http://www.w3.org/2009/xmldsig11#">' . "\r\n" .
 408                 self::encodeXMLParameters($curve, $pre, $options) . "\r\n" .
 409                 '<' . $pre . 'PublicKey>' . Base64::encode($publicKey) . '</' . $pre . 'PublicKey>' . "\r\n" .
 410                 '</' . $pre . 'ECDSAKeyValue>';
 411      }
 412  
 413      /**
 414       * Encode Parameters
 415       *
 416       * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
 417       * @param string $pre
 418       * @param array $options optional
 419       * @return string|false
 420       */
 421      private static function encodeXMLParameters(BaseCurve $curve, $pre, array $options = [])
 422      {
 423          $result = self::encodeParameters($curve, true, $options);
 424  
 425          if (isset($result['namedCurve'])) {
 426              $namedCurve = '<' . $pre . 'NamedCurve URI="urn:oid:' . self::$curveOIDs[$result['namedCurve']] . '" />';
 427              return self::$rfc4050 ?
 428                  '<DomainParameters>' . str_replace('URI', 'URN', $namedCurve) . '</DomainParameters>' :
 429                  $namedCurve;
 430          }
 431  
 432          if (self::$rfc4050) {
 433              $xml = '<' . $pre . 'ExplicitParams>' . "\r\n" .
 434                    '<' . $pre . 'FieldParams>' . "\r\n";
 435              $temp = $result['specifiedCurve'];
 436              switch ($temp['fieldID']['fieldType']) {
 437                  case 'prime-field':
 438                      $xml .= '<' . $pre . 'PrimeFieldParamsType>' . "\r\n" .
 439                             '<' . $pre . 'P>' . $temp['fieldID']['parameters'] . '</' . $pre . 'P>' . "\r\n" .
 440                             '</' . $pre . 'PrimeFieldParamsType>' . "\r\n";
 441                      $a = $curve->getA();
 442                      $b = $curve->getB();
 443                      list($x, $y) = $curve->getBasePoint();
 444                      break;
 445                  default:
 446                      throw new UnsupportedCurveException('Field Type of ' . $temp['fieldID']['fieldType'] . ' is not supported');
 447              }
 448              $xml .= '</' . $pre . 'FieldParams>' . "\r\n" .
 449                     '<' . $pre . 'CurveParamsType>' . "\r\n" .
 450                     '<' . $pre . 'A>' . $a . '</' . $pre . 'A>' . "\r\n" .
 451                     '<' . $pre . 'B>' . $b . '</' . $pre . 'B>' . "\r\n" .
 452                     '</' . $pre . 'CurveParamsType>' . "\r\n" .
 453                     '<' . $pre . 'BasePointParams>' . "\r\n" .
 454                     '<' . $pre . 'BasePoint>' . "\r\n" .
 455                     '<' . $pre . 'ECPointType>' . "\r\n" .
 456                     '<' . $pre . 'X>' . $x . '</' . $pre . 'X>' . "\r\n" .
 457                     '<' . $pre . 'Y>' . $y . '</' . $pre . 'Y>' . "\r\n" .
 458                     '</' . $pre . 'ECPointType>' . "\r\n" .
 459                     '</' . $pre . 'BasePoint>' . "\r\n" .
 460                     '<' . $pre . 'Order>' . $curve->getOrder() . '</' . $pre . 'Order>' . "\r\n" .
 461                     '</' . $pre . 'BasePointParams>' . "\r\n" .
 462                     '</' . $pre . 'ExplicitParams>' . "\r\n";
 463  
 464              return $xml;
 465          }
 466  
 467          if (isset($result['specifiedCurve'])) {
 468              $xml = '<' . $pre . 'ECParameters>' . "\r\n" .
 469                     '<' . $pre . 'FieldID>' . "\r\n";
 470              $temp = $result['specifiedCurve'];
 471              switch ($temp['fieldID']['fieldType']) {
 472                  case 'prime-field':
 473                      $xml .= '<' . $pre . 'Prime>' . "\r\n" .
 474                             '<' . $pre . 'P>' . Base64::encode($temp['fieldID']['parameters']->toBytes()) . '</' . $pre . 'P>' . "\r\n" .
 475                             '</' . $pre . 'Prime>' . "\r\n" ;
 476                      break;
 477                  default:
 478                      throw new UnsupportedCurveException('Field Type of ' . $temp['fieldID']['fieldType'] . ' is not supported');
 479              }
 480              $xml .= '</' . $pre . 'FieldID>' . "\r\n" .
 481                     '<' . $pre . 'Curve>' . "\r\n" .
 482                     '<' . $pre . 'A>' . Base64::encode($temp['curve']['a']) . '</' . $pre . 'A>' . "\r\n" .
 483                     '<' . $pre . 'B>' . Base64::encode($temp['curve']['b']) . '</' . $pre . 'B>' . "\r\n" .
 484                     '</' . $pre . 'Curve>' . "\r\n" .
 485                     '<' . $pre . 'Base>' . Base64::encode($temp['base']) . '</' . $pre . 'Base>' . "\r\n" .
 486                     '<' . $pre . 'Order>' . Base64::encode($temp['order']) . '</' . $pre . 'Order>' . "\r\n" .
 487                     '</' . $pre . 'ECParameters>';
 488              return $xml;
 489          }
 490      }
 491  }


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