Spomky-Labs /
jose
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | /* |
||
| 4 | * The MIT License (MIT) |
||
| 5 | * |
||
| 6 | * Copyright (c) 2014-2016 Spomky-Labs |
||
| 7 | * |
||
| 8 | * This software may be modified and distributed under the terms |
||
| 9 | * of the MIT license. See the LICENSE file for details. |
||
| 10 | */ |
||
| 11 | |||
| 12 | namespace Jose\Util; |
||
| 13 | |||
| 14 | /** |
||
| 15 | * Pure-PHP PKCS#1 compliant implementation of RSA. |
||
| 16 | * |
||
| 17 | * @author Jim Wigginton <[email protected]> |
||
| 18 | */ |
||
| 19 | final class RSA |
||
| 20 | { |
||
| 21 | /**#@+ |
||
| 22 | * @see self::encrypt() |
||
| 23 | * @see self::decrypt() |
||
| 24 | */ |
||
| 25 | /** |
||
| 26 | * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding} |
||
| 27 | * (OAEP) for encryption / decryption. |
||
| 28 | * |
||
| 29 | * Uses sha1 by default. |
||
| 30 | * |
||
| 31 | * @see self::setHash() |
||
| 32 | * @see self::setMGFHash() |
||
| 33 | */ |
||
| 34 | const ENCRYPTION_OAEP = 1; |
||
| 35 | |||
| 36 | /** |
||
| 37 | * Use PKCS#1 padding. |
||
| 38 | * |
||
| 39 | * Although self::ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards |
||
| 40 | * compatibility with protocols (like SSH-1) written before OAEP's introduction. |
||
| 41 | */ |
||
| 42 | const ENCRYPTION_PKCS1 = 2; |
||
| 43 | |||
| 44 | /**#@-*/ |
||
| 45 | |||
| 46 | /**#@+ |
||
| 47 | * @see self::sign() |
||
| 48 | * @see self::verify() |
||
| 49 | * @see self::setHash() |
||
| 50 | */ |
||
| 51 | /** |
||
| 52 | * Use the Probabilistic Signature Scheme for signing. |
||
| 53 | * |
||
| 54 | * Uses sha1 by default. |
||
| 55 | * |
||
| 56 | * @see self::setSaltLength() |
||
| 57 | * @see self::setMGFHash() |
||
| 58 | */ |
||
| 59 | const SIGNATURE_PSS = 1; |
||
| 60 | /** |
||
| 61 | * Use the PKCS#1 scheme by default. |
||
| 62 | * |
||
| 63 | * Although self::SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards |
||
| 64 | * compatibility with protocols (like SSH-2) written before PSS's introduction. |
||
| 65 | */ |
||
| 66 | const SIGNATURE_PKCS1 = 2; |
||
| 67 | /**#@-*/ |
||
| 68 | |||
| 69 | /**#@+ |
||
| 70 | * @see \phpseclib\Crypt\RSA::createKey() |
||
| 71 | */ |
||
| 72 | /** |
||
| 73 | * ASN1 Integer. |
||
| 74 | */ |
||
| 75 | const ASN1_INTEGER = 2; |
||
| 76 | /** |
||
| 77 | * ASN1 Bit String. |
||
| 78 | */ |
||
| 79 | const ASN1_BITSTRING = 3; |
||
| 80 | /** |
||
| 81 | * ASN1 Octet String. |
||
| 82 | */ |
||
| 83 | const ASN1_OCTETSTRING = 4; |
||
| 84 | /** |
||
| 85 | * ASN1 Object Identifier. |
||
| 86 | */ |
||
| 87 | const ASN1_OBJECT = 6; |
||
| 88 | /** |
||
| 89 | * ASN1 Sequence (with the constucted bit set). |
||
| 90 | */ |
||
| 91 | const ASN1_SEQUENCE = 48; |
||
| 92 | /**#@-*/ |
||
| 93 | |||
| 94 | /**#@+ |
||
| 95 | * @see \phpseclib\Crypt\RSA::__construct() |
||
| 96 | */ |
||
| 97 | /** |
||
| 98 | * To use the pure-PHP implementation. |
||
| 99 | */ |
||
| 100 | const MODE_INTERNAL = 1; |
||
| 101 | /** |
||
| 102 | * To use the OpenSSL library. |
||
| 103 | * |
||
| 104 | * (if enabled; otherwise, the internal implementation will be used) |
||
| 105 | */ |
||
| 106 | const MODE_OPENSSL = 2; |
||
| 107 | /**#@-*/ |
||
| 108 | |||
| 109 | /**#@+ |
||
| 110 | * @see \phpseclib\Crypt\RSA::createKey() |
||
| 111 | * @see \phpseclib\Crypt\RSA::setPrivateKeyFormat() |
||
| 112 | */ |
||
| 113 | /** |
||
| 114 | * PKCS#1 formatted private key. |
||
| 115 | * |
||
| 116 | * Used by OpenSSH |
||
| 117 | */ |
||
| 118 | const PRIVATE_FORMAT_PKCS1 = 0; |
||
| 119 | /** |
||
| 120 | * PuTTY formatted private key. |
||
| 121 | */ |
||
| 122 | const PRIVATE_FORMAT_PUTTY = 1; |
||
| 123 | /** |
||
| 124 | * XML formatted private key. |
||
| 125 | */ |
||
| 126 | const PRIVATE_FORMAT_XML = 2; |
||
| 127 | /** |
||
| 128 | * PKCS#8 formatted private key. |
||
| 129 | */ |
||
| 130 | const PRIVATE_FORMAT_PKCS8 = 8; |
||
| 131 | /**#@-*/ |
||
| 132 | |||
| 133 | /**#@+ |
||
| 134 | * @see \phpseclib\Crypt\RSA::createKey() |
||
| 135 | * @see \phpseclib\Crypt\RSA::setPublicKeyFormat() |
||
| 136 | */ |
||
| 137 | /** |
||
| 138 | * Raw public key. |
||
| 139 | * |
||
| 140 | * An array containing two \Jose\Util\BigInteger objects. |
||
| 141 | * |
||
| 142 | * The exponent can be indexed with any of the following: |
||
| 143 | * |
||
| 144 | * 0, e, exponent, publicExponent |
||
| 145 | * |
||
| 146 | * The modulus can be indexed with any of the following: |
||
| 147 | * |
||
| 148 | * 1, n, modulo, modulus |
||
| 149 | */ |
||
| 150 | const PUBLIC_FORMAT_RAW = 3; |
||
| 151 | /** |
||
| 152 | * PKCS#1 formatted public key (raw). |
||
| 153 | * |
||
| 154 | * Used by File/X509.php |
||
| 155 | * |
||
| 156 | * Has the following header: |
||
| 157 | * |
||
| 158 | * -----BEGIN RSA PUBLIC KEY----- |
||
| 159 | * |
||
| 160 | * Analogous to ssh-keygen's pem format (as specified by -m) |
||
| 161 | */ |
||
| 162 | const PUBLIC_FORMAT_PKCS1 = 4; |
||
| 163 | const PUBLIC_FORMAT_PKCS1_RAW = 4; |
||
| 164 | /** |
||
| 165 | * XML formatted public key. |
||
| 166 | */ |
||
| 167 | const PUBLIC_FORMAT_XML = 5; |
||
| 168 | /** |
||
| 169 | * OpenSSH formatted public key. |
||
| 170 | * |
||
| 171 | * Place in $HOME/.ssh/authorized_keys |
||
| 172 | */ |
||
| 173 | const PUBLIC_FORMAT_OPENSSH = 6; |
||
| 174 | /** |
||
| 175 | * PKCS#1 formatted public key (encapsulated). |
||
| 176 | * |
||
| 177 | * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set) |
||
| 178 | * |
||
| 179 | * Has the following header: |
||
| 180 | * |
||
| 181 | * -----BEGIN PUBLIC KEY----- |
||
| 182 | * |
||
| 183 | * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8 |
||
| 184 | * is specific to private keys it's basically creating a DER-encoded wrapper |
||
| 185 | * for keys. This just extends that same concept to public keys (much like ssh-keygen) |
||
| 186 | */ |
||
| 187 | const PUBLIC_FORMAT_PKCS8 = 7; |
||
| 188 | /**#@-*/ |
||
| 189 | |||
| 190 | /** |
||
| 191 | * Precomputed Zero. |
||
| 192 | * |
||
| 193 | * @var array |
||
| 194 | */ |
||
| 195 | private $zero; |
||
| 196 | |||
| 197 | /** |
||
| 198 | * Precomputed One. |
||
| 199 | * |
||
| 200 | * @var array |
||
| 201 | */ |
||
| 202 | private $one; |
||
| 203 | |||
| 204 | /** |
||
| 205 | * Private Key Format. |
||
| 206 | * |
||
| 207 | * @var int |
||
| 208 | */ |
||
| 209 | private $privateKeyFormat = self::PRIVATE_FORMAT_PKCS1; |
||
| 210 | |||
| 211 | /** |
||
| 212 | * Public Key Format. |
||
| 213 | * |
||
| 214 | * @var int |
||
| 215 | */ |
||
| 216 | private $publicKeyFormat = self::PUBLIC_FORMAT_PKCS8; |
||
| 217 | |||
| 218 | /** |
||
| 219 | * Modulus (ie. n). |
||
| 220 | * |
||
| 221 | * @var \Jose\Util\BigInteger |
||
| 222 | */ |
||
| 223 | private $modulus; |
||
| 224 | |||
| 225 | /** |
||
| 226 | * Modulus length. |
||
| 227 | * |
||
| 228 | * @var \Jose\Util\BigInteger |
||
| 229 | */ |
||
| 230 | private $k; |
||
| 231 | |||
| 232 | /** |
||
| 233 | * Exponent (ie. e or d). |
||
| 234 | * |
||
| 235 | * @var \Jose\Util\BigInteger |
||
| 236 | */ |
||
| 237 | private $exponent; |
||
| 238 | |||
| 239 | /** |
||
| 240 | * Primes for Chinese Remainder Theorem (ie. p and q). |
||
| 241 | * |
||
| 242 | * @var array |
||
| 243 | */ |
||
| 244 | private $primes; |
||
| 245 | |||
| 246 | /** |
||
| 247 | * Exponents for Chinese Remainder Theorem (ie. dP and dQ). |
||
| 248 | * |
||
| 249 | * @var array |
||
| 250 | */ |
||
| 251 | private $exponents; |
||
| 252 | |||
| 253 | /** |
||
| 254 | * Coefficients for Chinese Remainder Theorem (ie. qInv). |
||
| 255 | * |
||
| 256 | * @var array |
||
| 257 | */ |
||
| 258 | private $coefficients; |
||
| 259 | |||
| 260 | /** |
||
| 261 | * Hash name. |
||
| 262 | * |
||
| 263 | * @var string |
||
| 264 | */ |
||
| 265 | private $hashName; |
||
| 266 | |||
| 267 | /** |
||
| 268 | * Hash function. |
||
| 269 | * |
||
| 270 | * @var \Jose\Util\Hash |
||
| 271 | */ |
||
| 272 | private $hash; |
||
| 273 | |||
| 274 | /** |
||
| 275 | * Length of hash function output. |
||
| 276 | * |
||
| 277 | * @var int |
||
| 278 | */ |
||
| 279 | private $hLen; |
||
| 280 | |||
| 281 | /** |
||
| 282 | * Length of salt. |
||
| 283 | * |
||
| 284 | * @var int |
||
| 285 | */ |
||
| 286 | private $sLen; |
||
| 287 | |||
| 288 | /** |
||
| 289 | * Hash function for the Mask Generation Function. |
||
| 290 | * |
||
| 291 | * @var \Jose\Util\Hash |
||
| 292 | */ |
||
| 293 | private $mgfHash; |
||
| 294 | |||
| 295 | /** |
||
| 296 | * Length of MGF hash function output. |
||
| 297 | * |
||
| 298 | * @var int |
||
| 299 | */ |
||
| 300 | private $mgfHLen; |
||
| 301 | |||
| 302 | /** |
||
| 303 | * Encryption mode. |
||
| 304 | * |
||
| 305 | * @var int |
||
| 306 | */ |
||
| 307 | private $encryptionMode = self::ENCRYPTION_OAEP; |
||
| 308 | |||
| 309 | /** |
||
| 310 | * Signature mode. |
||
| 311 | * |
||
| 312 | * @var int |
||
| 313 | */ |
||
| 314 | private $signatureMode = self::SIGNATURE_PSS; |
||
| 315 | |||
| 316 | /** |
||
| 317 | * Public Exponent. |
||
| 318 | * |
||
| 319 | * @var mixed |
||
| 320 | */ |
||
| 321 | private $publicExponent = false; |
||
| 322 | |||
| 323 | /** |
||
| 324 | * Password. |
||
| 325 | * |
||
| 326 | * @var string |
||
| 327 | */ |
||
| 328 | private $password = false; |
||
| 329 | |||
| 330 | /** |
||
| 331 | * Components. |
||
| 332 | * |
||
| 333 | * For use with parsing XML formatted keys. PHP's XML Parser functions use utilized - instead of PHP's DOM functions - |
||
| 334 | * because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't. |
||
| 335 | * |
||
| 336 | * @see self::_start_element_handler() |
||
| 337 | * |
||
| 338 | * @var array |
||
| 339 | */ |
||
| 340 | private $components = []; |
||
| 341 | |||
| 342 | /** |
||
| 343 | * Current String. |
||
| 344 | * |
||
| 345 | * For use with parsing XML formatted keys. |
||
| 346 | * |
||
| 347 | * @see self::_character_handler() |
||
| 348 | * @see self::_stop_element_handler() |
||
| 349 | * |
||
| 350 | * @var mixed |
||
| 351 | */ |
||
| 352 | private $current; |
||
| 353 | |||
| 354 | /** |
||
| 355 | * OpenSSL configuration file name. |
||
| 356 | * |
||
| 357 | * Set to null to use system configuration file. |
||
| 358 | * |
||
| 359 | * @see self::createKey() |
||
| 360 | * |
||
| 361 | * @var mixed |
||
| 362 | * @Access public |
||
| 363 | */ |
||
| 364 | private $configFile; |
||
| 365 | |||
| 366 | /** |
||
| 367 | * Public key comment field. |
||
| 368 | * |
||
| 369 | * @var string |
||
| 370 | */ |
||
| 371 | private $comment = 'phpseclib-generated-key'; |
||
| 372 | |||
| 373 | /** |
||
| 374 | * The constructor. |
||
| 375 | * |
||
| 376 | * If you want to make use of the openssl extension, you'll need to set the mode manually, yourself. The reason |
||
| 377 | * \phpseclib\Crypt\RSA doesn't do it is because OpenSSL doesn't fail gracefully. openssl_pkey_new(), in particular, requires |
||
| 378 | * openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late. |
||
| 379 | */ |
||
| 380 | public function __construct() |
||
| 381 | { |
||
| 382 | $this->configFile = dirname(__FILE__).'/../openssl.cnf'; |
||
| 383 | |||
| 384 | if (!defined('CRYPT_RSA_MODE')) { |
||
| 385 | switch (true) { |
||
| 386 | // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular, |
||
| 387 | // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger |
||
| 388 | // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either. |
||
| 389 | case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'): |
||
| 390 | define('CRYPT_RSA_MODE', self::MODE_INTERNAL); |
||
| 391 | break; |
||
| 392 | case extension_loaded('openssl') && file_exists($this->configFile): |
||
| 393 | // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work |
||
| 394 | ob_start(); |
||
| 395 | @phpinfo(); |
||
| 396 | $content = ob_get_contents(); |
||
| 397 | ob_end_clean(); |
||
| 398 | |||
| 399 | preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); |
||
| 400 | |||
| 401 | $versions = []; |
||
| 402 | if (!empty($matches[1])) { |
||
| 403 | for ($i = 0; $i < count($matches[1]); $i++) { |
||
| 404 | $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); |
||
| 405 | |||
| 406 | // Remove letter part in OpenSSL version |
||
| 407 | if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) { |
||
| 408 | $versions[$matches[1][$i]] = $fullVersion; |
||
| 409 | } else { |
||
| 410 | $versions[$matches[1][$i]] = $m[0]; |
||
| 411 | } |
||
| 412 | } |
||
| 413 | } |
||
| 414 | |||
| 415 | // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+ |
||
| 416 | switch (true) { |
||
| 417 | case !isset($versions['Header']): |
||
| 418 | case !isset($versions['Library']): |
||
| 419 | case $versions['Header'] == $versions['Library']: |
||
| 420 | define('CRYPT_RSA_MODE', self::MODE_OPENSSL); |
||
| 421 | break; |
||
| 422 | default: |
||
| 423 | define('CRYPT_RSA_MODE', self::MODE_INTERNAL); |
||
| 424 | define('MATH_BIGINTEGER_OPENSSL_DISABLE', true); |
||
| 425 | } |
||
| 426 | break; |
||
| 427 | default: |
||
| 428 | define('CRYPT_RSA_MODE', self::MODE_INTERNAL); |
||
| 429 | } |
||
| 430 | } |
||
| 431 | |||
| 432 | $this->zero = BigInteger::createFromDecimalString('0'); |
||
|
0 ignored issues
–
show
|
|||
| 433 | $this->one = BigInteger::createFromDecimalString('1'); |
||
|
0 ignored issues
–
show
It seems like
\Jose\Util\BigInteger::c...eFromDecimalString('1') of type object<Jose\Util\BigInteger> is incompatible with the declared type array of property $one.
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. Loading history...
|
|||
| 434 | |||
| 435 | $this->hash = new Hash('sha1'); |
||
| 436 | $this->hLen = 20; |
||
| 437 | $this->hashName = 'sha1'; |
||
| 438 | $this->mgfHash = new Hash('sha1'); |
||
| 439 | $this->mgfHLen = 20; |
||
| 440 | } |
||
| 441 | |||
| 442 | /** |
||
| 443 | * Break a public or private key down into its constituant components. |
||
| 444 | * |
||
| 445 | * @see self::_convertPublicKey() |
||
| 446 | * @see self::_convertPrivateKey() |
||
| 447 | * |
||
| 448 | * @param string $key |
||
| 449 | * @param int $type |
||
| 450 | * |
||
| 451 | * @return array |
||
| 452 | */ |
||
| 453 | private function _parseKey($key, $type) |
||
| 454 | { |
||
| 455 | if ($type != self::PUBLIC_FORMAT_RAW && !is_string($key)) { |
||
| 456 | return false; |
||
| 457 | } |
||
| 458 | |||
| 459 | switch ($type) { |
||
| 460 | case self::PUBLIC_FORMAT_RAW: |
||
| 461 | if (!is_array($key)) { |
||
| 462 | return false; |
||
| 463 | } |
||
| 464 | $components = []; |
||
| 465 | switch (true) { |
||
| 466 | case isset($key['e']): |
||
| 467 | $components['publicExponent'] = $key['e']->copy(); |
||
| 468 | break; |
||
| 469 | case isset($key['exponent']): |
||
| 470 | $components['publicExponent'] = $key['exponent']->copy(); |
||
| 471 | break; |
||
| 472 | case isset($key['publicExponent']): |
||
| 473 | $components['publicExponent'] = $key['publicExponent']->copy(); |
||
| 474 | break; |
||
| 475 | case isset($key[0]): |
||
| 476 | $components['publicExponent'] = $key[0]->copy(); |
||
| 477 | } |
||
| 478 | switch (true) { |
||
| 479 | case isset($key['n']): |
||
| 480 | $components['modulus'] = $key['n']->copy(); |
||
| 481 | break; |
||
| 482 | case isset($key['modulo']): |
||
| 483 | $components['modulus'] = $key['modulo']->copy(); |
||
| 484 | break; |
||
| 485 | case isset($key['modulus']): |
||
| 486 | $components['modulus'] = $key['modulus']->copy(); |
||
| 487 | break; |
||
| 488 | case isset($key[1]): |
||
| 489 | $components['modulus'] = $key[1]->copy(); |
||
| 490 | } |
||
| 491 | |||
| 492 | return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false; |
||
| 493 | case self::PRIVATE_FORMAT_PKCS1: |
||
| 494 | case self::PRIVATE_FORMAT_PKCS8: |
||
| 495 | case self::PUBLIC_FORMAT_PKCS1: |
||
| 496 | /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is |
||
| 497 | "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to |
||
| 498 | protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding |
||
| 499 | two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here: |
||
| 500 | |||
| 501 | http://tools.ietf.org/html/rfc1421#section-4.6.1.1 |
||
| 502 | http://tools.ietf.org/html/rfc1421#section-4.6.1.3 |
||
| 503 | |||
| 504 | DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell. |
||
| 505 | DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation |
||
| 506 | function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's |
||
| 507 | own implementation. ie. the implementation *is* the standard and any bugs that may exist in that |
||
| 508 | implementation are part of the standard, as well. |
||
| 509 | |||
| 510 | * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */ |
||
| 511 | if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) { |
||
| 512 | $iv = pack('H*', trim($matches[2])); |
||
| 513 | $symkey = pack('H*', md5($this->password.substr($iv, 0, 8))); // symkey is short for symmetric key |
||
| 514 | $symkey .= pack('H*', md5($symkey.$this->password.substr($iv, 0, 8))); |
||
| 515 | // remove the Proc-Type / DEK-Info sections as they're no longer needed |
||
| 516 | $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key); |
||
| 517 | $ciphertext = $this->_extractBER($key); |
||
| 518 | if ($ciphertext === false) { |
||
| 519 | $ciphertext = $key; |
||
| 520 | } |
||
| 521 | switch ($matches[1]) { |
||
| 522 | case 'AES-256-CBC': |
||
| 523 | $crypto = new AES(); |
||
| 524 | break; |
||
| 525 | case 'AES-128-CBC': |
||
| 526 | $symkey = substr($symkey, 0, 16); |
||
| 527 | $crypto = new AES(); |
||
| 528 | break; |
||
| 529 | case 'DES-EDE3-CFB': |
||
| 530 | $crypto = new TripleDES(Base::MODE_CFB); |
||
| 531 | break; |
||
| 532 | case 'DES-EDE3-CBC': |
||
| 533 | $symkey = substr($symkey, 0, 24); |
||
| 534 | $crypto = new TripleDES(); |
||
| 535 | break; |
||
| 536 | case 'DES-CBC': |
||
| 537 | $crypto = new DES(); |
||
| 538 | break; |
||
| 539 | default: |
||
| 540 | return false; |
||
| 541 | } |
||
| 542 | $crypto->setKey($symkey); |
||
| 543 | $crypto->setIV($iv); |
||
| 544 | $decoded = $crypto->decrypt($ciphertext); |
||
| 545 | } else { |
||
| 546 | $decoded = $this->_extractBER($key); |
||
| 547 | } |
||
| 548 | |||
| 549 | if ($decoded !== false) { |
||
| 550 | $key = $decoded; |
||
| 551 | } |
||
| 552 | |||
| 553 | $components = []; |
||
| 554 | |||
| 555 | if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { |
||
| 556 | return false; |
||
| 557 | } |
||
| 558 | if ($this->_decodeLength($key) != strlen($key)) { |
||
| 559 | return false; |
||
| 560 | } |
||
| 561 | |||
| 562 | $tag = ord($this->_string_shift($key)); |
||
| 563 | /* intended for keys for which OpenSSL's asn1parse returns the following: |
||
| 564 | |||
| 565 | 0:d=0 hl=4 l= 631 cons: SEQUENCE |
||
| 566 | 4:d=1 hl=2 l= 1 prim: INTEGER :00 |
||
| 567 | 7:d=1 hl=2 l= 13 cons: SEQUENCE |
||
| 568 | 9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption |
||
| 569 | 20:d=2 hl=2 l= 0 prim: NULL |
||
| 570 | 22:d=1 hl=4 l= 609 prim: OCTET STRING |
||
| 571 | |||
| 572 | ie. PKCS8 keys*/ |
||
| 573 | |||
| 574 | if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") { |
||
| 575 | $this->_string_shift($key, 3); |
||
| 576 | $tag = self::ASN1_SEQUENCE; |
||
| 577 | } |
||
| 578 | |||
| 579 | if ($tag == self::ASN1_SEQUENCE) { |
||
| 580 | $temp = $this->_string_shift($key, $this->_decodeLength($key)); |
||
| 581 | if (ord($this->_string_shift($temp)) != self::ASN1_OBJECT) { |
||
| 582 | return false; |
||
| 583 | } |
||
| 584 | $length = $this->_decodeLength($temp); |
||
| 585 | switch ($this->_string_shift($temp, $length)) { |
||
| 586 | case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption |
||
| 587 | break; |
||
| 588 | case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC |
||
| 589 | /* |
||
| 590 | PBEParameter ::= SEQUENCE { |
||
| 591 | salt OCTET STRING (SIZE(8)), |
||
| 592 | iterationCount INTEGER } |
||
| 593 | */ |
||
| 594 | if (ord($this->_string_shift($temp)) != self::ASN1_SEQUENCE) { |
||
| 595 | return false; |
||
| 596 | } |
||
| 597 | if ($this->_decodeLength($temp) != strlen($temp)) { |
||
| 598 | return false; |
||
| 599 | } |
||
| 600 | $this->_string_shift($temp); // assume it's an octet string |
||
| 601 | $salt = $this->_string_shift($temp, $this->_decodeLength($temp)); |
||
| 602 | if (ord($this->_string_shift($temp)) != self::ASN1_INTEGER) { |
||
| 603 | return false; |
||
| 604 | } |
||
| 605 | $this->_decodeLength($temp); |
||
| 606 | list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT)); |
||
| 607 | $this->_string_shift($key); // assume it's an octet string |
||
| 608 | $length = $this->_decodeLength($key); |
||
| 609 | if (strlen($key) != $length) { |
||
| 610 | return false; |
||
| 611 | } |
||
| 612 | |||
| 613 | $crypto = new DES(); |
||
| 614 | $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount); |
||
| 615 | $key = $crypto->decrypt($key); |
||
| 616 | if ($key === false) { |
||
| 617 | return false; |
||
| 618 | } |
||
| 619 | |||
| 620 | return $this->_parseKey($key, self::PRIVATE_FORMAT_PKCS1); |
||
| 621 | default: |
||
| 622 | return false; |
||
| 623 | } |
||
| 624 | /* intended for keys for which OpenSSL's asn1parse returns the following: |
||
| 625 | |||
| 626 | 0:d=0 hl=4 l= 290 cons: SEQUENCE |
||
| 627 | 4:d=1 hl=2 l= 13 cons: SEQUENCE |
||
| 628 | 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption |
||
| 629 | 17:d=2 hl=2 l= 0 prim: NULL |
||
| 630 | 19:d=1 hl=4 l= 271 prim: BIT STRING */ |
||
| 631 | $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag |
||
| 632 | $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length |
||
| 633 | // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of |
||
| 634 | // unused bits in the final subsequent octet. The number shall be in the range zero to seven." |
||
| 635 | // -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2) |
||
| 636 | if ($tag == self::ASN1_BITSTRING) { |
||
| 637 | $this->_string_shift($key); |
||
| 638 | } |
||
| 639 | if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { |
||
| 640 | return false; |
||
| 641 | } |
||
| 642 | if ($this->_decodeLength($key) != strlen($key)) { |
||
| 643 | return false; |
||
| 644 | } |
||
| 645 | $tag = ord($this->_string_shift($key)); |
||
| 646 | } |
||
| 647 | if ($tag != self::ASN1_INTEGER) { |
||
| 648 | return false; |
||
| 649 | } |
||
| 650 | |||
| 651 | $length = $this->_decodeLength($key); |
||
| 652 | $temp = $this->_string_shift($key, $length); |
||
| 653 | if (strlen($temp) != 1 || ord($temp) > 2) { |
||
| 654 | $components['modulus'] = BigInteger::createFromBinaryString($temp); |
||
| 655 | $this->_string_shift($key); // skip over self::ASN1_INTEGER |
||
| 656 | $length = $this->_decodeLength($key); |
||
| 657 | $components[$type == self::PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = BigInteger::createFromBinaryString($this->_string_shift($key, $length)); |
||
| 658 | |||
| 659 | return $components; |
||
| 660 | } |
||
| 661 | if (ord($this->_string_shift($key)) != self::ASN1_INTEGER) { |
||
| 662 | return false; |
||
| 663 | } |
||
| 664 | $length = $this->_decodeLength($key); |
||
| 665 | $components['modulus'] = BigInteger::createFromBinaryString($this->_string_shift($key, $length)); |
||
| 666 | $this->_string_shift($key); |
||
| 667 | $length = $this->_decodeLength($key); |
||
| 668 | $components['publicExponent'] = BigInteger::createFromBinaryString($this->_string_shift($key, $length)); |
||
| 669 | $this->_string_shift($key); |
||
| 670 | $length = $this->_decodeLength($key); |
||
| 671 | $components['privateExponent'] = BigInteger::createFromBinaryString($this->_string_shift($key, $length)); |
||
| 672 | $this->_string_shift($key); |
||
| 673 | $length = $this->_decodeLength($key); |
||
| 674 | $components['primes'] = [1 => BigInteger::createFromBinaryString($this->_string_shift($key, $length))]; |
||
| 675 | $this->_string_shift($key); |
||
| 676 | $length = $this->_decodeLength($key); |
||
| 677 | $components['primes'][] = BigInteger::createFromBinaryString($this->_string_shift($key, $length)); |
||
| 678 | $this->_string_shift($key); |
||
| 679 | $length = $this->_decodeLength($key); |
||
| 680 | $components['exponents'] = [1 => BigInteger::createFromBinaryString($this->_string_shift($key, $length))]; |
||
| 681 | $this->_string_shift($key); |
||
| 682 | $length = $this->_decodeLength($key); |
||
| 683 | $components['exponents'][] = BigInteger::createFromBinaryString($this->_string_shift($key, $length)); |
||
| 684 | $this->_string_shift($key); |
||
| 685 | $length = $this->_decodeLength($key); |
||
| 686 | $components['coefficients'] = [2 => BigInteger::createFromBinaryString($this->_string_shift($key, $length))]; |
||
| 687 | |||
| 688 | if (!empty($key)) { |
||
| 689 | if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { |
||
| 690 | return false; |
||
| 691 | } |
||
| 692 | $this->_decodeLength($key); |
||
| 693 | while (!empty($key)) { |
||
| 694 | if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { |
||
| 695 | return false; |
||
| 696 | } |
||
| 697 | $this->_decodeLength($key); |
||
| 698 | $key = substr($key, 1); |
||
| 699 | $length = $this->_decodeLength($key); |
||
| 700 | $components['primes'][] = BigInteger::createFromBinaryString($this->_string_shift($key, $length)); |
||
| 701 | $this->_string_shift($key); |
||
| 702 | $length = $this->_decodeLength($key); |
||
| 703 | $components['exponents'][] = BigInteger::createFromBinaryString($this->_string_shift($key, $length)); |
||
| 704 | $this->_string_shift($key); |
||
| 705 | $length = $this->_decodeLength($key); |
||
| 706 | $components['coefficients'][] = BigInteger::createFromBinaryString($this->_string_shift($key, $length)); |
||
| 707 | } |
||
| 708 | } |
||
| 709 | |||
| 710 | return $components; |
||
| 711 | case self::PUBLIC_FORMAT_OPENSSH: |
||
| 712 | $parts = explode(' ', $key, 3); |
||
| 713 | |||
| 714 | $key = isset($parts[1]) ? base64_decode($parts[1]) : false; |
||
| 715 | if ($key === false) { |
||
| 716 | return false; |
||
| 717 | } |
||
| 718 | |||
| 719 | $comment = isset($parts[2]) ? $parts[2] : false; |
||
| 720 | |||
| 721 | $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa"; |
||
| 722 | |||
| 723 | if (strlen($key) <= 4) { |
||
| 724 | return false; |
||
| 725 | } |
||
| 726 | extract(unpack('Nlength', $this->_string_shift($key, 4))); |
||
| 727 | $publicExponent = BigInteger::createFromBinaryString($this->_string_shift($key, $length)); |
||
| 728 | if (strlen($key) <= 4) { |
||
| 729 | return false; |
||
| 730 | } |
||
| 731 | extract(unpack('Nlength', $this->_string_shift($key, 4))); |
||
| 732 | $modulus = BigInteger::createFromBinaryString($this->_string_shift($key, $length)); |
||
| 733 | |||
| 734 | if ($cleanup && strlen($key)) { |
||
| 735 | if (strlen($key) <= 4) { |
||
| 736 | return false; |
||
| 737 | } |
||
| 738 | extract(unpack('Nlength', $this->_string_shift($key, 4))); |
||
| 739 | $realModulus = BigInteger::createFromBinaryString($this->_string_shift($key, $length)); |
||
| 740 | |||
| 741 | return strlen($key) ? false : [ |
||
| 742 | 'modulus' => $realModulus, |
||
| 743 | 'publicExponent' => $modulus, |
||
| 744 | 'comment' => $comment, |
||
| 745 | ]; |
||
| 746 | } else { |
||
| 747 | return strlen($key) ? false : [ |
||
| 748 | 'modulus' => $modulus, |
||
| 749 | 'publicExponent' => $publicExponent, |
||
| 750 | 'comment' => $comment, |
||
| 751 | ]; |
||
| 752 | } |
||
| 753 | // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue |
||
| 754 | // http://en.wikipedia.org/wiki/XML_Signature |
||
| 755 | case self::PRIVATE_FORMAT_XML: |
||
| 756 | case self::PUBLIC_FORMAT_XML: |
||
| 757 | $this->components = []; |
||
| 758 | |||
| 759 | $xml = xml_parser_create('UTF-8'); |
||
| 760 | xml_set_object($xml, $this); |
||
| 761 | xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler'); |
||
| 762 | xml_set_character_data_handler($xml, '_data_handler'); |
||
| 763 | // add <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added |
||
| 764 | if (!xml_parse($xml, '<xml>'.$key.'</xml>')) { |
||
| 765 | return false; |
||
| 766 | } |
||
| 767 | |||
| 768 | return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false; |
||
| 769 | // from PuTTY's SSHPUBK.C |
||
| 770 | case self::PRIVATE_FORMAT_PUTTY: |
||
| 771 | $components = []; |
||
| 772 | $key = preg_split('#\r\n|\r|\n#', $key); |
||
| 773 | $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0])); |
||
| 774 | if ($type != 'ssh-rsa') { |
||
| 775 | return false; |
||
| 776 | } |
||
| 777 | $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1])); |
||
| 778 | $comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2])); |
||
| 779 | |||
| 780 | $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3])); |
||
| 781 | $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength)))); |
||
| 782 | $public = substr($public, 11); |
||
| 783 | extract(unpack('Nlength', $this->_string_shift($public, 4))); |
||
| 784 | $components['publicExponent'] = BigInteger::createFromBinaryString($this->_string_shift($public, $length)); |
||
| 785 | extract(unpack('Nlength', $this->_string_shift($public, 4))); |
||
| 786 | $components['modulus'] = BigInteger::createFromBinaryString($this->_string_shift($public, $length)); |
||
| 787 | |||
| 788 | $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4])); |
||
| 789 | $private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength)))); |
||
| 790 | |||
| 791 | switch ($encryption) { |
||
| 792 | case 'aes256-cbc': |
||
| 793 | $symkey = ''; |
||
| 794 | $sequence = 0; |
||
| 795 | while (strlen($symkey) < 32) { |
||
| 796 | $temp = pack('Na*', $sequence++, $this->password); |
||
| 797 | $symkey .= pack('H*', sha1($temp)); |
||
| 798 | } |
||
| 799 | $symkey = substr($symkey, 0, 32); |
||
| 800 | $crypto = new AES(); |
||
| 801 | } |
||
| 802 | |||
| 803 | if ($encryption != 'none') { |
||
| 804 | $crypto->setKey($symkey); |
||
| 805 | $crypto->disablePadding(); |
||
| 806 | $private = $crypto->decrypt($private); |
||
| 807 | if ($private === false) { |
||
| 808 | return false; |
||
| 809 | } |
||
| 810 | } |
||
| 811 | |||
| 812 | extract(unpack('Nlength', $this->_string_shift($private, 4))); |
||
| 813 | if (strlen($private) < $length) { |
||
| 814 | return false; |
||
| 815 | } |
||
| 816 | $components['privateExponent'] = BigInteger::createFromBinaryString($this->_string_shift($private, $length), true); |
||
| 817 | extract(unpack('Nlength', $this->_string_shift($private, 4))); |
||
| 818 | if (strlen($private) < $length) { |
||
| 819 | return false; |
||
| 820 | } |
||
| 821 | $components['primes'] = [1 => BigInteger::createFromBinaryString($this->_string_shift($private, $length), true)]; |
||
| 822 | extract(unpack('Nlength', $this->_string_shift($private, 4))); |
||
| 823 | if (strlen($private) < $length) { |
||
| 824 | return false; |
||
| 825 | } |
||
| 826 | $components['primes'][] = BigInteger::createFromBinaryString($this->_string_shift($private, $length), true); |
||
| 827 | |||
| 828 | $temp = $components['primes'][1]->subtract($this->one); |
||
| 829 | $components['exponents'] = [1 => $components['publicExponent']->modInverse($temp)]; |
||
| 830 | $temp = $components['primes'][2]->subtract($this->one); |
||
| 831 | $components['exponents'][] = $components['publicExponent']->modInverse($temp); |
||
| 832 | |||
| 833 | extract(unpack('Nlength', $this->_string_shift($private, 4))); |
||
| 834 | if (strlen($private) < $length) { |
||
| 835 | return false; |
||
| 836 | } |
||
| 837 | $components['coefficients'] = [2 => BigInteger::createFromBinaryString($this->_string_shift($private, $length), true)]; |
||
| 838 | |||
| 839 | return $components; |
||
| 840 | } |
||
| 841 | } |
||
| 842 | |||
| 843 | /** |
||
| 844 | * Start Element Handler. |
||
| 845 | * |
||
| 846 | * Called by xml_set_element_handler() |
||
| 847 | * |
||
| 848 | * @param resource $parser |
||
| 849 | * @param string $name |
||
| 850 | * @param array $attribs |
||
| 851 | */ |
||
| 852 | private function _start_element_handler($parser, $name, $attribs) |
||
| 853 | { |
||
| 854 | //$name = strtoupper($name); |
||
| 855 | switch ($name) { |
||
| 856 | case 'MODULUS': |
||
| 857 | $this->current = &$this->components['modulus']; |
||
| 858 | break; |
||
| 859 | case 'EXPONENT': |
||
| 860 | $this->current = &$this->components['publicExponent']; |
||
| 861 | break; |
||
| 862 | case 'P': |
||
| 863 | $this->current = &$this->components['primes'][1]; |
||
| 864 | break; |
||
| 865 | case 'Q': |
||
| 866 | $this->current = &$this->components['primes'][2]; |
||
| 867 | break; |
||
| 868 | case 'DP': |
||
| 869 | $this->current = &$this->components['exponents'][1]; |
||
| 870 | break; |
||
| 871 | case 'DQ': |
||
| 872 | $this->current = &$this->components['exponents'][2]; |
||
| 873 | break; |
||
| 874 | case 'INVERSEQ': |
||
| 875 | $this->current = &$this->components['coefficients'][2]; |
||
| 876 | break; |
||
| 877 | case 'D': |
||
| 878 | $this->current = &$this->components['privateExponent']; |
||
| 879 | } |
||
| 880 | $this->current = ''; |
||
| 881 | } |
||
| 882 | |||
| 883 | /** |
||
| 884 | * Stop Element Handler. |
||
| 885 | */ |
||
| 886 | private function _stop_element_handler() |
||
| 887 | { |
||
| 888 | if (isset($this->current)) { |
||
| 889 | $this->current = BigInteger::createFromBinaryString(base64_decode($this->current)); |
||
| 890 | unset($this->current); |
||
| 891 | } |
||
| 892 | } |
||
| 893 | |||
| 894 | /** |
||
| 895 | * Data Handler. |
||
| 896 | * |
||
| 897 | * Called by xml_set_character_data_handler() |
||
| 898 | * |
||
| 899 | * @param resource $parser |
||
| 900 | * @param string $data |
||
| 901 | */ |
||
| 902 | public function _data_handler($parser, $data) |
||
| 903 | { |
||
| 904 | if (!isset($this->current) || is_object($this->current)) { |
||
| 905 | return; |
||
| 906 | } |
||
| 907 | $this->current .= trim($data); |
||
| 908 | } |
||
| 909 | |||
| 910 | /** |
||
| 911 | * Loads a public or private key. |
||
| 912 | * |
||
| 913 | * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed) |
||
| 914 | * |
||
| 915 | * @param string $key |
||
| 916 | * @param int $type optional |
||
| 917 | */ |
||
| 918 | public function loadKey($key, $type = false) |
||
| 919 | { |
||
| 920 | if ($key instanceof self) { |
||
| 921 | $this->privateKeyFormat = $key->privateKeyFormat; |
||
| 922 | $this->publicKeyFormat = $key->publicKeyFormat; |
||
| 923 | $this->k = $key->k; |
||
| 924 | $this->hLen = $key->hLen; |
||
| 925 | $this->sLen = $key->sLen; |
||
| 926 | $this->mgfHLen = $key->mgfHLen; |
||
| 927 | $this->encryptionMode = $key->encryptionMode; |
||
| 928 | $this->signatureMode = $key->signatureMode; |
||
| 929 | $this->password = $key->password; |
||
| 930 | $this->configFile = $key->configFile; |
||
| 931 | $this->comment = $key->comment; |
||
| 932 | |||
| 933 | if (is_object($key->hash)) { |
||
| 934 | $this->hash = new Hash($key->hash->getHash()); |
||
| 935 | } |
||
| 936 | if (is_object($key->mgfHash)) { |
||
| 937 | $this->mgfHash = new Hash($key->mgfHash->getHash()); |
||
| 938 | } |
||
| 939 | |||
| 940 | if (is_object($key->modulus)) { |
||
| 941 | $this->modulus = $key->modulus->copy(); |
||
| 942 | } |
||
| 943 | if (is_object($key->exponent)) { |
||
| 944 | $this->exponent = $key->exponent->copy(); |
||
| 945 | } |
||
| 946 | if (is_object($key->publicExponent)) { |
||
| 947 | $this->publicExponent = $key->publicExponent->copy(); |
||
| 948 | } |
||
| 949 | |||
| 950 | $this->primes = []; |
||
| 951 | $this->exponents = []; |
||
| 952 | $this->coefficients = []; |
||
| 953 | |||
| 954 | foreach ($this->primes as $prime) { |
||
| 955 | $this->primes[] = $prime->copy(); |
||
| 956 | } |
||
| 957 | foreach ($this->exponents as $exponent) { |
||
| 958 | $this->exponents[] = $exponent->copy(); |
||
| 959 | } |
||
| 960 | foreach ($this->coefficients as $coefficient) { |
||
| 961 | $this->coefficients[] = $coefficient->copy(); |
||
| 962 | } |
||
| 963 | |||
| 964 | return true; |
||
| 965 | } |
||
| 966 | |||
| 967 | if ($type === false) { |
||
| 968 | $types = [ |
||
| 969 | self::PUBLIC_FORMAT_RAW, |
||
| 970 | self::PRIVATE_FORMAT_PKCS1, |
||
| 971 | self::PRIVATE_FORMAT_XML, |
||
| 972 | self::PRIVATE_FORMAT_PUTTY, |
||
| 973 | self::PUBLIC_FORMAT_OPENSSH, |
||
| 974 | ]; |
||
| 975 | foreach ($types as $type) { |
||
| 976 | $components = $this->_parseKey($key, $type); |
||
| 977 | if ($components !== false) { |
||
| 978 | break; |
||
| 979 | } |
||
| 980 | } |
||
| 981 | } else { |
||
| 982 | $components = $this->_parseKey($key, $type); |
||
| 983 | } |
||
| 984 | |||
| 985 | if ($components === false) { |
||
| 986 | return false; |
||
| 987 | } |
||
| 988 | |||
| 989 | if (isset($components['comment']) && $components['comment'] !== false) { |
||
| 990 | $this->comment = $components['comment']; |
||
| 991 | } |
||
| 992 | $this->modulus = $components['modulus']; |
||
| 993 | $this->k = strlen($this->modulus->toBytes()); |
||
| 994 | $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent']; |
||
| 995 | if (isset($components['primes'])) { |
||
| 996 | $this->primes = $components['primes']; |
||
| 997 | $this->exponents = $components['exponents']; |
||
| 998 | $this->coefficients = $components['coefficients']; |
||
| 999 | $this->publicExponent = $components['publicExponent']; |
||
| 1000 | } else { |
||
| 1001 | $this->primes = []; |
||
| 1002 | $this->exponents = []; |
||
| 1003 | $this->coefficients = []; |
||
| 1004 | $this->publicExponent = false; |
||
| 1005 | } |
||
| 1006 | |||
| 1007 | switch ($type) { |
||
| 1008 | case self::PUBLIC_FORMAT_OPENSSH: |
||
| 1009 | case self::PUBLIC_FORMAT_RAW: |
||
| 1010 | $this->setPublicKey(); |
||
| 1011 | break; |
||
| 1012 | case self::PRIVATE_FORMAT_PKCS1: |
||
| 1013 | switch (true) { |
||
| 1014 | case strpos($key, '-BEGIN PUBLIC KEY-') !== false: |
||
| 1015 | case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false: |
||
| 1016 | $this->setPublicKey(); |
||
| 1017 | } |
||
| 1018 | } |
||
| 1019 | |||
| 1020 | return true; |
||
| 1021 | } |
||
| 1022 | |||
| 1023 | /** |
||
| 1024 | * DER-decode the length. |
||
| 1025 | * |
||
| 1026 | * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See |
||
| 1027 | * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. |
||
| 1028 | * |
||
| 1029 | * @param string $string |
||
| 1030 | * |
||
| 1031 | * @return int |
||
| 1032 | */ |
||
| 1033 | private function _decodeLength(&$string) |
||
| 1034 | { |
||
| 1035 | $length = ord($this->_string_shift($string)); |
||
| 1036 | if ($length & 0x80) { // definite length, long form |
||
| 1037 | $length &= 0x7F; |
||
| 1038 | $temp = $this->_string_shift($string, $length); |
||
| 1039 | list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)); |
||
| 1040 | } |
||
| 1041 | |||
| 1042 | return $length; |
||
| 1043 | } |
||
| 1044 | |||
| 1045 | /** |
||
| 1046 | * String Shift. |
||
| 1047 | * |
||
| 1048 | * Inspired by array_shift |
||
| 1049 | * |
||
| 1050 | * @param string $string |
||
| 1051 | * @param int $index |
||
| 1052 | * |
||
| 1053 | * @return string |
||
| 1054 | */ |
||
| 1055 | private function _string_shift(&$string, $index = 1) |
||
| 1056 | { |
||
| 1057 | $substr = substr($string, 0, $index); |
||
| 1058 | $string = substr($string, $index); |
||
| 1059 | |||
| 1060 | return $substr; |
||
| 1061 | } |
||
| 1062 | |||
| 1063 | /** |
||
| 1064 | * Determines which hashing function should be used. |
||
| 1065 | * |
||
| 1066 | * Used with signature production / verification and (if the encryption mode is self::ENCRYPTION_OAEP) encryption and |
||
| 1067 | * decryption. If $hash isn't supported, sha1 is used. |
||
| 1068 | * |
||
| 1069 | * @param string $hash |
||
| 1070 | */ |
||
| 1071 | public function setHash($hash) |
||
| 1072 | { |
||
| 1073 | switch ($hash) { |
||
| 1074 | case 'sha1': |
||
| 1075 | $this->hLen = 20; |
||
| 1076 | break; |
||
| 1077 | case 'sha256': |
||
| 1078 | $this->hLen = 32; |
||
| 1079 | break; |
||
| 1080 | case 'sha384': |
||
| 1081 | $this->hLen = 48; |
||
| 1082 | break; |
||
| 1083 | case 'sha512': |
||
| 1084 | $this->hLen = 64; |
||
| 1085 | break; |
||
| 1086 | default: |
||
| 1087 | throw new \InvalidArgumentException('Unsupported hash algorithm.'); |
||
| 1088 | } |
||
| 1089 | $this->hash = new Hash($hash); |
||
| 1090 | } |
||
| 1091 | |||
| 1092 | /** |
||
| 1093 | * Determines which hashing function should be used for the mask generation function. |
||
| 1094 | * |
||
| 1095 | * The mask generation function is used by self::ENCRYPTION_OAEP and self::SIGNATURE_PSS and although it's |
||
| 1096 | * best if Hash and MGFHash are set to the same thing this is not a requirement. |
||
| 1097 | * |
||
| 1098 | * @param string $hash |
||
| 1099 | */ |
||
| 1100 | public function setMGFHash($hash) |
||
| 1101 | { |
||
| 1102 | switch ($hash) { |
||
| 1103 | case 'sha1': |
||
| 1104 | $this->mgfHLen = 20; |
||
| 1105 | break; |
||
| 1106 | case 'sha256': |
||
| 1107 | $this->mgfHash = 32; |
||
| 1108 | break; |
||
| 1109 | case 'sha384': |
||
| 1110 | $this->mgfHash = 48; |
||
| 1111 | break; |
||
| 1112 | case 'sha512': |
||
| 1113 | $this->mgfHash = 64; |
||
| 1114 | break; |
||
| 1115 | default: |
||
| 1116 | throw new \InvalidArgumentException('Unsupported hash algorithm.'); |
||
| 1117 | } |
||
| 1118 | $this->mgfHash = new Hash($hash); |
||
| 1119 | } |
||
| 1120 | |||
| 1121 | /** |
||
| 1122 | * Determines the salt length. |
||
| 1123 | * |
||
| 1124 | * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}: |
||
| 1125 | * |
||
| 1126 | * Typical salt lengths in octets are hLen (the length of the output |
||
| 1127 | * of the hash function Hash) and 0. |
||
| 1128 | * |
||
| 1129 | * @param int $format |
||
| 1130 | */ |
||
| 1131 | public function setSaltLength($sLen) |
||
| 1132 | { |
||
| 1133 | $this->sLen = $sLen; |
||
| 1134 | } |
||
| 1135 | |||
| 1136 | /** |
||
| 1137 | * Integer-to-Octet-String primitive. |
||
| 1138 | * |
||
| 1139 | * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}. |
||
| 1140 | * |
||
| 1141 | * @param \Jose\Util\BigInteger $x |
||
| 1142 | * @param int $xLen |
||
| 1143 | * |
||
| 1144 | * @return string |
||
| 1145 | */ |
||
| 1146 | private function _i2osp($x, $xLen) |
||
| 1147 | { |
||
| 1148 | $x = $x->toBytes(); |
||
| 1149 | if (strlen($x) > $xLen) { |
||
| 1150 | user_error('Integer too large'); |
||
| 1151 | |||
| 1152 | return false; |
||
| 1153 | } |
||
| 1154 | |||
| 1155 | return str_pad($x, $xLen, chr(0), STR_PAD_LEFT); |
||
| 1156 | } |
||
| 1157 | |||
| 1158 | /** |
||
| 1159 | * Octet-String-to-Integer primitive. |
||
| 1160 | * |
||
| 1161 | * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}. |
||
| 1162 | * |
||
| 1163 | * @param string $x |
||
| 1164 | * |
||
| 1165 | * @return \Jose\Util\BigInteger |
||
| 1166 | */ |
||
| 1167 | private function _os2ip($x) |
||
| 1168 | { |
||
| 1169 | return BigInteger::createFromBinaryString($x); |
||
| 1170 | } |
||
| 1171 | |||
| 1172 | /** |
||
| 1173 | * Exponentiate with or without Chinese Remainder Theorem. |
||
| 1174 | * |
||
| 1175 | * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}. |
||
| 1176 | * |
||
| 1177 | * @param \Jose\Util\BigInteger $x |
||
| 1178 | * |
||
| 1179 | * @return \Jose\Util\BigInteger |
||
| 1180 | */ |
||
| 1181 | private function _exponentiate($x) |
||
| 1182 | { |
||
| 1183 | if (empty($this->primes) || empty($this->coefficients) || empty($this->exponents)) { |
||
| 1184 | return $x->modPow($this->exponent, $this->modulus); |
||
|
0 ignored issues
–
show
|
|||
| 1185 | } |
||
| 1186 | |||
| 1187 | $num_primes = count($this->primes); |
||
| 1188 | |||
| 1189 | if (defined('CRYPT_RSA_DISABLE_BLINDING')) { |
||
| 1190 | $m_i = [ |
||
| 1191 | 1 => $x->modPow($this->exponents[1], $this->primes[1]), |
||
| 1192 | 2 => $x->modPow($this->exponents[2], $this->primes[2]), |
||
| 1193 | ]; |
||
| 1194 | $h = $m_i[1]->subtract($m_i[2]); |
||
|
0 ignored issues
–
show
It seems like
$m_i[2] can also be of type boolean; however, Jose\Util\BigInteger::subtract() does only seem to accept object<Jose\Util\BigInteger>, maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. Loading history...
|
|||
| 1195 | $h = $h->multiply($this->coefficients[2]); |
||
| 1196 | list(, $h) = $h->divide($this->primes[1]); |
||
| 1197 | $m = $m_i[2]->add($h->multiply($this->primes[2])); |
||
| 1198 | |||
| 1199 | $r = $this->primes[1]; |
||
| 1200 | for ($i = 3; $i <= $num_primes; $i++) { |
||
| 1201 | $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]); |
||
| 1202 | |||
| 1203 | $r = $r->multiply($this->primes[$i - 1]); |
||
| 1204 | |||
| 1205 | $h = $m_i->subtract($m); |
||
| 1206 | $h = $h->multiply($this->coefficients[$i]); |
||
| 1207 | list(, $h) = $h->divide($this->primes[$i]); |
||
| 1208 | |||
| 1209 | $m = $m->add($r->multiply($h)); |
||
| 1210 | } |
||
| 1211 | } else { |
||
| 1212 | $smallest = $this->primes[1]; |
||
| 1213 | for ($i = 2; $i <= $num_primes; $i++) { |
||
| 1214 | if ($smallest->compare($this->primes[$i]) > 0) { |
||
| 1215 | $smallest = $this->primes[$i]; |
||
| 1216 | } |
||
| 1217 | } |
||
| 1218 | |||
| 1219 | $one = BigInteger::createFromDecimalString('1'); |
||
| 1220 | |||
| 1221 | $r = $one->random($one, $smallest->subtract($one)); |
||
| 1222 | |||
| 1223 | $m_i = [ |
||
| 1224 | 1 => $this->_blind($x, $r, 1), |
||
| 1225 | 2 => $this->_blind($x, $r, 2), |
||
| 1226 | ]; |
||
| 1227 | $h = $m_i[1]->subtract($m_i[2]); |
||
| 1228 | $h = $h->multiply($this->coefficients[2]); |
||
| 1229 | list(, $h) = $h->divide($this->primes[1]); |
||
| 1230 | $m = $m_i[2]->add($h->multiply($this->primes[2])); |
||
| 1231 | |||
| 1232 | $r = $this->primes[1]; |
||
| 1233 | for ($i = 3; $i <= $num_primes; $i++) { |
||
| 1234 | $m_i = $this->_blind($x, $r, $i); |
||
| 1235 | |||
| 1236 | $r = $r->multiply($this->primes[$i - 1]); |
||
| 1237 | |||
| 1238 | $h = $m_i->subtract($m); |
||
| 1239 | $h = $h->multiply($this->coefficients[$i]); |
||
| 1240 | list(, $h) = $h->divide($this->primes[$i]); |
||
| 1241 | |||
| 1242 | $m = $m->add($r->multiply($h)); |
||
| 1243 | } |
||
| 1244 | } |
||
| 1245 | |||
| 1246 | return $m; |
||
| 1247 | } |
||
| 1248 | |||
| 1249 | /** |
||
| 1250 | * Performs RSA Blinding. |
||
| 1251 | * |
||
| 1252 | * Protects against timing attacks by employing RSA Blinding. |
||
| 1253 | * Returns $x->modPow($this->exponents[$i], $this->primes[$i]) |
||
| 1254 | * |
||
| 1255 | * @param \Jose\Util\BigInteger $x |
||
| 1256 | * @param \Jose\Util\BigInteger $r |
||
| 1257 | * @param int $i |
||
| 1258 | * |
||
| 1259 | * @return \Jose\Util\BigInteger |
||
| 1260 | */ |
||
| 1261 | private function _blind($x, $r, $i) |
||
| 1262 | { |
||
| 1263 | $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i])); |
||
|
0 ignored issues
–
show
It seems like
$r->modPow($this->public...ent, $this->primes[$i]) targeting Jose\Util\BigInteger::modPow() can also be of type boolean; however, Jose\Util\BigInteger::multiply() does only seem to accept object<Jose\Util\BigInteger>, maybe add an additional type check?
This check looks at variables that are passed out again to other methods. If the outgoing method call has stricter type requirements than the method itself, an issue is raised. An additional type check may prevent trouble. Loading history...
|
|||
| 1264 | $x = $x->modPow($this->exponents[$i], $this->primes[$i]); |
||
| 1265 | |||
| 1266 | $r = $r->modInverse($this->primes[$i]); |
||
| 1267 | $x = $x->multiply($r); |
||
| 1268 | list(, $x) = $x->divide($this->primes[$i]); |
||
| 1269 | |||
| 1270 | return $x; |
||
| 1271 | } |
||
| 1272 | |||
| 1273 | /** |
||
| 1274 | * Performs blinded RSA equality testing. |
||
| 1275 | * |
||
| 1276 | * Protects against a particular type of timing attack described. |
||
| 1277 | * |
||
| 1278 | * See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson In Timing Attacks (or, Don't use MessageDigest.isEquals)} |
||
| 1279 | * |
||
| 1280 | * Thanks for the heads up singpolyma! |
||
| 1281 | * |
||
| 1282 | * @param string $x |
||
| 1283 | * @param string $y |
||
| 1284 | * |
||
| 1285 | * @return bool |
||
| 1286 | */ |
||
| 1287 | private function _equals($x, $y) |
||
| 1288 | { |
||
| 1289 | if (strlen($x) != strlen($y)) { |
||
| 1290 | return false; |
||
| 1291 | } |
||
| 1292 | |||
| 1293 | $result = 0; |
||
| 1294 | for ($i = 0; $i < strlen($x); $i++) { |
||
| 1295 | $result |= ord($x[$i]) ^ ord($y[$i]); |
||
| 1296 | } |
||
| 1297 | |||
| 1298 | return $result == 0; |
||
| 1299 | } |
||
| 1300 | |||
| 1301 | /** |
||
| 1302 | * RSAEP. |
||
| 1303 | * |
||
| 1304 | * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}. |
||
| 1305 | * |
||
| 1306 | * @param \Jose\Util\BigInteger $m |
||
| 1307 | * |
||
| 1308 | * @return \Jose\Util\BigInteger |
||
| 1309 | */ |
||
| 1310 | private function _rsaep($m) |
||
| 1311 | { |
||
| 1312 | if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) { |
||
| 1313 | user_error('Message representative out of range'); |
||
| 1314 | |||
| 1315 | return false; |
||
| 1316 | } |
||
| 1317 | |||
| 1318 | return $this->_exponentiate($m); |
||
| 1319 | } |
||
| 1320 | |||
| 1321 | /** |
||
| 1322 | * RSADP. |
||
| 1323 | * |
||
| 1324 | * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}. |
||
| 1325 | * |
||
| 1326 | * @param \Jose\Util\BigInteger $c |
||
| 1327 | * |
||
| 1328 | * @return \Jose\Util\BigInteger |
||
| 1329 | */ |
||
| 1330 | private function _rsadp($c) |
||
| 1331 | { |
||
| 1332 | if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) { |
||
| 1333 | user_error('Ciphertext representative out of range'); |
||
| 1334 | |||
| 1335 | return false; |
||
| 1336 | } |
||
| 1337 | |||
| 1338 | return $this->_exponentiate($c); |
||
| 1339 | } |
||
| 1340 | |||
| 1341 | /** |
||
| 1342 | * RSASP1. |
||
| 1343 | * |
||
| 1344 | * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}. |
||
| 1345 | * |
||
| 1346 | * @param \Jose\Util\BigInteger $m |
||
| 1347 | * |
||
| 1348 | * @return \Jose\Util\BigInteger |
||
| 1349 | */ |
||
| 1350 | private function _rsasp1($m) |
||
| 1351 | { |
||
| 1352 | if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) { |
||
| 1353 | user_error('Message representative out of range'); |
||
| 1354 | |||
| 1355 | return false; |
||
| 1356 | } |
||
| 1357 | |||
| 1358 | return $this->_exponentiate($m); |
||
| 1359 | } |
||
| 1360 | |||
| 1361 | /** |
||
| 1362 | * RSAVP1. |
||
| 1363 | * |
||
| 1364 | * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}. |
||
| 1365 | * |
||
| 1366 | * @param \Jose\Util\BigInteger $s |
||
| 1367 | * |
||
| 1368 | * @return \Jose\Util\BigInteger |
||
| 1369 | */ |
||
| 1370 | private function _rsavp1($s) |
||
| 1371 | { |
||
| 1372 | if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) { |
||
| 1373 | user_error('Signature representative out of range'); |
||
| 1374 | |||
| 1375 | return false; |
||
| 1376 | } |
||
| 1377 | |||
| 1378 | return $this->_exponentiate($s); |
||
| 1379 | } |
||
| 1380 | |||
| 1381 | /** |
||
| 1382 | * MGF1. |
||
| 1383 | * |
||
| 1384 | * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}. |
||
| 1385 | * |
||
| 1386 | * @param string $mgfSeed |
||
| 1387 | * @param int $mgfLen |
||
| 1388 | * |
||
| 1389 | * @return string |
||
| 1390 | */ |
||
| 1391 | private function _mgf1($mgfSeed, $maskLen) |
||
| 1392 | { |
||
| 1393 | // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output. |
||
| 1394 | |||
| 1395 | $t = ''; |
||
| 1396 | $count = ceil($maskLen / $this->mgfHLen); |
||
| 1397 | for ($i = 0; $i < $count; $i++) { |
||
| 1398 | $c = pack('N', $i); |
||
| 1399 | $t .= $this->mgfHash->hash($mgfSeed.$c); |
||
| 1400 | } |
||
| 1401 | |||
| 1402 | return substr($t, 0, $maskLen); |
||
| 1403 | } |
||
| 1404 | |||
| 1405 | /** |
||
| 1406 | * RSAES-OAEP-ENCRYPT. |
||
| 1407 | * |
||
| 1408 | * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and |
||
| 1409 | * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}. |
||
| 1410 | * |
||
| 1411 | * @param string $m |
||
| 1412 | * @param string $l |
||
| 1413 | * |
||
| 1414 | * @return string |
||
| 1415 | */ |
||
| 1416 | private function _rsaes_oaep_encrypt($m, $l = '') |
||
| 1417 | { |
||
| 1418 | $mLen = strlen($m); |
||
| 1419 | |||
| 1420 | // Length checking |
||
| 1421 | |||
| 1422 | // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error |
||
| 1423 | // be output. |
||
| 1424 | |||
| 1425 | if ($mLen > $this->k - 2 * $this->hLen - 2) { |
||
| 1426 | user_error('Message too long'); |
||
| 1427 | |||
| 1428 | return false; |
||
| 1429 | } |
||
| 1430 | |||
| 1431 | // EME-OAEP encoding |
||
| 1432 | |||
| 1433 | $lHash = $this->hash->hash($l); |
||
| 1434 | $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2); |
||
| 1435 | $db = $lHash.$ps.chr(1).$m; |
||
| 1436 | $seed = random_bytes($this->hLen); |
||
| 1437 | $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1); |
||
| 1438 | $maskedDB = $db ^ $dbMask; |
||
| 1439 | $seedMask = $this->_mgf1($maskedDB, $this->hLen); |
||
| 1440 | $maskedSeed = $seed ^ $seedMask; |
||
| 1441 | $em = chr(0).$maskedSeed.$maskedDB; |
||
| 1442 | |||
| 1443 | // RSA encryption |
||
| 1444 | |||
| 1445 | $m = $this->_os2ip($em); |
||
| 1446 | $c = $this->_rsaep($m); |
||
| 1447 | $c = $this->_i2osp($c, $this->k); |
||
| 1448 | |||
| 1449 | // Output the ciphertext C |
||
| 1450 | |||
| 1451 | return $c; |
||
| 1452 | } |
||
| 1453 | |||
| 1454 | /** |
||
| 1455 | * RSAES-OAEP-DECRYPT. |
||
| 1456 | * |
||
| 1457 | * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}. The fact that the error |
||
| 1458 | * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2: |
||
| 1459 | * |
||
| 1460 | * Note. Care must be taken to ensure that an opponent cannot |
||
| 1461 | * distinguish the different error conditions in Step 3.g, whether by |
||
| 1462 | * error message or timing, or, more generally, learn partial |
||
| 1463 | * information about the encoded message EM. Otherwise an opponent may |
||
| 1464 | * be able to obtain useful information about the decryption of the |
||
| 1465 | * ciphertext C, leading to a chosen-ciphertext attack such as the one |
||
| 1466 | * observed by Manger [36]. |
||
| 1467 | * |
||
| 1468 | * As for $l... to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}: |
||
| 1469 | * |
||
| 1470 | * Both the encryption and the decryption operations of RSAES-OAEP take |
||
| 1471 | * the value of a label L as input. In this version of PKCS #1, L is |
||
| 1472 | * the empty string; other uses of the label are outside the scope of |
||
| 1473 | * this document. |
||
| 1474 | * |
||
| 1475 | * @param string $c |
||
| 1476 | * @param string $l |
||
| 1477 | * |
||
| 1478 | * @return string |
||
| 1479 | */ |
||
| 1480 | private function _rsaes_oaep_decrypt($c, $l = '') |
||
| 1481 | { |
||
| 1482 | // Length checking |
||
| 1483 | |||
| 1484 | // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error |
||
| 1485 | // be output. |
||
| 1486 | |||
| 1487 | if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) { |
||
| 1488 | user_error('Decryption error'); |
||
| 1489 | |||
| 1490 | return false; |
||
| 1491 | } |
||
| 1492 | |||
| 1493 | // RSA decryption |
||
| 1494 | |||
| 1495 | $c = $this->_os2ip($c); |
||
| 1496 | $m = $this->_rsadp($c); |
||
| 1497 | if ($m === false) { |
||
| 1498 | user_error('Decryption error'); |
||
| 1499 | |||
| 1500 | return false; |
||
| 1501 | } |
||
| 1502 | $em = $this->_i2osp($m, $this->k); |
||
| 1503 | |||
| 1504 | // EME-OAEP decoding |
||
| 1505 | |||
| 1506 | $lHash = $this->hash->hash($l); |
||
| 1507 | $y = ord($em[0]); |
||
| 1508 | $maskedSeed = substr($em, 1, $this->hLen); |
||
| 1509 | $maskedDB = substr($em, $this->hLen + 1); |
||
| 1510 | $seedMask = $this->_mgf1($maskedDB, $this->hLen); |
||
| 1511 | $seed = $maskedSeed ^ $seedMask; |
||
| 1512 | $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1); |
||
| 1513 | $db = $maskedDB ^ $dbMask; |
||
| 1514 | $lHash2 = substr($db, 0, $this->hLen); |
||
| 1515 | $m = substr($db, $this->hLen); |
||
| 1516 | if ($lHash != $lHash2) { |
||
| 1517 | user_error('Decryption error'); |
||
| 1518 | |||
| 1519 | return false; |
||
| 1520 | } |
||
| 1521 | $m = ltrim($m, chr(0)); |
||
| 1522 | if (ord($m[0]) != 1) { |
||
| 1523 | user_error('Decryption error'); |
||
| 1524 | |||
| 1525 | return false; |
||
| 1526 | } |
||
| 1527 | |||
| 1528 | // Output the message M |
||
| 1529 | |||
| 1530 | return substr($m, 1); |
||
| 1531 | } |
||
| 1532 | |||
| 1533 | /** |
||
| 1534 | * RSAES-PKCS1-V1_5-ENCRYPT. |
||
| 1535 | * |
||
| 1536 | * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}. |
||
| 1537 | * |
||
| 1538 | * @param string $m |
||
| 1539 | * |
||
| 1540 | * @return string |
||
| 1541 | */ |
||
| 1542 | private function _rsaes_pkcs1_v1_5_encrypt($m) |
||
| 1543 | { |
||
| 1544 | $mLen = strlen($m); |
||
| 1545 | |||
| 1546 | // Length checking |
||
| 1547 | |||
| 1548 | if ($mLen > $this->k - 11) { |
||
| 1549 | user_error('Message too long'); |
||
| 1550 | |||
| 1551 | return false; |
||
| 1552 | } |
||
| 1553 | |||
| 1554 | // EME-PKCS1-v1_5 encoding |
||
| 1555 | |||
| 1556 | $psLen = $this->k - $mLen - 3; |
||
| 1557 | $ps = ''; |
||
| 1558 | while (strlen($ps) != $psLen) { |
||
| 1559 | $temp = random_bytes($psLen - strlen($ps)); |
||
| 1560 | $temp = str_replace("\x00", '', $temp); |
||
| 1561 | $ps .= $temp; |
||
| 1562 | } |
||
| 1563 | $type = 2; |
||
| 1564 | // see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done |
||
| 1565 | if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) { |
||
| 1566 | $type = 1; |
||
| 1567 | // "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF" |
||
| 1568 | $ps = str_repeat("\xFF", $psLen); |
||
| 1569 | } |
||
| 1570 | $em = chr(0).chr($type).$ps.chr(0).$m; |
||
| 1571 | |||
| 1572 | // RSA encryption |
||
| 1573 | $m = $this->_os2ip($em); |
||
| 1574 | $c = $this->_rsaep($m); |
||
| 1575 | $c = $this->_i2osp($c, $this->k); |
||
| 1576 | |||
| 1577 | // Output the ciphertext C |
||
| 1578 | |||
| 1579 | return $c; |
||
| 1580 | } |
||
| 1581 | |||
| 1582 | /** |
||
| 1583 | * RSAES-PKCS1-V1_5-DECRYPT. |
||
| 1584 | * |
||
| 1585 | * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}. |
||
| 1586 | * |
||
| 1587 | * For compatibility purposes, this function departs slightly from the description given in RFC3447. |
||
| 1588 | * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the |
||
| 1589 | * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the |
||
| 1590 | * public key should have the second byte set to 2. In RFC3447 (PKCS#1 v2.1), the second byte is supposed |
||
| 1591 | * to be 2 regardless of which key is used. For compatibility purposes, we'll just check to make sure the |
||
| 1592 | * second byte is 2 or less. If it is, we'll accept the decrypted string as valid. |
||
| 1593 | * |
||
| 1594 | * As a consequence of this, a private key encrypted ciphertext produced with \phpseclib\Crypt\RSA may not decrypt |
||
| 1595 | * with a strictly PKCS#1 v1.5 compliant RSA implementation. Public key encrypted ciphertext's should but |
||
| 1596 | * not private key encrypted ciphertext's. |
||
| 1597 | * |
||
| 1598 | * @param string $c |
||
| 1599 | * |
||
| 1600 | * @return string |
||
| 1601 | */ |
||
| 1602 | private function _rsaes_pkcs1_v1_5_decrypt($c) |
||
| 1603 | { |
||
| 1604 | // Length checking |
||
| 1605 | |||
| 1606 | if (strlen($c) != $this->k) { // or if k < 11 |
||
| 1607 | user_error('Decryption error'); |
||
| 1608 | |||
| 1609 | return false; |
||
| 1610 | } |
||
| 1611 | |||
| 1612 | // RSA decryption |
||
| 1613 | |||
| 1614 | $c = $this->_os2ip($c); |
||
| 1615 | $m = $this->_rsadp($c); |
||
| 1616 | |||
| 1617 | if ($m === false) { |
||
| 1618 | user_error('Decryption error'); |
||
| 1619 | |||
| 1620 | return false; |
||
| 1621 | } |
||
| 1622 | $em = $this->_i2osp($m, $this->k); |
||
| 1623 | |||
| 1624 | // EME-PKCS1-v1_5 decoding |
||
| 1625 | |||
| 1626 | if (ord($em[0]) != 0 || ord($em[1]) > 2) { |
||
| 1627 | user_error('Decryption error'); |
||
| 1628 | |||
| 1629 | return false; |
||
| 1630 | } |
||
| 1631 | |||
| 1632 | $ps = substr($em, 2, strpos($em, chr(0), 2) - 2); |
||
| 1633 | $m = substr($em, strlen($ps) + 3); |
||
| 1634 | |||
| 1635 | if (strlen($ps) < 8) { |
||
| 1636 | user_error('Decryption error'); |
||
| 1637 | |||
| 1638 | return false; |
||
| 1639 | } |
||
| 1640 | |||
| 1641 | // Output M |
||
| 1642 | |||
| 1643 | return $m; |
||
| 1644 | } |
||
| 1645 | |||
| 1646 | /** |
||
| 1647 | * EMSA-PSS-ENCODE. |
||
| 1648 | * |
||
| 1649 | * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}. |
||
| 1650 | * |
||
| 1651 | * @param string $m |
||
| 1652 | * @param int $emBits |
||
| 1653 | */ |
||
| 1654 | private function _emsa_pss_encode($m, $emBits) |
||
| 1655 | { |
||
| 1656 | // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error |
||
| 1657 | // be output. |
||
| 1658 | |||
| 1659 | $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8) |
||
| 1660 | $sLen = $this->sLen ? $this->sLen : $this->hLen; |
||
| 1661 | |||
| 1662 | $mHash = $this->hash->hash($m); |
||
| 1663 | if ($emLen < $this->hLen + $sLen + 2) { |
||
| 1664 | user_error('Encoding error'); |
||
| 1665 | |||
| 1666 | return false; |
||
| 1667 | } |
||
| 1668 | |||
| 1669 | $salt = random_bytes($sLen); |
||
| 1670 | $m2 = "\0\0\0\0\0\0\0\0".$mHash.$salt; |
||
| 1671 | $h = $this->hash->hash($m2); |
||
| 1672 | $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2); |
||
| 1673 | $db = $ps.chr(1).$salt; |
||
| 1674 | $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1); |
||
| 1675 | $maskedDB = $db ^ $dbMask; |
||
| 1676 | $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0]; |
||
| 1677 | $em = $maskedDB.$h.chr(0xBC); |
||
| 1678 | |||
| 1679 | return $em; |
||
| 1680 | } |
||
| 1681 | |||
| 1682 | /** |
||
| 1683 | * EMSA-PSS-VERIFY. |
||
| 1684 | * |
||
| 1685 | * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}. |
||
| 1686 | * |
||
| 1687 | * @param string $m |
||
| 1688 | * @param string $em |
||
| 1689 | * @param int $emBits |
||
| 1690 | * |
||
| 1691 | * @return string |
||
| 1692 | */ |
||
| 1693 | private function _emsa_pss_verify($m, $em, $emBits) |
||
| 1694 | { |
||
| 1695 | // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error |
||
| 1696 | // be output. |
||
| 1697 | |||
| 1698 | $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8); |
||
| 1699 | $sLen = $this->sLen ? $this->sLen : $this->hLen; |
||
| 1700 | |||
| 1701 | $mHash = $this->hash->hash($m); |
||
| 1702 | if ($emLen < $this->hLen + $sLen + 2) { |
||
| 1703 | return false; |
||
| 1704 | } |
||
| 1705 | |||
| 1706 | if ($em[strlen($em) - 1] != chr(0xBC)) { |
||
| 1707 | return false; |
||
| 1708 | } |
||
| 1709 | |||
| 1710 | $maskedDB = substr($em, 0, -$this->hLen - 1); |
||
| 1711 | $h = substr($em, -$this->hLen - 1, $this->hLen); |
||
| 1712 | $temp = chr(0xFF << ($emBits & 7)); |
||
| 1713 | if ((~$maskedDB[0] & $temp) != $temp) { |
||
| 1714 | return false; |
||
| 1715 | } |
||
| 1716 | $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1); |
||
| 1717 | $db = $maskedDB ^ $dbMask; |
||
| 1718 | $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0]; |
||
| 1719 | $temp = $emLen - $this->hLen - $sLen - 2; |
||
| 1720 | if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) { |
||
| 1721 | return false; |
||
| 1722 | } |
||
| 1723 | $salt = substr($db, $temp + 1); // should be $sLen long |
||
| 1724 | $m2 = "\0\0\0\0\0\0\0\0".$mHash.$salt; |
||
| 1725 | $h2 = $this->hash->hash($m2); |
||
| 1726 | |||
| 1727 | return $this->_equals($h, $h2); |
||
| 1728 | } |
||
| 1729 | |||
| 1730 | /** |
||
| 1731 | * RSASSA-PSS-SIGN. |
||
| 1732 | * |
||
| 1733 | * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}. |
||
| 1734 | * |
||
| 1735 | * @param string $m |
||
| 1736 | * |
||
| 1737 | * @return string |
||
| 1738 | */ |
||
| 1739 | private function _rsassa_pss_sign($m) |
||
| 1740 | { |
||
| 1741 | // EMSA-PSS encoding |
||
| 1742 | |||
| 1743 | $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1); |
||
| 1744 | |||
| 1745 | // RSA signature |
||
| 1746 | |||
| 1747 | $m = $this->_os2ip($em); |
||
| 1748 | $s = $this->_rsasp1($m); |
||
| 1749 | $s = $this->_i2osp($s, $this->k); |
||
| 1750 | |||
| 1751 | // Output the signature S |
||
| 1752 | |||
| 1753 | return $s; |
||
| 1754 | } |
||
| 1755 | |||
| 1756 | /** |
||
| 1757 | * RSASSA-PSS-VERIFY. |
||
| 1758 | * |
||
| 1759 | * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}. |
||
| 1760 | * |
||
| 1761 | * @param string $m |
||
| 1762 | * @param string $s |
||
| 1763 | * |
||
| 1764 | * @return string |
||
| 1765 | */ |
||
| 1766 | private function _rsassa_pss_verify($m, $s) |
||
| 1767 | { |
||
| 1768 | // Length checking |
||
| 1769 | |||
| 1770 | if (strlen($s) != $this->k) { |
||
| 1771 | user_error('Invalid signature'); |
||
| 1772 | |||
| 1773 | return false; |
||
| 1774 | } |
||
| 1775 | |||
| 1776 | // RSA verification |
||
| 1777 | |||
| 1778 | $modBits = 8 * $this->k; |
||
| 1779 | |||
| 1780 | $s2 = $this->_os2ip($s); |
||
| 1781 | $m2 = $this->_rsavp1($s2); |
||
| 1782 | if ($m2 === false) { |
||
| 1783 | user_error('Invalid signature'); |
||
| 1784 | |||
| 1785 | return false; |
||
| 1786 | } |
||
| 1787 | $em = $this->_i2osp($m2, $modBits >> 3); |
||
| 1788 | if ($em === false) { |
||
| 1789 | user_error('Invalid signature'); |
||
| 1790 | |||
| 1791 | return false; |
||
| 1792 | } |
||
| 1793 | |||
| 1794 | // EMSA-PSS verification |
||
| 1795 | |||
| 1796 | return $this->_emsa_pss_verify($m, $em, $modBits - 1); |
||
| 1797 | } |
||
| 1798 | |||
| 1799 | /** |
||
| 1800 | * EMSA-PKCS1-V1_5-ENCODE. |
||
| 1801 | * |
||
| 1802 | * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}. |
||
| 1803 | * |
||
| 1804 | * @param string $m |
||
| 1805 | * @param int $emLen |
||
| 1806 | * |
||
| 1807 | * @return string |
||
| 1808 | */ |
||
| 1809 | private function _emsa_pkcs1_v1_5_encode($m, $emLen) |
||
| 1810 | { |
||
| 1811 | $h = $this->hash->hash($m); |
||
| 1812 | if ($h === false) { |
||
| 1813 | return false; |
||
| 1814 | } |
||
| 1815 | |||
| 1816 | // see http://tools.ietf.org/html/rfc3447#page-43 |
||
| 1817 | switch ($this->hashName) { |
||
| 1818 | case 'md2': |
||
| 1819 | $t = pack('H*', '3020300c06082a864886f70d020205000410'); |
||
| 1820 | break; |
||
| 1821 | case 'md5': |
||
| 1822 | $t = pack('H*', '3020300c06082a864886f70d020505000410'); |
||
| 1823 | break; |
||
| 1824 | case 'sha1': |
||
| 1825 | $t = pack('H*', '3021300906052b0e03021a05000414'); |
||
| 1826 | break; |
||
| 1827 | case 'sha256': |
||
| 1828 | $t = pack('H*', '3031300d060960864801650304020105000420'); |
||
| 1829 | break; |
||
| 1830 | case 'sha384': |
||
| 1831 | $t = pack('H*', '3041300d060960864801650304020205000430'); |
||
| 1832 | break; |
||
| 1833 | case 'sha512': |
||
| 1834 | $t = pack('H*', '3051300d060960864801650304020305000440'); |
||
| 1835 | } |
||
| 1836 | $t .= $h; |
||
| 1837 | $tLen = strlen($t); |
||
| 1838 | |||
| 1839 | if ($emLen < $tLen + 11) { |
||
| 1840 | user_error('Intended encoded message length too short'); |
||
| 1841 | |||
| 1842 | return false; |
||
| 1843 | } |
||
| 1844 | |||
| 1845 | $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3); |
||
| 1846 | |||
| 1847 | $em = "\0\1$ps\0$t"; |
||
| 1848 | |||
| 1849 | return $em; |
||
| 1850 | } |
||
| 1851 | |||
| 1852 | /** |
||
| 1853 | * RSASSA-PKCS1-V1_5-SIGN. |
||
| 1854 | * |
||
| 1855 | * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}. |
||
| 1856 | * |
||
| 1857 | * @param string $m |
||
| 1858 | * |
||
| 1859 | * @return string |
||
| 1860 | */ |
||
| 1861 | private function _rsassa_pkcs1_v1_5_sign($m) |
||
| 1862 | { |
||
| 1863 | // EMSA-PKCS1-v1_5 encoding |
||
| 1864 | |||
| 1865 | $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); |
||
| 1866 | if ($em === false) { |
||
| 1867 | user_error('RSA modulus too short'); |
||
| 1868 | |||
| 1869 | return false; |
||
| 1870 | } |
||
| 1871 | |||
| 1872 | // RSA signature |
||
| 1873 | |||
| 1874 | $m = $this->_os2ip($em); |
||
| 1875 | $s = $this->_rsasp1($m); |
||
| 1876 | $s = $this->_i2osp($s, $this->k); |
||
| 1877 | |||
| 1878 | // Output the signature S |
||
| 1879 | |||
| 1880 | return $s; |
||
| 1881 | } |
||
| 1882 | |||
| 1883 | /** |
||
| 1884 | * RSASSA-PKCS1-V1_5-VERIFY. |
||
| 1885 | * |
||
| 1886 | * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}. |
||
| 1887 | * |
||
| 1888 | * @param string $m |
||
| 1889 | * |
||
| 1890 | * @return string |
||
| 1891 | */ |
||
| 1892 | private function _rsassa_pkcs1_v1_5_verify($m, $s) |
||
| 1893 | { |
||
| 1894 | // Length checking |
||
| 1895 | |||
| 1896 | if (strlen($s) != $this->k) { |
||
| 1897 | user_error('Invalid signature'); |
||
| 1898 | |||
| 1899 | return false; |
||
| 1900 | } |
||
| 1901 | |||
| 1902 | // RSA verification |
||
| 1903 | |||
| 1904 | $s = $this->_os2ip($s); |
||
| 1905 | $m2 = $this->_rsavp1($s); |
||
| 1906 | if ($m2 === false) { |
||
| 1907 | user_error('Invalid signature'); |
||
| 1908 | |||
| 1909 | return false; |
||
| 1910 | } |
||
| 1911 | $em = $this->_i2osp($m2, $this->k); |
||
| 1912 | if ($em === false) { |
||
| 1913 | user_error('Invalid signature'); |
||
| 1914 | |||
| 1915 | return false; |
||
| 1916 | } |
||
| 1917 | |||
| 1918 | // EMSA-PKCS1-v1_5 encoding |
||
| 1919 | |||
| 1920 | $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); |
||
| 1921 | if ($em2 === false) { |
||
| 1922 | user_error('RSA modulus too short'); |
||
| 1923 | |||
| 1924 | return false; |
||
| 1925 | } |
||
| 1926 | |||
| 1927 | // Compare |
||
| 1928 | return $this->_equals($em, $em2); |
||
| 1929 | } |
||
| 1930 | |||
| 1931 | /** |
||
| 1932 | * Set Encryption Mode. |
||
| 1933 | * |
||
| 1934 | * Valid values include self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1. |
||
| 1935 | * |
||
| 1936 | * @param int $mode |
||
| 1937 | */ |
||
| 1938 | public function setEncryptionMode($mode) |
||
| 1939 | { |
||
| 1940 | $this->encryptionMode = $mode; |
||
| 1941 | } |
||
| 1942 | |||
| 1943 | /** |
||
| 1944 | * Set Signature Mode. |
||
| 1945 | * |
||
| 1946 | * Valid values include self::SIGNATURE_PSS and self::SIGNATURE_PKCS1 |
||
| 1947 | * |
||
| 1948 | * @param int $mode |
||
| 1949 | */ |
||
| 1950 | public function setSignatureMode($mode) |
||
| 1951 | { |
||
| 1952 | $this->signatureMode = $mode; |
||
| 1953 | } |
||
| 1954 | |||
| 1955 | /** |
||
| 1956 | * Encryption. |
||
| 1957 | * |
||
| 1958 | * Both self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1 both place limits on how long $plaintext can be. |
||
| 1959 | * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will |
||
| 1960 | * be concatenated together. |
||
| 1961 | * |
||
| 1962 | * @see self::decrypt() |
||
| 1963 | * |
||
| 1964 | * @param string $plaintext |
||
| 1965 | * |
||
| 1966 | * @return string |
||
| 1967 | */ |
||
| 1968 | public function encrypt($plaintext) |
||
| 1969 | { |
||
| 1970 | switch ($this->encryptionMode) { |
||
| 1971 | case self::ENCRYPTION_PKCS1: |
||
| 1972 | $length = $this->k - 11; |
||
| 1973 | if ($length <= 0) { |
||
| 1974 | return false; |
||
| 1975 | } |
||
| 1976 | |||
| 1977 | $plaintext = str_split($plaintext, $length); |
||
| 1978 | $ciphertext = ''; |
||
| 1979 | foreach ($plaintext as $m) { |
||
| 1980 | $ciphertext .= $this->_rsaes_pkcs1_v1_5_encrypt($m); |
||
| 1981 | } |
||
| 1982 | |||
| 1983 | return $ciphertext; |
||
| 1984 | case self::ENCRYPTION_OAEP: |
||
| 1985 | default: |
||
| 1986 | $length = $this->k - 2 * $this->hLen - 2; |
||
| 1987 | if ($length <= 0) { |
||
| 1988 | return false; |
||
| 1989 | } |
||
| 1990 | |||
| 1991 | $plaintext = str_split($plaintext, $length); |
||
| 1992 | $ciphertext = ''; |
||
| 1993 | foreach ($plaintext as $m) { |
||
| 1994 | $ciphertext .= $this->_rsaes_oaep_encrypt($m); |
||
| 1995 | } |
||
| 1996 | |||
| 1997 | return $ciphertext; |
||
| 1998 | } |
||
| 1999 | } |
||
| 2000 | |||
| 2001 | /** |
||
| 2002 | * Decryption. |
||
| 2003 | * |
||
| 2004 | * @see self::encrypt() |
||
| 2005 | * |
||
| 2006 | * @param string $plaintext |
||
| 2007 | * |
||
| 2008 | * @return string |
||
| 2009 | */ |
||
| 2010 | public function decrypt($ciphertext) |
||
| 2011 | { |
||
| 2012 | if ($this->k <= 0) { |
||
| 2013 | return false; |
||
| 2014 | } |
||
| 2015 | |||
| 2016 | $ciphertext = str_split($ciphertext, $this->k); |
||
| 2017 | $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT); |
||
| 2018 | |||
| 2019 | $plaintext = ''; |
||
| 2020 | |||
| 2021 | switch ($this->encryptionMode) { |
||
| 2022 | case self::ENCRYPTION_PKCS1: |
||
| 2023 | $decrypt = '_rsaes_pkcs1_v1_5_decrypt'; |
||
| 2024 | break; |
||
| 2025 | case self::ENCRYPTION_OAEP: |
||
| 2026 | default: |
||
| 2027 | $decrypt = '_rsaes_oaep_decrypt'; |
||
| 2028 | } |
||
| 2029 | |||
| 2030 | foreach ($ciphertext as $c) { |
||
| 2031 | $temp = $this->$decrypt($c); |
||
| 2032 | if ($temp === false) { |
||
| 2033 | return false; |
||
| 2034 | } |
||
| 2035 | $plaintext .= $temp; |
||
| 2036 | } |
||
| 2037 | |||
| 2038 | return $plaintext; |
||
| 2039 | } |
||
| 2040 | |||
| 2041 | /** |
||
| 2042 | * Create a signature. |
||
| 2043 | * |
||
| 2044 | * @see self::verify() |
||
| 2045 | * |
||
| 2046 | * @param string $message |
||
| 2047 | * |
||
| 2048 | * @return string |
||
| 2049 | */ |
||
| 2050 | public function sign($message) |
||
| 2051 | { |
||
| 2052 | if (empty($this->modulus) || empty($this->exponent)) { |
||
| 2053 | return false; |
||
| 2054 | } |
||
| 2055 | |||
| 2056 | switch ($this->signatureMode) { |
||
| 2057 | case self::SIGNATURE_PKCS1: |
||
| 2058 | return $this->_rsassa_pkcs1_v1_5_sign($message); |
||
| 2059 | case self::SIGNATURE_PSS: |
||
| 2060 | default: |
||
| 2061 | return $this->_rsassa_pss_sign($message); |
||
| 2062 | } |
||
| 2063 | } |
||
| 2064 | |||
| 2065 | /** |
||
| 2066 | * Verifies a signature. |
||
| 2067 | * |
||
| 2068 | * @see self::sign() |
||
| 2069 | * |
||
| 2070 | * @param string $message |
||
| 2071 | * @param string $signature |
||
| 2072 | * |
||
| 2073 | * @return bool |
||
| 2074 | */ |
||
| 2075 | public function verify($message, $signature) |
||
| 2076 | { |
||
| 2077 | if (empty($this->modulus) || empty($this->exponent)) { |
||
| 2078 | return false; |
||
| 2079 | } |
||
| 2080 | |||
| 2081 | switch ($this->signatureMode) { |
||
| 2082 | case self::SIGNATURE_PKCS1: |
||
| 2083 | return $this->_rsassa_pkcs1_v1_5_verify($message, $signature); |
||
| 2084 | case self::SIGNATURE_PSS: |
||
| 2085 | default: |
||
| 2086 | return $this->_rsassa_pss_verify($message, $signature); |
||
| 2087 | } |
||
| 2088 | } |
||
| 2089 | |||
| 2090 | /** |
||
| 2091 | * Extract raw BER from Base64 encoding. |
||
| 2092 | * |
||
| 2093 | * @param string $str |
||
| 2094 | * |
||
| 2095 | * @return string |
||
| 2096 | */ |
||
| 2097 | private function _extractBER($str) |
||
| 2098 | { |
||
| 2099 | /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them |
||
| 2100 | * above and beyond the ceritificate. |
||
| 2101 | * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line: |
||
| 2102 | * |
||
| 2103 | * Bag Attributes |
||
| 2104 | * localKeyID: 01 00 00 00 |
||
| 2105 | * subject=/O=organization/OU=org unit/CN=common name |
||
| 2106 | * issuer=/O=organization/CN=common name |
||
| 2107 | */ |
||
| 2108 | $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1); |
||
| 2109 | // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff |
||
| 2110 | $temp = preg_replace('#-+[^-]+-+#', '', $temp); |
||
| 2111 | // remove new lines |
||
| 2112 | $temp = str_replace(["\r", "\n", ' '], '', $temp); |
||
| 2113 | $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; |
||
| 2114 | |||
| 2115 | return $temp != false ? $temp : $str; |
||
| 2116 | } |
||
| 2117 | |||
| 2118 | /** |
||
| 2119 | * Defines the public key. |
||
| 2120 | * |
||
| 2121 | * Some private key formats define the public exponent and some don't. Those that don't define it are problematic when |
||
| 2122 | * used in certain contexts. For example, in SSH-2, RSA authentication works by sending the public key along with a |
||
| 2123 | * message signed by the private key to the server. The SSH-2 server looks the public key up in an index of public keys |
||
| 2124 | * and if it's present then proceeds to verify the signature. Problem is, if your private key doesn't include the public |
||
| 2125 | * exponent this won't work unless you manually add the public exponent. phpseclib tries to guess if the key being used |
||
| 2126 | * is the public key but in the event that it guesses incorrectly you might still want to explicitly set the key as being |
||
| 2127 | * public. |
||
| 2128 | * |
||
| 2129 | * Do note that when a new key is loaded the index will be cleared. |
||
| 2130 | * |
||
| 2131 | * Returns true on success, false on failure |
||
| 2132 | * |
||
| 2133 | * @see self::getPublicKey() |
||
| 2134 | * |
||
| 2135 | * @param string $key optional |
||
| 2136 | * @param int $type optional |
||
| 2137 | * |
||
| 2138 | * @return bool |
||
| 2139 | */ |
||
| 2140 | private function setPublicKey($key = false, $type = false) |
||
| 2141 | { |
||
| 2142 | // if a public key has already been loaded return false |
||
| 2143 | if (!empty($this->publicExponent)) { |
||
| 2144 | return false; |
||
| 2145 | } |
||
| 2146 | |||
| 2147 | if ($key === false && !empty($this->modulus)) { |
||
| 2148 | $this->publicExponent = $this->exponent; |
||
| 2149 | |||
| 2150 | return true; |
||
| 2151 | } |
||
| 2152 | |||
| 2153 | if ($type === false) { |
||
| 2154 | $types = [ |
||
| 2155 | self::PUBLIC_FORMAT_RAW, |
||
| 2156 | self::PUBLIC_FORMAT_PKCS1, |
||
| 2157 | self::PUBLIC_FORMAT_XML, |
||
| 2158 | self::PUBLIC_FORMAT_OPENSSH, |
||
| 2159 | ]; |
||
| 2160 | foreach ($types as $type) { |
||
| 2161 | $components = $this->_parseKey($key, $type); |
||
| 2162 | if ($components !== false) { |
||
| 2163 | break; |
||
| 2164 | } |
||
| 2165 | } |
||
| 2166 | } else { |
||
| 2167 | $components = $this->_parseKey($key, $type); |
||
| 2168 | } |
||
| 2169 | |||
| 2170 | if ($components === false) { |
||
| 2171 | return false; |
||
| 2172 | } |
||
| 2173 | |||
| 2174 | if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) { |
||
| 2175 | $this->modulus = $components['modulus']; |
||
| 2176 | $this->exponent = $this->publicExponent = $components['publicExponent']; |
||
| 2177 | |||
| 2178 | return true; |
||
| 2179 | } |
||
| 2180 | |||
| 2181 | $this->publicExponent = $components['publicExponent']; |
||
| 2182 | |||
| 2183 | return true; |
||
| 2184 | } |
||
| 2185 | } |
||
| 2186 |
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..