Subversion Repositories oidplus

Rev

Rev 874 | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /**
  4.  * "PKCS1" (RFC5915) Formatted EC Key Handler
  5.  *
  6.  * PHP version 5
  7.  *
  8.  * Used by File/X509.php
  9.  *
  10.  * Processes keys with the following headers:
  11.  *
  12.  * -----BEGIN EC PRIVATE KEY-----
  13.  * -----BEGIN EC PARAMETERS-----
  14.  *
  15.  * Technically, PKCS1 is for RSA keys, only, but we're using PKCS1 to describe
  16.  * DSA, whose format isn't really formally described anywhere, so might as well
  17.  * use it to describe this, too. PKCS1 is easier to remember than RFC5915, after
  18.  * all. I suppose this could also be named IETF but idk
  19.  *
  20.  * @author    Jim Wigginton <terrafrost@php.net>
  21.  * @copyright 2015 Jim Wigginton
  22.  * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  23.  * @link      http://phpseclib.sourceforge.net
  24.  */
  25.  
  26. namespace phpseclib3\Crypt\EC\Formats\Keys;
  27.  
  28. use phpseclib3\Common\Functions\Strings;
  29. use phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor;
  30. use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
  31. use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
  32. use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
  33. use phpseclib3\Exception\UnsupportedCurveException;
  34. use phpseclib3\File\ASN1;
  35. use phpseclib3\File\ASN1\Maps;
  36. use phpseclib3\Math\BigInteger;
  37.  
  38. /**
  39.  * "PKCS1" (RFC5915) Formatted EC Key Handler
  40.  *
  41.  * @author  Jim Wigginton <terrafrost@php.net>
  42.  */
  43. abstract class PKCS1 extends Progenitor
  44. {
  45.     use Common;
  46.  
  47.     /**
  48.      * Break a public or private key down into its constituent components
  49.      *
  50.      * @param string $key
  51.      * @param string $password optional
  52.      * @return array
  53.      */
  54.     public static function load($key, $password = '')
  55.     {
  56.         self::initialize_static_variables();
  57.  
  58.         if (!Strings::is_stringable($key)) {
  59.             throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
  60.         }
  61.  
  62.         if (strpos($key, 'BEGIN EC PARAMETERS') && strpos($key, 'BEGIN EC PRIVATE KEY')) {
  63.             $components = [];
  64.  
  65.             preg_match('#-*BEGIN EC PRIVATE KEY-*[^-]*-*END EC PRIVATE KEY-*#s', $key, $matches);
  66.             $decoded = parent::load($matches[0], $password);
  67.             $decoded = ASN1::decodeBER($decoded);
  68.             if (!$decoded) {
  69.                 throw new \RuntimeException('Unable to decode BER');
  70.             }
  71.  
  72.             $ecPrivate = ASN1::asn1map($decoded[0], Maps\ECPrivateKey::MAP);
  73.             if (!is_array($ecPrivate)) {
  74.                 throw new \RuntimeException('Unable to perform ASN1 mapping');
  75.             }
  76.  
  77.             if (isset($ecPrivate['parameters'])) {
  78.                 $components['curve'] = self::loadCurveByParam($ecPrivate['parameters']);
  79.             }
  80.  
  81.             preg_match('#-*BEGIN EC PARAMETERS-*[^-]*-*END EC PARAMETERS-*#s', $key, $matches);
  82.             $decoded = parent::load($matches[0], '');
  83.             $decoded = ASN1::decodeBER($decoded);
  84.             if (!$decoded) {
  85.                 throw new \RuntimeException('Unable to decode BER');
  86.             }
  87.             $ecParams = ASN1::asn1map($decoded[0], Maps\ECParameters::MAP);
  88.             if (!is_array($ecParams)) {
  89.                 throw new \RuntimeException('Unable to perform ASN1 mapping');
  90.             }
  91.             $ecParams = self::loadCurveByParam($ecParams);
  92.  
  93.             // comparing $ecParams and $components['curve'] directly won't work because they'll have different Math\Common\FiniteField classes
  94.             // even if the modulo is the same
  95.             if (isset($components['curve']) && self::encodeParameters($ecParams, false, []) != self::encodeParameters($components['curve'], false, [])) {
  96.                 throw new \RuntimeException('EC PARAMETERS does not correspond to EC PRIVATE KEY');
  97.             }
  98.  
  99.             if (!isset($components['curve'])) {
  100.                 $components['curve'] = $ecParams;
  101.             }
  102.  
  103.             $components['dA'] = new BigInteger($ecPrivate['privateKey'], 256);
  104.             $components['curve']->rangeCheck($components['dA']);
  105.             $components['QA'] = isset($ecPrivate['publicKey']) ?
  106.                 self::extractPoint($ecPrivate['publicKey'], $components['curve']) :
  107.                 $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);
  108.  
  109.             return $components;
  110.         }
  111.  
  112.         $key = parent::load($key, $password);
  113.  
  114.         $decoded = ASN1::decodeBER($key);
  115.         if (!$decoded) {
  116.             throw new \RuntimeException('Unable to decode BER');
  117.         }
  118.  
  119.         $key = ASN1::asn1map($decoded[0], Maps\ECParameters::MAP);
  120.         if (is_array($key)) {
  121.             return ['curve' => self::loadCurveByParam($key)];
  122.         }
  123.  
  124.         $key = ASN1::asn1map($decoded[0], Maps\ECPrivateKey::MAP);
  125.         if (!is_array($key)) {
  126.             throw new \RuntimeException('Unable to perform ASN1 mapping');
  127.         }
  128.         if (!isset($key['parameters'])) {
  129.             throw new \RuntimeException('Key cannot be loaded without parameters');
  130.         }
  131.  
  132.         $components = [];
  133.         $components['curve'] = self::loadCurveByParam($key['parameters']);
  134.         $components['dA'] = new BigInteger($key['privateKey'], 256);
  135.         $components['QA'] = isset($ecPrivate['publicKey']) ?
  136.             self::extractPoint($ecPrivate['publicKey'], $components['curve']) :
  137.             $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);
  138.  
  139.         return $components;
  140.     }
  141.  
  142.     /**
  143.      * Convert EC parameters to the appropriate format
  144.      *
  145.      * @return string
  146.      */
  147.     public static function saveParameters(BaseCurve $curve, array $options = [])
  148.     {
  149.         self::initialize_static_variables();
  150.  
  151.         if ($curve instanceof TwistedEdwardsCurve || $curve instanceof MontgomeryCurve) {
  152.             throw new UnsupportedCurveException('TwistedEdwards and Montgomery Curves are not supported');
  153.         }
  154.  
  155.         $key = self::encodeParameters($curve, false, $options);
  156.  
  157.         return "-----BEGIN EC PARAMETERS-----\r\n" .
  158.                chunk_split(Strings::base64_encode($key), 64) .
  159.                "-----END EC PARAMETERS-----\r\n";
  160.     }
  161.  
  162.     /**
  163.      * Convert a private key to the appropriate format.
  164.      *
  165.      * @param \phpseclib3\Math\BigInteger $privateKey
  166.      * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
  167.      * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
  168.      * @param string $secret optional
  169.      * @param string $password optional
  170.      * @param array $options optional
  171.      * @return string
  172.      */
  173.     public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $secret = null, $password = '', array $options = [])
  174.     {
  175.         self::initialize_static_variables();
  176.  
  177.         if ($curve instanceof TwistedEdwardsCurve  || $curve instanceof MontgomeryCurve) {
  178.             throw new UnsupportedCurveException('TwistedEdwards Curves are not supported');
  179.         }
  180.  
  181.         $publicKey = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();
  182.  
  183.         $key = [
  184.             'version' => 'ecPrivkeyVer1',
  185.             'privateKey' => $privateKey->toBytes(),
  186.             'parameters' => new ASN1\Element(self::encodeParameters($curve)),
  187.             'publicKey' => "\0" . $publicKey
  188.         ];
  189.  
  190.         $key = ASN1::encodeDER($key, Maps\ECPrivateKey::MAP);
  191.  
  192.         return self::wrapPrivateKey($key, 'EC', $password, $options);
  193.     }
  194. }
  195.