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 14namespace phpseclib3\Crypt\EC; 15 16use phpseclib3\Common\Functions\Strings; 17use phpseclib3\Crypt\Common; 18use phpseclib3\Crypt\EC; 19use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve; 20use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; 21use phpseclib3\Crypt\EC\Curves\Ed25519; 22use phpseclib3\Crypt\EC\Formats\Keys\PKCS1; 23use phpseclib3\Crypt\EC\Formats\Signature\ASN1 as ASN1Signature; 24use phpseclib3\Crypt\Hash; 25use phpseclib3\Exception\UnsupportedOperationException; 26use phpseclib3\Math\BigInteger; 27 28/** 29 * EC Public Key 30 * 31 * @package EC 32 * @author Jim Wigginton <terrafrost@php.net> 33 * @access public 34 */ 35class 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