[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/vendor/phpseclib/phpseclib/phpseclib/Crypt/ -> EC.php (source)

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