[ Index ] |
PHP Cross Reference of DokuWiki |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Base Class for all asymmetric key ciphers 5 * 6 * PHP version 5 7 * 8 * @author Jim Wigginton <terrafrost@php.net> 9 * @copyright 2016 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\Common; 15 16 use phpseclib3\Crypt\DSA; 17 use phpseclib3\Crypt\Hash; 18 use phpseclib3\Crypt\RSA; 19 use phpseclib3\Exception\NoKeyLoadedException; 20 use phpseclib3\Exception\UnsupportedFormatException; 21 use phpseclib3\Math\BigInteger; 22 23 /** 24 * Base Class for all asymmetric cipher classes 25 * 26 * @author Jim Wigginton <terrafrost@php.net> 27 */ 28 abstract class AsymmetricKey 29 { 30 /** 31 * Precomputed Zero 32 * 33 * @var \phpseclib3\Math\BigInteger 34 */ 35 protected static $zero; 36 37 /** 38 * Precomputed One 39 * 40 * @var \phpseclib3\Math\BigInteger 41 */ 42 protected static $one; 43 44 /** 45 * Format of the loaded key 46 * 47 * @var string 48 */ 49 protected $format; 50 51 /** 52 * Hash function 53 * 54 * @var \phpseclib3\Crypt\Hash 55 */ 56 protected $hash; 57 58 /** 59 * HMAC function 60 * 61 * @var \phpseclib3\Crypt\Hash 62 */ 63 private $hmac; 64 65 /** 66 * Supported plugins (lower case) 67 * 68 * @see self::initialize_static_variables() 69 * @var array 70 */ 71 private static $plugins = []; 72 73 /** 74 * Invisible plugins 75 * 76 * @see self::initialize_static_variables() 77 * @var array 78 */ 79 private static $invisiblePlugins = []; 80 81 /** 82 * Available Engines 83 * 84 * @var boolean[] 85 */ 86 protected static $engines = []; 87 88 /** 89 * Key Comment 90 * 91 * @var null|string 92 */ 93 private $comment; 94 95 /** 96 * @param string $type 97 * @return array|string 98 */ 99 abstract public function toString($type, array $options = []); 100 101 /** 102 * The constructor 103 */ 104 protected function __construct() 105 { 106 self::initialize_static_variables(); 107 108 $this->hash = new Hash('sha256'); 109 $this->hmac = new Hash('sha256'); 110 } 111 112 /** 113 * Initialize static variables 114 */ 115 protected static function initialize_static_variables() 116 { 117 if (!isset(self::$zero)) { 118 self::$zero = new BigInteger(0); 119 self::$one = new BigInteger(1); 120 } 121 122 self::loadPlugins('Keys'); 123 if (static::ALGORITHM != 'RSA' && static::ALGORITHM != 'DH') { 124 self::loadPlugins('Signature'); 125 } 126 } 127 128 /** 129 * Load the key 130 * 131 * @param string $key 132 * @param string $password optional 133 * @return \phpseclib3\Crypt\Common\PublicKey|\phpseclib3\Crypt\Common\PrivateKey 134 */ 135 public static function load($key, $password = false) 136 { 137 self::initialize_static_variables(); 138 139 $class = new \ReflectionClass(static::class); 140 if ($class->isFinal()) { 141 throw new \RuntimeException('load() should not be called from final classes (' . static::class . ')'); 142 } 143 144 $components = false; 145 foreach (self::$plugins[static::ALGORITHM]['Keys'] as $format) { 146 if (isset(self::$invisiblePlugins[static::ALGORITHM]) && in_array($format, self::$invisiblePlugins[static::ALGORITHM])) { 147 continue; 148 } 149 try { 150 $components = $format::load($key, $password); 151 } catch (\Exception $e) { 152 $components = false; 153 } 154 if ($components !== false) { 155 break; 156 } 157 } 158 159 if ($components === false) { 160 throw new NoKeyLoadedException('Unable to read key'); 161 } 162 163 $components['format'] = $format; 164 $components['secret'] = isset($components['secret']) ? $components['secret'] : ''; 165 $comment = isset($components['comment']) ? $components['comment'] : null; 166 $new = static::onLoad($components); 167 $new->format = $format; 168 $new->comment = $comment; 169 return $new instanceof PrivateKey ? 170 $new->withPassword($password) : 171 $new; 172 } 173 174 /** 175 * Loads a private key 176 * 177 * @return PrivateKey 178 * @param string|array $key 179 * @param string $password optional 180 */ 181 public static function loadPrivateKey($key, $password = '') 182 { 183 $key = self::load($key, $password); 184 if (!$key instanceof PrivateKey) { 185 throw new NoKeyLoadedException('The key that was loaded was not a private key'); 186 } 187 return $key; 188 } 189 190 /** 191 * Loads a public key 192 * 193 * @return PublicKey 194 * @param string|array $key 195 */ 196 public static function loadPublicKey($key) 197 { 198 $key = self::load($key); 199 if (!$key instanceof PublicKey) { 200 throw new NoKeyLoadedException('The key that was loaded was not a public key'); 201 } 202 return $key; 203 } 204 205 /** 206 * Loads parameters 207 * 208 * @return AsymmetricKey 209 * @param string|array $key 210 */ 211 public static function loadParameters($key) 212 { 213 $key = self::load($key); 214 if (!$key instanceof PrivateKey && !$key instanceof PublicKey) { 215 throw new NoKeyLoadedException('The key that was loaded was not a parameter'); 216 } 217 return $key; 218 } 219 220 /** 221 * Load the key, assuming a specific format 222 * 223 * @param string $type 224 * @param string $key 225 * @param string $password optional 226 * @return static 227 */ 228 public static function loadFormat($type, $key, $password = false) 229 { 230 self::initialize_static_variables(); 231 232 $components = false; 233 $format = strtolower($type); 234 if (isset(self::$plugins[static::ALGORITHM]['Keys'][$format])) { 235 $format = self::$plugins[static::ALGORITHM]['Keys'][$format]; 236 $components = $format::load($key, $password); 237 } 238 239 if ($components === false) { 240 throw new NoKeyLoadedException('Unable to read key'); 241 } 242 243 $components['format'] = $format; 244 $components['secret'] = isset($components['secret']) ? $components['secret'] : ''; 245 246 $new = static::onLoad($components); 247 $new->format = $format; 248 return $new instanceof PrivateKey ? 249 $new->withPassword($password) : 250 $new; 251 } 252 253 /** 254 * Loads a private key 255 * 256 * @return PrivateKey 257 * @param string $type 258 * @param string $key 259 * @param string $password optional 260 */ 261 public static function loadPrivateKeyFormat($type, $key, $password = false) 262 { 263 $key = self::loadFormat($type, $key, $password); 264 if (!$key instanceof PrivateKey) { 265 throw new NoKeyLoadedException('The key that was loaded was not a private key'); 266 } 267 return $key; 268 } 269 270 /** 271 * Loads a public key 272 * 273 * @return PublicKey 274 * @param string $type 275 * @param string $key 276 */ 277 public static function loadPublicKeyFormat($type, $key) 278 { 279 $key = self::loadFormat($type, $key); 280 if (!$key instanceof PublicKey) { 281 throw new NoKeyLoadedException('The key that was loaded was not a public key'); 282 } 283 return $key; 284 } 285 286 /** 287 * Loads parameters 288 * 289 * @return AsymmetricKey 290 * @param string $type 291 * @param string|array $key 292 */ 293 public static function loadParametersFormat($type, $key) 294 { 295 $key = self::loadFormat($type, $key); 296 if (!$key instanceof PrivateKey && !$key instanceof PublicKey) { 297 throw new NoKeyLoadedException('The key that was loaded was not a parameter'); 298 } 299 return $key; 300 } 301 302 /** 303 * Validate Plugin 304 * 305 * @param string $format 306 * @param string $type 307 * @param string $method optional 308 * @return mixed 309 */ 310 protected static function validatePlugin($format, $type, $method = null) 311 { 312 $type = strtolower($type); 313 if (!isset(self::$plugins[static::ALGORITHM][$format][$type])) { 314 throw new UnsupportedFormatException("$type is not a supported format"); 315 } 316 $type = self::$plugins[static::ALGORITHM][$format][$type]; 317 if (isset($method) && !method_exists($type, $method)) { 318 throw new UnsupportedFormatException("$type does not implement $method"); 319 } 320 321 return $type; 322 } 323 324 /** 325 * Load Plugins 326 * 327 * @param string $format 328 */ 329 private static function loadPlugins($format) 330 { 331 if (!isset(self::$plugins[static::ALGORITHM][$format])) { 332 self::$plugins[static::ALGORITHM][$format] = []; 333 foreach (new \DirectoryIterator(__DIR__ . '/../' . static::ALGORITHM . '/Formats/' . $format . '/') as $file) { 334 if ($file->getExtension() != 'php') { 335 continue; 336 } 337 $name = $file->getBasename('.php'); 338 if ($name[0] == '.') { 339 continue; 340 } 341 $type = 'phpseclib3\Crypt\\' . static::ALGORITHM . '\\Formats\\' . $format . '\\' . $name; 342 $reflect = new \ReflectionClass($type); 343 if ($reflect->isTrait()) { 344 continue; 345 } 346 self::$plugins[static::ALGORITHM][$format][strtolower($name)] = $type; 347 if ($reflect->hasConstant('IS_INVISIBLE')) { 348 self::$invisiblePlugins[static::ALGORITHM][] = $type; 349 } 350 } 351 } 352 } 353 354 /** 355 * Returns a list of supported formats. 356 * 357 * @return array 358 */ 359 public static function getSupportedKeyFormats() 360 { 361 self::initialize_static_variables(); 362 363 return self::$plugins[static::ALGORITHM]['Keys']; 364 } 365 366 /** 367 * Add a fileformat plugin 368 * 369 * The plugin needs to either already be loaded or be auto-loadable. 370 * Loading a plugin whose shortname overwrite an existing shortname will overwrite the old plugin. 371 * 372 * @see self::load() 373 * @param string $fullname 374 * @return bool 375 */ 376 public static function addFileFormat($fullname) 377 { 378 self::initialize_static_variables(); 379 380 if (class_exists($fullname)) { 381 $meta = new \ReflectionClass($fullname); 382 $shortname = $meta->getShortName(); 383 self::$plugins[static::ALGORITHM]['Keys'][strtolower($shortname)] = $fullname; 384 if ($meta->hasConstant('IS_INVISIBLE')) { 385 self::$invisiblePlugins[static::ALGORITHM][] = strtolower($shortname); 386 } 387 } 388 } 389 390 /** 391 * Returns the format of the loaded key. 392 * 393 * If the key that was loaded wasn't in a valid or if the key was auto-generated 394 * with RSA::createKey() then this will throw an exception. 395 * 396 * @see self::load() 397 * @return mixed 398 */ 399 public function getLoadedFormat() 400 { 401 if (empty($this->format)) { 402 throw new NoKeyLoadedException('This key was created with createKey - it was not loaded with load. Therefore there is no "loaded format"'); 403 } 404 405 $meta = new \ReflectionClass($this->format); 406 return $meta->getShortName(); 407 } 408 409 /** 410 * Returns the key's comment 411 * 412 * Not all key formats support comments. If you want to set a comment use toString() 413 * 414 * @return null|string 415 */ 416 public function getComment() 417 { 418 return $this->comment; 419 } 420 421 /** 422 * Tests engine validity 423 * 424 */ 425 public static function useBestEngine() 426 { 427 static::$engines = [ 428 'PHP' => true, 429 'OpenSSL' => extension_loaded('openssl'), 430 // this test can be satisfied by either of the following: 431 // http://php.net/manual/en/book.sodium.php 432 // https://github.com/paragonie/sodium_compat 433 'libsodium' => function_exists('sodium_crypto_sign_keypair') 434 ]; 435 436 return static::$engines; 437 } 438 439 /** 440 * Flag to use internal engine only (useful for unit testing) 441 * 442 */ 443 public static function useInternalEngine() 444 { 445 static::$engines = [ 446 'PHP' => true, 447 'OpenSSL' => false, 448 'libsodium' => false 449 ]; 450 } 451 452 /** 453 * __toString() magic method 454 * 455 * @return string 456 */ 457 public function __toString() 458 { 459 return $this->toString('PKCS8'); 460 } 461 462 /** 463 * Determines which hashing function should be used 464 * 465 * @param string $hash 466 */ 467 public function withHash($hash) 468 { 469 $new = clone $this; 470 471 $new->hash = new Hash($hash); 472 $new->hmac = new Hash($hash); 473 474 return $new; 475 } 476 477 /** 478 * Returns the hash algorithm currently being used 479 * 480 */ 481 public function getHash() 482 { 483 return clone $this->hash; 484 } 485 486 /** 487 * Compute the pseudorandom k for signature generation, 488 * using the process specified for deterministic DSA. 489 * 490 * @param string $h1 491 * @return string 492 */ 493 protected function computek($h1) 494 { 495 $v = str_repeat("\1", strlen($h1)); 496 497 $k = str_repeat("\0", strlen($h1)); 498 499 $x = $this->int2octets($this->x); 500 $h1 = $this->bits2octets($h1); 501 502 $this->hmac->setKey($k); 503 $k = $this->hmac->hash($v . "\0" . $x . $h1); 504 $this->hmac->setKey($k); 505 $v = $this->hmac->hash($v); 506 $k = $this->hmac->hash($v . "\1" . $x . $h1); 507 $this->hmac->setKey($k); 508 $v = $this->hmac->hash($v); 509 510 $qlen = $this->q->getLengthInBytes(); 511 512 while (true) { 513 $t = ''; 514 while (strlen($t) < $qlen) { 515 $v = $this->hmac->hash($v); 516 $t = $t . $v; 517 } 518 $k = $this->bits2int($t); 519 520 if (!$k->equals(self::$zero) && $k->compare($this->q) < 0) { 521 break; 522 } 523 $k = $this->hmac->hash($v . "\0"); 524 $this->hmac->setKey($k); 525 $v = $this->hmac->hash($v); 526 } 527 528 return $k; 529 } 530 531 /** 532 * Integer to Octet String 533 * 534 * @param \phpseclib3\Math\BigInteger $v 535 * @return string 536 */ 537 private function int2octets($v) 538 { 539 $out = $v->toBytes(); 540 $rolen = $this->q->getLengthInBytes(); 541 if (strlen($out) < $rolen) { 542 return str_pad($out, $rolen, "\0", STR_PAD_LEFT); 543 } elseif (strlen($out) > $rolen) { 544 return substr($out, -$rolen); 545 } else { 546 return $out; 547 } 548 } 549 550 /** 551 * Bit String to Integer 552 * 553 * @param string $in 554 * @return \phpseclib3\Math\BigInteger 555 */ 556 protected function bits2int($in) 557 { 558 $v = new BigInteger($in, 256); 559 $vlen = strlen($in) << 3; 560 $qlen = $this->q->getLength(); 561 if ($vlen > $qlen) { 562 return $v->bitwise_rightShift($vlen - $qlen); 563 } 564 return $v; 565 } 566 567 /** 568 * Bit String to Octet String 569 * 570 * @param string $in 571 * @return string 572 */ 573 private function bits2octets($in) 574 { 575 $z1 = $this->bits2int($in); 576 $z2 = $z1->subtract($this->q); 577 return $z2->compare(self::$zero) < 0 ? 578 $this->int2octets($z1) : 579 $this->int2octets($z2); 580 } 581 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body