[ Index ] |
PHP Cross Reference of DokuWiki |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body