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.  * Pure-PHP implementation of EC.
  5.  *
  6.  * PHP version 5
  7.  *
  8.  * Here's an example of how to create signatures and verify signatures with this library:
  9.  * <code>
  10.  * <?php
  11.  * include 'vendor/autoload.php';
  12.  *
  13.  * $private = \phpseclib3\Crypt\EC::createKey('secp256k1');
  14.  * $public = $private->getPublicKey();
  15.  *
  16.  * $plaintext = 'terrafrost';
  17.  *
  18.  * $signature = $private->sign($plaintext);
  19.  *
  20.  * echo $public->verify($plaintext, $signature) ? 'verified' : 'unverified';
  21.  * ?>
  22.  * </code>
  23.  *
  24.  * @category  Crypt
  25.  * @package   EC
  26.  * @author    Jim Wigginton <terrafrost@php.net>
  27.  * @copyright 2016 Jim Wigginton
  28.  * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  29.  * @link      http://phpseclib.sourceforge.net
  30.  */
  31.  
  32. namespace phpseclib3\Crypt;
  33.  
  34. use phpseclib3\Crypt\Common\AsymmetricKey;
  35. use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
  36. use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
  37. use phpseclib3\Crypt\EC\Curves\Curve25519;
  38. use phpseclib3\Crypt\EC\Curves\Ed25519;
  39. use phpseclib3\Crypt\EC\Curves\Ed448;
  40. use phpseclib3\Crypt\EC\Formats\Keys\PKCS1;
  41. use phpseclib3\Crypt\EC\Parameters;
  42. use phpseclib3\Crypt\EC\PrivateKey;
  43. use phpseclib3\Crypt\EC\PublicKey;
  44. use phpseclib3\Exception\UnsupportedAlgorithmException;
  45. use phpseclib3\Exception\UnsupportedCurveException;
  46. use phpseclib3\Exception\UnsupportedOperationException;
  47. use phpseclib3\File\ASN1;
  48. use phpseclib3\File\ASN1\Maps\ECParameters;
  49. use phpseclib3\Math\BigInteger;
  50.  
  51. /**
  52.  * Pure-PHP implementation of EC.
  53.  *
  54.  * @package EC
  55.  * @author  Jim Wigginton <terrafrost@php.net>
  56.  * @access  public
  57.  */
  58. abstract class EC extends AsymmetricKey
  59. {
  60.     /**
  61.      * Algorithm Name
  62.      *
  63.      * @var string
  64.      * @access private
  65.      */
  66.     const ALGORITHM = 'EC';
  67.  
  68.     /**
  69.      * Public Key QA
  70.      *
  71.      * @var object[]
  72.      */
  73.     protected $QA;
  74.  
  75.     /**
  76.      * Curve
  77.      *
  78.      * @var \phpseclib3\Crypt\EC\BaseCurves\Base
  79.      */
  80.     protected $curve;
  81.  
  82.     /**
  83.      * Signature Format
  84.      *
  85.      * @var string
  86.      * @access private
  87.      */
  88.     protected $format;
  89.  
  90.     /**
  91.      * Signature Format (Short)
  92.      *
  93.      * @var string
  94.      * @access private
  95.      */
  96.     protected $shortFormat;
  97.  
  98.     /**
  99.      * Curve Name
  100.      *
  101.      * @var string
  102.      */
  103.     private $curveName;
  104.  
  105.     /**
  106.      * Curve Order
  107.      *
  108.      * Used for deterministic ECDSA
  109.      *
  110.      * @var \phpseclib3\Math\BigInteger
  111.      */
  112.     protected $q;
  113.  
  114.     /**
  115.      * Alias for the private key
  116.      *
  117.      * Used for deterministic ECDSA. AsymmetricKey expects $x. I don't like x because
  118.      * with x you have x * the base point yielding an (x, y)-coordinate that is the
  119.      * public key. But the x is different depending on which side of the equal sign
  120.      * you're on. It's less ambiguous if you do dA * base point = (x, y)-coordinate.
  121.      *
  122.      * @var \phpseclib3\Math\BigInteger
  123.      */
  124.     protected $x;
  125.  
  126.     /**
  127.      * Context
  128.      *
  129.      * @var string
  130.      */
  131.     protected $context;
  132.  
  133.     /**
  134.      * Create public / private key pair.
  135.      *
  136.      * @access public
  137.      * @param string $curve
  138.      * @return \phpseclib3\Crypt\EC\PrivateKey
  139.      */
  140.     public static function createKey($curve)
  141.     {
  142.         self::initialize_static_variables();
  143.  
  144.         if (!isset(self::$engines['PHP'])) {
  145.             self::useBestEngine();
  146.         }
  147.  
  148.         $curve = strtolower($curve);
  149.         if (self::$engines['libsodium'] && $curve == 'ed25519' && function_exists('sodium_crypto_sign_keypair')) {
  150.             $kp = sodium_crypto_sign_keypair();
  151.  
  152.             $privatekey = EC::loadFormat('libsodium', sodium_crypto_sign_secretkey($kp));
  153.             //$publickey = EC::loadFormat('libsodium', sodium_crypto_sign_publickey($kp));
  154.  
  155.             $privatekey->curveName = 'Ed25519';
  156.             //$publickey->curveName = $curve;
  157.  
  158.             return $privatekey;
  159.         }
  160.  
  161.         $privatekey = new PrivateKey();
  162.  
  163.         $curveName = $curve;
  164.         if (preg_match('#(?:^curve|^ed)\d+$#', $curveName)) {
  165.             $curveName = ucfirst($curveName);
  166.         } elseif (substr($curveName, 0, 10) == 'brainpoolp') {
  167.             $curveName = 'brainpoolP' . substr($curveName, 10);
  168.         }
  169.         $curve = '\phpseclib3\Crypt\EC\Curves\\' . $curveName;
  170.  
  171.         if (!class_exists($curve)) {
  172.             throw new UnsupportedCurveException('Named Curve of ' . $curveName . ' is not supported');
  173.         }
  174.  
  175.         $reflect = new \ReflectionClass($curve);
  176.         $curveName = $reflect->isFinal() ?
  177.             $reflect->getParentClass()->getShortName() :
  178.             $reflect->getShortName();
  179.  
  180.         $curve = new $curve();
  181.         $privatekey->dA = $dA = $curve->createRandomMultiplier();
  182.         if ($curve instanceof Curve25519 && self::$engines['libsodium']) {
  183.             //$r = pack('H*', '0900000000000000000000000000000000000000000000000000000000000000');
  184.             //$QA = sodium_crypto_scalarmult($dA->toBytes(), $r);
  185.             $QA = sodium_crypto_box_publickey_from_secretkey($dA->toBytes());
  186.             $privatekey->QA = [$curve->convertInteger(new BigInteger(strrev($QA), 256))];
  187.         } else {
  188.             $privatekey->QA = $curve->multiplyPoint($curve->getBasePoint(), $dA);
  189.         }
  190.         $privatekey->curve = $curve;
  191.  
  192.         //$publickey = clone $privatekey;
  193.         //unset($publickey->dA);
  194.         //unset($publickey->x);
  195.  
  196.         $privatekey->curveName = $curveName;
  197.         //$publickey->curveName = $curveName;
  198.  
  199.         if ($privatekey->curve instanceof TwistedEdwardsCurve) {
  200.             return $privatekey->withHash($curve::HASH);
  201.         }
  202.  
  203.         return $privatekey;
  204.     }
  205.  
  206.     /**
  207.      * OnLoad Handler
  208.      *
  209.      * @return bool
  210.      * @access protected
  211.      * @param array $components
  212.      */
  213.     protected static function onLoad($components)
  214.     {
  215.         if (!isset(self::$engines['PHP'])) {
  216.             self::useBestEngine();
  217.         }
  218.  
  219.         if (!isset($components['dA']) && !isset($components['QA'])) {
  220.             $new = new Parameters();
  221.             $new->curve = $components['curve'];
  222.             return $new;
  223.         }
  224.  
  225.         $new = isset($components['dA']) ?
  226.             new PrivateKey() :
  227.             new PublicKey();
  228.         $new->curve = $components['curve'];
  229.         $new->QA = $components['QA'];
  230.  
  231.         if (isset($components['dA'])) {
  232.             $new->dA = $components['dA'];
  233.         }
  234.  
  235.         if ($new->curve instanceof TwistedEdwardsCurve) {
  236.             return $new->withHash($components['curve']::HASH);
  237.         }
  238.  
  239.         return $new;
  240.     }
  241.  
  242.     /**
  243.      * Constructor
  244.      *
  245.      * PublicKey and PrivateKey objects can only be created from abstract RSA class
  246.      */
  247.     protected function __construct()
  248.     {
  249.         $this->sigFormat = self::validatePlugin('Signature', 'ASN1');
  250.         $this->shortFormat = 'ASN1';
  251.  
  252.         parent::__construct();
  253.     }
  254.  
  255.     /**
  256.      * Returns the curve
  257.      *
  258.      * Returns a string if it's a named curve, an array if not
  259.      *
  260.      * @access public
  261.      * @return string|array
  262.      */
  263.     public function getCurve()
  264.     {
  265.         if ($this->curveName) {
  266.             return $this->curveName;
  267.         }
  268.  
  269.         if ($this->curve instanceof MontgomeryCurve) {
  270.             $this->curveName = $this->curve instanceof Curve25519 ? 'Curve25519' : 'Curve448';
  271.             return $this->curveName;
  272.         }
  273.  
  274.         if ($this->curve instanceof TwistedEdwardsCurve) {
  275.             $this->curveName = $this->curve instanceof Ed25519 ? 'Ed25519' : 'Ed448';
  276.             return $this->curveName;
  277.         }
  278.  
  279.         $params = $this->getParameters()->toString('PKCS8', ['namedCurve' => true]);
  280.         $decoded = ASN1::extractBER($params);
  281.         $decoded = ASN1::decodeBER($decoded);
  282.         $decoded = ASN1::asn1map($decoded[0], ECParameters::MAP);
  283.         if (isset($decoded['namedCurve'])) {
  284.             $this->curveName = $decoded['namedCurve'];
  285.             return $decoded['namedCurve'];
  286.         }
  287.  
  288.         if (!$namedCurves) {
  289.             PKCS1::useSpecifiedCurve();
  290.         }
  291.  
  292.         return $decoded;
  293.     }
  294.  
  295.     /**
  296.      * Returns the key size
  297.      *
  298.      * Quoting https://tools.ietf.org/html/rfc5656#section-2,
  299.      *
  300.      * "The size of a set of elliptic curve domain parameters on a prime
  301.      *  curve is defined as the number of bits in the binary representation
  302.      *  of the field order, commonly denoted by p.  Size on a
  303.      *  characteristic-2 curve is defined as the number of bits in the binary
  304.      *  representation of the field, commonly denoted by m.  A set of
  305.      *  elliptic curve domain parameters defines a group of order n generated
  306.      *  by a base point P"
  307.      *
  308.      * @access public
  309.      * @return int
  310.      */
  311.     public function getLength()
  312.     {
  313.         return $this->curve->getLength();
  314.     }
  315.  
  316.     /**
  317.      * Returns the current engine being used
  318.      *
  319.      * @see self::useInternalEngine()
  320.      * @see self::useBestEngine()
  321.      * @access public
  322.      * @return string
  323.      */
  324.     public function getEngine()
  325.     {
  326.         if (!isset(self::$engines['PHP'])) {
  327.             self::useBestEngine();
  328.         }
  329.         if ($this->curve instanceof TwistedEdwardsCurve) {
  330.             return $this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context) ?
  331.                 'libsodium' : 'PHP';
  332.         }
  333.  
  334.         return self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods()) ?
  335.             'OpenSSL' : 'PHP';
  336.     }
  337.  
  338.     /**
  339.      * Returns the public key coordinates as a string
  340.      *
  341.      * Used by ECDH
  342.      *
  343.      * @return string
  344.      */
  345.     public function getEncodedCoordinates()
  346.     {
  347.         if ($this->curve instanceof MontgomeryCurve) {
  348.             return strrev($this->QA[0]->toBytes(true));
  349.         }
  350.         if ($this->curve instanceof TwistedEdwardsCurve) {
  351.             return $this->curve->encodePoint($this->QA);
  352.         }
  353.         return "\4" . $this->QA[0]->toBytes(true) . $this->QA[1]->toBytes(true);
  354.     }
  355.  
  356.     /**
  357.      * Returns the parameters
  358.      *
  359.      * @see self::getPublicKey()
  360.      * @access public
  361.      * @param string $type optional
  362.      * @return mixed
  363.      */
  364.     public function getParameters($type = 'PKCS1')
  365.     {
  366.         $type = self::validatePlugin('Keys', $type, 'saveParameters');
  367.  
  368.         $key = $type::saveParameters($this->curve);
  369.  
  370.         return EC::load($key, 'PKCS1')
  371.             ->withHash($this->hash->getHash())
  372.             ->withSignatureFormat($this->shortFormat);
  373.     }
  374.  
  375.     /**
  376.      * Determines the signature padding mode
  377.      *
  378.      * Valid values are: ASN1, SSH2, Raw
  379.      *
  380.      * @access public
  381.      * @param string $format
  382.      */
  383.     public function withSignatureFormat($format)
  384.     {
  385.         if ($this->curve instanceof MontgomeryCurve) {
  386.             throw new UnsupportedOperationException('Montgomery Curves cannot be used to create signatures');
  387.         }
  388.  
  389.         $new = clone $this;
  390.         $new->shortFormat = $format;
  391.         $new->sigFormat = self::validatePlugin('Signature', $format);
  392.         return $new;
  393.     }
  394.  
  395.     /**
  396.      * Returns the signature format currently being used
  397.      *
  398.      * @access public
  399.      */
  400.     public function getSignatureFormat()
  401.     {
  402.         return $this->shortFormat;
  403.     }
  404.  
  405.     /**
  406.      * Sets the context
  407.      *
  408.      * Used by Ed25519 / Ed448.
  409.      *
  410.      * @see self::sign()
  411.      * @see self::verify()
  412.      * @access public
  413.      * @param string $context optional
  414.      */
  415.     public function withContext($context = null)
  416.     {
  417.         if (!$this->curve instanceof TwistedEdwardsCurve) {
  418.             throw new UnsupportedCurveException('Only Ed25519 and Ed448 support contexts');
  419.         }
  420.  
  421.         $new = clone $this;
  422.         if (!isset($context)) {
  423.             $new->context = null;
  424.             return $new;
  425.         }
  426.         if (!is_string($context)) {
  427.             throw new \InvalidArgumentException('setContext expects a string');
  428.         }
  429.         if (strlen($context) > 255) {
  430.             throw new \LengthException('The context is supposed to be, at most, 255 bytes long');
  431.         }
  432.         $new->context = $context;
  433.         return $new;
  434.     }
  435.  
  436.     /**
  437.      * Returns the signature format currently being used
  438.      *
  439.      * @access public
  440.      */
  441.     public function getContext()
  442.     {
  443.         return $this->context;
  444.     }
  445.  
  446.     /**
  447.      * Determines which hashing function should be used
  448.      *
  449.      * @access public
  450.      * @param string $hash
  451.      */
  452.     public function withHash($hash)
  453.     {
  454.         if ($this->curve instanceof MontgomeryCurve) {
  455.             throw new UnsupportedOperationException('Montgomery Curves cannot be used to create signatures');
  456.         }
  457.         if ($this->curve instanceof Ed25519 && $hash != 'sha512') {
  458.             throw new UnsupportedAlgorithmException('Ed25519 only supports sha512 as a hash');
  459.         }
  460.         if ($this->curve instanceof Ed448 && $hash != 'shake256-912') {
  461.             throw new UnsupportedAlgorithmException('Ed448 only supports shake256 with a length of 114 bytes');
  462.         }
  463.  
  464.         return parent::withHash($hash);
  465.     }
  466.  
  467.     /**
  468.      * __toString() magic method
  469.      *
  470.      * @return string
  471.      */
  472.     public function __toString()
  473.     {
  474.         if ($this->curve instanceof MontgomeryCurve) {
  475.             return '';
  476.         }
  477.  
  478.         return parent::__toString();
  479.     }
  480. }
  481.