1<?php 2 3/** 4 * Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA. 5 * 6 * PHP version 5 7 * 8 * Here's an example of how to encrypt and decrypt text with this library: 9 * <code> 10 * <?php 11 * include 'vendor/autoload.php'; 12 * 13 * $private = Crypt\RSA::createKey(); 14 * $public = $private->getPublicKey(); 15 * 16 * $plaintext = 'terrafrost'; 17 * 18 * $ciphertext = $public->encrypt($plaintext); 19 * 20 * echo $private->decrypt($ciphertext); 21 * ?> 22 * </code> 23 * 24 * Here's an example of how to create signatures and verify signatures with this library: 25 * <code> 26 * <?php 27 * include 'vendor/autoload.php'; 28 * 29 * $private = Crypt\RSA::createKey(); 30 * $public = $private->getPublicKey(); 31 * 32 * $plaintext = 'terrafrost'; 33 * 34 * $signature = $private->sign($plaintext); 35 * 36 * echo $public->verify($plaintext, $signature) ? 'verified' : 'unverified'; 37 * ?> 38 * </code> 39 * 40 * One thing to consider when using this: so phpseclib uses PSS mode by default. 41 * Technically, id-RSASSA-PSS has a different key format than rsaEncryption. So 42 * should phpseclib save to the id-RSASSA-PSS format by default or the 43 * rsaEncryption format? For stand-alone keys I figure rsaEncryption is better 44 * because SSH doesn't use PSS and idk how many SSH servers would be able to 45 * decode an id-RSASSA-PSS key. For X.509 certificates the id-RSASSA-PSS 46 * format is used by default (unless you change it up to use PKCS1 instead) 47 * 48 * @author Jim Wigginton <terrafrost@php.net> 49 * @copyright 2009 Jim Wigginton 50 * @license http://www.opensource.org/licenses/mit-license.html MIT License 51 * @link http://phpseclib.sourceforge.net 52 */ 53 54namespace phpseclib3\Crypt; 55 56use phpseclib3\Crypt\Common\AsymmetricKey; 57use phpseclib3\Crypt\RSA\Formats\Keys\PSS; 58use phpseclib3\Crypt\RSA\PrivateKey; 59use phpseclib3\Crypt\RSA\PublicKey; 60use phpseclib3\Exception\BadConfigurationException; 61use phpseclib3\Exception\InconsistentSetupException; 62use phpseclib3\Exception\UnsupportedAlgorithmException; 63use phpseclib3\Math\BigInteger; 64 65/** 66 * Pure-PHP PKCS#1 compliant implementation of RSA. 67 * 68 * @author Jim Wigginton <terrafrost@php.net> 69 */ 70abstract class RSA extends AsymmetricKey 71{ 72 /** 73 * Algorithm Name 74 * 75 * @var string 76 */ 77 const ALGORITHM = 'RSA'; 78 79 /** 80 * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding} 81 * (OAEP) for encryption / decryption. 82 * 83 * Uses sha256 by default 84 * 85 * @see self::setHash() 86 * @see self::setMGFHash() 87 * @see self::encrypt() 88 * @see self::decrypt() 89 */ 90 const ENCRYPTION_OAEP = 1; 91 92 /** 93 * Use PKCS#1 padding. 94 * 95 * Although self::PADDING_OAEP / self::PADDING_PSS offers more security, including PKCS#1 padding is necessary for purposes of backwards 96 * compatibility with protocols (like SSH-1) written before OAEP's introduction. 97 * 98 * @see self::encrypt() 99 * @see self::decrypt() 100 */ 101 const ENCRYPTION_PKCS1 = 2; 102 103 /** 104 * Do not use any padding 105 * 106 * Although this method is not recommended it can none-the-less sometimes be useful if you're trying to decrypt some legacy 107 * stuff, if you're trying to diagnose why an encrypted message isn't decrypting, etc. 108 * 109 * @see self::encrypt() 110 * @see self::decrypt() 111 */ 112 const ENCRYPTION_NONE = 4; 113 114 /** 115 * Use the Probabilistic Signature Scheme for signing 116 * 117 * Uses sha256 and 0 as the salt length 118 * 119 * @see self::setSaltLength() 120 * @see self::setMGFHash() 121 * @see self::setHash() 122 * @see self::sign() 123 * @see self::verify() 124 * @see self::setHash() 125 */ 126 const SIGNATURE_PSS = 16; 127 128 /** 129 * Use a relaxed version of PKCS#1 padding for signature verification 130 * 131 * @see self::sign() 132 * @see self::verify() 133 * @see self::setHash() 134 */ 135 const SIGNATURE_RELAXED_PKCS1 = 32; 136 137 /** 138 * Use PKCS#1 padding for signature verification 139 * 140 * @see self::sign() 141 * @see self::verify() 142 * @see self::setHash() 143 */ 144 const SIGNATURE_PKCS1 = 64; 145 146 /** 147 * Encryption padding mode 148 * 149 * @var int 150 */ 151 protected $encryptionPadding = self::ENCRYPTION_OAEP; 152 153 /** 154 * Signature padding mode 155 * 156 * @var int 157 */ 158 protected $signaturePadding = self::SIGNATURE_PSS; 159 160 /** 161 * Length of hash function output 162 * 163 * @var int 164 */ 165 protected $hLen; 166 167 /** 168 * Length of salt 169 * 170 * @var int 171 */ 172 protected $sLen; 173 174 /** 175 * Label 176 * 177 * @var string 178 */ 179 protected $label = ''; 180 181 /** 182 * Hash function for the Mask Generation Function 183 * 184 * @var Hash 185 */ 186 protected $mgfHash; 187 188 /** 189 * Length of MGF hash function output 190 * 191 * @var int 192 */ 193 protected $mgfHLen; 194 195 /** 196 * Modulus (ie. n) 197 * 198 * @var Math\BigInteger 199 */ 200 protected $modulus; 201 202 /** 203 * Modulus length 204 * 205 * @var Math\BigInteger 206 */ 207 protected $k; 208 209 /** 210 * Exponent (ie. e or d) 211 * 212 * @var Math\BigInteger 213 */ 214 protected $exponent; 215 216 /** 217 * Default public exponent 218 * 219 * @var int 220 * @link http://en.wikipedia.org/wiki/65537_%28number%29 221 */ 222 private static $defaultExponent = 65537; 223 224 /** 225 * Enable Blinding? 226 * 227 * @var bool 228 */ 229 protected static $enableBlinding = true; 230 231 /** 232 * Smallest Prime 233 * 234 * Per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller 235 * than 256 bits. As a consequence if the key you're trying to create is 1024 bits and you've set smallestPrime 236 * to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). At least if 237 * engine is set to self::ENGINE_INTERNAL. If Engine is set to self::ENGINE_OPENSSL then smallest Prime is 238 * ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key generation when there's 239 * a chance neither gmp nor OpenSSL are installed) 240 * 241 * @var int 242 */ 243 private static $smallestPrime = 4096; 244 245 /** 246 * Public Exponent 247 * 248 * @var Math\BigInteger 249 */ 250 protected $publicExponent; 251 252 /** 253 * Forced Engine 254 * 255 * @var ?string 256 * @see parent::forceEngine() 257 */ 258 protected static $forcedEngine = null; 259 260 /** 261 * Sets the public exponent for key generation 262 * 263 * This will be 65537 unless changed. 264 * 265 * @param int $val 266 */ 267 public static function setExponent($val) 268 { 269 self::$defaultExponent = $val; 270 } 271 272 /** 273 * Sets the smallest prime number in bits. Used for key generation 274 * 275 * This will be 4096 unless changed. 276 * 277 * @param int $val 278 */ 279 public static function setSmallestPrime($val) 280 { 281 self::$smallestPrime = $val; 282 } 283 284 /** 285 * Create a private key 286 * 287 * The public key can be extracted from the private key 288 * 289 * @return PrivateKey 290 * @param int $bits 291 */ 292 public static function createKey($bits = 2048) 293 { 294 self::initialize_static_variables(); 295 296 $class = new \ReflectionClass(static::class); 297 if ($class->isFinal()) { 298 throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')'); 299 } 300 301 if (self::$forcedEngine == 'libsodium' || (self::$forcedEngine == 'OpenSSL' && !function_exists('openssl_pkey_new'))) { 302 throw new BadConfigurationException('Engine ' . self::$forcedEngine . ' is forced but unsupported for RSA'); 303 } 304 305 $regSize = $bits >> 1; // divide by two to see how many bits P and Q would be 306 if ($regSize > self::$smallestPrime) { 307 $num_primes = floor($bits / self::$smallestPrime); 308 $regSize = self::$smallestPrime; 309 } else { 310 $num_primes = 2; 311 } 312 313 if ($num_primes == 2 && $bits >= 384 && self::$defaultExponent == 65537) { 314 // at this point the only two supported values for self::$forcedEngine are OpenSSL, PHP and null 315 // if it's either OpenSSL or null we'll use OpenSSL (if it's available) 316 if (self::$forcedEngine !== 'PHP' && function_exists('openssl_pkey_new')) { 317 $config = []; 318 if (self::$configFile) { 319 $config['config'] = self::$configFile; 320 } 321 // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum 322 $rsa = openssl_pkey_new(['private_key_bits' => $bits] + $config); 323 if (!$rsa || !openssl_pkey_export($rsa, $privatekeystr, null, $config)) { 324 if (isset(self::$forcedEngine)) { 325 throw new BadConfigurationException('Engine OpenSSL is forced but produced an error - ' . openssl_error_string()); 326 } 327 } else { 328 // clear the buffer of error strings stemming from a minimalistic openssl.cnf 329 // https://github.com/php/php-src/issues/11054 talks about other errors this'll pick up 330 while (openssl_error_string() !== false) { 331 } 332 333 return RSA::load($privatekeystr); 334 } 335 } 336 } 337 338 static $e; 339 if (!isset($e)) { 340 $e = new BigInteger(self::$defaultExponent); 341 } 342 343 $n = clone self::$one; 344 $exponents = $coefficients = $primes = []; 345 $lcm = [ 346 'top' => clone self::$one, 347 'bottom' => false 348 ]; 349 350 do { 351 for ($i = 1; $i <= $num_primes; $i++) { 352 if ($i != $num_primes) { 353 $primes[$i] = BigInteger::randomPrime($regSize); 354 } else { 355 $minMax = BigInteger::minMaxBits($bits); 356 $min = $minMax['min']; 357 $max = $minMax['max']; 358 list($min) = $min->divide($n); 359 $min = $min->add(self::$one); 360 list($max) = $max->divide($n); 361 $primes[$i] = BigInteger::randomRangePrime($min, $max); 362 } 363 364 // the first coefficient is calculated differently from the rest 365 // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1]) 366 if ($i > 2) { 367 $coefficients[$i] = $n->modInverse($primes[$i]); 368 } 369 370 $n = $n->multiply($primes[$i]); 371 372 $temp = $primes[$i]->subtract(self::$one); 373 374 // textbook RSA implementations use Euler's totient function instead of the least common multiple. 375 // see http://en.wikipedia.org/wiki/Euler%27s_totient_function 376 $lcm['top'] = $lcm['top']->multiply($temp); 377 $lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp); 378 } 379 380 list($temp) = $lcm['top']->divide($lcm['bottom']); 381 $gcd = $temp->gcd($e); 382 $i0 = 1; 383 } while (!$gcd->equals(self::$one)); 384 385 $coefficients[2] = $primes[2]->modInverse($primes[1]); 386 387 $d = $e->modInverse($temp); 388 389 foreach ($primes as $i => $prime) { 390 $temp = $prime->subtract(self::$one); 391 $exponents[$i] = $e->modInverse($temp); 392 } 393 394 // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>: 395 // RSAPrivateKey ::= SEQUENCE { 396 // version Version, 397 // modulus INTEGER, -- n 398 // publicExponent INTEGER, -- e 399 // privateExponent INTEGER, -- d 400 // prime1 INTEGER, -- p 401 // prime2 INTEGER, -- q 402 // exponent1 INTEGER, -- d mod (p-1) 403 // exponent2 INTEGER, -- d mod (q-1) 404 // coefficient INTEGER, -- (inverse of q) mod p 405 // otherPrimeInfos OtherPrimeInfos OPTIONAL 406 // } 407 $privatekey = new PrivateKey(); 408 $privatekey->modulus = $n; 409 $privatekey->k = $bits >> 3; 410 $privatekey->publicExponent = $e; 411 $privatekey->exponent = $d; 412 $privatekey->primes = $primes; 413 $privatekey->exponents = $exponents; 414 $privatekey->coefficients = $coefficients; 415 416 /* 417 $publickey = new PublicKey; 418 $publickey->modulus = $n; 419 $publickey->k = $bits >> 3; 420 $publickey->exponent = $e; 421 $publickey->publicExponent = $e; 422 $publickey->isPublic = true; 423 */ 424 425 return $privatekey; 426 } 427 428 /** 429 * OnLoad Handler 430 * 431 * @return bool 432 */ 433 protected static function onLoad(array $components) 434 { 435 $key = $components['isPublicKey'] ? 436 new PublicKey() : 437 new PrivateKey(); 438 439 $key->modulus = $components['modulus']; 440 $key->publicExponent = $components['publicExponent']; 441 $key->k = $key->modulus->getLengthInBytes(); 442 443 if ($components['isPublicKey'] || !isset($components['privateExponent'])) { 444 $key->exponent = $key->publicExponent; 445 } else { 446 $key->privateExponent = $components['privateExponent']; 447 $key->exponent = $key->privateExponent; 448 $key->primes = $components['primes']; 449 $key->exponents = $components['exponents']; 450 $key->coefficients = $components['coefficients']; 451 } 452 453 if ($components['format'] == PSS::class) { 454 // in the X509 world RSA keys are assumed to use PKCS1 padding by default. only if the key is 455 // explicitly a PSS key is the use of PSS assumed. phpseclib does not work like this. phpseclib 456 // uses PSS padding by default. it assumes the more secure method by default and altho it provides 457 // for the less secure PKCS1 method you have to go out of your way to use it. this is consistent 458 // with the latest trends in crypto. libsodium (NaCl) is actually a little more extreme in that 459 // not only does it defaults to the most secure methods - it doesn't even let you choose less 460 // secure methods 461 //$key = $key->withPadding(self::SIGNATURE_PSS); 462 if (isset($components['hash'])) { 463 $key = $key->withHash($components['hash']); 464 } 465 if (isset($components['MGFHash'])) { 466 $key = $key->withMGFHash($components['MGFHash']); 467 } 468 if (isset($components['saltLength'])) { 469 $key = $key->withSaltLength($components['saltLength']); 470 } 471 } 472 473 return $key; 474 } 475 476 /** 477 * Constructor 478 * 479 * PublicKey and PrivateKey objects can only be created from abstract RSA class 480 */ 481 protected function __construct() 482 { 483 parent::__construct(); 484 485 $this->hLen = $this->hash->getLengthInBytes(); 486 $this->mgfHash = new Hash('sha256'); 487 $this->mgfHLen = $this->mgfHash->getLengthInBytes(); 488 } 489 490 /** 491 * Integer-to-Octet-String primitive 492 * 493 * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}. 494 * 495 * @param bool|Math\BigInteger $x 496 * @param int $xLen 497 * @return bool|string 498 */ 499 protected function i2osp($x, $xLen) 500 { 501 if ($x === false) { 502 return false; 503 } 504 $x = $x->toBytes(); 505 if (strlen($x) > $xLen) { 506 throw new \OutOfRangeException('Resultant string length out of range'); 507 } 508 return str_pad($x, $xLen, chr(0), STR_PAD_LEFT); 509 } 510 511 /** 512 * Octet-String-to-Integer primitive 513 * 514 * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}. 515 * 516 * @param string $x 517 * @return Math\BigInteger 518 */ 519 protected function os2ip($x) 520 { 521 return new BigInteger($x, 256); 522 } 523 524 /** 525 * EMSA-PKCS1-V1_5-ENCODE 526 * 527 * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}. 528 * 529 * @param string $m 530 * @param int $emLen 531 * @throws \LengthException if the intended encoded message length is too short 532 * @return string 533 */ 534 protected function emsa_pkcs1_v1_5_encode($m, $emLen) 535 { 536 $h = $this->hash->hash($m); 537 538 // see http://tools.ietf.org/html/rfc3447#page-43 539 switch ($this->hash->getHash()) { 540 case 'md2': 541 $t = "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02\x05\x00\x04\x10"; 542 break; 543 case 'md5': 544 $t = "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10"; 545 break; 546 case 'sha1': 547 $t = "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14"; 548 break; 549 case 'sha256': 550 $t = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20"; 551 break; 552 case 'sha384': 553 $t = "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30"; 554 break; 555 case 'sha512': 556 $t = "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40"; 557 break; 558 // from https://www.emc.com/collateral/white-papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf#page=40 559 case 'sha224': 560 $t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c"; 561 break; 562 case 'sha512/224': 563 $t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x05\x05\x00\x04\x1c"; 564 break; 565 case 'sha512/256': 566 $t = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x06\x05\x00\x04\x20"; 567 break; 568 // the following 3x algorithms are not specified in PKCS1 v2.2, however, some standards none-the-less do use them: 569 // https://sk-eid.github.io/smart-id-documentation/rp-api/changes.html#_security_enhancements 570 // the OIDs are from this URL: 571 // https://csrc.nist.gov/projects/computer-security-objects-register/algorithm-registration#Hash 572 case 'sha3/224': 573 $t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x07\x05\x00\x04\x1c"; 574 break; 575 case 'sha3/256': 576 $t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x08\x05\x00\x04\x20"; 577 break; 578 case 'sha3/384': 579 $t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x09\x05\x00\x04\x30"; 580 break; 581 case 'sha3/512': 582 $t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x0A\x05\x00\x04\x40"; 583 } 584 $t .= $h; 585 $tLen = strlen($t); 586 587 if ($emLen < $tLen + 11) { 588 throw new \LengthException('Intended encoded message length too short'); 589 } 590 591 $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3); 592 593 $em = "\0\1$ps\0$t"; 594 595 return $em; 596 } 597 598 /** 599 * EMSA-PKCS1-V1_5-ENCODE (without NULL) 600 * 601 * Quoting https://tools.ietf.org/html/rfc8017#page-65, 602 * 603 * "The parameters field associated with id-sha1, id-sha224, id-sha256, 604 * id-sha384, id-sha512, id-sha512/224, and id-sha512/256 should 605 * generally be omitted, but if present, it shall have a value of type 606 * NULL" 607 * 608 * @param string $m 609 * @param int $emLen 610 * @return string 611 */ 612 protected function emsa_pkcs1_v1_5_encode_without_null($m, $emLen) 613 { 614 $h = $this->hash->hash($m); 615 616 // see http://tools.ietf.org/html/rfc3447#page-43 617 switch ($this->hash->getHash()) { 618 case 'sha1': 619 $t = "\x30\x1f\x30\x07\x06\x05\x2b\x0e\x03\x02\x1a\x04\x14"; 620 break; 621 case 'sha256': 622 $t = "\x30\x2f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x04\x20"; 623 break; 624 case 'sha384': 625 $t = "\x30\x3f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x04\x30"; 626 break; 627 case 'sha512': 628 $t = "\x30\x4f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x04\x40"; 629 break; 630 // from https://www.emc.com/collateral/white-papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf#page=40 631 case 'sha224': 632 $t = "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x04\x1c"; 633 break; 634 case 'sha512/224': 635 $t = "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x05\x04\x1c"; 636 break; 637 case 'sha512/256': 638 $t = "\x30\x2f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x06\x04\x20"; 639 break; 640 // the following 3x algorithms are not specified in PKCS1 v2.2, however, some standards none-the-less do use them: 641 // https://sk-eid.github.io/smart-id-documentation/rp-api/changes.html#_security_enhancements 642 // the OIDs are from this URL: 643 // https://csrc.nist.gov/projects/computer-security-objects-register/algorithm-registration#Hash 644 case 'sha3/224': 645 $t = "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x07\x04\x1c"; 646 break; 647 case 'sha3/256': 648 $t = "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x08\x04\x20"; 649 break; 650 case 'sha3/384': 651 $t = "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x09\x04\x30"; 652 break; 653 case 'sha3/512': 654 $t = "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x0A\x04\x40"; 655 break; 656 default: 657 throw new UnsupportedAlgorithmException('md2 and md5 require NULLs'); 658 } 659 $t .= $h; 660 $tLen = strlen($t); 661 662 if ($emLen < $tLen + 11) { 663 throw new \LengthException('Intended encoded message length too short'); 664 } 665 666 $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3); 667 668 $em = "\0\1$ps\0$t"; 669 670 return $em; 671 } 672 673 /** 674 * MGF1 675 * 676 * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}. 677 * 678 * @param string $mgfSeed 679 * @param int $maskLen 680 * @return string 681 */ 682 protected function mgf1($mgfSeed, $maskLen) 683 { 684 // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output. 685 686 $t = ''; 687 $count = ceil($maskLen / $this->mgfHLen); 688 for ($i = 0; $i < $count; $i++) { 689 $c = pack('N', $i); 690 $t .= $this->mgfHash->hash($mgfSeed . $c); 691 } 692 693 return substr($t, 0, $maskLen); 694 } 695 696 /** 697 * Returns the key size 698 * 699 * More specifically, this returns the size of the modulo in bits. 700 * 701 * @return int 702 */ 703 public function getLength() 704 { 705 return !isset($this->modulus) ? 0 : $this->modulus->getLength(); 706 } 707 708 /** 709 * Determines which hashing function should be used 710 * 711 * Used with signature production / verification and (if the encryption mode is self::PADDING_OAEP) encryption and 712 * decryption. 713 * 714 * @param string $hash 715 */ 716 public function withHash($hash) 717 { 718 $new = clone $this; 719 720 // Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example. 721 switch (strtolower($hash)) { 722 case 'md2': 723 case 'md5': 724 case 'sha1': 725 case 'sha256': 726 case 'sha384': 727 case 'sha512': 728 case 'sha224': 729 case 'sha512/224': 730 case 'sha512/256': 731 case 'sha3/224': 732 case 'sha3/256': 733 case 'sha3/384': 734 case 'sha3/512': 735 $new->hash = new Hash($hash); 736 break; 737 default: 738 throw new UnsupportedAlgorithmException( 739 "The only supported hash algorithms are: md2, md5, sha1, sha256, sha384, sha512, sha224, sha512/224, sha512/256 - $hash provided" 740 ); 741 } 742 $new->hLen = $new->hash->getLengthInBytes(); 743 744 return $new; 745 } 746 747 /** 748 * Determines which hashing function should be used for the mask generation function 749 * 750 * The mask generation function is used by self::PADDING_OAEP and self::PADDING_PSS and although it's 751 * best if Hash and MGFHash are set to the same thing this is not a requirement. 752 * 753 * @param string $hash 754 */ 755 public function withMGFHash($hash) 756 { 757 $new = clone $this; 758 759 // Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example. 760 switch (strtolower($hash)) { 761 case 'md2': 762 case 'md5': 763 case 'sha1': 764 case 'sha256': 765 case 'sha384': 766 case 'sha512': 767 case 'sha224': 768 case 'sha512/224': 769 case 'sha512/256': 770 case 'sha3/224': 771 case 'sha3/256': 772 case 'sha3/384': 773 case 'sha3/512': 774 $new->mgfHash = new Hash($hash); 775 break; 776 default: 777 throw new UnsupportedAlgorithmException( 778 "The only supported hash algorithms are: md2, md5, sha1, sha256, sha384, sha512, sha224, sha512/224, sha512/256 - $hash provided" 779 ); 780 } 781 $new->mgfHLen = $new->mgfHash->getLengthInBytes(); 782 783 return $new; 784 } 785 786 /** 787 * Returns the MGF hash algorithm currently being used 788 * 789 */ 790 public function getMGFHash() 791 { 792 return clone $this->mgfHash; 793 } 794 795 /** 796 * Determines the salt length 797 * 798 * Used by RSA::PADDING_PSS 799 * 800 * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}: 801 * 802 * Typical salt lengths in octets are hLen (the length of the output 803 * of the hash function Hash) and 0. 804 * 805 * @param int $sLen 806 */ 807 public function withSaltLength($sLen) 808 { 809 $new = clone $this; 810 $new->sLen = $sLen; 811 return $new; 812 } 813 814 /** 815 * Returns the salt length currently being used 816 * 817 */ 818 public function getSaltLength() 819 { 820 return $this->sLen !== null ? $this->sLen : $this->hLen; 821 } 822 823 /** 824 * Determines the label 825 * 826 * Used by RSA::PADDING_OAEP 827 * 828 * To quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}: 829 * 830 * Both the encryption and the decryption operations of RSAES-OAEP take 831 * the value of a label L as input. In this version of PKCS #1, L is 832 * the empty string; other uses of the label are outside the scope of 833 * this document. 834 * 835 * @param string $label 836 */ 837 public function withLabel($label) 838 { 839 $new = clone $this; 840 $new->label = $label; 841 return $new; 842 } 843 844 /** 845 * Returns the label currently being used 846 * 847 */ 848 public function getLabel() 849 { 850 return $this->label; 851 } 852 853 /** 854 * Determines the padding modes 855 * 856 * Example: $key->withPadding(RSA::ENCRYPTION_PKCS1 | RSA::SIGNATURE_PKCS1); 857 * 858 * @param int $padding 859 */ 860 public function withPadding($padding) 861 { 862 $masks = [ 863 self::ENCRYPTION_OAEP, 864 self::ENCRYPTION_PKCS1, 865 self::ENCRYPTION_NONE 866 ]; 867 $encryptedCount = 0; 868 $selected = 0; 869 foreach ($masks as $mask) { 870 if ($padding & $mask) { 871 $selected = $mask; 872 $encryptedCount++; 873 } 874 } 875 if ($encryptedCount > 1) { 876 throw new InconsistentSetupException('Multiple encryption padding modes have been selected; at most only one should be selected'); 877 } 878 $encryptionPadding = $selected; 879 880 $masks = [ 881 self::SIGNATURE_PSS, 882 self::SIGNATURE_RELAXED_PKCS1, 883 self::SIGNATURE_PKCS1 884 ]; 885 $signatureCount = 0; 886 $selected = 0; 887 foreach ($masks as $mask) { 888 if ($padding & $mask) { 889 $selected = $mask; 890 $signatureCount++; 891 } 892 } 893 if ($signatureCount > 1) { 894 throw new InconsistentSetupException('Multiple signature padding modes have been selected; at most only one should be selected'); 895 } 896 $signaturePadding = $selected; 897 898 $new = clone $this; 899 if ($encryptedCount) { 900 $new->encryptionPadding = $encryptionPadding; 901 } 902 if ($signatureCount) { 903 $new->signaturePadding = $signaturePadding; 904 } 905 return $new; 906 } 907 908 /** 909 * Returns the padding currently being used 910 * 911 */ 912 public function getPadding() 913 { 914 return $this->signaturePadding | $this->encryptionPadding; 915 } 916 917 /** 918 * Enable RSA Blinding 919 * 920 */ 921 public static function enableBlinding() 922 { 923 static::$enableBlinding = true; 924 } 925 926 /** 927 * Disable RSA Blinding 928 * 929 */ 930 public static function disableBlinding() 931 { 932 static::$enableBlinding = false; 933 } 934 935 /** 936 * Handles OpenSSL encryption / decryption / signature creation / verification 937 * 938 * @param string $func 939 * @param string $message 940 * @param ?string $signature 941 * @return bool|string|null 942 */ 943 protected function handleOpenSSL($func, $message, $signature = null) 944 { 945 switch ($func) { 946 case 'openssl_verify': 947 case 'openssl_sign': 948 $paddingType = 'signaturePadding'; 949 break; 950 case 'openssl_public_encrypt': 951 case 'openssl_private_decrypt': 952 $paddingType = 'encryptionPadding'; 953 } 954 955 if (self::$forcedEngine === 'libsodium') { 956 throw new BadConfigurationException('Engine libsodium is not supported for RSA'); 957 } 958 959 if ((isset(self::$forcedEngine) && self::$forcedEngine !== 'PHP') && $this->$paddingType === self::SIGNATURE_RELAXED_PKCS1) { 960 throw new BadConfigurationException('Only the PHP engine can be used with relaxed PKCS1 padding'); 961 } 962 963 if (self::$forcedEngine !== 'PHP') { 964 if (self::$forcedEngine === 'OpenSSL' && !function_exists($func)) { 965 throw new BadConfigurationException('Engine OpenSSL is forced but unavailable for RSA'); 966 } 967 if ($this->$paddingType === self::SIGNATURE_PSS) { 968 switch (true) { 969 case !defined('OPENSSL_PKCS1_PSS_PADDING'): 970 $error = 'Engine OpenSSL is forced but PSS encryption requires PHP >= 8.5.0'; 971 break; 972 case $this->hash->getHash() !== $this->mgfHash->getHash(): 973 $error = 'Engine OpenSSL is forced but can\'t be used because the Hash and MGF Hash do not match'; 974 break; 975 case $this->getSaltLength() !== $this->hLen: 976 $error = 'Engine OpenSSL is forced but can\'t be used because the salt length doesn\'t match the hash length'; 977 } 978 } 979 if ($this->$paddingType === self::ENCRYPTION_OAEP) { 980 switch (true) { 981 case $this->hash->getHash() !== $this->mgfHash->getHash(): 982 $error = 'Engine OpenSSL is forced but can\'t be used because the Hash and MGF Hash do not match'; 983 break; 984 case $this->hash->getHash() !== 'sha1' && PHP_VERSION_ID < 80500: 985 $error = 'Engine OpenSSL is forced but non-sha1 hashes are only supported on PHP 8.5.0+'; 986 break; 987 case strlen($this->label): 988 $error = 'Engine OpenSSL is forced but can\'t be used because the label is not the empty string'; 989 } 990 } 991 if (isset($error)) { 992 if (self::$forcedEngine === 'OpenSSL') { 993 throw new BadConfigurationException($error); 994 } 995 } elseif ($paddingType === 'signaturePadding') { 996 switch (true) { 997 case $this->signaturePadding === self::SIGNATURE_PSS && defined('OPENSSL_PKCS1_PSS_PADDING'): 998 case $this->signaturePadding !== self::SIGNATURE_PSS && function_exists($func): 999 $key = $this instanceof PrivateKey ? 1000 $this->withPassword()->toString('PKCS8') : 1001 $this->toString('PKCS8'); 1002 if ($func === 'openssl_sign' && strpos($key, 'PUBLIC') !== false) { 1003 if (self::$forcedEngine === 'OpenSSL') { 1004 throw new BadConfigurationException('Engine OpenSSL is forced but cannot be used because the private key does not have the prime components within it'); 1005 } 1006 break; 1007 } 1008 $hash = $this->hash->getHash(); 1009 1010 // on github actions, php 7.0 and 7.1 on windows emit the following warning: 1011 // openssl_sign(): supplied key param cannot be coerced into a private key 1012 set_error_handler(function ($errno, $errstr) { 1013 throw new BadConfigurationException("Engine OpenSSL is forced but got error: $errstr"); 1014 }); 1015 try { 1016 $result = $this->signaturePadding === self::SIGNATURE_PSS ? 1017 $func($message, $signature, $key, $hash, OPENSSL_PKCS1_PSS_PADDING) : 1018 $func($message, $signature, $key, $hash); 1019 } catch (BadConfigurationException $e) { 1020 if (self::$forcedEngine === 'OpenSSL') { 1021 throw $e; 1022 } 1023 $result = false; 1024 } finally { 1025 restore_error_handler(); 1026 } 1027 1028 if ($func === 'openssl_verify' && $result !== -1 && $result !== false) { 1029 return (bool) $result; 1030 } 1031 if ($result) { 1032 return $signature; 1033 } 1034 if (self::$forcedEngine === 'OpenSSL') { 1035 throw new BadConfigurationException('Engine OpenSSL is forced but was unable to create signature because of ' . openssl_error_string()); 1036 } 1037 } 1038 } else { 1039 if ($this->encryptionPadding !== self::ENCRYPTION_OAEP || PHP_VERSION_ID >= 80500) { 1040 $key = $this->toString('PKCS8'); 1041 if ($func === 'openssl_private_decrypt' && strpos($key, 'PUBLIC') !== false) { 1042 if ($this->encryptionPadding === self::ENCRYPTION_OAEP) { 1043 if (self::$forcedEngine === 'OpenSSL') { 1044 throw new BadConfigurationException('Engine OpenSSL is forced but cannot be used because openssl_public_decrypt() doesn\'t have a hash parameter like openssl_private_decrypt() does'); 1045 } 1046 return null; 1047 } 1048 $func = 'openssl_public_decrypt'; 1049 } 1050 $hash = $this->hash->getHash(); 1051 $output = ''; 1052 switch ($this->encryptionPadding) { 1053 case self::ENCRYPTION_NONE: 1054 case self::ENCRYPTION_PKCS1: 1055 $padding = $this->encryptionPadding === self::ENCRYPTION_NONE ? OPENSSL_NO_PADDING : OPENSSL_PKCS1_PADDING; 1056 $result = $func($message, $output, $key, $padding); 1057 break; 1058 //case self::ENCRYPTION_OAEP: 1059 default: 1060 $result = $func($message, $output, $key, OPENSSL_PKCS1_OAEP_PADDING, $hash); 1061 } 1062 if ($result) { 1063 return $output; 1064 } 1065 } 1066 } 1067 return null; 1068 } 1069 } 1070} 1071