Subversion Repositories oidplus

Rev

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

  1. <?php
  2.  
  3. /**
  4.  * Generic EC Key Parsing Helper functions
  5.  *
  6.  * PHP version 5
  7.  *
  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\EC\Formats\Keys;
  15.  
  16. use phpseclib3\Common\Functions\Strings;
  17. use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
  18. use phpseclib3\Crypt\EC\BaseCurves\Binary as BinaryCurve;
  19. use phpseclib3\Crypt\EC\BaseCurves\Prime as PrimeCurve;
  20. use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
  21. use phpseclib3\Exception\UnsupportedCurveException;
  22. use phpseclib3\File\ASN1;
  23. use phpseclib3\File\ASN1\Maps;
  24. use phpseclib3\Math\BigInteger;
  25.  
  26. /**
  27.  * Generic EC Key Parsing Helper functions
  28.  *
  29.  * @author  Jim Wigginton <terrafrost@php.net>
  30.  */
  31. trait Common
  32. {
  33.     /**
  34.      * Curve OIDs
  35.      *
  36.      * @var array
  37.      */
  38.     private static $curveOIDs = [];
  39.  
  40.     /**
  41.      * Child OIDs loaded
  42.      *
  43.      * @var bool
  44.      */
  45.     protected static $childOIDsLoaded = false;
  46.  
  47.     /**
  48.      * Use Named Curves
  49.      *
  50.      * @var bool
  51.      */
  52.     private static $useNamedCurves = true;
  53.  
  54.     /**
  55.      * Initialize static variables
  56.      */
  57.     private static function initialize_static_variables()
  58.     {
  59.         if (empty(self::$curveOIDs)) {
  60.             // the sec* curves are from the standards for efficient cryptography group
  61.             // sect* curves are curves over binary finite fields
  62.             // secp* curves are curves over prime finite fields
  63.             // sec*r* curves are regular curves; sec*k* curves are koblitz curves
  64.             // brainpool*r* curves are regular prime finite field curves
  65.             // brainpool*t* curves are twisted versions of the brainpool*r* curves
  66.             self::$curveOIDs = [
  67.                 'prime192v1' => '1.2.840.10045.3.1.1', // J.5.1, example 1 (aka secp192r1)
  68.                 'prime192v2' => '1.2.840.10045.3.1.2', // J.5.1, example 2
  69.                 'prime192v3' => '1.2.840.10045.3.1.3', // J.5.1, example 3
  70.                 'prime239v1' => '1.2.840.10045.3.1.4', // J.5.2, example 1
  71.                 'prime239v2' => '1.2.840.10045.3.1.5', // J.5.2, example 2
  72.                 'prime239v3' => '1.2.840.10045.3.1.6', // J.5.2, example 3
  73.                 'prime256v1' => '1.2.840.10045.3.1.7', // J.5.3, example 1 (aka secp256r1)
  74.  
  75.                 // https://tools.ietf.org/html/rfc5656#section-10
  76.                 'nistp256' => '1.2.840.10045.3.1.7', // aka secp256r1
  77.                 'nistp384' => '1.3.132.0.34', // aka secp384r1
  78.                 'nistp521' => '1.3.132.0.35', // aka secp521r1
  79.  
  80.                 'nistk163' => '1.3.132.0.1', // aka sect163k1
  81.                 'nistp192' => '1.2.840.10045.3.1.1', // aka secp192r1
  82.                 'nistp224' => '1.3.132.0.33', // aka secp224r1
  83.                 'nistk233' => '1.3.132.0.26', // aka sect233k1
  84.                 'nistb233' => '1.3.132.0.27', // aka sect233r1
  85.                 'nistk283' => '1.3.132.0.16', // aka sect283k1
  86.                 'nistk409' => '1.3.132.0.36', // aka sect409k1
  87.                 'nistb409' => '1.3.132.0.37', // aka sect409r1
  88.                 'nistt571' => '1.3.132.0.38', // aka sect571k1
  89.  
  90.                 // from https://tools.ietf.org/html/rfc5915
  91.                 'secp192r1' => '1.2.840.10045.3.1.1', // aka prime192v1
  92.                 'sect163k1' => '1.3.132.0.1',
  93.                 'sect163r2' => '1.3.132.0.15',
  94.                 'secp224r1' => '1.3.132.0.33',
  95.                 'sect233k1' => '1.3.132.0.26',
  96.                 'sect233r1' => '1.3.132.0.27',
  97.                 'secp256r1' => '1.2.840.10045.3.1.7', // aka prime256v1
  98.                 'sect283k1' => '1.3.132.0.16',
  99.                 'sect283r1' => '1.3.132.0.17',
  100.                 'secp384r1' => '1.3.132.0.34',
  101.                 'sect409k1' => '1.3.132.0.36',
  102.                 'sect409r1' => '1.3.132.0.37',
  103.                 'secp521r1' => '1.3.132.0.35',
  104.                 'sect571k1' => '1.3.132.0.38',
  105.                 'sect571r1' => '1.3.132.0.39',
  106.                 // from http://www.secg.org/SEC2-Ver-1.0.pdf
  107.                 'secp112r1' => '1.3.132.0.6',
  108.                 'secp112r2' => '1.3.132.0.7',
  109.                 'secp128r1' => '1.3.132.0.28',
  110.                 'secp128r2' => '1.3.132.0.29',
  111.                 'secp160k1' => '1.3.132.0.9',
  112.                 'secp160r1' => '1.3.132.0.8',
  113.                 'secp160r2' => '1.3.132.0.30',
  114.                 'secp192k1' => '1.3.132.0.31',
  115.                 'secp224k1' => '1.3.132.0.32',
  116.                 'secp256k1' => '1.3.132.0.10',
  117.  
  118.                 'sect113r1' => '1.3.132.0.4',
  119.                 'sect113r2' => '1.3.132.0.5',
  120.                 'sect131r1' => '1.3.132.0.22',
  121.                 'sect131r2' => '1.3.132.0.23',
  122.                 'sect163r1' => '1.3.132.0.2',
  123.                 'sect193r1' => '1.3.132.0.24',
  124.                 'sect193r2' => '1.3.132.0.25',
  125.                 'sect239k1' => '1.3.132.0.3',
  126.  
  127.                 // from http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.202.2977&rep=rep1&type=pdf#page=36
  128.                 /*
  129.                 'c2pnb163v1' => '1.2.840.10045.3.0.1', // J.4.1, example 1
  130.                 'c2pnb163v2' => '1.2.840.10045.3.0.2', // J.4.1, example 2
  131.                 'c2pnb163v3' => '1.2.840.10045.3.0.3', // J.4.1, example 3
  132.                 'c2pnb172w1' => '1.2.840.10045.3.0.4', // J.4.2, example 1
  133.                 'c2tnb191v1' => '1.2.840.10045.3.0.5', // J.4.3, example 1
  134.                 'c2tnb191v2' => '1.2.840.10045.3.0.6', // J.4.3, example 2
  135.                 'c2tnb191v3' => '1.2.840.10045.3.0.7', // J.4.3, example 3
  136.                 'c2onb191v4' => '1.2.840.10045.3.0.8', // J.4.3, example 4
  137.                 'c2onb191v5' => '1.2.840.10045.3.0.9', // J.4.3, example 5
  138.                 'c2pnb208w1' => '1.2.840.10045.3.0.10', // J.4.4, example 1
  139.                 'c2tnb239v1' => '1.2.840.10045.3.0.11', // J.4.5, example 1
  140.                 'c2tnb239v2' => '1.2.840.10045.3.0.12', // J.4.5, example 2
  141.                 'c2tnb239v3' => '1.2.840.10045.3.0.13', // J.4.5, example 3
  142.                 'c2onb239v4' => '1.2.840.10045.3.0.14', // J.4.5, example 4
  143.                 'c2onb239v5' => '1.2.840.10045.3.0.15', // J.4.5, example 5
  144.                 'c2pnb272w1' => '1.2.840.10045.3.0.16', // J.4.6, example 1
  145.                 'c2pnb304w1' => '1.2.840.10045.3.0.17', // J.4.7, example 1
  146.                 'c2tnb359v1' => '1.2.840.10045.3.0.18', // J.4.8, example 1
  147.                 'c2pnb368w1' => '1.2.840.10045.3.0.19', // J.4.9, example 1
  148.                 'c2tnb431r1' => '1.2.840.10045.3.0.20', // J.4.10, example 1
  149.                 */
  150.  
  151.                 // http://www.ecc-brainpool.org/download/Domain-parameters.pdf
  152.                 // https://tools.ietf.org/html/rfc5639
  153.                 'brainpoolP160r1' => '1.3.36.3.3.2.8.1.1.1',
  154.                 'brainpoolP160t1' => '1.3.36.3.3.2.8.1.1.2',
  155.                 'brainpoolP192r1' => '1.3.36.3.3.2.8.1.1.3',
  156.                 'brainpoolP192t1' => '1.3.36.3.3.2.8.1.1.4',
  157.                 'brainpoolP224r1' => '1.3.36.3.3.2.8.1.1.5',
  158.                 'brainpoolP224t1' => '1.3.36.3.3.2.8.1.1.6',
  159.                 'brainpoolP256r1' => '1.3.36.3.3.2.8.1.1.7',
  160.                 'brainpoolP256t1' => '1.3.36.3.3.2.8.1.1.8',
  161.                 'brainpoolP320r1' => '1.3.36.3.3.2.8.1.1.9',
  162.                 'brainpoolP320t1' => '1.3.36.3.3.2.8.1.1.10',
  163.                 'brainpoolP384r1' => '1.3.36.3.3.2.8.1.1.11',
  164.                 'brainpoolP384t1' => '1.3.36.3.3.2.8.1.1.12',
  165.                 'brainpoolP512r1' => '1.3.36.3.3.2.8.1.1.13',
  166.                 'brainpoolP512t1' => '1.3.36.3.3.2.8.1.1.14'
  167.             ];
  168.             ASN1::loadOIDs([
  169.                 'prime-field' => '1.2.840.10045.1.1',
  170.                 'characteristic-two-field' => '1.2.840.10045.1.2',
  171.                 'characteristic-two-basis' => '1.2.840.10045.1.2.3',
  172.                 // per http://www.secg.org/SEC1-Ver-1.0.pdf#page=84, gnBasis "not used here"
  173.                 'gnBasis' => '1.2.840.10045.1.2.3.1', // NULL
  174.                 'tpBasis' => '1.2.840.10045.1.2.3.2', // Trinomial
  175.                 'ppBasis' => '1.2.840.10045.1.2.3.3'  // Pentanomial
  176.             ] + self::$curveOIDs);
  177.         }
  178.     }
  179.  
  180.     /**
  181.      * Explicitly set the curve
  182.      *
  183.      * If the key contains an implicit curve phpseclib needs the curve
  184.      * to be explicitly provided
  185.      *
  186.      * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
  187.      */
  188.     public static function setImplicitCurve(BaseCurve $curve)
  189.     {
  190.         self::$implicitCurve = $curve;
  191.     }
  192.  
  193.     /**
  194.      * Returns an instance of \phpseclib3\Crypt\EC\BaseCurves\Base based
  195.      * on the curve parameters
  196.      *
  197.      * @param array $params
  198.      * @return \phpseclib3\Crypt\EC\BaseCurves\Base|false
  199.      */
  200.     protected static function loadCurveByParam(array $params)
  201.     {
  202.         if (count($params) > 1) {
  203.             throw new \RuntimeException('No parameters are present');
  204.         }
  205.         if (isset($params['namedCurve'])) {
  206.             $curve = '\phpseclib3\Crypt\EC\Curves\\' . $params['namedCurve'];
  207.             if (!class_exists($curve)) {
  208.                 throw new UnsupportedCurveException('Named Curve of ' . $params['namedCurve'] . ' is not supported');
  209.             }
  210.             return new $curve();
  211.         }
  212.         if (isset($params['implicitCurve'])) {
  213.             if (!isset(self::$implicitCurve)) {
  214.                 throw new \RuntimeException('Implicit curves can be provided by calling setImplicitCurve');
  215.             }
  216.             return self::$implicitCurve;
  217.         }
  218.         if (isset($params['specifiedCurve'])) {
  219.             $data = $params['specifiedCurve'];
  220.             switch ($data['fieldID']['fieldType']) {
  221.                 case 'prime-field':
  222.                     $curve = new PrimeCurve();
  223.                     $curve->setModulo($data['fieldID']['parameters']);
  224.                     $curve->setCoefficients(
  225.                         new BigInteger($data['curve']['a'], 256),
  226.                         new BigInteger($data['curve']['b'], 256)
  227.                     );
  228.                     $point = self::extractPoint("\0" . $data['base'], $curve);
  229.                     $curve->setBasePoint(...$point);
  230.                     $curve->setOrder($data['order']);
  231.                     return $curve;
  232.                 case 'characteristic-two-field':
  233.                     $curve = new BinaryCurve();
  234.                     $params = ASN1::decodeBER($data['fieldID']['parameters']);
  235.                     $params = ASN1::asn1map($params[0], Maps\Characteristic_two::MAP);
  236.                     $modulo = [(int) $params['m']->toString()];
  237.                     switch ($params['basis']) {
  238.                         case 'tpBasis':
  239.                             $modulo[] = (int) $params['parameters']->toString();
  240.                             break;
  241.                         case 'ppBasis':
  242.                             $temp = ASN1::decodeBER($params['parameters']);
  243.                             $temp = ASN1::asn1map($temp[0], Maps\Pentanomial::MAP);
  244.                             $modulo[] = (int) $temp['k3']->toString();
  245.                             $modulo[] = (int) $temp['k2']->toString();
  246.                             $modulo[] = (int) $temp['k1']->toString();
  247.                     }
  248.                     $modulo[] = 0;
  249.                     $curve->setModulo(...$modulo);
  250.                     $len = ceil($modulo[0] / 8);
  251.                     $curve->setCoefficients(
  252.                         Strings::bin2hex($data['curve']['a']),
  253.                         Strings::bin2hex($data['curve']['b'])
  254.                     );
  255.                     $point = self::extractPoint("\0" . $data['base'], $curve);
  256.                     $curve->setBasePoint(...$point);
  257.                     $curve->setOrder($data['order']);
  258.                     return $curve;
  259.                 default:
  260.                     throw new UnsupportedCurveException('Field Type of ' . $data['fieldID']['fieldType'] . ' is not supported');
  261.             }
  262.         }
  263.         throw new \RuntimeException('No valid parameters are present');
  264.     }
  265.  
  266.     /**
  267.      * Extract points from a string
  268.      *
  269.      * Supports both compressed and uncompressed points
  270.      *
  271.      * @param string $str
  272.      * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
  273.      * @return object[]
  274.      */
  275.     public static function extractPoint($str, BaseCurve $curve)
  276.     {
  277.         if ($curve instanceof TwistedEdwardsCurve) {
  278.             // first step of point deciding as discussed at the following URL's:
  279.             // https://tools.ietf.org/html/rfc8032#section-5.1.3
  280.             // https://tools.ietf.org/html/rfc8032#section-5.2.3
  281.             $y = $str;
  282.             $y = strrev($y);
  283.             $sign = (bool) (ord($y[0]) & 0x80);
  284.             $y[0] = $y[0] & chr(0x7F);
  285.             $y = new BigInteger($y, 256);
  286.             if ($y->compare($curve->getModulo()) >= 0) {
  287.                 throw new \RuntimeException('The Y coordinate should not be >= the modulo');
  288.             }
  289.             $point = $curve->recoverX($y, $sign);
  290.             if (!$curve->verifyPoint($point)) {
  291.                 throw new \RuntimeException('Unable to verify that point exists on curve');
  292.             }
  293.             return $point;
  294.         }
  295.  
  296.         // the first byte of a bit string represents the number of bits in the last byte that are to be ignored but,
  297.         // currently, bit strings wanting a non-zero amount of bits trimmed are not supported
  298.         if (($val = Strings::shift($str)) != "\0") {
  299.             throw new \UnexpectedValueException('extractPoint expects the first byte to be null - not ' . Strings::bin2hex($val));
  300.         }
  301.         if ($str == "\0") {
  302.             return [];
  303.         }
  304.  
  305.         $keylen = strlen($str);
  306.         $order = $curve->getLengthInBytes();
  307.         // point compression is being used
  308.         if ($keylen == $order + 1) {
  309.             return $curve->derivePoint($str);
  310.         }
  311.  
  312.         // point compression is not being used
  313.         if ($keylen == 2 * $order + 1) {
  314.             preg_match("#(.)(.{{$order}})(.{{$order}})#s", $str, $matches);
  315.             list(, $w, $x, $y) = $matches;
  316.             if ($w != "\4") {
  317.                 throw new \UnexpectedValueException('The first byte of an uncompressed point should be 04 - not ' . Strings::bin2hex($val));
  318.             }
  319.             $point = [
  320.                 $curve->convertInteger(new BigInteger($x, 256)),
  321.                 $curve->convertInteger(new BigInteger($y, 256))
  322.             ];
  323.  
  324.             if (!$curve->verifyPoint($point)) {
  325.                 throw new \RuntimeException('Unable to verify that point exists on curve');
  326.             }
  327.  
  328.             return $point;
  329.         }
  330.  
  331.         throw new \UnexpectedValueException('The string representation of the points is not of an appropriate length');
  332.     }
  333.  
  334.     /**
  335.      * Encode Parameters
  336.      *
  337.      * @todo Maybe at some point this could be moved to __toString() for each of the curves?
  338.      * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
  339.      * @param bool $returnArray optional
  340.      * @param array $options optional
  341.      * @return string|false
  342.      */
  343.     private static function encodeParameters(BaseCurve $curve, $returnArray = false, array $options = [])
  344.     {
  345.         $useNamedCurves = isset($options['namedCurve']) ? $options['namedCurve'] : self::$useNamedCurves;
  346.  
  347.         $reflect = new \ReflectionClass($curve);
  348.         $name = $reflect->getShortName();
  349.         if ($useNamedCurves) {
  350.             if (isset(self::$curveOIDs[$name])) {
  351.                 if ($reflect->isFinal()) {
  352.                     $reflect = $reflect->getParentClass();
  353.                     $name = $reflect->getShortName();
  354.                 }
  355.                 return $returnArray ?
  356.                     ['namedCurve' => $name] :
  357.                     ASN1::encodeDER(['namedCurve' => $name], Maps\ECParameters::MAP);
  358.             }
  359.             foreach (new \DirectoryIterator(__DIR__ . '/../../Curves/') as $file) {
  360.                 if ($file->getExtension() != 'php') {
  361.                     continue;
  362.                 }
  363.                 $testName = $file->getBasename('.php');
  364.                 $class = 'phpseclib3\Crypt\EC\Curves\\' . $testName;
  365.                 $reflect = new \ReflectionClass($class);
  366.                 if ($reflect->isFinal()) {
  367.                     continue;
  368.                 }
  369.                 $candidate = new $class();
  370.                 switch ($name) {
  371.                     case 'Prime':
  372.                         if (!$candidate instanceof PrimeCurve) {
  373.                             break;
  374.                         }
  375.                         if (!$candidate->getModulo()->equals($curve->getModulo())) {
  376.                             break;
  377.                         }
  378.                         if ($candidate->getA()->toBytes() != $curve->getA()->toBytes()) {
  379.                             break;
  380.                         }
  381.                         if ($candidate->getB()->toBytes() != $curve->getB()->toBytes()) {
  382.                             break;
  383.                         }
  384.  
  385.                         list($candidateX, $candidateY) = $candidate->getBasePoint();
  386.                         list($curveX, $curveY) = $curve->getBasePoint();
  387.                         if ($candidateX->toBytes() != $curveX->toBytes()) {
  388.                             break;
  389.                         }
  390.                         if ($candidateY->toBytes() != $curveY->toBytes()) {
  391.                             break;
  392.                         }
  393.  
  394.                         return $returnArray ?
  395.                             ['namedCurve' => $testName] :
  396.                             ASN1::encodeDER(['namedCurve' => $testName], Maps\ECParameters::MAP);
  397.                     case 'Binary':
  398.                         if (!$candidate instanceof BinaryCurve) {
  399.                             break;
  400.                         }
  401.                         if ($candidate->getModulo() != $curve->getModulo()) {
  402.                             break;
  403.                         }
  404.                         if ($candidate->getA()->toBytes() != $curve->getA()->toBytes()) {
  405.                             break;
  406.                         }
  407.                         if ($candidate->getB()->toBytes() != $curve->getB()->toBytes()) {
  408.                             break;
  409.                         }
  410.  
  411.                         list($candidateX, $candidateY) = $candidate->getBasePoint();
  412.                         list($curveX, $curveY) = $curve->getBasePoint();
  413.                         if ($candidateX->toBytes() != $curveX->toBytes()) {
  414.                             break;
  415.                         }
  416.                         if ($candidateY->toBytes() != $curveY->toBytes()) {
  417.                             break;
  418.                         }
  419.  
  420.                         return $returnArray ?
  421.                             ['namedCurve' => $testName] :
  422.                             ASN1::encodeDER(['namedCurve' => $testName], Maps\ECParameters::MAP);
  423.                 }
  424.             }
  425.         }
  426.  
  427.         $order = $curve->getOrder();
  428.         // we could try to calculate the order thusly:
  429.         // https://crypto.stackexchange.com/a/27914/4520
  430.         // https://en.wikipedia.org/wiki/Schoof%E2%80%93Elkies%E2%80%93Atkin_algorithm
  431.         if (!$order) {
  432.             throw new \RuntimeException('Specified Curves need the order to be specified');
  433.         }
  434.         $point = $curve->getBasePoint();
  435.         $x = $point[0]->toBytes();
  436.         $y = $point[1]->toBytes();
  437.  
  438.         if ($curve instanceof PrimeCurve) {
  439.             /*
  440.              * valid versions are:
  441.              *
  442.              * ecdpVer1:
  443.              *   - neither the curve or the base point are generated verifiably randomly.
  444.              * ecdpVer2:
  445.              *   - curve and base point are generated verifiably at random and curve.seed is present
  446.              * ecdpVer3:
  447.              *   - base point is generated verifiably at random but curve is not. curve.seed is present
  448.              */
  449.             // other (optional) parameters can be calculated using the methods discused at
  450.             // https://crypto.stackexchange.com/q/28947/4520
  451.             $data = [
  452.                 'version' => 'ecdpVer1',
  453.                 'fieldID' => [
  454.                     'fieldType' => 'prime-field',
  455.                     'parameters' => $curve->getModulo()
  456.                 ],
  457.                 'curve' => [
  458.                     'a' => $curve->getA()->toBytes(),
  459.                     'b' => $curve->getB()->toBytes()
  460.                 ],
  461.                 'base' => "\4" . $x . $y,
  462.                 'order' => $order
  463.             ];
  464.  
  465.             return $returnArray ?
  466.                 ['specifiedCurve' => $data] :
  467.                 ASN1::encodeDER(['specifiedCurve' => $data], Maps\ECParameters::MAP);
  468.         }
  469.         if ($curve instanceof BinaryCurve) {
  470.             $modulo = $curve->getModulo();
  471.             $basis = count($modulo);
  472.             $m = array_shift($modulo);
  473.             array_pop($modulo); // the last parameter should always be 0
  474.             //rsort($modulo);
  475.             switch ($basis) {
  476.                 case 3:
  477.                     $basis = 'tpBasis';
  478.                     $modulo = new BigInteger($modulo[0]);
  479.                     break;
  480.                 case 5:
  481.                     $basis = 'ppBasis';
  482.                     // these should be in strictly ascending order (hence the commented out rsort above)
  483.                     $modulo = [
  484.                         'k1' => new BigInteger($modulo[2]),
  485.                         'k2' => new BigInteger($modulo[1]),
  486.                         'k3' => new BigInteger($modulo[0])
  487.                     ];
  488.                     $modulo = ASN1::encodeDER($modulo, Maps\Pentanomial::MAP);
  489.                     $modulo = new ASN1\Element($modulo);
  490.             }
  491.             $params = ASN1::encodeDER([
  492.                 'm' => new BigInteger($m),
  493.                 'basis' => $basis,
  494.                 'parameters' => $modulo
  495.             ], Maps\Characteristic_two::MAP);
  496.             $params = new ASN1\Element($params);
  497.             $a = ltrim($curve->getA()->toBytes(), "\0");
  498.             if (!strlen($a)) {
  499.                 $a = "\0";
  500.             }
  501.             $b = ltrim($curve->getB()->toBytes(), "\0");
  502.             if (!strlen($b)) {
  503.                 $b = "\0";
  504.             }
  505.             $data = [
  506.                 'version' => 'ecdpVer1',
  507.                 'fieldID' => [
  508.                     'fieldType' => 'characteristic-two-field',
  509.                     'parameters' => $params
  510.                 ],
  511.                 'curve' => [
  512.                     'a' => $a,
  513.                     'b' => $b
  514.                 ],
  515.                 'base' => "\4" . $x . $y,
  516.                 'order' => $order
  517.             ];
  518.  
  519.             return $returnArray ?
  520.                 ['specifiedCurve' => $data] :
  521.                 ASN1::encodeDER(['specifiedCurve' => $data], Maps\ECParameters::MAP);
  522.         }
  523.  
  524.         throw new UnsupportedCurveException('Curve cannot be serialized');
  525.     }
  526.  
  527.     /**
  528.      * Use Specified Curve
  529.      *
  530.      * A specified curve has all the coefficients, the base points, etc, explicitely included.
  531.      * A specified curve is a more verbose way of representing a curve
  532.      */
  533.     public static function useSpecifiedCurve()
  534.     {
  535.         self::$useNamedCurves = false;
  536.     }
  537.  
  538.     /**
  539.      * Use Named Curve
  540.      *
  541.      * A named curve does not include any parameters. It is up to the EC parameters to
  542.      * know what the coefficients, the base points, etc, are from the name of the curve.
  543.      * A named curve is a more concise way of representing a curve
  544.      */
  545.     public static function useNamedCurve()
  546.     {
  547.         self::$useNamedCurves = true;
  548.     }
  549. }
  550.