[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/ -> AsymmetricKey.php (source)

   1  <?php
   2  
   3  /**
   4   * Base Class for all asymmetric key ciphers
   5   *
   6   * PHP version 5
   7   *
   8   * @author    Jim Wigginton <terrafrost@php.net>
   9   * @copyright 2016 Jim Wigginton
  10   * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  11   * @link      http://phpseclib.sourceforge.net
  12   */
  13  
  14  namespace phpseclib3\Crypt\Common;
  15  
  16  use phpseclib3\Crypt\DSA;
  17  use phpseclib3\Crypt\Hash;
  18  use phpseclib3\Crypt\RSA;
  19  use phpseclib3\Exception\NoKeyLoadedException;
  20  use phpseclib3\Exception\UnsupportedFormatException;
  21  use phpseclib3\Math\BigInteger;
  22  
  23  /**
  24   * Base Class for all asymmetric cipher classes
  25   *
  26   * @author  Jim Wigginton <terrafrost@php.net>
  27   */
  28  abstract class AsymmetricKey
  29  {
  30      /**
  31       * Precomputed Zero
  32       *
  33       * @var \phpseclib3\Math\BigInteger
  34       */
  35      protected static $zero;
  36  
  37      /**
  38       * Precomputed One
  39       *
  40       * @var \phpseclib3\Math\BigInteger
  41       */
  42      protected static $one;
  43  
  44      /**
  45       * Format of the loaded key
  46       *
  47       * @var string
  48       */
  49      protected $format;
  50  
  51      /**
  52       * Hash function
  53       *
  54       * @var \phpseclib3\Crypt\Hash
  55       */
  56      protected $hash;
  57  
  58      /**
  59       * HMAC function
  60       *
  61       * @var \phpseclib3\Crypt\Hash
  62       */
  63      private $hmac;
  64  
  65      /**
  66       * Supported plugins (lower case)
  67       *
  68       * @see self::initialize_static_variables()
  69       * @var array
  70       */
  71      private static $plugins = [];
  72  
  73      /**
  74       * Invisible plugins
  75       *
  76       * @see self::initialize_static_variables()
  77       * @var array
  78       */
  79      private static $invisiblePlugins = [];
  80  
  81      /**
  82       * Available Engines
  83       *
  84       * @var boolean[]
  85       */
  86      protected static $engines = [];
  87  
  88      /**
  89       * Key Comment
  90       *
  91       * @var null|string
  92       */
  93      private $comment;
  94  
  95      /**
  96       * @param string $type
  97       * @return array|string
  98       */
  99      abstract public function toString($type, array $options = []);
 100  
 101      /**
 102       * The constructor
 103       */
 104      protected function __construct()
 105      {
 106          self::initialize_static_variables();
 107  
 108          $this->hash = new Hash('sha256');
 109          $this->hmac = new Hash('sha256');
 110      }
 111  
 112      /**
 113       * Initialize static variables
 114       */
 115      protected static function initialize_static_variables()
 116      {
 117          if (!isset(self::$zero)) {
 118              self::$zero = new BigInteger(0);
 119              self::$one = new BigInteger(1);
 120          }
 121  
 122          self::loadPlugins('Keys');
 123          if (static::ALGORITHM != 'RSA' && static::ALGORITHM != 'DH') {
 124              self::loadPlugins('Signature');
 125          }
 126      }
 127  
 128      /**
 129       * Load the key
 130       *
 131       * @param string $key
 132       * @param string $password optional
 133       * @return \phpseclib3\Crypt\Common\PublicKey|\phpseclib3\Crypt\Common\PrivateKey
 134       */
 135      public static function load($key, $password = false)
 136      {
 137          self::initialize_static_variables();
 138  
 139          $class = new \ReflectionClass(static::class);
 140          if ($class->isFinal()) {
 141              throw new \RuntimeException('load() should not be called from final classes (' . static::class . ')');
 142          }
 143  
 144          $components = false;
 145          foreach (self::$plugins[static::ALGORITHM]['Keys'] as $format) {
 146              if (isset(self::$invisiblePlugins[static::ALGORITHM]) && in_array($format, self::$invisiblePlugins[static::ALGORITHM])) {
 147                  continue;
 148              }
 149              try {
 150                  $components = $format::load($key, $password);
 151              } catch (\Exception $e) {
 152                  $components = false;
 153              }
 154              if ($components !== false) {
 155                  break;
 156              }
 157          }
 158  
 159          if ($components === false) {
 160              throw new NoKeyLoadedException('Unable to read key');
 161          }
 162  
 163          $components['format'] = $format;
 164          $components['secret'] = isset($components['secret']) ? $components['secret'] : '';
 165          $comment = isset($components['comment']) ? $components['comment'] : null;
 166          $new = static::onLoad($components);
 167          $new->format = $format;
 168          $new->comment = $comment;
 169          return $new instanceof PrivateKey ?
 170              $new->withPassword($password) :
 171              $new;
 172      }
 173  
 174      /**
 175       * Loads a private key
 176       *
 177       * @return PrivateKey
 178       * @param string|array $key
 179       * @param string $password optional
 180       */
 181      public static function loadPrivateKey($key, $password = '')
 182      {
 183          $key = self::load($key, $password);
 184          if (!$key instanceof PrivateKey) {
 185              throw new NoKeyLoadedException('The key that was loaded was not a private key');
 186          }
 187          return $key;
 188      }
 189  
 190      /**
 191       * Loads a public key
 192       *
 193       * @return PublicKey
 194       * @param string|array $key
 195       */
 196      public static function loadPublicKey($key)
 197      {
 198          $key = self::load($key);
 199          if (!$key instanceof PublicKey) {
 200              throw new NoKeyLoadedException('The key that was loaded was not a public key');
 201          }
 202          return $key;
 203      }
 204  
 205      /**
 206       * Loads parameters
 207       *
 208       * @return AsymmetricKey
 209       * @param string|array $key
 210       */
 211      public static function loadParameters($key)
 212      {
 213          $key = self::load($key);
 214          if (!$key instanceof PrivateKey && !$key instanceof PublicKey) {
 215              throw new NoKeyLoadedException('The key that was loaded was not a parameter');
 216          }
 217          return $key;
 218      }
 219  
 220      /**
 221       * Load the key, assuming a specific format
 222       *
 223       * @param string $type
 224       * @param string $key
 225       * @param string $password optional
 226       * @return static
 227       */
 228      public static function loadFormat($type, $key, $password = false)
 229      {
 230          self::initialize_static_variables();
 231  
 232          $components = false;
 233          $format = strtolower($type);
 234          if (isset(self::$plugins[static::ALGORITHM]['Keys'][$format])) {
 235              $format = self::$plugins[static::ALGORITHM]['Keys'][$format];
 236              $components = $format::load($key, $password);
 237          }
 238  
 239          if ($components === false) {
 240              throw new NoKeyLoadedException('Unable to read key');
 241          }
 242  
 243          $components['format'] = $format;
 244          $components['secret'] = isset($components['secret']) ? $components['secret'] : '';
 245  
 246          $new = static::onLoad($components);
 247          $new->format = $format;
 248          return $new instanceof PrivateKey ?
 249              $new->withPassword($password) :
 250              $new;
 251      }
 252  
 253      /**
 254       * Loads a private key
 255       *
 256       * @return PrivateKey
 257       * @param string $type
 258       * @param string $key
 259       * @param string $password optional
 260       */
 261      public static function loadPrivateKeyFormat($type, $key, $password = false)
 262      {
 263          $key = self::loadFormat($type, $key, $password);
 264          if (!$key instanceof PrivateKey) {
 265              throw new NoKeyLoadedException('The key that was loaded was not a private key');
 266          }
 267          return $key;
 268      }
 269  
 270      /**
 271       * Loads a public key
 272       *
 273       * @return PublicKey
 274       * @param string $type
 275       * @param string $key
 276       */
 277      public static function loadPublicKeyFormat($type, $key)
 278      {
 279          $key = self::loadFormat($type, $key);
 280          if (!$key instanceof PublicKey) {
 281              throw new NoKeyLoadedException('The key that was loaded was not a public key');
 282          }
 283          return $key;
 284      }
 285  
 286      /**
 287       * Loads parameters
 288       *
 289       * @return AsymmetricKey
 290       * @param string $type
 291       * @param string|array $key
 292       */
 293      public static function loadParametersFormat($type, $key)
 294      {
 295          $key = self::loadFormat($type, $key);
 296          if (!$key instanceof PrivateKey && !$key instanceof PublicKey) {
 297              throw new NoKeyLoadedException('The key that was loaded was not a parameter');
 298          }
 299          return $key;
 300      }
 301  
 302      /**
 303       * Validate Plugin
 304       *
 305       * @param string $format
 306       * @param string $type
 307       * @param string $method optional
 308       * @return mixed
 309       */
 310      protected static function validatePlugin($format, $type, $method = null)
 311      {
 312          $type = strtolower($type);
 313          if (!isset(self::$plugins[static::ALGORITHM][$format][$type])) {
 314              throw new UnsupportedFormatException("$type is not a supported format");
 315          }
 316          $type = self::$plugins[static::ALGORITHM][$format][$type];
 317          if (isset($method) && !method_exists($type, $method)) {
 318              throw new UnsupportedFormatException("$type does not implement $method");
 319          }
 320  
 321          return $type;
 322      }
 323  
 324      /**
 325       * Load Plugins
 326       *
 327       * @param string $format
 328       */
 329      private static function loadPlugins($format)
 330      {
 331          if (!isset(self::$plugins[static::ALGORITHM][$format])) {
 332              self::$plugins[static::ALGORITHM][$format] = [];
 333              foreach (new \DirectoryIterator(__DIR__ . '/../' . static::ALGORITHM . '/Formats/' . $format . '/') as $file) {
 334                  if ($file->getExtension() != 'php') {
 335                      continue;
 336                  }
 337                  $name = $file->getBasename('.php');
 338                  if ($name[0] == '.') {
 339                      continue;
 340                  }
 341                  $type = 'phpseclib3\Crypt\\' . static::ALGORITHM . '\\Formats\\' . $format . '\\' . $name;
 342                  $reflect = new \ReflectionClass($type);
 343                  if ($reflect->isTrait()) {
 344                      continue;
 345                  }
 346                  self::$plugins[static::ALGORITHM][$format][strtolower($name)] = $type;
 347                  if ($reflect->hasConstant('IS_INVISIBLE')) {
 348                      self::$invisiblePlugins[static::ALGORITHM][] = $type;
 349                  }
 350              }
 351          }
 352      }
 353  
 354      /**
 355       * Returns a list of supported formats.
 356       *
 357       * @return array
 358       */
 359      public static function getSupportedKeyFormats()
 360      {
 361          self::initialize_static_variables();
 362  
 363          return self::$plugins[static::ALGORITHM]['Keys'];
 364      }
 365  
 366      /**
 367       * Add a fileformat plugin
 368       *
 369       * The plugin needs to either already be loaded or be auto-loadable.
 370       * Loading a plugin whose shortname overwrite an existing shortname will overwrite the old plugin.
 371       *
 372       * @see self::load()
 373       * @param string $fullname
 374       * @return bool
 375       */
 376      public static function addFileFormat($fullname)
 377      {
 378          self::initialize_static_variables();
 379  
 380          if (class_exists($fullname)) {
 381              $meta = new \ReflectionClass($fullname);
 382              $shortname = $meta->getShortName();
 383              self::$plugins[static::ALGORITHM]['Keys'][strtolower($shortname)] = $fullname;
 384              if ($meta->hasConstant('IS_INVISIBLE')) {
 385                  self::$invisiblePlugins[static::ALGORITHM][] = strtolower($shortname);
 386              }
 387          }
 388      }
 389  
 390      /**
 391       * Returns the format of the loaded key.
 392       *
 393       * If the key that was loaded wasn't in a valid or if the key was auto-generated
 394       * with RSA::createKey() then this will throw an exception.
 395       *
 396       * @see self::load()
 397       * @return mixed
 398       */
 399      public function getLoadedFormat()
 400      {
 401          if (empty($this->format)) {
 402              throw new NoKeyLoadedException('This key was created with createKey - it was not loaded with load. Therefore there is no "loaded format"');
 403          }
 404  
 405          $meta = new \ReflectionClass($this->format);
 406          return $meta->getShortName();
 407      }
 408  
 409      /**
 410       * Returns the key's comment
 411       *
 412       * Not all key formats support comments. If you want to set a comment use toString()
 413       *
 414       * @return null|string
 415       */
 416      public function getComment()
 417      {
 418          return $this->comment;
 419      }
 420  
 421      /**
 422       * Tests engine validity
 423       *
 424       */
 425      public static function useBestEngine()
 426      {
 427          static::$engines = [
 428              'PHP' => true,
 429              'OpenSSL' => extension_loaded('openssl'),
 430              // this test can be satisfied by either of the following:
 431              // http://php.net/manual/en/book.sodium.php
 432              // https://github.com/paragonie/sodium_compat
 433              'libsodium' => function_exists('sodium_crypto_sign_keypair')
 434          ];
 435  
 436          return static::$engines;
 437      }
 438  
 439      /**
 440       * Flag to use internal engine only (useful for unit testing)
 441       *
 442       */
 443      public static function useInternalEngine()
 444      {
 445          static::$engines = [
 446              'PHP' => true,
 447              'OpenSSL' => false,
 448              'libsodium' => false
 449          ];
 450      }
 451  
 452      /**
 453       * __toString() magic method
 454       *
 455       * @return string
 456       */
 457      public function __toString()
 458      {
 459          return $this->toString('PKCS8');
 460      }
 461  
 462      /**
 463       * Determines which hashing function should be used
 464       *
 465       * @param string $hash
 466       */
 467      public function withHash($hash)
 468      {
 469          $new = clone $this;
 470  
 471          $new->hash = new Hash($hash);
 472          $new->hmac = new Hash($hash);
 473  
 474          return $new;
 475      }
 476  
 477      /**
 478       * Returns the hash algorithm currently being used
 479       *
 480       */
 481      public function getHash()
 482      {
 483          return clone $this->hash;
 484      }
 485  
 486      /**
 487       * Compute the pseudorandom k for signature generation,
 488       * using the process specified for deterministic DSA.
 489       *
 490       * @param string $h1
 491       * @return string
 492       */
 493      protected function computek($h1)
 494      {
 495          $v = str_repeat("\1", strlen($h1));
 496  
 497          $k = str_repeat("\0", strlen($h1));
 498  
 499          $x = $this->int2octets($this->x);
 500          $h1 = $this->bits2octets($h1);
 501  
 502          $this->hmac->setKey($k);
 503          $k = $this->hmac->hash($v . "\0" . $x . $h1);
 504          $this->hmac->setKey($k);
 505          $v = $this->hmac->hash($v);
 506          $k = $this->hmac->hash($v . "\1" . $x . $h1);
 507          $this->hmac->setKey($k);
 508          $v = $this->hmac->hash($v);
 509  
 510          $qlen = $this->q->getLengthInBytes();
 511  
 512          while (true) {
 513              $t = '';
 514              while (strlen($t) < $qlen) {
 515                  $v = $this->hmac->hash($v);
 516                  $t = $t . $v;
 517              }
 518              $k = $this->bits2int($t);
 519  
 520              if (!$k->equals(self::$zero) && $k->compare($this->q) < 0) {
 521                  break;
 522              }
 523              $k = $this->hmac->hash($v . "\0");
 524              $this->hmac->setKey($k);
 525              $v = $this->hmac->hash($v);
 526          }
 527  
 528          return $k;
 529      }
 530  
 531      /**
 532       * Integer to Octet String
 533       *
 534       * @param \phpseclib3\Math\BigInteger $v
 535       * @return string
 536       */
 537      private function int2octets($v)
 538      {
 539          $out = $v->toBytes();
 540          $rolen = $this->q->getLengthInBytes();
 541          if (strlen($out) < $rolen) {
 542              return str_pad($out, $rolen, "\0", STR_PAD_LEFT);
 543          } elseif (strlen($out) > $rolen) {
 544              return substr($out, -$rolen);
 545          } else {
 546              return $out;
 547          }
 548      }
 549  
 550      /**
 551       * Bit String to Integer
 552       *
 553       * @param string $in
 554       * @return \phpseclib3\Math\BigInteger
 555       */
 556      protected function bits2int($in)
 557      {
 558          $v = new BigInteger($in, 256);
 559          $vlen = strlen($in) << 3;
 560          $qlen = $this->q->getLength();
 561          if ($vlen > $qlen) {
 562              return $v->bitwise_rightShift($vlen - $qlen);
 563          }
 564          return $v;
 565      }
 566  
 567      /**
 568       * Bit String to Octet String
 569       *
 570       * @param string $in
 571       * @return string
 572       */
 573      private function bits2octets($in)
 574      {
 575          $z1 = $this->bits2int($in);
 576          $z2 = $z1->subtract($this->q);
 577          return $z2->compare(self::$zero) < 0 ?
 578              $this->int2octets($z1) :
 579              $this->int2octets($z2);
 580      }
 581  }