1<?php 2 3/** 4 * RSA Private Key 5 * 6 * @author Jim Wigginton <terrafrost@php.net> 7 * @copyright 2015 Jim Wigginton 8 * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 * @link http://phpseclib.sourceforge.net 10 */ 11 12namespace phpseclib3\Crypt\RSA; 13 14use phpseclib3\Crypt\Common; 15use phpseclib3\Crypt\Random; 16use phpseclib3\Crypt\RSA; 17use phpseclib3\Crypt\RSA\Formats\Keys\PSS; 18use phpseclib3\Exception\UnsupportedFormatException; 19use phpseclib3\Math\BigInteger; 20 21/** 22 * Raw RSA Key Handler 23 * 24 * @author Jim Wigginton <terrafrost@php.net> 25 */ 26final class PrivateKey extends RSA implements Common\PrivateKey 27{ 28 use Common\Traits\PasswordProtected; 29 30 /** 31 * Primes for Chinese Remainder Theorem (ie. p and q) 32 * 33 * @var array 34 */ 35 protected $primes; 36 37 /** 38 * Exponents for Chinese Remainder Theorem (ie. dP and dQ) 39 * 40 * @var array 41 */ 42 protected $exponents; 43 44 /** 45 * Coefficients for Chinese Remainder Theorem (ie. qInv) 46 * 47 * @var array 48 */ 49 protected $coefficients; 50 51 /** 52 * Private Exponent 53 * 54 * @var BigInteger 55 */ 56 protected $privateExponent; 57 58 /** 59 * RSADP 60 * 61 * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}. 62 * 63 * @return bool|BigInteger 64 */ 65 private function rsadp(BigInteger $c) 66 { 67 if ($c->compare(self::$zero) < 0 || $c->compare($this->modulus) > 0) { 68 throw new \OutOfRangeException('Ciphertext representative out of range'); 69 } 70 return $this->exponentiate($c); 71 } 72 73 /** 74 * RSASP1 75 * 76 * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}. 77 * 78 * @return bool|BigInteger 79 */ 80 private function rsasp1(BigInteger $m) 81 { 82 if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) { 83 throw new \OutOfRangeException('Signature representative out of range'); 84 } 85 return $this->exponentiate($m); 86 } 87 88 /** 89 * Exponentiate 90 * 91 * @param BigInteger $x 92 * @return BigInteger 93 */ 94 protected function exponentiate(BigInteger $x) 95 { 96 switch (true) { 97 case empty($this->primes): 98 case $this->primes[1]->equals(self::$zero): 99 case empty($this->coefficients): 100 case $this->coefficients[2]->equals(self::$zero): 101 case empty($this->exponents): 102 case $this->exponents[1]->equals(self::$zero): 103 return $x->modPow($this->exponent, $this->modulus); 104 } 105 106 $num_primes = count($this->primes); 107 108 if (!static::$enableBlinding) { 109 $m_i = [ 110 1 => $x->modPow($this->exponents[1], $this->primes[1]), 111 2 => $x->modPow($this->exponents[2], $this->primes[2]) 112 ]; 113 $h = $m_i[1]->subtract($m_i[2]); 114 $h = $h->multiply($this->coefficients[2]); 115 list(, $h) = $h->divide($this->primes[1]); 116 $m = $m_i[2]->add($h->multiply($this->primes[2])); 117 118 $r = $this->primes[1]; 119 for ($i = 3; $i <= $num_primes; $i++) { 120 $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]); 121 122 $r = $r->multiply($this->primes[$i - 1]); 123 124 $h = $m_i->subtract($m); 125 $h = $h->multiply($this->coefficients[$i]); 126 list(, $h) = $h->divide($this->primes[$i]); 127 128 $m = $m->add($r->multiply($h)); 129 } 130 } else { 131 $smallest = $this->primes[1]; 132 for ($i = 2; $i <= $num_primes; $i++) { 133 if ($smallest->compare($this->primes[$i]) > 0) { 134 $smallest = $this->primes[$i]; 135 } 136 } 137 138 $r = BigInteger::randomRange(self::$one, $smallest->subtract(self::$one)); 139 140 $m_i = [ 141 1 => $this->blind($x, $r, 1), 142 2 => $this->blind($x, $r, 2) 143 ]; 144 $h = $m_i[1]->subtract($m_i[2]); 145 $h = $h->multiply($this->coefficients[2]); 146 list(, $h) = $h->divide($this->primes[1]); 147 $m = $m_i[2]->add($h->multiply($this->primes[2])); 148 149 $r = $this->primes[1]; 150 for ($i = 3; $i <= $num_primes; $i++) { 151 $m_i = $this->blind($x, $r, $i); 152 153 $r = $r->multiply($this->primes[$i - 1]); 154 155 $h = $m_i->subtract($m); 156 $h = $h->multiply($this->coefficients[$i]); 157 list(, $h) = $h->divide($this->primes[$i]); 158 159 $m = $m->add($r->multiply($h)); 160 } 161 } 162 163 return $m; 164 } 165 166 /** 167 * Performs RSA Blinding 168 * 169 * Protects against timing attacks by employing RSA Blinding. 170 * Returns $x->modPow($this->exponents[$i], $this->primes[$i]) 171 * 172 * @param BigInteger $x 173 * @param BigInteger $r 174 * @param int $i 175 * @return BigInteger 176 */ 177 private function blind(BigInteger $x, BigInteger $r, $i) 178 { 179 $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i])); 180 $x = $x->modPow($this->exponents[$i], $this->primes[$i]); 181 182 $r = $r->modInverse($this->primes[$i]); 183 $x = $x->multiply($r); 184 list(, $x) = $x->divide($this->primes[$i]); 185 186 return $x; 187 } 188 189 /** 190 * EMSA-PSS-ENCODE 191 * 192 * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}. 193 * 194 * @return string 195 * @param string $m 196 * @throws \RuntimeException on encoding error 197 * @param int $emBits 198 */ 199 private function emsa_pss_encode($m, $emBits) 200 { 201 // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error 202 // be output. 203 204 $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8) 205 $sLen = $this->sLen !== null ? $this->sLen : $this->hLen; 206 207 $mHash = $this->hash->hash($m); 208 if ($emLen < $this->hLen + $sLen + 2) { 209 throw new \LengthException('RSA modulus too short'); 210 } 211 212 $salt = Random::string($sLen); 213 $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt; 214 $h = $this->hash->hash($m2); 215 $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2); 216 $db = $ps . chr(1) . $salt; 217 $dbMask = $this->mgf1($h, $emLen - $this->hLen - 1); // ie. stlren($db) 218 $maskedDB = $db ^ $dbMask; 219 $maskedDB[0] = ~chr(256 - (1 << ($emBits & 7))) & $maskedDB[0]; 220 $em = $maskedDB . $h . chr(0xBC); 221 222 return $em; 223 } 224 225 /** 226 * RSASSA-PSS-SIGN 227 * 228 * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}. 229 * 230 * @param string $m 231 * @return bool|string 232 */ 233 private function rsassa_pss_sign($m) 234 { 235 // EMSA-PSS encoding 236 237 $em = $this->emsa_pss_encode($m, 8 * $this->k - 1); 238 239 // RSA signature 240 241 $m = $this->os2ip($em); 242 $s = $this->rsasp1($m); 243 $s = $this->i2osp($s, $this->k); 244 245 // Output the signature S 246 247 return $s; 248 } 249 250 /** 251 * RSASSA-PKCS1-V1_5-SIGN 252 * 253 * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}. 254 * 255 * @param string $m 256 * @throws \LengthException if the RSA modulus is too short 257 * @return bool|string 258 */ 259 private function rsassa_pkcs1_v1_5_sign($m) 260 { 261 // EMSA-PKCS1-v1_5 encoding 262 263 // If the encoding operation outputs "intended encoded message length too short," output "RSA modulus 264 // too short" and stop. 265 try { 266 $em = $this->emsa_pkcs1_v1_5_encode($m, $this->k); 267 } catch (\LengthException $e) { 268 throw new \LengthException('RSA modulus too short'); 269 } 270 271 // RSA signature 272 273 $m = $this->os2ip($em); 274 $s = $this->rsasp1($m); 275 $s = $this->i2osp($s, $this->k); 276 277 // Output the signature S 278 279 return $s; 280 } 281 282 /** 283 * Create a signature 284 * 285 * @see self::verify() 286 * @param string $message 287 * @return string 288 */ 289 public function sign($message) 290 { 291 $result = $this->handleOpenSSL('openssl_sign', $message); 292 if ($result !== null) { 293 return $result; 294 } 295 296 switch ($this->signaturePadding) { 297 case self::SIGNATURE_PKCS1: 298 case self::SIGNATURE_RELAXED_PKCS1: 299 return $this->rsassa_pkcs1_v1_5_sign($message); 300 //case self::SIGNATURE_PSS: 301 default: 302 return $this->rsassa_pss_sign($message); 303 } 304 } 305 306 /** 307 * RSAES-PKCS1-V1_5-DECRYPT 308 * 309 * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}. 310 * 311 * @param string $c 312 * @return bool|string 313 */ 314 private function rsaes_pkcs1_v1_5_decrypt($c) 315 { 316 // Length checking 317 318 if (strlen($c) != $this->k) { // or if k < 11 319 throw new \LengthException('Ciphertext representative too long'); 320 } 321 322 // RSA decryption 323 324 $c = $this->os2ip($c); 325 $m = $this->rsadp($c); 326 $em = $this->i2osp($m, $this->k); 327 328 // EME-PKCS1-v1_5 decoding 329 330 if (ord($em[0]) != 0 || ord($em[1]) > 2) { 331 throw new \RuntimeException('Decryption error'); 332 } 333 334 $ps = substr($em, 2, strpos($em, chr(0), 2) - 2); 335 $m = substr($em, strlen($ps) + 3); 336 337 if (strlen($ps) < 8) { 338 throw new \RuntimeException('Decryption error'); 339 } 340 341 // Output M 342 343 return $m; 344 } 345 346 /** 347 * RSAES-OAEP-DECRYPT 348 * 349 * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}. The fact that the error 350 * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2: 351 * 352 * Note. Care must be taken to ensure that an opponent cannot 353 * distinguish the different error conditions in Step 3.g, whether by 354 * error message or timing, or, more generally, learn partial 355 * information about the encoded message EM. Otherwise an opponent may 356 * be able to obtain useful information about the decryption of the 357 * ciphertext C, leading to a chosen-ciphertext attack such as the one 358 * observed by Manger [36]. 359 * 360 * @param string $c 361 * @return bool|string 362 */ 363 private function rsaes_oaep_decrypt($c) 364 { 365 // Length checking 366 367 // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error 368 // be output. 369 370 if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) { 371 throw new \LengthException('Ciphertext representative too long'); 372 } 373 374 // RSA decryption 375 376 $c = $this->os2ip($c); 377 $m = $this->rsadp($c); 378 $em = $this->i2osp($m, $this->k); 379 380 // EME-OAEP decoding 381 382 $lHash = $this->hash->hash($this->label); 383 $y = ord($em[0]); 384 $maskedSeed = substr($em, 1, $this->hLen); 385 $maskedDB = substr($em, $this->hLen + 1); 386 $seedMask = $this->mgf1($maskedDB, $this->hLen); 387 $seed = $maskedSeed ^ $seedMask; 388 $dbMask = $this->mgf1($seed, $this->k - $this->hLen - 1); 389 $db = $maskedDB ^ $dbMask; 390 $lHash2 = substr($db, 0, $this->hLen); 391 $m = substr($db, $this->hLen); 392 $hashesMatch = hash_equals($lHash, $lHash2); 393 $leadingZeros = 1; 394 $patternMatch = 0; 395 $offset = 0; 396 for ($i = 0; $i < strlen($m); $i++) { 397 $patternMatch |= $leadingZeros & ($m[$i] === "\1"); 398 $leadingZeros &= $m[$i] === "\0"; 399 $offset += $patternMatch ? 0 : 1; 400 } 401 402 // we do | instead of || to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation 403 // to protect against timing attacks 404 if (!$hashesMatch | !$patternMatch) { 405 throw new \RuntimeException('Decryption error'); 406 } 407 408 // Output the message M 409 410 return substr($m, $offset + 1); 411 } 412 413 /** 414 * Raw Encryption / Decryption 415 * 416 * Doesn't use padding and is not recommended. 417 * 418 * @param string $m 419 * @return bool|string 420 * @throws \LengthException if strlen($m) > $this->k 421 */ 422 private function raw_encrypt($m) 423 { 424 if (strlen($m) > $this->k) { 425 throw new \LengthException('Ciphertext representative too long'); 426 } 427 428 $temp = $this->os2ip($m); 429 $temp = $this->rsadp($temp); 430 return $this->i2osp($temp, $this->k); 431 } 432 433 /** 434 * Decryption 435 * 436 * @see self::encrypt() 437 * @param string $ciphertext 438 * @return bool|string 439 */ 440 public function decrypt($ciphertext) 441 { 442 $result = $this->handleOpenSSL('openssl_private_decrypt', $ciphertext); 443 if ($result !== null) { 444 return $result; 445 } 446 447 switch ($this->encryptionPadding) { 448 case self::ENCRYPTION_NONE: 449 return $this->raw_encrypt($ciphertext); 450 case self::ENCRYPTION_PKCS1: 451 return $this->rsaes_pkcs1_v1_5_decrypt($ciphertext); 452 //case self::ENCRYPTION_OAEP: 453 default: 454 return $this->rsaes_oaep_decrypt($ciphertext); 455 } 456 } 457 458 /** 459 * Returns the public key 460 * 461 * @return mixed 462 */ 463 public function getPublicKey() 464 { 465 $type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey'); 466 if (empty($this->modulus) || empty($this->publicExponent)) { 467 throw new \RuntimeException('Public key components not found'); 468 } 469 470 $key = $type::savePublicKey($this->modulus, $this->publicExponent); 471 return RSA::loadFormat('PKCS8', $key) 472 ->withHash($this->hash->getHash()) 473 ->withMGFHash($this->mgfHash->getHash()) 474 ->withSaltLength($this->sLen) 475 ->withLabel($this->label) 476 ->withPadding($this->signaturePadding | $this->encryptionPadding); 477 } 478 479 /** 480 * Returns the private key 481 * 482 * @param string $type 483 * @param array $options optional 484 * @return string 485 */ 486 public function toString($type, array $options = []) 487 { 488 $type = self::validatePlugin( 489 'Keys', 490 $type, 491 empty($this->primes) ? 'savePublicKey' : 'savePrivateKey' 492 ); 493 494 if ($type == PSS::class) { 495 if ($this->signaturePadding == self::SIGNATURE_PSS) { 496 $options += [ 497 'hash' => $this->hash->getHash(), 498 'MGFHash' => $this->mgfHash->getHash(), 499 'saltLength' => $this->getSaltLength() 500 ]; 501 } else { 502 throw new UnsupportedFormatException('The PSS format can only be used when the signature method has been explicitly set to PSS'); 503 } 504 } 505 506 if (empty($this->primes)) { 507 return $type::savePublicKey($this->modulus, $this->exponent, $options); 508 } 509 510 return $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password, $options); 511 512 /* 513 $key = $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password, $options); 514 if ($key !== false || count($this->primes) == 2) { 515 return $key; 516 } 517 518 $nSize = $this->getSize() >> 1; 519 520 $primes = [1 => clone self::$one, clone self::$one]; 521 $i = 1; 522 foreach ($this->primes as $prime) { 523 $primes[$i] = $primes[$i]->multiply($prime); 524 if ($primes[$i]->getLength() >= $nSize) { 525 $i++; 526 } 527 } 528 529 $exponents = []; 530 $coefficients = [2 => $primes[2]->modInverse($primes[1])]; 531 532 foreach ($primes as $i => $prime) { 533 $temp = $prime->subtract(self::$one); 534 $exponents[$i] = $this->modulus->modInverse($temp); 535 } 536 537 return $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $primes, $exponents, $coefficients, $this->password, $options); 538 */ 539 } 540} 541