Subversion Repositories oidplus

Rev

Rev 846 | Rev 1042 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

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