Subversion Repositories oidplus

Rev

Rev 874 | 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.  * @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. 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. }
  531.