[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/ -> PrivateKey.php (source)

   1  <?php
   2  
   3  /**
   4   * RSA Private Key
   5   *
   6   * @author    Jim Wigginton <terrafrost@php.net>
   7   * @copyright 2015 Jim Wigginton
   8   * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
   9   * @link      http://phpseclib.sourceforge.net
  10   */
  11  
  12  namespace phpseclib3\Crypt\RSA;
  13  
  14  use phpseclib3\Crypt\Common;
  15  use phpseclib3\Crypt\Random;
  16  use phpseclib3\Crypt\RSA;
  17  use phpseclib3\Crypt\RSA\Formats\Keys\PSS;
  18  use phpseclib3\Exception\UnsupportedFormatException;
  19  use phpseclib3\Math\BigInteger;
  20  
  21  /**
  22   * Raw RSA Key Handler
  23   *
  24   * @author  Jim Wigginton <terrafrost@php.net>
  25   */
  26  final class PrivateKey extends RSA implements Common\PrivateKey
  27  {
  28      use Common\Traits\PasswordProtected;
  29  
  30      /**
  31       * Primes for Chinese Remainder Theorem (ie. p and q)
  32       *
  33       * @var array
  34       */
  35      protected $primes;
  36  
  37      /**
  38       * Exponents for Chinese Remainder Theorem (ie. dP and dQ)
  39       *
  40       * @var array
  41       */
  42      protected $exponents;
  43  
  44      /**
  45       * Coefficients for Chinese Remainder Theorem (ie. qInv)
  46       *
  47       * @var array
  48       */
  49      protected $coefficients;
  50  
  51      /**
  52       * Private Exponent
  53       *
  54       * @var \phpseclib3\Math\BigInteger
  55       */
  56      protected $privateExponent;
  57  
  58      /**
  59       * RSADP
  60       *
  61       * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
  62       *
  63       * @return bool|\phpseclib3\Math\BigInteger
  64       */
  65      private function rsadp(BigInteger $c)
  66      {
  67          if ($c->compare(self::$zero) < 0 || $c->compare($this->modulus) > 0) {
  68              throw new \OutOfRangeException('Ciphertext representative out of range');
  69          }
  70          return $this->exponentiate($c);
  71      }
  72  
  73      /**
  74       * RSASP1
  75       *
  76       * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
  77       *
  78       * @return bool|\phpseclib3\Math\BigInteger
  79       */
  80      private function rsasp1(BigInteger $m)
  81      {
  82          if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) {
  83              throw new \OutOfRangeException('Signature representative out of range');
  84          }
  85          return $this->exponentiate($m);
  86      }
  87  
  88      /**
  89       * Exponentiate
  90       *
  91       * @param \phpseclib3\Math\BigInteger $x
  92       * @return \phpseclib3\Math\BigInteger
  93       */
  94      protected function exponentiate(BigInteger $x)
  95      {
  96          switch (true) {
  97              case empty($this->primes):
  98              case $this->primes[1]->equals(self::$zero):
  99              case empty($this->coefficients):
 100              case $this->coefficients[2]->equals(self::$zero):
 101              case empty($this->exponents):
 102              case $this->exponents[1]->equals(self::$zero):
 103                  return $x->modPow($this->exponent, $this->modulus);
 104          }
 105  
 106          $num_primes = count($this->primes);
 107  
 108          if (!static::$enableBlinding) {
 109              $m_i = [
 110                  1 => $x->modPow($this->exponents[1], $this->primes[1]),
 111                  2 => $x->modPow($this->exponents[2], $this->primes[2])
 112              ];
 113              $h = $m_i[1]->subtract($m_i[2]);
 114              $h = $h->multiply($this->coefficients[2]);
 115              list(, $h) = $h->divide($this->primes[1]);
 116              $m = $m_i[2]->add($h->multiply($this->primes[2]));
 117  
 118              $r = $this->primes[1];
 119              for ($i = 3; $i <= $num_primes; $i++) {
 120                  $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
 121  
 122                  $r = $r->multiply($this->primes[$i - 1]);
 123  
 124                  $h = $m_i->subtract($m);
 125                  $h = $h->multiply($this->coefficients[$i]);
 126                  list(, $h) = $h->divide($this->primes[$i]);
 127  
 128                  $m = $m->add($r->multiply($h));
 129              }
 130          } else {
 131              $smallest = $this->primes[1];
 132              for ($i = 2; $i <= $num_primes; $i++) {
 133                  if ($smallest->compare($this->primes[$i]) > 0) {
 134                      $smallest = $this->primes[$i];
 135                  }
 136              }
 137  
 138              $r = BigInteger::randomRange(self::$one, $smallest->subtract(self::$one));
 139  
 140              $m_i = [
 141                  1 => $this->blind($x, $r, 1),
 142                  2 => $this->blind($x, $r, 2)
 143              ];
 144              $h = $m_i[1]->subtract($m_i[2]);
 145              $h = $h->multiply($this->coefficients[2]);
 146              list(, $h) = $h->divide($this->primes[1]);
 147              $m = $m_i[2]->add($h->multiply($this->primes[2]));
 148  
 149              $r = $this->primes[1];
 150              for ($i = 3; $i <= $num_primes; $i++) {
 151                  $m_i = $this->blind($x, $r, $i);
 152  
 153                  $r = $r->multiply($this->primes[$i - 1]);
 154  
 155                  $h = $m_i->subtract($m);
 156                  $h = $h->multiply($this->coefficients[$i]);
 157                  list(, $h) = $h->divide($this->primes[$i]);
 158  
 159                  $m = $m->add($r->multiply($h));
 160              }
 161          }
 162  
 163          return $m;
 164      }
 165  
 166      /**
 167       * Performs RSA Blinding
 168       *
 169       * Protects against timing attacks by employing RSA Blinding.
 170       * Returns $x->modPow($this->exponents[$i], $this->primes[$i])
 171       *
 172       * @param \phpseclib3\Math\BigInteger $x
 173       * @param \phpseclib3\Math\BigInteger $r
 174       * @param int $i
 175       * @return \phpseclib3\Math\BigInteger
 176       */
 177      private function blind(BigInteger $x, BigInteger $r, $i)
 178      {
 179          $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
 180          $x = $x->modPow($this->exponents[$i], $this->primes[$i]);
 181  
 182          $r = $r->modInverse($this->primes[$i]);
 183          $x = $x->multiply($r);
 184          list(, $x) = $x->divide($this->primes[$i]);
 185  
 186          return $x;
 187      }
 188  
 189      /**
 190       * EMSA-PSS-ENCODE
 191       *
 192       * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
 193       *
 194       * @return string
 195       * @param string $m
 196       * @throws \RuntimeException on encoding error
 197       * @param int $emBits
 198       */
 199      private function emsa_pss_encode($m, $emBits)
 200      {
 201          // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
 202          // be output.
 203  
 204          $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
 205          $sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
 206  
 207          $mHash = $this->hash->hash($m);
 208          if ($emLen < $this->hLen + $sLen + 2) {
 209              throw new \LengthException('RSA modulus too short');
 210          }
 211  
 212          $salt = Random::string($sLen);
 213          $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
 214          $h = $this->hash->hash($m2);
 215          $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
 216          $db = $ps . chr(1) . $salt;
 217          $dbMask = $this->mgf1($h, $emLen - $this->hLen - 1); // ie. stlren($db)
 218          $maskedDB = $db ^ $dbMask;
 219          $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
 220          $em = $maskedDB . $h . chr(0xBC);
 221  
 222          return $em;
 223      }
 224  
 225      /**
 226       * RSASSA-PSS-SIGN
 227       *
 228       * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
 229       *
 230       * @param string $m
 231       * @return bool|string
 232       */
 233      private function rsassa_pss_sign($m)
 234      {
 235          // EMSA-PSS encoding
 236  
 237          $em = $this->emsa_pss_encode($m, 8 * $this->k - 1);
 238  
 239          // RSA signature
 240  
 241          $m = $this->os2ip($em);
 242          $s = $this->rsasp1($m);
 243          $s = $this->i2osp($s, $this->k);
 244  
 245          // Output the signature S
 246  
 247          return $s;
 248      }
 249  
 250      /**
 251       * RSASSA-PKCS1-V1_5-SIGN
 252       *
 253       * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
 254       *
 255       * @param string $m
 256       * @throws \LengthException if the RSA modulus is too short
 257       * @return bool|string
 258       */
 259      private function rsassa_pkcs1_v1_5_sign($m)
 260      {
 261          // EMSA-PKCS1-v1_5 encoding
 262  
 263          // If the encoding operation outputs "intended encoded message length too short," output "RSA modulus
 264          // too short" and stop.
 265          try {
 266              $em = $this->emsa_pkcs1_v1_5_encode($m, $this->k);
 267          } catch (\LengthException $e) {
 268              throw new \LengthException('RSA modulus too short');
 269          }
 270  
 271          // RSA signature
 272  
 273          $m = $this->os2ip($em);
 274          $s = $this->rsasp1($m);
 275          $s = $this->i2osp($s, $this->k);
 276  
 277          // Output the signature S
 278  
 279          return $s;
 280      }
 281  
 282      /**
 283       * Create a signature
 284       *
 285       * @see self::verify()
 286       * @param string $message
 287       * @return string
 288       */
 289      public function sign($message)
 290      {
 291          switch ($this->signaturePadding) {
 292              case self::SIGNATURE_PKCS1:
 293              case self::SIGNATURE_RELAXED_PKCS1:
 294                  return $this->rsassa_pkcs1_v1_5_sign($message);
 295              //case self::SIGNATURE_PSS:
 296              default:
 297                  return $this->rsassa_pss_sign($message);
 298          }
 299      }
 300  
 301      /**
 302       * RSAES-PKCS1-V1_5-DECRYPT
 303       *
 304       * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
 305       *
 306       * @param string $c
 307       * @return bool|string
 308       */
 309      private function rsaes_pkcs1_v1_5_decrypt($c)
 310      {
 311          // Length checking
 312  
 313          if (strlen($c) != $this->k) { // or if k < 11
 314              throw new \LengthException('Ciphertext representative too long');
 315          }
 316  
 317          // RSA decryption
 318  
 319          $c = $this->os2ip($c);
 320          $m = $this->rsadp($c);
 321          $em = $this->i2osp($m, $this->k);
 322  
 323          // EME-PKCS1-v1_5 decoding
 324  
 325          if (ord($em[0]) != 0 || ord($em[1]) > 2) {
 326              throw new \RuntimeException('Decryption error');
 327          }
 328  
 329          $ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
 330          $m = substr($em, strlen($ps) + 3);
 331  
 332          if (strlen($ps) < 8) {
 333              throw new \RuntimeException('Decryption error');
 334          }
 335  
 336          // Output M
 337  
 338          return $m;
 339      }
 340  
 341      /**
 342       * RSAES-OAEP-DECRYPT
 343       *
 344       * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}.  The fact that the error
 345       * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
 346       *
 347       *    Note.  Care must be taken to ensure that an opponent cannot
 348       *    distinguish the different error conditions in Step 3.g, whether by
 349       *    error message or timing, or, more generally, learn partial
 350       *    information about the encoded message EM.  Otherwise an opponent may
 351       *    be able to obtain useful information about the decryption of the
 352       *    ciphertext C, leading to a chosen-ciphertext attack such as the one
 353       *    observed by Manger [36].
 354       *
 355       * @param string $c
 356       * @return bool|string
 357       */
 358      private function rsaes_oaep_decrypt($c)
 359      {
 360          // Length checking
 361  
 362          // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
 363          // be output.
 364  
 365          if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
 366              throw new \LengthException('Ciphertext representative too long');
 367          }
 368  
 369          // RSA decryption
 370  
 371          $c = $this->os2ip($c);
 372          $m = $this->rsadp($c);
 373          $em = $this->i2osp($m, $this->k);
 374  
 375          // EME-OAEP decoding
 376  
 377          $lHash = $this->hash->hash($this->label);
 378          $y = ord($em[0]);
 379          $maskedSeed = substr($em, 1, $this->hLen);
 380          $maskedDB = substr($em, $this->hLen + 1);
 381          $seedMask = $this->mgf1($maskedDB, $this->hLen);
 382          $seed = $maskedSeed ^ $seedMask;
 383          $dbMask = $this->mgf1($seed, $this->k - $this->hLen - 1);
 384          $db = $maskedDB ^ $dbMask;
 385          $lHash2 = substr($db, 0, $this->hLen);
 386          $m = substr($db, $this->hLen);
 387          $hashesMatch = hash_equals($lHash, $lHash2);
 388          $leadingZeros = 1;
 389          $patternMatch = 0;
 390          $offset = 0;
 391          for ($i = 0; $i < strlen($m); $i++) {
 392              $patternMatch |= $leadingZeros & ($m[$i] === "\1");
 393              $leadingZeros &= $m[$i] === "\0";
 394              $offset += $patternMatch ? 0 : 1;
 395          }
 396  
 397          // we do | instead of || to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation
 398          // to protect against timing attacks
 399          if (!$hashesMatch | !$patternMatch) {
 400              throw new \RuntimeException('Decryption error');
 401          }
 402  
 403          // Output the message M
 404  
 405          return substr($m, $offset + 1);
 406      }
 407  
 408      /**
 409       * Raw Encryption / Decryption
 410       *
 411       * Doesn't use padding and is not recommended.
 412       *
 413       * @param string $m
 414       * @return bool|string
 415       * @throws \LengthException if strlen($m) > $this->k
 416       */
 417      private function raw_encrypt($m)
 418      {
 419          if (strlen($m) > $this->k) {
 420              throw new \LengthException('Ciphertext representative too long');
 421          }
 422  
 423          $temp = $this->os2ip($m);
 424          $temp = $this->rsadp($temp);
 425          return  $this->i2osp($temp, $this->k);
 426      }
 427  
 428      /**
 429       * Decryption
 430       *
 431       * @see self::encrypt()
 432       * @param string $ciphertext
 433       * @return bool|string
 434       */
 435      public function decrypt($ciphertext)
 436      {
 437          switch ($this->encryptionPadding) {
 438              case self::ENCRYPTION_NONE:
 439                  return $this->raw_encrypt($ciphertext);
 440              case self::ENCRYPTION_PKCS1:
 441                  return $this->rsaes_pkcs1_v1_5_decrypt($ciphertext);
 442              //case self::ENCRYPTION_OAEP:
 443              default:
 444                  return $this->rsaes_oaep_decrypt($ciphertext);
 445          }
 446      }
 447  
 448      /**
 449       * Returns the public key
 450       *
 451       * @return mixed
 452       */
 453      public function getPublicKey()
 454      {
 455          $type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey');
 456          if (empty($this->modulus) || empty($this->publicExponent)) {
 457              throw new \RuntimeException('Public key components not found');
 458          }
 459  
 460          $key = $type::savePublicKey($this->modulus, $this->publicExponent);
 461          return RSA::loadFormat('PKCS8', $key)
 462              ->withHash($this->hash->getHash())
 463              ->withMGFHash($this->mgfHash->getHash())
 464              ->withSaltLength($this->sLen)
 465              ->withLabel($this->label)
 466              ->withPadding($this->signaturePadding | $this->encryptionPadding);
 467      }
 468  
 469      /**
 470       * Returns the private key
 471       *
 472       * @param string $type
 473       * @param array $options optional
 474       * @return string
 475       */
 476      public function toString($type, array $options = [])
 477      {
 478          $type = self::validatePlugin(
 479              'Keys',
 480              $type,
 481              empty($this->primes) ? 'savePublicKey' : 'savePrivateKey'
 482          );
 483  
 484          if ($type == PSS::class) {
 485              if ($this->signaturePadding == self::SIGNATURE_PSS) {
 486                  $options += [
 487                      'hash' => $this->hash->getHash(),
 488                      'MGFHash' => $this->mgfHash->getHash(),
 489                      'saltLength' => $this->getSaltLength()
 490                  ];
 491              } else {
 492                  throw new UnsupportedFormatException('The PSS format can only be used when the signature method has been explicitly set to PSS');
 493              }
 494          }
 495  
 496          if (empty($this->primes)) {
 497              return $type::savePublicKey($this->modulus, $this->exponent, $options);
 498          }
 499  
 500          return $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password, $options);
 501  
 502          /*
 503          $key = $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password, $options);
 504          if ($key !== false || count($this->primes) == 2) {
 505              return $key;
 506          }
 507  
 508          $nSize = $this->getSize() >> 1;
 509  
 510          $primes = [1 => clone self::$one, clone self::$one];
 511          $i = 1;
 512          foreach ($this->primes as $prime) {
 513              $primes[$i] = $primes[$i]->multiply($prime);
 514              if ($primes[$i]->getLength() >= $nSize) {
 515                  $i++;
 516              }
 517          }
 518  
 519          $exponents = [];
 520          $coefficients = [2 => $primes[2]->modInverse($primes[1])];
 521  
 522          foreach ($primes as $i => $prime) {
 523              $temp = $prime->subtract(self::$one);
 524              $exponents[$i] = $this->modulus->modInverse($temp);
 525          }
 526  
 527          return $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $primes, $exponents, $coefficients, $this->password, $options);
 528          */
 529      }
 530  }