[ Index ] |
PHP Cross Reference of DokuWiki |
[Summary view] [Print] [Text view]
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 12 namespace phpseclib3\Crypt\RSA; 13 14 use phpseclib3\Crypt\Common; 15 use phpseclib3\Crypt\Random; 16 use phpseclib3\Crypt\RSA; 17 use phpseclib3\Crypt\RSA\Formats\Keys\PSS; 18 use phpseclib3\Exception\UnsupportedFormatException; 19 use phpseclib3\Math\BigInteger; 20 21 /** 22 * Raw RSA Key Handler 23 * 24 * @author Jim Wigginton <terrafrost@php.net> 25 */ 26 final 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 \phpseclib3\Math\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|\phpseclib3\Math\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|\phpseclib3\Math\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 \phpseclib3\Math\BigInteger $x 92 * @return \phpseclib3\Math\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 \phpseclib3\Math\BigInteger $x 173 * @param \phpseclib3\Math\BigInteger $r 174 * @param int $i 175 * @return \phpseclib3\Math\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(0xFF << ($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 switch ($this->signaturePadding) { 292 case self::SIGNATURE_PKCS1: 293 case self::SIGNATURE_RELAXED_PKCS1: 294 return $this->rsassa_pkcs1_v1_5_sign($message); 295 //case self::SIGNATURE_PSS: 296 default: 297 return $this->rsassa_pss_sign($message); 298 } 299 } 300 301 /** 302 * RSAES-PKCS1-V1_5-DECRYPT 303 * 304 * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}. 305 * 306 * @param string $c 307 * @return bool|string 308 */ 309 private function rsaes_pkcs1_v1_5_decrypt($c) 310 { 311 // Length checking 312 313 if (strlen($c) != $this->k) { // or if k < 11 314 throw new \LengthException('Ciphertext representative too long'); 315 } 316 317 // RSA decryption 318 319 $c = $this->os2ip($c); 320 $m = $this->rsadp($c); 321 $em = $this->i2osp($m, $this->k); 322 323 // EME-PKCS1-v1_5 decoding 324 325 if (ord($em[0]) != 0 || ord($em[1]) > 2) { 326 throw new \RuntimeException('Decryption error'); 327 } 328 329 $ps = substr($em, 2, strpos($em, chr(0), 2) - 2); 330 $m = substr($em, strlen($ps) + 3); 331 332 if (strlen($ps) < 8) { 333 throw new \RuntimeException('Decryption error'); 334 } 335 336 // Output M 337 338 return $m; 339 } 340 341 /** 342 * RSAES-OAEP-DECRYPT 343 * 344 * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}. The fact that the error 345 * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2: 346 * 347 * Note. Care must be taken to ensure that an opponent cannot 348 * distinguish the different error conditions in Step 3.g, whether by 349 * error message or timing, or, more generally, learn partial 350 * information about the encoded message EM. Otherwise an opponent may 351 * be able to obtain useful information about the decryption of the 352 * ciphertext C, leading to a chosen-ciphertext attack such as the one 353 * observed by Manger [36]. 354 * 355 * @param string $c 356 * @return bool|string 357 */ 358 private function rsaes_oaep_decrypt($c) 359 { 360 // Length checking 361 362 // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error 363 // be output. 364 365 if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) { 366 throw new \LengthException('Ciphertext representative too long'); 367 } 368 369 // RSA decryption 370 371 $c = $this->os2ip($c); 372 $m = $this->rsadp($c); 373 $em = $this->i2osp($m, $this->k); 374 375 // EME-OAEP decoding 376 377 $lHash = $this->hash->hash($this->label); 378 $y = ord($em[0]); 379 $maskedSeed = substr($em, 1, $this->hLen); 380 $maskedDB = substr($em, $this->hLen + 1); 381 $seedMask = $this->mgf1($maskedDB, $this->hLen); 382 $seed = $maskedSeed ^ $seedMask; 383 $dbMask = $this->mgf1($seed, $this->k - $this->hLen - 1); 384 $db = $maskedDB ^ $dbMask; 385 $lHash2 = substr($db, 0, $this->hLen); 386 $m = substr($db, $this->hLen); 387 $hashesMatch = hash_equals($lHash, $lHash2); 388 $leadingZeros = 1; 389 $patternMatch = 0; 390 $offset = 0; 391 for ($i = 0; $i < strlen($m); $i++) { 392 $patternMatch |= $leadingZeros & ($m[$i] === "\1"); 393 $leadingZeros &= $m[$i] === "\0"; 394 $offset += $patternMatch ? 0 : 1; 395 } 396 397 // we do | instead of || to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation 398 // to protect against timing attacks 399 if (!$hashesMatch | !$patternMatch) { 400 throw new \RuntimeException('Decryption error'); 401 } 402 403 // Output the message M 404 405 return substr($m, $offset + 1); 406 } 407 408 /** 409 * Raw Encryption / Decryption 410 * 411 * Doesn't use padding and is not recommended. 412 * 413 * @param string $m 414 * @return bool|string 415 * @throws \LengthException if strlen($m) > $this->k 416 */ 417 private function raw_encrypt($m) 418 { 419 if (strlen($m) > $this->k) { 420 throw new \LengthException('Ciphertext representative too long'); 421 } 422 423 $temp = $this->os2ip($m); 424 $temp = $this->rsadp($temp); 425 return $this->i2osp($temp, $this->k); 426 } 427 428 /** 429 * Decryption 430 * 431 * @see self::encrypt() 432 * @param string $ciphertext 433 * @return bool|string 434 */ 435 public function decrypt($ciphertext) 436 { 437 switch ($this->encryptionPadding) { 438 case self::ENCRYPTION_NONE: 439 return $this->raw_encrypt($ciphertext); 440 case self::ENCRYPTION_PKCS1: 441 return $this->rsaes_pkcs1_v1_5_decrypt($ciphertext); 442 //case self::ENCRYPTION_OAEP: 443 default: 444 return $this->rsaes_oaep_decrypt($ciphertext); 445 } 446 } 447 448 /** 449 * Returns the public key 450 * 451 * @return mixed 452 */ 453 public function getPublicKey() 454 { 455 $type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey'); 456 if (empty($this->modulus) || empty($this->publicExponent)) { 457 throw new \RuntimeException('Public key components not found'); 458 } 459 460 $key = $type::savePublicKey($this->modulus, $this->publicExponent); 461 return RSA::loadFormat('PKCS8', $key) 462 ->withHash($this->hash->getHash()) 463 ->withMGFHash($this->mgfHash->getHash()) 464 ->withSaltLength($this->sLen) 465 ->withLabel($this->label) 466 ->withPadding($this->signaturePadding | $this->encryptionPadding); 467 } 468 469 /** 470 * Returns the private key 471 * 472 * @param string $type 473 * @param array $options optional 474 * @return string 475 */ 476 public function toString($type, array $options = []) 477 { 478 $type = self::validatePlugin( 479 'Keys', 480 $type, 481 empty($this->primes) ? 'savePublicKey' : 'savePrivateKey' 482 ); 483 484 if ($type == PSS::class) { 485 if ($this->signaturePadding == self::SIGNATURE_PSS) { 486 $options += [ 487 'hash' => $this->hash->getHash(), 488 'MGFHash' => $this->mgfHash->getHash(), 489 'saltLength' => $this->getSaltLength() 490 ]; 491 } else { 492 throw new UnsupportedFormatException('The PSS format can only be used when the signature method has been explicitly set to PSS'); 493 } 494 } 495 496 if (empty($this->primes)) { 497 return $type::savePublicKey($this->modulus, $this->exponent, $options); 498 } 499 500 return $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password, $options); 501 502 /* 503 $key = $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password, $options); 504 if ($key !== false || count($this->primes) == 2) { 505 return $key; 506 } 507 508 $nSize = $this->getSize() >> 1; 509 510 $primes = [1 => clone self::$one, clone self::$one]; 511 $i = 1; 512 foreach ($this->primes as $prime) { 513 $primes[$i] = $primes[$i]->multiply($prime); 514 if ($primes[$i]->getLength() >= $nSize) { 515 $i++; 516 } 517 } 518 519 $exponents = []; 520 $coefficients = [2 => $primes[2]->modInverse($primes[1])]; 521 522 foreach ($primes as $i => $prime) { 523 $temp = $prime->subtract(self::$one); 524 $exponents[$i] = $this->modulus->modInverse($temp); 525 } 526 527 return $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $primes, $exponents, $coefficients, $this->password, $options); 528 */ 529 } 530 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body