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.  * EC Public Key
  5.  *
  6.  * @category  Crypt
  7.  * @package   EC
  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;
  15.  
  16. use phpseclib3\Common\Functions\Strings;
  17. use phpseclib3\Crypt\Common;
  18. use phpseclib3\Crypt\EC;
  19. use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
  20. use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
  21. use phpseclib3\Crypt\EC\Curves\Ed25519;
  22. use phpseclib3\Crypt\EC\Formats\Keys\PKCS1;
  23. use phpseclib3\Crypt\EC\Formats\Signature\ASN1 as ASN1Signature;
  24. use phpseclib3\Crypt\Hash;
  25. use phpseclib3\Exception\UnsupportedOperationException;
  26. use phpseclib3\Math\BigInteger;
  27.  
  28. /**
  29.  * EC Public Key
  30.  *
  31.  * @package EC
  32.  * @author  Jim Wigginton <terrafrost@php.net>
  33.  * @access  public
  34.  */
  35. class PublicKey extends EC implements Common\PublicKey
  36. {
  37.     use Common\Traits\Fingerprint;
  38.  
  39.     /**
  40.      * Verify a signature
  41.      *
  42.      * @see self::verify()
  43.      * @access public
  44.      * @param string $message
  45.      * @param string $signature
  46.      * @return mixed
  47.      */
  48.     public function verify($message, $signature)
  49.     {
  50.         if ($this->curve instanceof MontgomeryCurve) {
  51.             throw new UnsupportedOperationException('Montgomery Curves cannot be used to create signatures');
  52.         }
  53.  
  54.         $shortFormat = $this->shortFormat;
  55.         $format = $this->sigFormat;
  56.         if ($format === false) {
  57.             return false;
  58.         }
  59.  
  60.         $order = $this->curve->getOrder();
  61.  
  62.         if ($this->curve instanceof TwistedEdwardsCurve) {
  63.             if ($shortFormat == 'SSH2') {
  64.                 list(, $signature) = Strings::unpackSSH2('ss', $signature);
  65.             }
  66.  
  67.             if ($this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context)) {
  68.                 return sodium_crypto_sign_verify_detached($signature, $message, $this->toString('libsodium'));
  69.             }
  70.  
  71.             $curve = $this->curve;
  72.             if (strlen($signature) != 2 * $curve::SIZE) {
  73.                 return false;
  74.             }
  75.  
  76.             $R = substr($signature, 0, $curve::SIZE);
  77.             $S = substr($signature, $curve::SIZE);
  78.  
  79.             try {
  80.                 $R = PKCS1::extractPoint($R, $curve);
  81.                 $R = $this->curve->convertToInternal($R);
  82.             } catch (\Exception $e) {
  83.                 return false;
  84.             }
  85.  
  86.             $S = strrev($S);
  87.             $S = new BigInteger($S, 256);
  88.  
  89.             if ($S->compare($order) >= 0) {
  90.                 return false;
  91.             }
  92.  
  93.             $A = $curve->encodePoint($this->QA);
  94.  
  95.             if ($curve instanceof Ed25519) {
  96.                 $dom2 = !isset($this->context) ? '' :
  97.                     'SigEd25519 no Ed25519 collisions' . "\0" . chr(strlen($this->context)) . $this->context;
  98.             } else {
  99.                 $context = isset($this->context) ? $this->context : '';
  100.                 $dom2 = 'SigEd448' . "\0" . chr(strlen($context)) . $context;
  101.             }
  102.  
  103.             $hash = new Hash($curve::HASH);
  104.             $k = $hash->hash($dom2 . substr($signature, 0, $curve::SIZE) . $A . $message);
  105.             $k = strrev($k);
  106.             $k = new BigInteger($k, 256);
  107.             list(, $k) = $k->divide($order);
  108.  
  109.             $qa = $curve->convertToInternal($this->QA);
  110.  
  111.             $lhs = $curve->multiplyPoint($curve->getBasePoint(), $S);
  112.             $rhs = $curve->multiplyPoint($qa, $k);
  113.             $rhs = $curve->addPoint($rhs, $R);
  114.             $rhs = $curve->convertToAffine($rhs);
  115.  
  116.             return $lhs[0]->equals($rhs[0]) && $lhs[1]->equals($rhs[1]);
  117.         }
  118.  
  119.         $params = $format::load($signature);
  120.         if ($params === false || count($params) != 2) {
  121.             return false;
  122.         }
  123.         extract($params);
  124.  
  125.         if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) {
  126.             $sig = $format != 'ASN1' ? ASN1Signature::save($r, $s) : $signature;
  127.  
  128.             $result = openssl_verify($message, $sig, $this->toString('PKCS8', ['namedCurve' => false]), $this->hash->getHash());
  129.  
  130.             if ($result != -1) {
  131.                 return (bool) $result;
  132.             }
  133.         }
  134.  
  135.         $n_1 = $order->subtract(self::$one);
  136.         if (!$r->between(self::$one, $n_1) || !$s->between(self::$one, $n_1)) {
  137.             return false;
  138.         }
  139.  
  140.         $e = $this->hash->hash($message);
  141.         $e = new BigInteger($e, 256);
  142.  
  143.         $Ln = $this->hash->getLength() - $order->getLength();
  144.         $z = $Ln > 0 ? $e->bitwise_rightShift($Ln) : $e;
  145.  
  146.         $w = $s->modInverse($order);
  147.         list(, $u1) = $z->multiply($w)->divide($order);
  148.         list(, $u2) = $r->multiply($w)->divide($order);
  149.  
  150.         $u1 = $this->curve->convertInteger($u1);
  151.         $u2 = $this->curve->convertInteger($u2);
  152.  
  153.         list($x1, $y1) = $this->curve->multiplyAddPoints(
  154.             [$this->curve->getBasePoint(), $this->QA],
  155.             [$u1, $u2]
  156.         );
  157.  
  158.         $x1 = $x1->toBigInteger();
  159.         list(, $x1) = $x1->divide($order);
  160.  
  161.         return $x1->equals($r);
  162.     }
  163.  
  164.     /**
  165.      * Returns the public key
  166.      *
  167.      * @param string $type
  168.      * @param array $options optional
  169.      * @return string
  170.      */
  171.     public function toString($type, array $options = [])
  172.     {
  173.         $type = self::validatePlugin('Keys', $type, 'savePublicKey');
  174.  
  175.         return $type::savePublicKey($this->curve, $this->QA, $options);
  176.     }
  177. }
  178.