diff --git a/.travis.yml b/.travis.yml index 6f7b22c48..b6a5d303b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,13 +22,15 @@ matrix: dist: xenial - php: 7.4 dist: xenial + - php: 8.0 + dist: bionic before_install: true install: - wget http://ftp.gnu.org/gnu/parallel/parallel-20170822.tar.bz2 - tar -xvjf parallel* - - cd parallel* + - cd parallel-20170822 - ./configure - make - sudo make install diff --git a/CHANGELOG.md b/CHANGELOG.md index f38529e79..25e253114 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,34 @@ # Changelog +## 2.0.31 - 2021-04-06 + +- X509: always parse the first cert of a bundle (#1568) +- SSH2: behave like putty with broken publickey auth (#1572) +- SSH2: don't close channel on unexpected response to channel request (#1631) +- RSA: support keys with PSS algorithm identifier (#1584) +- RSA: cleanup RSA PKCS#1 v1.5 signature verification (CVE-2021-30130) +- SFTP/Stream: make it so you can write past the end of a file (#1618) +- SFTP: fix undefined index notice in stream touch() (#1615) +- SFTP: digit only filenames were converted to integers by php (#1623) +- BigInteger: fix issue with toBits on 32-bit PHP 8 installs +- Crypt: use a custom error handler for mcrypt to avoid deprecation errors + +## 2.0.30 - 2020-12-16 + +- X509: don't attempt to parse multi-cert PEMs (#1542) +- SFTP: add stream to get method (#1546) +- SFTP: progress callback should report actual downloaded bytes (#1543) +- SSH2: end connection faster for algorithm mismatch +- SSH2: add setKeepAlive() method (#1529) +- ANSI: fix PHP8 compatibility issues + +## 2.0.29 - 2020-09-07 + +- SFTP: add enableDatePreservation() / disableDatePreservation() (#1496) +- SFTP: uploads on low speed networks could get in infinite loop (#1507) +- SSH2: when building algo list look at if crypto engine is set (#1500) +- X509: really looong base64 encoded strings broke extractBER() (#1486) + ## 2.0.28 - 2020-07-08 - SFTP: realpath('') produced an error (#1474) diff --git a/README.md b/README.md index 56260b0bc..099486dc9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # phpseclib - PHP Secure Communications Library -[![Build Status](https://travis-ci.org/phpseclib/phpseclib.svg?branch=2.0)](https://travis-ci.org/phpseclib/phpseclib) +[![Build Status](https://travis-ci.com/phpseclib/phpseclib.svg?branch=2.0)](https://travis-ci.com/phpseclib/phpseclib) ## Supporting phpseclib @@ -10,16 +10,16 @@ ## Introduction -MIT-licensed pure-PHP implementations of an arbitrary-precision integer -arithmetic library, fully PKCS#1 (v2.1) compliant RSA, DES, 3DES, RC4, Rijndael, -AES, Blowfish, Twofish, SSH-1, SSH-2, SFTP, and X.509 +MIT-licensed pure-PHP implementations of the following: + +SSH-2, SFTP, X.509, an arbitrary-precision integer arithmetic library, Ed25519 / Ed449 / Curve25519 / Curve449, ECDSA / ECDH (with support for 66 curves), RSA (PKCS#1 v2.2 compliant), DSA / DH, DES / 3DES / RC4 / Rijndael / AES / Blowfish / Twofish / Salsa20 / ChaCha20, GCM / Poly1305 * [Browse Git](https://github.com/phpseclib/phpseclib) ## Documentation -* [Documentation / Manual](http://phpseclib.sourceforge.net/) -* [API Documentation](https://api.phpseclib.org/2.0/) (generated by Sami) +* [Documentation / Manual](https://phpseclib.com/) +* [API Documentation](https://api.phpseclib.com/2.0/) (generated by Doctum) ## Branches @@ -29,6 +29,14 @@ AES, Blowfish, Twofish, SSH-1, SSH-2, SFTP, and X.509 * Unstable API * Do not use in production +### 3.0 + +* Long term support (LTS) release +* Major expansion of cryptographic primitives +* Minimum PHP version: 5.6.1 +* PSR-4 autoloading with namespace rooted at `\phpseclib3` +* Install via Composer: `composer require phpseclib/phpseclib:~3.0` + ### 2.0 * Long term support (LTS) release diff --git a/build/build.xml b/build/build.xml index 92d3923f4..26ab24b8a 100644 --- a/build/build.xml +++ b/build/build.xml @@ -4,7 +4,7 @@ default="all" > - + @@ -22,11 +22,4 @@ tests/" dir=".." checkreturn="true" passthru="true" /> - - - - - - diff --git a/build/code-sniffer-ruleset-tests.xml b/build/code-sniffer-ruleset-tests.xml index 7169012eb..8767eee94 100644 --- a/build/code-sniffer-ruleset-tests.xml +++ b/build/code-sniffer-ruleset-tests.xml @@ -11,6 +11,7 @@ using underscore. --> + diff --git a/build/sami.conf.php b/build/sami.conf.php deleted file mode 100644 index 9fe0286c4..000000000 --- a/build/sami.conf.php +++ /dev/null @@ -1,32 +0,0 @@ -classes[$name]); - } -} - -$iterator = Symfony\Component\Finder\Finder::create() - ->files() - ->name('*.php') - ->in(__DIR__ . '/../phpseclib/') -; - -$versions = Sami\Version\GitVersionCollection::create(__DIR__ . '/../') - ->add('1.0') - ->add('2.0') - ->add('master') -; - -return new Sami\Sami($iterator, array( - 'theme' => 'enhanced', - 'versions' => $versions, - 'title' => 'phpseclib API Documentation', - 'build_dir' => __DIR__.'/api/output/%version%', - 'cache_dir' => __DIR__.'/api/cache/%version%', - 'default_opened_level' => 2, - 'store' => new MyArrayStore, -)); diff --git a/composer.json b/composer.json index b4e8a1c9c..08b9c7c91 100644 --- a/composer.json +++ b/composer.json @@ -55,8 +55,7 @@ }, "require-dev": { "phing/phing": "~2.7", - "phpunit/phpunit": "^4.8.35|^5.7|^6.0", - "sami/sami": "~2.0", + "phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4", "squizlabs/php_codesniffer": "~2.0" }, "suggest": { diff --git a/phpseclib/Crypt/Base.php b/phpseclib/Crypt/Base.php index 224eaa521..8822b9b88 100644 --- a/phpseclib/Crypt/Base.php +++ b/phpseclib/Crypt/Base.php @@ -779,12 +779,14 @@ function encrypt($plaintext) } if ($this->engine === self::ENGINE_MCRYPT) { + set_error_handler(array($this, 'do_nothing')); + if ($this->changed) { $this->_setupMcrypt(); $this->changed = false; } if ($this->enchanged) { - @mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); + mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); $this->enchanged = false; } @@ -817,15 +819,15 @@ function encrypt($plaintext) if ($len >= $block_size) { if ($this->enbuffer['enmcrypt_init'] === false || $len > $this->cfb_init_len) { if ($this->enbuffer['enmcrypt_init'] === true) { - @mcrypt_generic_init($this->enmcrypt, $this->key, $iv); + mcrypt_generic_init($this->enmcrypt, $this->key, $iv); $this->enbuffer['enmcrypt_init'] = false; } - $ciphertext.= @mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % $block_size)); + $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % $block_size)); $iv = substr($ciphertext, -$block_size); $len%= $block_size; } else { while ($len >= $block_size) { - $iv = @mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, $block_size); + $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, $block_size); $ciphertext.= $iv; $len-= $block_size; $i+= $block_size; @@ -834,22 +836,26 @@ function encrypt($plaintext) } if ($len) { - $iv = @mcrypt_generic($this->ecb, $iv); + $iv = mcrypt_generic($this->ecb, $iv); $block = $iv ^ substr($plaintext, -$len); $iv = substr_replace($iv, $block, 0, $len); $ciphertext.= $block; $pos = $len; } + restore_error_handler(); + return $ciphertext; } - $ciphertext = @mcrypt_generic($this->enmcrypt, $plaintext); + $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); if (!$this->continuousBuffer) { - @mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); + mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); } + restore_error_handler(); + return $ciphertext; } @@ -1118,13 +1124,14 @@ function decrypt($ciphertext) } if ($this->engine === self::ENGINE_MCRYPT) { + set_error_handler(array($this, 'do_nothing')); $block_size = $this->block_size; if ($this->changed) { $this->_setupMcrypt(); $this->changed = false; } if ($this->dechanged) { - @mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); + mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); $this->dechanged = false; } @@ -1152,26 +1159,30 @@ function decrypt($ciphertext) } if ($len >= $block_size) { $cb = substr($ciphertext, $i, $len - $len % $block_size); - $plaintext.= @mcrypt_generic($this->ecb, $iv . $cb) ^ $cb; + $plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb; $iv = substr($cb, -$block_size); $len%= $block_size; } if ($len) { - $iv = @mcrypt_generic($this->ecb, $iv); + $iv = mcrypt_generic($this->ecb, $iv); $plaintext.= $iv ^ substr($ciphertext, -$len); $iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len); $pos = $len; } + restore_error_handler(); + return $plaintext; } - $plaintext = @mdecrypt_generic($this->demcrypt, $ciphertext); + $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); if (!$this->continuousBuffer) { - @mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); + mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); } + restore_error_handler(); + return $this->paddable ? $this->_unpad($plaintext) : $plaintext; } @@ -1649,9 +1660,12 @@ function isValidEngine($engine) } return false; case self::ENGINE_MCRYPT: - return $this->cipher_name_mcrypt && + set_error_handler(array($this, 'do_nothing')); + $result = $this->cipher_name_mcrypt && extension_loaded('mcrypt') && - in_array($this->cipher_name_mcrypt, @mcrypt_list_algorithms()); + in_array($this->cipher_name_mcrypt, mcrypt_list_algorithms()); + restore_error_handler(); + return $result; case self::ENGINE_INTERNAL: return true; } @@ -1728,17 +1742,19 @@ function _setEngine() } if ($this->engine != self::ENGINE_MCRYPT && $this->enmcrypt) { + set_error_handler(array($this, 'do_nothing')); // Closing the current mcrypt resource(s). _mcryptSetup() will, if needed, // (re)open them with the module named in $this->cipher_name_mcrypt - @mcrypt_module_close($this->enmcrypt); - @mcrypt_module_close($this->demcrypt); + mcrypt_module_close($this->enmcrypt); + mcrypt_module_close($this->demcrypt); $this->enmcrypt = null; $this->demcrypt = null; if ($this->ecb) { - @mcrypt_module_close($this->ecb); + mcrypt_module_close($this->ecb); $this->ecb = null; } + restore_error_handler(); } $this->changed = true; @@ -1851,19 +1867,19 @@ function _setupMcrypt() self::MODE_STREAM => MCRYPT_MODE_STREAM, ); - $this->demcrypt = @mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); - $this->enmcrypt = @mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); + $this->demcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); + $this->enmcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); // we need the $ecb mcrypt resource (only) in MODE_CFB with enableContinuousBuffer() // to workaround mcrypt's broken ncfb implementation in buffered mode // see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps} if ($this->mode == self::MODE_CFB) { - $this->ecb = @mcrypt_module_open($this->cipher_name_mcrypt, '', MCRYPT_MODE_ECB, ''); + $this->ecb = mcrypt_module_open($this->cipher_name_mcrypt, '', MCRYPT_MODE_ECB, ''); } } // else should mcrypt_generic_deinit be called? if ($this->mode == self::MODE_CFB) { - @mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size)); + mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size)); } } @@ -2627,7 +2643,7 @@ function &_getLambdaFunctions() * * @see self::_setupInlineCrypt() * @access private - * @param $bytes + * @param string $bytes * @return string */ function _hashInlineCryptFunction($bytes) @@ -2696,4 +2712,13 @@ function safe_intval_inline() return $safeint . '((fmod(floor($temp / 0x80000000), 2) & 1) << 31))'; } } + + /** + * Dummy error handler to suppress mcrypt errors + * + * @access private + */ + function do_nothing() + { + } } diff --git a/phpseclib/Crypt/Hash.php b/phpseclib/Crypt/Hash.php index a61668209..248b65ef7 100644 --- a/phpseclib/Crypt/Hash.php +++ b/phpseclib/Crypt/Hash.php @@ -849,7 +849,6 @@ function _not($int) * _sha256() adds multiple unsigned 32-bit integers. Since PHP doesn't support unsigned integers and since the * possibility of overflow exists, care has to be taken. BigInteger could be used but this should be faster. * - * @param int $... * @return int * @see self::_sha256() * @access private diff --git a/phpseclib/Crypt/RSA.php b/phpseclib/Crypt/RSA.php index 72be6eeb1..811d039d3 100644 --- a/phpseclib/Crypt/RSA.php +++ b/phpseclib/Crypt/RSA.php @@ -537,7 +537,7 @@ function __construct() * @access public * @param int $bits * @param int $timeout - * @param array $p + * @param array $partial */ function createKey($bits = 1024, $timeout = false, $partial = array()) { @@ -716,7 +716,12 @@ function createKey($bits = 1024, $timeout = false, $partial = array()) * * @access private * @see self::setPrivateKeyFormat() - * @param string $RSAPrivateKey + * @param Math_BigInteger $n + * @param Math_BigInteger $e + * @param Math_BigInteger $d + * @param array $primes + * @param array $exponents + * @param array $coefficients * @return string */ function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients) @@ -997,8 +1002,9 @@ function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients) * * @access private * @see self::setPublicKeyFormat() - * @param string $RSAPrivateKey - * @return string + * @param Math_BigInteger $n + * @param Math_BigInteger $e + * @return string|array */ function _convertPublicKey($n, $e) { @@ -1213,6 +1219,7 @@ function. As is, the definitive authority on this encoding scheme isn't the IET $length = $this->_decodeLength($temp); switch ($this->_string_shift($temp, $length)) { case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption + case "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0A": // rsaPSS break; case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC /* @@ -1539,6 +1546,8 @@ function. As is, the definitive authority on this encoding scheme isn't the IET return $components; } + + return false; } /** @@ -1878,7 +1887,6 @@ function setPrivateKey($key = false, $type = false) * * @see self::getPublicKey() * @access public - * @param string $key * @param int $type optional */ function getPublicKey($type = self::PUBLIC_FORMAT_PKCS8) @@ -1936,7 +1944,6 @@ function getPublicKeyFingerprint($algorithm = 'md5') * * @see self::getPublicKey() * @access public - * @param string $key * @param int $type optional * @return mixed */ @@ -1961,8 +1968,7 @@ function getPrivateKey($type = self::PUBLIC_FORMAT_PKCS1) * * @see self::getPrivateKey() * @access private - * @param string $key - * @param int $type optional + * @param int $mode optional */ function _getPrivatePublicKey($mode = self::PUBLIC_FORMAT_PKCS8) { @@ -2179,7 +2185,7 @@ function setMGFHash($hash) * of the hash function Hash) and 0. * * @access public - * @param int $format + * @param int $sLen */ function setSaltLength($sLen) { @@ -2212,7 +2218,7 @@ function _i2osp($x, $xLen) * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}. * * @access private - * @param string $x + * @param int|string|resource $x * @return \phpseclib\Math\BigInteger */ function _os2ip($x) @@ -2439,7 +2445,7 @@ function _rsavp1($s) * * @access private * @param string $mgfSeed - * @param int $mgfLen + * @param int $maskLen * @return string */ function _mgf1($mgfSeed, $maskLen) @@ -2911,6 +2917,59 @@ function _emsa_pkcs1_v1_5_encode($m, $emLen) return $em; } + /** + * EMSA-PKCS1-V1_5-ENCODE (without NULL) + * + * Quoting https://tools.ietf.org/html/rfc8017#page-65, + * + * "The parameters field associated with id-sha1, id-sha224, id-sha256, + * id-sha384, id-sha512, id-sha512/224, and id-sha512/256 should + * generally be omitted, but if present, it shall have a value of type + * NULL" + * + * @access private + * @param string $m + * @param int $emLen + * @return string + */ + function _emsa_pkcs1_v1_5_encode_without_null($m, $emLen) + { + $h = $this->hash->hash($m); + if ($h === false) { + return false; + } + + switch ($this->hashName) { + case 'sha1': + $t = pack('H*', '301f300706052b0e03021a0414'); + break; + case 'sha256': + $t = pack('H*', '302f300b06096086480165030402010420'); + break; + case 'sha384': + $t = pack('H*', '303f300b06096086480165030402020430'); + break; + case 'sha512': + $t = pack('H*', '304f300b06096086480165030402030440'); + break; + default: + return false; + } + $t.= $h; + $tLen = strlen($t); + + if ($emLen < $tLen + 11) { + user_error('Intended encoded message length too short'); + return false; + } + + $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3); + + $em = "\0\1$ps\0$t"; + + return $em; + } + /** * RSASSA-PKCS1-V1_5-SIGN * @@ -2948,6 +3007,7 @@ function _rsassa_pkcs1_v1_5_sign($m) * * @access private * @param string $m + * @param string $s * @return string */ function _rsassa_pkcs1_v1_5_verify($m, $s) @@ -2976,13 +3036,17 @@ function _rsassa_pkcs1_v1_5_verify($m, $s) // EMSA-PKCS1-v1_5 encoding $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); - if ($em2 === false) { + $em3 = $this->_emsa_pkcs1_v1_5_encode_without_null($m, $this->k); + + if ($em2 === false && $em3 === false) { user_error('RSA modulus too short'); return false; } // Compare - return $this->_equals($em, $em2); + + return ($em2 !== false && $this->_equals($em, $em2)) || + ($em3 !== false && $this->_equals($em, $em3)); } /** @@ -3088,7 +3152,7 @@ function encrypt($plaintext) * * @see self::encrypt() * @access public - * @param string $plaintext + * @param string $ciphertext * @return string */ function decrypt($ciphertext) diff --git a/phpseclib/File/ANSI.php b/phpseclib/File/ANSI.php index 334d10faf..b6874d357 100644 --- a/phpseclib/File/ANSI.php +++ b/phpseclib/File/ANSI.php @@ -203,8 +203,7 @@ function setDimensions($x, $y) /** * Set the number of lines that should be logged past the terminal height * - * @param int $x - * @param int $y + * @param int $history * @access public */ function setHistory($history) @@ -316,19 +315,20 @@ function appendString($source) $mods = explode(';', $match[1]); foreach ($mods as $mod) { switch ($mod) { - case 0: // Turn off character attributes + case '': + case '0': // Turn off character attributes $attr_cell = clone $this->base_attr_cell; break; - case 1: // Turn bold mode on + case '1': // Turn bold mode on $attr_cell->bold = true; break; - case 4: // Turn underline mode on + case '4': // Turn underline mode on $attr_cell->underline = true; break; - case 5: // Turn blinking mode on + case '5': // Turn blinking mode on $attr_cell->blink = true; break; - case 7: // Turn reverse video on + case '7': // Turn reverse video on $attr_cell->reverse = !$attr_cell->reverse; $temp = $attr_cell->background; $attr_cell->background = $attr_cell->foreground; @@ -341,23 +341,23 @@ function appendString($source) $back = &$attr_cell->{ $attr_cell->reverse ? 'foreground' : 'background' }; switch ($mod) { // @codingStandardsIgnoreStart - case 30: $front = 'black'; break; - case 31: $front = 'red'; break; - case 32: $front = 'green'; break; - case 33: $front = 'yellow'; break; - case 34: $front = 'blue'; break; - case 35: $front = 'magenta'; break; - case 36: $front = 'cyan'; break; - case 37: $front = 'white'; break; - - case 40: $back = 'black'; break; - case 41: $back = 'red'; break; - case 42: $back = 'green'; break; - case 43: $back = 'yellow'; break; - case 44: $back = 'blue'; break; - case 45: $back = 'magenta'; break; - case 46: $back = 'cyan'; break; - case 47: $back = 'white'; break; + case '30': $front = 'black'; break; + case '31': $front = 'red'; break; + case '32': $front = 'green'; break; + case '33': $front = 'yellow'; break; + case '34': $front = 'blue'; break; + case '35': $front = 'magenta'; break; + case '36': $front = 'cyan'; break; + case '37': $front = 'white'; break; + + case '40': $back = 'black'; break; + case '41': $back = 'red'; break; + case '42': $back = 'green'; break; + case '43': $back = 'yellow'; break; + case '44': $back = 'blue'; break; + case '45': $back = 'magenta'; break; + case '46': $back = 'cyan'; break; + case '47': $back = 'white'; break; // @codingStandardsIgnoreEnd default: diff --git a/phpseclib/File/ASN1.php b/phpseclib/File/ASN1.php index a304a000a..dc5b78f64 100644 --- a/phpseclib/File/ASN1.php +++ b/phpseclib/File/ASN1.php @@ -235,7 +235,7 @@ function _decode_ber($encoded, $start = 0, $encoded_pos = 0) $current = array('start' => $start); $type = ord($encoded[$encoded_pos++]); - $start++; + $startOffset = 1; $constructed = ($type >> 5) & 1; @@ -245,13 +245,20 @@ function _decode_ber($encoded, $start = 0, $encoded_pos = 0) // process septets (since the eighth bit is ignored, it's not an octet) do { $temp = ord($encoded[$encoded_pos++]); + $startOffset++; $loop = $temp >> 7; $tag <<= 7; - $tag |= $temp & 0x7F; - $start++; + $temp &= 0x7F; + // "bits 7 to 1 of the first subsequent octet shall not all be zero" + if ($startOffset == 2 && $temp == 0) { + return false; + } + $tag |= $temp; } while ($loop); } + $start+= $startOffset; + // Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13 $length = ord($encoded[$encoded_pos++]); $start++; @@ -344,13 +351,16 @@ function _decode_ber($encoded, $start = 0, $encoded_pos = 0) switch ($tag) { case self::TYPE_BOOLEAN: // "The contents octets shall consist of a single octet." -- paragraph 8.2.1 - //if (strlen($content) != 1) { - // return false; - //} + if ($constructed || strlen($content) != 1) { + return false; + } $current['content'] = (bool) ord($content[$content_pos]); break; case self::TYPE_INTEGER: case self::TYPE_ENUMERATED: + if ($constructed) { + return false; + } $current['content'] = new BigInteger(substr($content, $content_pos), -256); break; case self::TYPE_REAL: // not currently supported @@ -370,15 +380,15 @@ function _decode_ber($encoded, $start = 0, $encoded_pos = 0) $last = count($temp) - 1; for ($i = 0; $i < $last; $i++) { // all subtags should be bit strings - //if ($temp[$i]['type'] != self::TYPE_BIT_STRING) { - // return false; - //} + if ($temp[$i]['type'] != self::TYPE_BIT_STRING) { + return false; + } $current['content'].= substr($temp[$i]['content'], 1); } // all subtags should be bit strings - //if ($temp[$last]['type'] != self::TYPE_BIT_STRING) { - // return false; - //} + if ($temp[$last]['type'] != self::TYPE_BIT_STRING) { + return false; + } $current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1); } break; @@ -395,9 +405,9 @@ function _decode_ber($encoded, $start = 0, $encoded_pos = 0) } $content_pos += $temp['length']; // all subtags should be octet strings - //if ($temp['type'] != self::TYPE_OCTET_STRING) { - // return false; - //} + if ($temp['type'] != self::TYPE_OCTET_STRING) { + return false; + } $current['content'].= $temp['content']; $length+= $temp['length']; } @@ -408,12 +418,15 @@ function _decode_ber($encoded, $start = 0, $encoded_pos = 0) break; case self::TYPE_NULL: // "The contents octets shall not contain any octets." -- paragraph 8.8.2 - //if (strlen($content)) { - // return false; - //} + if ($constructed || strlen($content)) { + return false; + } break; case self::TYPE_SEQUENCE: case self::TYPE_SET: + if (!$constructed) { + return false; + } $offset = 0; $current['content'] = array(); $content_len = strlen($content); @@ -434,7 +447,13 @@ function _decode_ber($encoded, $start = 0, $encoded_pos = 0) } break; case self::TYPE_OBJECT_IDENTIFIER: + if ($constructed) { + return false; + } $current['content'] = $this->_decodeOID(substr($content, $content_pos)); + if ($current['content'] === false) { + return false; + } break; /* Each character string type shall be encoded as if it had been declared: [UNIVERSAL x] IMPLICIT OCTET STRING @@ -464,12 +483,20 @@ function _decode_ber($encoded, $start = 0, $encoded_pos = 0) case self::TYPE_UTF8_STRING: // ???? case self::TYPE_BMP_STRING: + if ($constructed) { + return false; + } $current['content'] = substr($content, $content_pos); break; case self::TYPE_UTC_TIME: case self::TYPE_GENERALIZED_TIME: + if ($constructed) { + return false; + } $current['content'] = $this->_decodeTime(substr($content, $content_pos), $tag); + break; default: + return false; } $start+= $length; @@ -790,7 +817,7 @@ function asn1map($decoded, $mapping, $special = array()) * * @param string $source * @param string $mapping - * @param int $idx + * @param array $special * @return string * @access public */ @@ -806,6 +833,7 @@ function encodeDER($source, $mapping, $special = array()) * @param string $source * @param string $mapping * @param int $idx + * @param array $special * @return string * @access private */ @@ -1126,6 +1154,11 @@ function _decodeOID($content) $oid = array(); $pos = 0; $len = strlen($content); + + if (ord($content[$len - 1]) & 0x80) { + return false; + } + $n = new BigInteger(); while ($pos < $len) { $temp = ord($content[$pos++]); @@ -1161,7 +1194,7 @@ function _decodeOID($content) * Called by _encode_der() * * @access private - * @param string $content + * @param string $source * @return string */ function _encodeOID($source) diff --git a/phpseclib/File/X509.php b/phpseclib/File/X509.php index ddbc61595..7b1b1cfad 100644 --- a/phpseclib/File/X509.php +++ b/phpseclib/File/X509.php @@ -1608,7 +1608,7 @@ function saveX509($cert, $format = self::FORMAT_PEM) * Map extension values from octet string to extension-specific internal * format. * - * @param array ref $root + * @param array $root (by reference) * @param string $path * @param object $asn1 * @access private @@ -1661,7 +1661,7 @@ function _mapInExtensions(&$root, $path, $asn1) * Map extension values from extension-specific internal format to * octet string. * - * @param array ref $root + * @param array $root (by reference) * @param string $path * @param object $asn1 * @access private @@ -1727,7 +1727,7 @@ function _mapOutExtensions(&$root, $path, $asn1) * Map attribute values from ANY type to attribute-specific internal * format. * - * @param array ref $root + * @param array $root (by reference) * @param string $path * @param object $asn1 * @access private @@ -1768,7 +1768,7 @@ function _mapInAttributes(&$root, $path, $asn1) * Map attribute values from attribute-specific internal format to * ANY type. * - * @param array ref $root + * @param array $root (by reference) * @param string $path * @param object $asn1 * @access private @@ -1811,7 +1811,7 @@ function _mapOutAttributes(&$root, $path, $asn1) * Map DN values from ANY type to DN-specific internal * format. * - * @param array ref $root + * @param array $root (by reference) * @param string $path * @param object $asn1 * @access private @@ -1841,7 +1841,7 @@ function _mapInDNs(&$root, $path, $asn1) * Map DN values from DN-specific internal format to * ANY type. * - * @param array ref $root + * @param array $root (by reference) * @param string $path * @param object $asn1 * @access private @@ -3195,7 +3195,8 @@ function getPublicKey() /** * Load a Certificate Signing Request * - * @param string $csr + * @param string|array $csr + * @param int $mode * @access public * @return mixed */ @@ -3332,7 +3333,7 @@ function saveCSR($csr, $format = self::FORMAT_PEM) * * https://developer.mozilla.org/en-US/docs/HTML/Element/keygen * - * @param string $csr + * @param string|array $spkac * @access public * @return mixed */ @@ -3403,7 +3404,7 @@ function loadSPKAC($spkac) /** * Save a SPKAC CSR request * - * @param array $csr + * @param string|array $spkac * @param int $format optional * @access public * @return string @@ -3447,6 +3448,7 @@ function saveSPKAC($spkac, $format = self::FORMAT_PEM) * Load a Certificate Revocation List * * @param string $crl + * @param int $mode * @access public * @return mixed */ @@ -4043,8 +4045,7 @@ function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption') /** * X.509 certificate signing helper function. * - * @param object $key - * @param \phpseclib\File\X509 $subject + * @param \phpseclib\File\X509 $key * @param string $signatureAlgorithm * @access public * @return mixed @@ -4119,7 +4120,7 @@ function setEndDate($date) * Set Serial Number * * @param string $serial - * @param $base optional + * @param int $base optional * @access public */ function setSerialNumber($serial, $base = -256) @@ -4782,7 +4783,6 @@ function setDomain() * Set the IP Addresses's which the cert is to be valid for * * @access public - * @param string $ipAddress optional */ function setIPAddress() { @@ -5054,11 +5054,16 @@ function _extractBER($str) * subject=/O=organization/OU=org unit/CN=common name * issuer=/O=organization/CN=common name */ - $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1); - // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff - $temp = preg_replace('#-+[^-]+-+#', '', $temp); + if (strlen($str) > ini_get('pcre.backtrack_limit')) { + $temp = $str; + } else { + $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1); + $temp = preg_replace('#-+END.*[\r\n ]*.*#ms', '', $str, 1); + } // remove new lines $temp = str_replace(array("\r", "\n", ' '), '', $temp); + // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff + $temp = preg_replace('#^-+[^-]+-+|-+[^-]+-+$#', '', $temp); $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; return $temp != false ? $temp : $str; } diff --git a/phpseclib/Math/BigInteger.php b/phpseclib/Math/BigInteger.php index e7f664670..fc24b9145 100644 --- a/phpseclib/Math/BigInteger.php +++ b/phpseclib/Math/BigInteger.php @@ -243,7 +243,7 @@ class BigInteger * ?> * * - * @param $x base-10 number or base-$base number if $base set. + * @param int|string|resource $x base-10 number or base-$base number if $base set. * @param int $base * @return \phpseclib\Math\BigInteger * @access public @@ -658,11 +658,11 @@ function toBits($twos_compliment = false) { $hex = $this->toHex($twos_compliment); $bits = ''; - for ($i = strlen($hex) - 8, $start = strlen($hex) & 7; $i >= $start; $i-=8) { - $bits = str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT) . $bits; + for ($i = strlen($hex) - 6, $start = strlen($hex) % 6; $i >= $start; $i-=6) { + $bits = str_pad(decbin(hexdec(substr($hex, $i, 6))), 24, '0', STR_PAD_LEFT) . $bits; } if ($start) { // hexdec('') == 0 - $bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8, '0', STR_PAD_LEFT) . $bits; + $bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8 * $start, '0', STR_PAD_LEFT) . $bits; } $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0'); @@ -1994,7 +1994,7 @@ function _squareReduce($x, $n, $mode) * * @see self::_slidingWindow() * @access private - * @param \phpseclib\Math\BigInteger + * @param \phpseclib\Math\BigInteger $n * @return \phpseclib\Math\BigInteger */ function _mod2($n) @@ -2688,7 +2688,7 @@ function abs() * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y). * * @param \phpseclib\Math\BigInteger $y - * @return int < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. + * @return int that is < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. * @access public * @see self::equals() * @internal Could return $this->subtract($x), but that's not as fast as what we do do. @@ -3090,7 +3090,7 @@ function bitwise_rightRotate($shift) * * Byte length is equal to $length. Uses \phpseclib\Crypt\Random if it's loaded and mt_rand if it's not. * - * @param int $length + * @param int $size * @return \phpseclib\Math\BigInteger * @access private */ @@ -3557,7 +3557,7 @@ function _rshift($shift) * * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision * - * @param \phpseclib\Math\BigInteger + * @param \phpseclib\Math\BigInteger $result * @return \phpseclib\Math\BigInteger * @see self::_trim() * @access private @@ -3634,8 +3634,8 @@ function _trim($value) /** * Array Repeat * - * @param $input Array - * @param $multiplier mixed + * @param array $input + * @param mixed $multiplier * @return array * @access private */ @@ -3649,8 +3649,8 @@ function _array_repeat($input, $multiplier) * * Shifts binary strings $shift bits, essentially multiplying by 2**$shift. * - * @param $x String - * @param $shift Integer + * @param string $x (by reference) + * @param int $shift * @return string * @access private */ @@ -3678,8 +3678,8 @@ function _base256_lshift(&$x, $shift) * * Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder. * - * @param $x String - * @param $shift Integer + * @param string $x (by referenc) + * @param int $shift * @return string * @access private */ diff --git a/phpseclib/Net/SFTP.php b/phpseclib/Net/SFTP.php index 7c821377b..34741831b 100644 --- a/phpseclib/Net/SFTP.php +++ b/phpseclib/Net/SFTP.php @@ -259,6 +259,16 @@ class SFTP extends SSH2 */ var $requestBuffer = array(); + /** + * Preserve timestamps on file downloads / uploads + * + * @see self::get() + * @see self::put() + * @var bool + * @access private + */ + var $preserveTime = false; + /** * Default Constructor. * @@ -406,7 +416,6 @@ function __construct($host, $port = 22, $timeout = 10) * Login * * @param string $username - * @param string $password * @return bool * @access public */ @@ -1015,7 +1024,7 @@ function _list($dir, $raw = true) uasort($contents, array(&$this, '_comparator')); } - return $raw ? $contents : array_keys($contents); + return $raw ? $contents : array_map('strval', array_keys($contents)); } /** @@ -1217,7 +1226,7 @@ function _remove_from_stat_cache($path) * * Mainly used by file_exists * - * @param string $dir + * @param string $path * @return mixed * @access private */ @@ -1772,6 +1781,8 @@ function symlink($target, $link) * Creates a directory. * * @param string $dir + * @param int $mode + * @param bool $recursive * @return bool * @access public */ @@ -1804,6 +1815,7 @@ function mkdir($dir, $mode = -1, $recursive = false) * Helper function for directory creation * * @param string $dir + * @param int $mode * @return bool * @access private */ @@ -2075,7 +2087,14 @@ function put($remote_file, $data, $mode = self::SOURCE_STRING, $start = -1, $loc } if ($mode & self::SOURCE_LOCAL_FILE) { - fclose($fp); + if ($this->preserveTime) { + $stat = fstat($fp); + $this->touch($remote_file, $stat['mtime'], $stat['atime']); + } + + if (isset($fp) && is_resource($fp)) { + fclose($fp); + } } return $this->_close_handle($handle); @@ -2198,7 +2217,7 @@ function get($remote_file, $local_file = false, $offset = 0, $length = -1, $prog $res_offset = $stat['size']; } else { $res_offset = 0; - if ($local_file !== false) { + if ($local_file !== false && !is_callable($local_file)) { $fp = fopen($local_file, 'wb'); if (!$fp) { return false; @@ -2208,7 +2227,7 @@ function get($remote_file, $local_file = false, $offset = 0, $length = -1, $prog } } - $fclose_check = $local_file !== false && !is_resource($local_file); + $fclose_check = $local_file !== false && !is_callable($local_file) && !is_resource($local_file); $start = $offset; $read = 0; @@ -2229,9 +2248,6 @@ function get($remote_file, $local_file = false, $offset = 0, $length = -1, $prog } $packet = null; $read+= $packet_size; - if (is_callable($progressCallback)) { - call_user_func($progressCallback, $read); - } $i++; } @@ -2258,9 +2274,14 @@ function get($remote_file, $local_file = false, $offset = 0, $length = -1, $prog $offset+= strlen($temp); if ($local_file === false) { $content.= $temp; + } elseif (is_callable($local_file)) { + $local_file($temp); } else { fputs($fp, $temp); } + if (is_callable($progressCallback)) { + call_user_func($progressCallback, $offset); + } $temp = null; break; case NET_SFTP_STATUS: @@ -2292,6 +2313,11 @@ function get($remote_file, $local_file = false, $offset = 0, $length = -1, $prog if ($fclose_check) { fclose($fp); + + if ($this->preserveTime) { + $stat = $this->stat($remote_file); + touch($local_file, $stat['mtime'], $stat['atime']); + } } if (!$this->_close_handle($handle)) { @@ -2713,6 +2739,7 @@ function _get_lstat_cache_prop($path, $prop) * * @param string $path * @param string $prop + * @param mixed $type * @return mixed * @access private */ @@ -2953,6 +2980,7 @@ function _parseLongname($longname) * * @param int $type * @param string $data + * @param int $request_id * @see self::_get_sftp_packet() * @see self::_send_channel_packet() * @return bool @@ -2960,6 +2988,10 @@ function _parseLongname($longname) */ function _send_sftp_packet($type, $data, $request_id = 1) { + // in SSH2.php the timeout is cumulative per function call. eg. exec() will + // timeout after 10s. but for SFTP.php it's cumulative per packet + $this->curTimeout = $this->timeout; + $packet = $this->use_request_id ? pack('NCNa*', strlen($data) + 5, $type, $request_id, $data) : pack('NCa*', strlen($data) + 1, $type, $data); @@ -2972,9 +3004,17 @@ function _send_sftp_packet($type, $data, $request_id = 1) $packet_type = '-> ' . $this->packet_types[$type] . ' (' . round($stop - $start, 4) . 's)'; if (NET_SFTP_LOGGING == self::LOG_REALTIME) { - echo "
\r\n" . $this->_format_log(array($data), array($packet_type)) . "\r\n
\r\n"; - flush(); - ob_flush(); + switch (PHP_SAPI) { + case 'cli': + $start = $stop = "\r\n"; + break; + default: + $start = '
';
+                        $stop = '
'; + } + echo $start . $this->_format_log(array($data), array($packet_type)) . $stop; + @flush(); + @ob_flush(); } else { $this->packet_type_log[] = $packet_type; if (NET_SFTP_LOGGING == self::LOG_COMPLEX) { @@ -3081,9 +3121,17 @@ function _get_sftp_packet($request_id = null) $packet_type = '<- ' . $this->packet_types[$this->packet_type] . ' (' . round($stop - $start, 4) . 's)'; if (NET_SFTP_LOGGING == self::LOG_REALTIME) { - echo "
\r\n" . $this->_format_log(array($packet), array($packet_type)) . "\r\n
\r\n"; - flush(); - ob_flush(); + switch (PHP_SAPI) { + case 'cli': + $start = $stop = "\r\n"; + break; + default: + $start = '
';
+                        $stop = '
'; + } + echo $start . $this->_format_log(array($packet), array($packet_type)) . $stop; + @flush(); + @ob_flush(); } else { $this->packet_type_log[] = $packet_type; if (NET_SFTP_LOGGING == self::LOG_COMPLEX) { @@ -3176,4 +3224,24 @@ function _disconnect($reason) $this->pwd = false; parent::_disconnect($reason); } + + /** + * Enable Date Preservation + * + * @access public + */ + function enableDatePreservation() + { + $this->preserveTime = true; + } + + /** + * Disable Date Preservation + * + * @access public + */ + function disableDatePreservation() + { + $this->preserveTime = false; + } } diff --git a/phpseclib/Net/SFTP/Stream.php b/phpseclib/Net/SFTP/Stream.php index d2c4425de..ec9e5841a 100644 --- a/phpseclib/Net/SFTP/Stream.php +++ b/phpseclib/Net/SFTP/Stream.php @@ -410,7 +410,7 @@ function _stream_seek($offset, $whence) { switch ($whence) { case SEEK_SET: - if ($offset >= $this->size || $offset < 0) { + if ($offset < 0) { return false; } break; @@ -447,7 +447,9 @@ function _stream_metadata($path, $option, $var) // and https://github.com/php/php-src/blob/master/main/php_streams.h#L592 switch ($option) { case 1: // PHP_STREAM_META_TOUCH - return $this->sftp->touch($path, $var[0], $var[1]); + $time = isset($var[0]) ? $var[0] : null; + $atime = isset($var[1]) ? $var[1] : null; + return $this->sftp->touch($path, $time, $atime); case 2: // PHP_STREAM_OWNER_NAME case 3: // PHP_STREAM_GROUP_NAME return false; @@ -626,7 +628,6 @@ function _mkdir($path, $mode, $options) * $options. What does 8 correspond to? * * @param string $path - * @param int $mode * @param int $options * @return bool * @access public @@ -768,8 +769,8 @@ function _stream_close() * If NET_SFTP_STREAM_LOGGING is defined all calls will be output on the screen and then (regardless of whether or not * NET_SFTP_STREAM_LOGGING is enabled) the parameters will be passed through to the appropriate method. * - * @param string - * @param array + * @param string $name + * @param array $arguments * @return mixed * @access public */ diff --git a/phpseclib/Net/SSH1.php b/phpseclib/Net/SSH1.php index ff48d5436..e372b8b92 100644 --- a/phpseclib/Net/SSH1.php +++ b/phpseclib/Net/SSH1.php @@ -812,6 +812,7 @@ function setTimeout($timeout) * @see self::interactiveRead() * @see self::interactiveWrite() * @param string $cmd + * @param bool $block * @return mixed * @access public */ @@ -1385,7 +1386,6 @@ function _rsa_crypt($m, $key) * named constants from it, using the value as the name of the constant and the index as the value of the constant. * If any of the constants that would be defined already exists, none of the constants will be defined. * - * @param array $array * @access private */ function _define_array() @@ -1584,7 +1584,8 @@ function getServerIdentification() * * Makes sure that only the last 1MB worth of packets will be logged * - * @param string $data + * @param int $protocol_flags + * @param string $message * @access private */ function _append_log($protocol_flags, $message) diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index ba83c9d0e..f8f8dcfde 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -687,6 +687,14 @@ class SSH2 */ var $curTimeout; + /** + * Keep Alive Interval + * + * @see self::setKeepAlive() + * @access private + */ + var $keepAlive; + /** * Real-time log file pointer * @@ -1538,6 +1546,32 @@ function _key_exchange($kexinit_payload_server = false) return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } + $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms); + if ($server_host_key_algorithm === false) { + user_error('No compatible server host key algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $mac_algorithm_in = $this->_array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client); + if ($mac_algorithm_in === false) { + user_error('No compatible server to client message authentication algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $compression_algorithm_out = $this->_array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server); + if ($compression_algorithm_out === false) { + user_error('No compatible client to server compression algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + //$this->decompress = $compression_algorithm_out == 'zlib'; + + $compression_algorithm_in = $this->_array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_client_to_server); + if ($compression_algorithm_in === false) { + user_error('No compatible server to client compression algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + //$this->compress = $compression_algorithm_in == 'zlib'; + // Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty. $exchange_hash_rfc4419 = ''; @@ -1773,12 +1807,6 @@ function _key_exchange($kexinit_payload_server = false) $this->session_id = $this->exchange_hash; } - $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms); - if ($server_host_key_algorithm === false) { - user_error('No compatible server host key algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - } - switch ($server_host_key_algorithm) { case 'ssh-dss': $expected_key_format = 'ssh-dss'; @@ -1903,14 +1931,14 @@ function _key_exchange($kexinit_payload_server = false) $this->decrypt->decrypt(str_repeat("\0", 1536)); } - $mac_algorithm = $this->_array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server); - if ($mac_algorithm === false) { + $mac_algorithm_out = $this->_array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server); + if ($mac_algorithm_out === false) { user_error('No compatible client to server message authentication algorithms found'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } $createKeyLength = 0; // ie. $mac_algorithm == 'none' - switch ($mac_algorithm) { + switch ($mac_algorithm_out) { case 'hmac-sha2-256': $this->hmac_create = new Hash('sha256'); $createKeyLength = 32; @@ -1931,17 +1959,11 @@ function _key_exchange($kexinit_payload_server = false) $this->hmac_create = new Hash('md5-96'); $createKeyLength = 16; } - $this->hmac_create->name = $mac_algorithm; - - $mac_algorithm = $this->_array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client); - if ($mac_algorithm === false) { - user_error('No compatible server to client message authentication algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - } + $this->hmac_create->name = $mac_algorithm_out; $checkKeyLength = 0; $this->hmac_size = 0; - switch ($mac_algorithm) { + switch ($mac_algorithm_in) { case 'hmac-sha2-256': $this->hmac_check = new Hash('sha256'); $checkKeyLength = 32; @@ -1967,7 +1989,7 @@ function _key_exchange($kexinit_payload_server = false) $checkKeyLength = 16; $this->hmac_size = 12; } - $this->hmac_check->name = $mac_algorithm; + $this->hmac_check->name = $mac_algorithm_in; $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id); while ($createKeyLength > strlen($key)) { @@ -1981,20 +2003,6 @@ function _key_exchange($kexinit_payload_server = false) } $this->hmac_check->setKey(substr($key, 0, $checkKeyLength)); - $compression_algorithm = $this->_array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server); - if ($compression_algorithm === false) { - user_error('No compatible client to server compression algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - } - //$this->decompress = $compression_algorithm == 'zlib'; - - $compression_algorithm = $this->_array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_client_to_server); - if ($compression_algorithm === false) { - user_error('No compatible server to client compression algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - } - //$this->compress = $compression_algorithm == 'zlib'; - return true; } @@ -2112,8 +2120,6 @@ function _bad_algorithm_candidate($algorithm) * The $password parameter can be a plaintext password, a \phpseclib\Crypt\RSA object or an array * * @param string $username - * @param mixed $password - * @param mixed $... * @return bool * @see self::_login() * @access public @@ -2125,11 +2131,13 @@ function login($username) // try logging with 'none' as an authentication method first since that's what // PuTTY does - if ($this->_login($username)) { - return true; - } - if (count($args) == 1) { - return false; + if (substr($this->server_identifier, 0, 13) != 'SSH-2.0-CoreFTP') { + if ($this->_login($username)) { + return true; + } + if (count($args) == 1) { + return false; + } } return call_user_func_array(array(&$this, '_login'), $args); } @@ -2138,8 +2146,6 @@ function login($username) * Login Helper * * @param string $username - * @param mixed $password - * @param mixed $... * @return bool * @see self::_login_helper() * @access private @@ -2400,7 +2406,6 @@ function _keyboard_interactive_login($username, $password) /** * Handle the keyboard-interactive requests / responses. * - * @param string $responses... * @return bool * @access private */ @@ -2545,7 +2550,7 @@ function _ssh_agent_login($username, $agent) * Login with an RSA private key * * @param string $username - * @param \phpseclib\Crypt\RSA $password + * @param \phpseclib\Crypt\RSA $privatekey * @return bool * @access private * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} @@ -2629,6 +2634,13 @@ function _privatekey_login($username, $privatekey) // we'll just take it on faith that the public key blob and the public key algorithm name are as // they should be $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PK_OK'); + break; + case NET_SSH2_MSG_USERAUTH_SUCCESS: + $this->bitmap |= self::MASK_LOGIN; + return true; + default: + user_error('Unexpected response to publickey authentication pt 1'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } $packet = $part1 . chr(1) . $part2; @@ -2663,7 +2675,8 @@ function _privatekey_login($username, $privatekey) return true; } - return false; + user_error('Unexpected response to publickey authentication pt 2'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } /** @@ -2680,6 +2693,19 @@ function setTimeout($timeout) $this->timeout = $this->curTimeout = $timeout; } + /** + * Set Keep Alive + * + * Sends an SSH2_MSG_IGNORE message every x seconds, if x is a positive non-zero number. + * + * @param int $interval + * @access public + */ + function setKeepAlive($interval) + { + $this->keepAlive = $interval; + } + /** * Get the output from stdError * @@ -2909,28 +2935,6 @@ function _initShell() return false; } - $response = $this->_get_binary_packet(); - if ($response === false) { - $this->bitmap = 0; - user_error('Connection closed by server'); - return false; - } - - if (!strlen($response)) { - return false; - } - list(, $type) = unpack('C', $this->_string_shift($response, 1)); - - switch ($type) { - case NET_SSH2_MSG_CHANNEL_SUCCESS: - // if a pty can't be opened maybe commands can still be executed - case NET_SSH2_MSG_CHANNEL_FAILURE: - break; - default: - user_error('Unable to request pseudo-terminal'); - return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); - } - $packet = pack( 'CNNa*C', NET_SSH2_MSG_CHANNEL_REQUEST, @@ -2943,14 +2947,7 @@ function _initShell() return false; } - $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST; - - $response = $this->_get_channel_packet(self::CHANNEL_SHELL); - if ($response === false) { - return false; - } - - $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA; + $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_IGNORE; $this->bitmap |= self::MASK_SHELL; @@ -3318,6 +3315,54 @@ function _reset_connection($reason) */ function _get_binary_packet($skip_channel_filter = false) { + if ($skip_channel_filter) { + $read = array($this->fsock); + $write = $except = null; + + if ($this->curTimeout <= 0) { + if ($this->keepAlive <= 0) { + @stream_select($read, $write, $except, null); + } else { + if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) { + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); + return $this->_get_binary_packet(true); + } + } + } else { + if ($this->curTimeout < 0) { + $this->is_timeout = true; + return true; + } + + $read = array($this->fsock); + $write = $except = null; + + $start = microtime(true); + + if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) { + if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) { + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); + $elapsed = microtime(true) - $start; + $this->curTimeout-= $elapsed; + return $this->_get_binary_packet(true); + } + $elapsed = microtime(true) - $start; + $this->curTimeout-= $elapsed; + } + + $sec = floor($this->curTimeout); + $usec = 1000000 * ($this->curTimeout - $sec); + + // on windows this returns a "Warning: Invalid CRT parameters detected" error + if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { + $this->is_timeout = true; + return true; + } + $elapsed = microtime(true) - $start; + $this->curTimeout-= $elapsed; + } + } + if (!is_resource($this->fsock) || feof($this->fsock)) { $this->bitmap = 0; user_error('Connection closed prematurely'); @@ -3469,9 +3514,19 @@ function _filter($payload, $skip_channel_filter) // only called when we've already logged in if (($this->bitmap & self::MASK_CONNECTED) && $this->isAuthenticated()) { switch (ord($payload[0])) { + case NET_SSH2_MSG_CHANNEL_REQUEST: + if (strlen($payload) == 31) { + extract(unpack('cpacket_type/Nchannel/Nlength', $payload)); + if (substr($payload, 9, $length) == 'keepalive@openssh.com' && isset($this->server_channels[$channel])) { + if (ord(substr($payload, 9 + $length))) { // want reply + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_SUCCESS, $this->server_channels[$channel])); + } + $payload = $this->_get_binary_packet($skip_channel_filter); + } + } + break; case NET_SSH2_MSG_CHANNEL_DATA: case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: - case NET_SSH2_MSG_CHANNEL_REQUEST: case NET_SSH2_MSG_CHANNEL_CLOSE: case NET_SSH2_MSG_CHANNEL_EOF: if (!$skip_channel_filter && !empty($this->server_channels)) { @@ -3651,8 +3706,9 @@ function isPTYEnabled() * * Returns the data as a string if it's available and false if not. * - * @param $client_channel - * @return mixed + * @param int $client_channel + * @param bool $skip_extended + * @return mixed|bool * @access private */ function _get_channel_packet($client_channel, $skip_extended = false) @@ -3666,36 +3722,13 @@ function _get_channel_packet($client_channel, $skip_extended = false) $response = $this->binary_packet_buffer; $this->binary_packet_buffer = false; } else { - $read = array($this->fsock); - $write = $except = null; - - if (!$this->curTimeout) { - @stream_select($read, $write, $except, null); - } else { - if ($this->curTimeout < 0) { - $this->is_timeout = true; - return true; - } - - $read = array($this->fsock); - $write = $except = null; - - $start = microtime(true); - $sec = floor($this->curTimeout); - $usec = 1000000 * ($this->curTimeout - $sec); - // on windows this returns a "Warning: Invalid CRT parameters detected" error - if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { - $this->is_timeout = true; - if ($client_channel == self::CHANNEL_EXEC && !$this->request_pty) { - $this->_close_channel($client_channel); - } - return true; + $response = $this->_get_binary_packet(true); + if ($response === true && $this->is_timeout) { + if ($client_channel == self::CHANNEL_EXEC && !$this->request_pty) { + $this->_close_channel($client_channel); } - $elapsed = microtime(true) - $start; - $this->curTimeout-= $elapsed; + return true; } - - $response = $this->_get_binary_packet(true); if ($response === false) { $this->bitmap = 0; user_error('Connection closed by server'); @@ -3843,6 +3876,16 @@ function _get_channel_packet($client_channel, $skip_extended = false) return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } break; + case NET_SSH2_MSG_IGNORE: + switch ($type) { + case NET_SSH2_MSG_CHANNEL_SUCCESS: + //$this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_DATA; + continue 3; + case NET_SSH2_MSG_CHANNEL_FAILURE: + user_error('Error opening channel'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + break; case NET_SSH2_MSG_CHANNEL_REQUEST: switch ($type) { case NET_SSH2_MSG_CHANNEL_SUCCESS: @@ -3862,6 +3905,10 @@ function _get_channel_packet($client_channel, $skip_extended = false) switch ($type) { case NET_SSH2_MSG_CHANNEL_DATA: + //if ($this->channel_status[$channel] == NET_SSH2_MSG_IGNORE) { + // $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_DATA; + //} + /* if ($channel == self::CHANNEL_EXEC) { // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server @@ -3962,7 +4009,7 @@ function _send_binary_packet($data, $logged = null) $packet.= $hmac; $start = microtime(true); - $result = strlen($packet) == fputs($this->fsock, $packet); + $result = strlen($packet) == @fputs($this->fsock, $packet); $stop = microtime(true); if (defined('NET_SSH2_LOGGING')) { @@ -3982,7 +4029,8 @@ function _send_binary_packet($data, $logged = null) * * Makes sure that only the last 1MB worth of packets will be logged * - * @param string $data + * @param string $message_number + * @param string $message * @access private */ function _append_log($message_number, $message) @@ -4187,7 +4235,6 @@ function _string_shift(&$string, $index = 1) * named constants from it, using the value as the name of the constant and the index as the value of the constant. * If any of the constants that would be defined already exists, none of the constants will be defined. * - * @param array $array * @access private */ function _define_array() @@ -4603,11 +4650,15 @@ function getSupportedEncryptionAlgorithms() //'none' // OPTIONAL no encryption; NOT RECOMMENDED ); - $engines = array( - Base::ENGINE_OPENSSL, - Base::ENGINE_MCRYPT, - Base::ENGINE_INTERNAL - ); + if ($this->crypto_engine) { + $engines = array($this->crypto_engine); + } else { + $engines = array( + Base::ENGINE_OPENSSL, + Base::ENGINE_MCRYPT, + Base::ENGINE_INTERNAL + ); + } $ciphers = array(); foreach ($engines as $engine) { diff --git a/tests/Functional/Net/SFTPLargeFileTest.php b/tests/Functional/Net/SFTPLargeFileTest.php index cc170e8a1..69baa8ea7 100644 --- a/tests/Functional/Net/SFTPLargeFileTest.php +++ b/tests/Functional/Net/SFTPLargeFileTest.php @@ -16,6 +16,7 @@ public static function setUpBeforeClass() if (!extension_loaded('mcrypt') && !extension_loaded('openssl')) { self::markTestSkipped('This test depends on mcrypt or openssl for performance.'); } + self::ensureConstant('CRYPT_HASH_MODE', 3); parent::setUpBeforeClass(); } diff --git a/tests/Functional/Net/SFTPUserStoryTest.php b/tests/Functional/Net/SFTPUserStoryTest.php index 5767eb9fc..ceffca1f3 100644 --- a/tests/Functional/Net/SFTPUserStoryTest.php +++ b/tests/Functional/Net/SFTPUserStoryTest.php @@ -135,6 +135,16 @@ public function testStatOnDir($sftp) return $sftp; } + static function demoCallback($length) + { + $r = substr(self::$buffer, 0, $length); + self::$buffer = substr(self::$buffer, $length); + if (strlen($r)) { + return $r; + } + return null; + } + /** * @depends testStatOnDir */ @@ -160,16 +170,6 @@ public function testPutSizeGetFile($sftp) return $sftp; } - static function callback($length) - { - $r = substr(self::$buffer, 0, $length); - self::$buffer = substr(self::$buffer, $length); - if (strlen($r)) { - return $r; - } - return null; - } - /** * @depends testStatOnDir */ @@ -177,7 +177,7 @@ public function testPutSizeGetFileCallback($sftp) { self::$buffer = self::$exampleData; $this->assertTrue( - $sftp->put('file1.txt', array(__CLASS__, 'callback'), $sftp::SOURCE_CALLBACK), + $sftp->put('file1.txt', array(__CLASS__, 'demoCallback'), $sftp::SOURCE_CALLBACK), 'Failed asserting that example data could be successfully put().' ); @@ -439,8 +439,7 @@ public function testLinkFile($sftp) */ public function testReadlink($sftp) { - $this->assertInternalType( - 'string', + $this->assertIsString( $sftp->readlink('symlink'), 'Failed asserting that a symlink\'s target could be read' ); @@ -455,14 +454,12 @@ public function testReadlink($sftp) public function testStatOnCWD($sftp) { $stat = $sftp->stat('.'); - $this->assertInternalType( - 'array', + $this->assertIsArray( $stat, 'Failed asserting that stat on . returns an array' ); $lstat = $sftp->lstat('.'); - $this->assertInternalType( - 'array', + $this->assertIsArray( $lstat, 'Failed asserting that lstat on . returns an array' ); @@ -604,8 +601,7 @@ public function testDeleteEmptyDir($sftp) 'Failed asserting that scratch directory could ' . 'be created.' ); - $this->assertInternalType( - 'array', + $this->assertIsArray( $sftp->stat(self::$scratchDir), 'Failed asserting that stat on an existant empty directory returns an array' ); diff --git a/tests/Functional/Net/SSH2Test.php b/tests/Functional/Net/SSH2Test.php index 1a9217429..5356580ae 100644 --- a/tests/Functional/Net/SSH2Test.php +++ b/tests/Functional/Net/SSH2Test.php @@ -124,7 +124,7 @@ public function testGetServerPublicHostKey() { $ssh = new SSH2($this->getEnv('SSH_HOSTNAME')); - $this->assertInternalType('string', $ssh->getServerPublicHostKey()); + $this->assertIsString($ssh->getServerPublicHostKey()); } public function testOpenSocketConnect() diff --git a/tests/PhpseclibTestCase.php b/tests/PhpseclibTestCase.php index 9da0d42ec..e07533a1b 100644 --- a/tests/PhpseclibTestCase.php +++ b/tests/PhpseclibTestCase.php @@ -101,4 +101,48 @@ protected static function reRequireFile($filename) } } } + + // assertIsArray was not introduced until PHPUnit 8 + public static function assertIsArray($actual, $message = '') + { + if (method_exists('\PHPUnit\Framework\TestCase', 'assertIsArray')) { + parent::assertIsArray($actual, $message); + return; + } + + parent::assertInternalType('array', $actual, $message); + } + + // assertIsString was not introduced until PHPUnit 8 + public static function assertIsString($actual, $message = '') + { + if (method_exists('\PHPUnit\Framework\TestCase', 'assertIsString')) { + parent::assertIsString($actual, $message); + return; + } + + parent::assertInternalType('string', $actual, $message); + } + + // assertContains is deprecated for strings in PHPUnit 8 + public static function assertStringContainsString($needle, $haystack, $message = '') + { + if (method_exists('\PHPUnit\Framework\TestCase', 'assertStringContainsString')) { + parent::assertStringContainsString($needle, $haystack, $message); + return; + } + + parent::assertContains($needle, $haystack, $message); + } + + // assertNotContains is deprecated for strings in PHPUnit 8 + public static function assertStringNotContainsString($needle, $haystack, $message = '') + { + if (method_exists('\PHPUnit\Framework\TestCase', 'assertStringContainsString')) { + parent::assertStringNotContainsString($needle, $haystack, $message); + return; + } + + parent::assertNotContains($needle, $haystack, $message); + } } diff --git a/tests/Unit/Crypt/AES/InternalTest.php b/tests/Unit/Crypt/AES/PurePHPTest.php similarity index 81% rename from tests/Unit/Crypt/AES/InternalTest.php rename to tests/Unit/Crypt/AES/PurePHPTest.php index b0433e844..8692ec623 100644 --- a/tests/Unit/Crypt/AES/InternalTest.php +++ b/tests/Unit/Crypt/AES/PurePHPTest.php @@ -7,7 +7,7 @@ use phpseclib\Crypt\Base; -class Unit_Crypt_AES_InternalTest extends Unit_Crypt_AES_TestCase +class Unit_Crypt_AES_PurePHPTest extends Unit_Crypt_AES_TestCase { protected function setUp() { diff --git a/tests/Unit/Crypt/Hash/SHA256_96Test.php b/tests/Unit/Crypt/Hash/SHA256_96Test.php index 85aaf295e..24a09be48 100644 --- a/tests/Unit/Crypt/Hash/SHA256_96Test.php +++ b/tests/Unit/Crypt/Hash/SHA256_96Test.php @@ -7,6 +7,10 @@ use phpseclib\Crypt\Hash; +if (version_compare(PHP_VERSION, '7.0', '>=')) { + require 'SHA256Test.php'; +} + class Unit_Crypt_Hash_SHA256_96Test extends Unit_Crypt_Hash_SHA256Test { public function getInstance() diff --git a/tests/Unit/Crypt/Hash/SHA512_96Test.php b/tests/Unit/Crypt/Hash/SHA512_96Test.php index 760fa243a..0d48a7fb4 100644 --- a/tests/Unit/Crypt/Hash/SHA512_96Test.php +++ b/tests/Unit/Crypt/Hash/SHA512_96Test.php @@ -7,6 +7,10 @@ use phpseclib\Crypt\Hash; +if (version_compare(PHP_VERSION, '7.0', '>=')) { + require 'SHA512Test.php'; +} + class Unit_Crypt_Hash_SHA512_96Test extends Unit_Crypt_Hash_SHA512Test { public function getInstance() diff --git a/tests/Unit/Crypt/RSA/LoadKeyTest.php b/tests/Unit/Crypt/RSA/LoadKeyTest.php index 09db5f538..ee3bcc59f 100644 --- a/tests/Unit/Crypt/RSA/LoadKeyTest.php +++ b/tests/Unit/Crypt/RSA/LoadKeyTest.php @@ -6,6 +6,7 @@ */ use phpseclib\Crypt\RSA; +use phpseclib\Math\BigInteger; class Unit_Crypt_RSA_LoadKeyTest extends PhpseclibTestCase { @@ -37,7 +38,7 @@ public function testPKCS1Key() -----END RSA PRIVATE KEY-----'; $this->assertTrue($rsa->loadKey($key)); - $this->assertInternalType('string', $rsa->getPrivateKey()); + $this->assertIsString($rsa->getPrivateKey()); } public function testPKCS1SpacesKey() @@ -60,7 +61,7 @@ public function testPKCS1SpacesKey() $key = str_replace(array("\r", "\n", "\r\n"), ' ', $key); $this->assertTrue($rsa->loadKey($key)); - $this->assertInternalType('string', $rsa->getPrivateKey()); + $this->assertIsString($rsa->getPrivateKey()); } public function testPKCS1NoHeaderKey() @@ -80,7 +81,7 @@ public function testPKCS1NoHeaderKey() 37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0='; $this->assertTrue($rsa->loadKey($key)); - $this->assertInternalType('string', $rsa->getPrivateKey()); + $this->assertIsString($rsa->getPrivateKey()); } public function testPKCS1NoWhitespaceNoHeaderKey() @@ -100,7 +101,7 @@ public function testPKCS1NoWhitespaceNoHeaderKey() '37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0='; $this->assertTrue($rsa->loadKey($key)); - $this->assertInternalType('string', $rsa->getPrivateKey()); + $this->assertIsString($rsa->getPrivateKey()); } public function testRawPKCS1Key() @@ -121,7 +122,7 @@ public function testRawPKCS1Key() $key = base64_decode($key); $this->assertTrue($rsa->loadKey($key)); - $this->assertInternalType('string', $rsa->getPrivateKey()); + $this->assertIsString($rsa->getPrivateKey()); } public function testLoadPKCS8PrivateKey() @@ -160,7 +161,7 @@ public function testLoadPKCS8PrivateKey() -----END ENCRYPTED PRIVATE KEY-----'; $this->assertTrue($rsa->loadKey($key)); - $this->assertInternalType('string', $rsa->getPrivateKey()); + $this->assertIsString($rsa->getPrivateKey()); } public function testSavePKCS8PrivateKey() @@ -185,7 +186,7 @@ public function testSavePKCS8PrivateKey() $this->assertTrue($rsa->loadKey($key)); $key = $rsa->getPrivateKey(RSA::PRIVATE_FORMAT_PKCS8); - $this->assertInternalType('string', $key); + $this->assertIsString($key); $this->assertTrue($rsa->loadKey($key)); } @@ -204,7 +205,7 @@ public function testPubKey1() -----END RSA PUBLIC KEY-----'; $this->assertTrue($rsa->loadKey($key)); - $this->assertInternalType('string', $rsa->getPublicKey()); + $this->assertIsString($rsa->getPublicKey()); $this->assertFalse($rsa->getPrivateKey()); } @@ -223,10 +224,68 @@ public function testPubKey2() -----END PUBLIC KEY-----'; $this->assertTrue($rsa->loadKey($key)); - $this->assertInternalType('string', $rsa->getPublicKey()); + $this->assertIsString($rsa->getPublicKey()); $this->assertFalse($rsa->getPrivateKey()); } + public function testPubKeyPssWithoutParams() + { + $rsa = new RSA(); + + // extracted from a SubjectPublicKeyInfo of a CSR created by OpenSSL + $key = '-----BEGIN PUBLIC KEY----- +MIIBIDALBgkqhkiG9w0BAQoDggEPADCCAQoCggEBANHPPf5tjTmEHtQvzi6+rItj +G3OUvh6Nihc9bXSu0xNFjl/9TdyIXstRUG/Lh07isHgZFEfXn4pmm/iZIQh09ACg +TjEau8rpcLB0BS9dDgTh8hvgkbdxWR2UPxk34bFcdgIplckslAfB4+/ebL+ObvUa +W3sZosTq3D6/qh0fujGZg/EKLJcNCHI27XMiAT5yWztSjHWwQm7LBwJ5uKlFLEDC +Z/+LIV/vPEIMfE6lA/+OnLKwVFB540eXQPuWar1ARHXN8PpiCqJHanddYMA5l/Cw +5R7kJ+CBoHzaPePXjB9V1bfzEBzBHb2ddiSjum+qtLWuH0Q7B8gPX9EjxIwuCzMC +AwEAAQ== +-----END PUBLIC KEY-----'; + + $this->assertTrue($rsa->loadKey($key)); + $this->assertIsString($rsa->getPublicKey()); + $this->assertFalse($rsa->getPrivateKey()); + } + + public function testPrivateKeyPssWithoutParams() + { + $rsa = new RSA(); + + $key = '-----BEGIN PRIVATE KEY----- +MIIEugIBADALBgkqhkiG9w0BAQoEggSmMIIEogIBAAKCAQEA0c89/m2NOYQe1C/O +Lr6si2Mbc5S+Ho2KFz1tdK7TE0WOX/1N3Ihey1FQb8uHTuKweBkUR9efimab+Jkh +CHT0AKBOMRq7yulwsHQFL10OBOHyG+CRt3FZHZQ/GTfhsVx2AimVySyUB8Hj795s +v45u9RpbexmixOrcPr+qHR+6MZmD8Qoslw0IcjbtcyIBPnJbO1KMdbBCbssHAnm4 +qUUsQMJn/4shX+88Qgx8TqUD/46csrBUUHnjR5dA+5ZqvUBEdc3w+mIKokdqd11g +wDmX8LDlHuQn4IGgfNo949eMH1XVt/MQHMEdvZ12JKO6b6q0ta4fRDsHyA9f0SPE +jC4LMwIDAQABAoIBAFPuTMWAO7Obh92oNhn7CvlDr1KgWSHNy0UavLOl0ChwddEu +erxTDWDWaZAfYkSLaL7SgYtv1ZG/FHvxfgZtCsNJXZ5FLISyt/LOpthYqGgJnxnJ +z2EMBfNQP6Gt+ipCa67XxeTRYXJs/OsTFnvW1cpVPe1TxwpxTaQIdlvqOkjmgCci +TRzH+Acj8unWDHAJpQkCOvmi+25sE0BMQYWnsfMSzm63Yk3SeZLIJKqoUdZhYMZU +6FK2DMDNR4TZps7s50MFlZfUUJfzgb4Hb4miiKzLPhf4q7rxS4VzrvUQ/81ySCwi +1LaSw5HoH1YMDT6rwcHMwHhzhu8X2CKlNIrri8ECgYEA7aiZAxmlY28LWcXHqqhZ +Yky76vLy/mbs0TfAVK2pSqyFhaGZe5daAJSIrVcZEEgAwR6/ZLITTWBuGdsHw6vF +GtSvkElLhopmQEs73kKqeBFLhpTqYXYVW0txi3jdWElie8fZa/Oa/sFLEeNsibQu +fbVWWGakf9458FDuR0i2k+ECgYEA4gBu2u6xkJzqOzOjBg5tNhxmzcPyt4Ds3ryA +e+C5hVCotd1EX6HZRPYjLEys0yUhiXDAn7ViEdtiXt9RYfpK+OKLGeTZ7pMCyZW+ +Yhc0i2XYqWSKUH3iNonp8B0JSkfEQBY2KlA7b5YZQZkr/Ml/WtoKeicHLBcdVxqa +t7krQZMCgYBMU7GQxVPQs4E5u8N8k8ThRTO1KYHRIs08BGPIzl1oli/r0xKwFtPZ +C9s5kJeEGxvi6jUd6fM5DpdNxoKf3TLYgyY/eMrA0wIz8/WuVErbdPKErp733izN +vVUiLhcom6j9iBnUCdDlsL6jaB8burqTtQGeMpjyWDTTcaqVSk0ZAQKBgCqc1EoZ +eYd/3rZc7R8mNzddsZCYorow5/izaDJzU+esJrNrzgmOFc5n7ofayTdip+knRlqW +s7AUQn8K8mhb7ijxZjLysJjIRV1HC8epAnJKOMjvuRimM7H+3Qo2H1tPHtTKm1nt +GNfYYFi7Dc0zHP0/YXxYwYRxs0mKLaP4mQxbAoGARHngPhGC0yM5KqxNrkHPVjLq +CHQy+e9GTPXtDLC3D7HAYyyzKqy4mdBDzMeLqA3a+iT2PXjn4w5zOEW8GAcRYRtG +3EyvclPmWtmCpU5xqD8ieFtQhMeW/XzJHjTXlcncz0PCkGVoQiuRvXWNAukNPg0D +BocC2CO6SNi4Qjr3NlM= +-----END PRIVATE KEY-----'; + + $this->assertTrue($rsa->loadKey($key)); + $this->assertIsString($rsa->getPublicKey()); + $this->assertIsString($rsa->getPrivateKey()); + } + public function testSSHPubKey() { $rsa = new RSA(); @@ -237,7 +296,7 @@ public function testSSHPubKey() 'phpseclib-generated-key'; $this->assertTrue($rsa->loadKey($key)); - $this->assertInternalType('string', $rsa->getPublicKey()); + $this->assertIsString($rsa->getPublicKey()); $this->assertFalse($rsa->getPrivateKey()); } @@ -381,6 +440,18 @@ public function testSetLoad() $rsa->loadKey($rsa); } + public function testRaw() + { + $key = array( + 'e' => new BigInteger('010001', 16), + 'n' => new BigInteger('00d0991cd1dc9519de1a5a935742cb3a0f998b0c7995eab3f204f2663b81229474105bd38e2e657e1acd095d8178a1bdcb0a7631e491771ff99d8e705017cff26d6632665f9171734aa5f5d80cc0ea10ef5e8dfa2199af3b7bf69703104b8bdab5db0510b6a7cf61ec9799f25000a0994ec287b711075cfdad79b90da273172dc0fd2127181b45564c602c102d8e63929d53597f8be7ee5709885d42a5ddf8e3142432e35a61e8baeeb1a6dd23a6a74672c2a95ad974ace161813df32daaca8ce008e67d3a48c1e3ce8aa4ddfd2df644783f582c669892b91a8613f2a5c67f520845c53f09f6faa760ae0d1b0fd64953cb26f22317494d2fd5d9bc72b5f56f3ea1', 16) + ); + + $rsa = new RSA; + $this->assertTrue($rsa->loadKey($key)); + $this->assertIsString("$rsa"); + } + /** * @group github980 */ @@ -416,7 +487,7 @@ public function testGoodBad() -----END RSA PUBLIC KEY-----'; $this->assertTrue($rsa->loadKey($key)); - $this->assertInternalType('string', $rsa->getPublicKey()); + $this->assertIsString($rsa->getPublicKey()); $this->assertFalse($rsa->loadKey('zzz')); $this->assertFalse($rsa->getPublicKey()); } diff --git a/tests/Unit/Crypt/RSA/ModeTest.php b/tests/Unit/Crypt/RSA/ModeTest.php index 788878b2b..854f90829 100644 --- a/tests/Unit/Crypt/RSA/ModeTest.php +++ b/tests/Unit/Crypt/RSA/ModeTest.php @@ -6,6 +6,7 @@ */ use phpseclib\Crypt\RSA; +use phpseclib\Math\BigInteger; class Unit_Crypt_RSA_ModeTest extends PhpseclibTestCase { @@ -115,4 +116,26 @@ public function testPSSSigsWithNonPowerOf2Key() $payload = 'eyJraWQiOiJ0RkMyVUloRnBUTV9FYTNxY09kX01xUVQxY0JCbTlrRkxTRGZlSmhzUkc4IiwiYWxnIjoiUFMyNTYifQ.eyJhcHAiOiJhY2NvdW50cG9ydGFsIiwic3ViIjoiNTliOGM4YzA5NTVhNDA5MDg2MGRmYmM3ZGQwMjVjZWEiLCJjbGlkIjoiZTQ5ZTA2N2JiMTFjNDcyMmEzNGIyYjNiOGE2YTYzNTUiLCJhbSI6InBhc3N3b3JkIiwicCI6ImVOcDFrRUZQd3pBTWhmXC9QdEVOYU5kQkc2bUZDNHNpbENNNXU0aTNXMHFSS0hFVDU5V1JzcXpZRUp4XC84M3ZQbkIxcUg3Rm5CZVNabEtNME9saGVZVUVWTXlHOEVUOEZnWDI4dkdqWG4wWkcrV2hSK01rWVBicGZacHI2U3E0N0RFYjBLYkRFT21CSUZuOTZKN1ZDaWg1Q2p4dWNRZDJmdHJlMCt2cSthZFFObUluK0poWEl0UlBvQ0xya1wvZ05VV3N3T09vSVwva0Q5ZVk4c05jRHFPUzNkanFWb3RPU21oRUo5b0hZZmFqZmpSRzFGSWpGRFwvOExtT2pKbVF3d0tBMnQ0aXJBQ2NncHo0dzBuN3BtXC84YXV2T0dFM2twVFZ2d0IzdzlQZk1YZnJJUTBhejRsaEtIdVBUMU42XC9sb1FJPSIsImlhaSI6IjU5YjhjOGMwOTU1YTQwOTA4NjBkZmJjN2RkMDI1Y2VhIiwiY2xzdmMiOiJhY2NvdW50cG9ydGFsIiwibHB2IjoxNTQ3Njc1NDM4LCJ0IjoicyIsImljIjp0cnVlLCJleHAiOjE1NDc3MDQyMzgsImlhdCI6MTU0NzY3NTQzOCwianRpIjoiZTE0N2UzM2UzNzVhNDkyNWJjMzdjZTRjMDIwMmJjNDYifQ'; $this->assertTrue($rsa->verify($payload, $sig)); } + + public function testPKCS1SigWithoutNull() + { + $rsa = new RSA(); + $rsa->loadKey(array( + 'n' => new BigInteger('0xE932AC92252F585B3A80A4DD76A897C8B7652952FE788F6EC8DD640587A1EE5647670A8AD +4C2BE0F9FA6E49C605ADF77B5174230AF7BD50E5D6D6D6D28CCF0A886A514CC72E51D209CC7 +72A52EF419F6A953F3135929588EBE9B351FCA61CED78F346FE00DBB6306E5C2A4C6DFC3779 +AF85AB417371CF34D8387B9B30AE46D7A5FF5A655B8D8455F1B94AE736989D60A6F2FD5CADB +FFBD504C5A756A2E6BB5CECC13BCA7503F6DF8B52ACE5C410997E98809DB4DC30D943DE4E81 +2A47553DCE54844A78E36401D13F77DC650619FED88D8B3926E3D8E319C80C744779AC5D6AB +E252896950917476ECE5E8FC27D5F053D6018D91B502C4787558A002B9283DA7', 16), + 'e' => new BigInteger('3') + )); + + $message = 'hello world!'; + $signature = pack('H*', 'a0073057133ff3758e7e111b4d7441f1d8cbe4b2dd5ee4316a14264290dee5ed7f175716639bd9bb43a14e4f9fcb9e84dedd35e2205caac04828b2c053f68176d971ea88534dd2eeec903043c3469fc69c206b2a8694fd262488441ed8852280c3d4994e9d42bd1d575c7024095f1a20665925c2175e089c0d731471f6cc145404edf5559fd2276e45e448086f71c78d0cc6628fad394a34e51e8c10bc39bfe09ed2f5f742cc68bee899d0a41e4c75b7b80afd1c321d89ccd9fe8197c44624d91cc935dfa48de3c201099b5b417be748aef29248527e8bbb173cab76b48478d4177b338fe1f1244e64d7d23f07add560d5ad50b68d6649a49d7bc3db686daaa7'); + + $rsa->setSignatureMode(RSA::SIGNATURE_PKCS1); + $rsa->setHash('sha256'); + $this->assertTrue($rsa->verify($message, $signature)); + } } diff --git a/tests/Unit/File/ASN1Test.php b/tests/Unit/File/ASN1Test.php index 3a44bdc48..d371154ed 100644 --- a/tests/Unit/File/ASN1Test.php +++ b/tests/Unit/File/ASN1Test.php @@ -79,7 +79,7 @@ public function testAnyString() $decoded = $asn1->decodeBER(base64_decode($str)); $result = $asn1->asn1map($decoded[0], $AS_REP); - $this->assertInternalType('array', $result); + $this->assertIsArray($result); } /** @@ -231,7 +231,7 @@ public function testIncorrectString() $decoded = $asn1->decodeBER(base64_decode($str)); $result = $asn1->asn1map($decoded[0], $AS_REP); - $this->assertInternalType('array', $result); + $this->assertIsArray($result); } /** @@ -276,7 +276,7 @@ public function testContextSpecificNonConstructed() { $asn1 = new ASN1(); $decoded = $asn1->decodeBER(base64_decode('MBaAFJtUo7c00HsI5EPZ4bkICfkOY2Pv')); - $this->assertInternalType('string', $decoded[0]['content'][0]['content']); + $this->assertIsString($decoded[0]['content'][0]['content']); } /** @@ -286,7 +286,7 @@ public function testEmptyContextTag() { $asn1 = new ASN1(); $decoded = $asn1->decodeBER("\xa0\x00"); - $this->assertInternalType('array', $decoded); + $this->assertIsArray($decoded); $this->assertCount(0, $decoded[0]['content']); } @@ -390,6 +390,62 @@ public function testExplicitImplicitDate() $a = $asn1->decodeBER($a); $a = $asn1->asn1map($a[0], $map); - $this->assertInternalType('array', $a); + $this->assertIsArray($a); + } + + public function testNullGarbage() + { + $asn1 = new ASN1(); + + $em = pack('H*', '3080305c0609608648016503040201054f8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888804207509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9'); + $decoded = $asn1->decodeBER($em); + $this->assertFalse($decoded[0]); + + $em = pack('H*', '3080307f0609608648016503040201057288888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888804207509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca90000'); + $decoded = $asn1->decodeBER($em); + $this->assertFalse($decoded[0]); + } + + public function testOIDGarbage() + { + $asn1 = new ASN1(); + + $em = pack('H*', '3080305c065860864801650304020188888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888050004207509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9'); + $decoded = $asn1->decodeBER($em); + $this->assertFalse($decoded[0]); + + $em = pack('H*', '3080307f067d608648016503040201888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888804207509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9'); + $decoded = $asn1->decodeBER($em); + $this->assertFalse($decoded[0]); + } + + public function testConstructedMismatch() + { + $asn1 = new ASN1(); + + $em = pack('H*', '1031300d0609608648016503040201050004207509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9'); + $decoded = $asn1->decodeBER($em); + $this->assertFalse($decoded[0]); + + $em = pack('H*', '3031100d0609608648016503040201050004207509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9'); + $decoded = $asn1->decodeBER($em); + $this->assertFalse($decoded[0]); + + $em = pack('H*', '3031300d2609608648016503040201050004207509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9'); + $decoded = $asn1->decodeBER($em); + $this->assertFalse($decoded[0]); + + $em = pack('H*', '3031300d06096086480165030402012d0004207509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9'); + $decoded = $asn1->decodeBER($em); + $this->assertFalse($decoded[0]); + } + + public function testBadTagSecondOctet() + { + $asn1 = new ASN1(); + + $em = pack('H*', '3033300f1f808080060960864801650304020104207509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9'); + $decoded = $asn1->decodeBER($em); + $this->assertFalse($decoded[0]); } } diff --git a/tests/Unit/File/X509/CSRTest.php b/tests/Unit/File/X509/CSRTest.php index f0313a5b7..93bf273ea 100644 --- a/tests/Unit/File/X509/CSRTest.php +++ b/tests/Unit/File/X509/CSRTest.php @@ -27,7 +27,7 @@ public function testLoadCSR() $spkac = $x509->loadCSR($test); - $this->assertInternalType('array', $spkac); + $this->assertIsArray($spkac); } public function testCSRWithAttributes() @@ -67,7 +67,7 @@ public function testCSRWithAttributes() $csr = $x509->loadCSR($test); - $this->assertInternalType('array', $csr); + $this->assertIsArray($csr); } public function testCSRDER() @@ -92,7 +92,7 @@ public function testCSRDER() $csr = $x509->loadCSR($csr); - $this->assertInternalType('array', $csr); + $this->assertIsArray($csr); } // on PHP 7.1, with older versions of phpseclib, this would produce a "A non-numeric value encountered" warning diff --git a/tests/Unit/File/X509/SPKACTest.php b/tests/Unit/File/X509/SPKACTest.php index d31a34404..2c5792d07 100644 --- a/tests/Unit/File/X509/SPKACTest.php +++ b/tests/Unit/File/X509/SPKACTest.php @@ -28,11 +28,11 @@ public function testLoadSPKAC() $spkac = $x509->loadSPKAC($test); - $this->assertInternalType('array', $spkac); + $this->assertIsArray($spkac); $spkac = $x509->loadSPKAC('SPKAC=' . $test); - $this->assertInternalType('array', $spkac); + $this->assertIsArray($spkac); $this->assertTrue( $x509->validateSignature(), @@ -41,7 +41,7 @@ public function testLoadSPKAC() $pubKey = $x509->getPublicKey(); - $this->assertInternalType('string', "$pubKey"); + $this->assertIsString("$pubKey"); } public function testSaveSPKAC() @@ -55,17 +55,17 @@ public function testSaveSPKAC() $x509->setChallenge('...'); $spkac = $x509->signSPKAC(); - $this->assertInternalType('array', $spkac); + $this->assertIsArray($spkac); - $this->assertInternalType('string', $x509->saveSPKAC($spkac)); + $this->assertIsString($x509->saveSPKAC($spkac)); $x509 = new X509(); $x509->setPrivateKey($privKey); $spkac = $x509->signSPKAC(); - $this->assertInternalType('array', $spkac); + $this->assertIsArray($spkac); - $this->assertInternalType('string', $x509->saveSPKAC($spkac)); + $this->assertIsString($x509->saveSPKAC($spkac)); } public function testBadSignatureSPKAC() diff --git a/tests/Unit/File/X509/X509Test.php b/tests/Unit/File/X509/X509Test.php index 0d019ede2..d40ad311f 100644 --- a/tests/Unit/File/X509/X509Test.php +++ b/tests/Unit/File/X509/X509Test.php @@ -58,7 +58,7 @@ public function testExtensionMapping() $cert = $x509->loadX509($test); - $this->assertInternalType('array', $cert['tbsCertificate']['extensions'][3]['extnValue']); + $this->assertIsArray($cert['tbsCertificate']['extensions'][3]['extnValue']); } public function testLoadUnsupportedExtension() @@ -811,4 +811,119 @@ public function testRandomString() $this->assertFalse($r); } + + /** + * @group github1542 + */ + public function testMultiCertPEM() + { + $a = '-----BEGIN CERTIFICATE----- +MIILODCCCSCgAwIBAgIQDh0LGipJ++wxFLj8X5MXKDANBgkqhkiG9w0BAQsFADCB +kDELMAkGA1UEBhMCVVMxDTALBgNVBAgTBFV0YWgxDTALBgNVBAcTBExlaGkxFzAV +BgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t +MS8wLQYDVQQDEyZEaWdpQ2VydCBWZXJpZmllZCBNYXJrIEludGVybWVkaWF0ZSBD +QTAeFw0yMDA3MzAwMDAwMDBaFw0yMTAxMjUxMjAwMDBaMIIBDjEdMBsGA1UEDxMU +UHJpdmF0ZSBPcmdhbml6YXRpb24xEzARBgsrBgEEAYI3PAIBAxMCVVMxGTAXBgsr +BgEEAYI3PAIBAhMIRGVsYXdhcmUxEDAOBgNVBAUTBzM2MzMwMTkxGTAXBgNVBAkT +EDEwMDAgVyBNYXVkZSBBdmUxDjAMBgNVBBETBTk0MDg1MQswCQYDVQQGEwJVUzET +MBEGA1UECBMKQ2FsaWZvcm5pYTESMBAGA1UEBxMJU3Vubnl2YWxlMR0wGwYDVQQK +ExRMaW5rZWRJbiBDb3Jwb3JhdGlvbjESMBAGCisGAQQBg55fAQMTAlVTMRcwFQYK +KwYBBAGDnl8BBBMHNTY3NTczOTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAOCl7WccAcvSaf5+pNsV82VjFuwdzEwjDYESZmIuurz95e+JtJZst/M3Hw90 +YxKSDV4LdaVFAogXy2F+Npit1KhbBEb8vbBkm4LJ3iM8teE/10JugLyxrcVi3LSj +iKHs+rqxcTJsVYoR+CuPLuAbu4xKi+xQ4tVafrFd0Y21n6OL8nB2SRISHF58kRXq +UDW/NippF1AhcdCc5L5EmXFPCpyWfv+UXgTj9i+/I9AWUC3diHckb5NXd/wS7Jmq +5FE0uixRGTixI5a9uZr0jasTtfhlVtvqFyDmzARB/q9IU0eXm3dtcCJISIXGum6o +yCFUk8pyYsGd/M5Fyw7zbmEqsucCAwEAAaOCBgswggYHMB8GA1UdIwQYMBaAFOsN +zmX0UnV7TbPUsz0w41AYq+NuMB0GA1UdDgQWBBSkuL0+t0wu/+y2xkUY/FOSsiuV +ODAXBgNVHREEEDAOggxsaW5rZWRpbi5jb20wEwYDVR0lBAwwCgYIKwYBBQUHAx8w +gZkGA1UdHwSBkTCBjjBFoEOgQYY/aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0Rp +Z2lDZXJ0VmVyaWZpZWRNYXJrSW50ZXJtZWRpYXRlQ0EuY3JsMEWgQ6BBhj9odHRw +Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRWZXJpZmllZE1hcmtJbnRlcm1l +ZGlhdGVDQS5jcmwwUAYDVR0gBEkwRzA3BglghkgBhv1sCgEwKjAoBggrBgEFBQcC +ARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAMBgorBgEEAYOeXwEBMF4G +CCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL2NhY2VydHMuZGlnaWNl +cnQuY29tL0RpZ2lDZXJ0VmVyaWZpZWRNYXJrSW50ZXJtZWRpYXRlQ0EuY3J0MAwG +A1UdEwEB/wQCMAAwggOsBggrBgEFBQcBDASCA54wggOaooIDlqCCA5IwggOOMIID +ijCCA4YWDWltYWdlL3N2Zyt4bWwwIzAhMAkGBSsOAwIaBQAEFGckN8uhuoNkcXXh +wRAm7wkz4JRLMIIDThaCA0pkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LEg0c0lB +QUFBQUFBQUNsMVR5MjdiTUJDODl5c0k5VXlhKytLanNITElJVWlCRnVqSjkxUlJR +cUdLSGNTQ25PYnJ1NVRVQkMwTVNKN2w3SEozZHJRL3o0OW03bC9PdytuWU51Q3dN +YTlQNC9IY05tV2Fuci9zZHBmTHhWM0luVjRlZCtpOTN5bS9NWmZoZmlwdEUySm9U +T21IeHpKdFlCNzZ5L1hwdFcyODhVWWpab24rdkR2M1AxNU9EOFBZdDgwMEhIL2I1 +M056dForR2FleXZ2ZzNIWC8zOTErTit0K0w5ODkxVWpITEh0dm5zWTB6WFd1Ryti +YjVMQkJkek5vR2NSTHdGenk1NzdpeWk4eHlzUXdETDNnR2dnZWlZWTBYczJWQjJu +T0ZRODFQb0hBcVltaFBGUUhLRXVSTDBIdk5CbHhBTGgrQlNsazZwb0R3VXJnUU1i +R3QxcVVBazJwVjlBRS9PQitxc0k1S2xwUlN0cG5GSWxSSlo3RWVDZHZQVzdQNmQ5 +T2JtWmgwVFdGd01ZakJrNXlHVnBFc0pNbU1BUjVHS1hmRmhPMzU3cWwwbnNFRGVl +Y29kQmcyVFZVblFjSFJBYkJBMHRDTGRpTDQ4OHRrdVVkZzRkbzF1SExzRlY0cjlD +Q3BsMXRJeDZKemVnMFZ4T2RGeUFTODhMMm03WU1sNnl1QkN5bVpycnNUb2N1YVpS +S094YUZhUXV5UTZGNXJ0cGI3UnUxd1N0SXdPcVV2NkZORjRWdFVqR1dGdEp2NUZn +S3p5d3d4TWprUnVXZFFBZEdEZEJqSjIzdXJGVEdvT0liU3FHRUZhNjc0RGJUQkg0 +eTBuOVFBWjBqWHE2WVpDckhZNmlGYlI3cXYwa04rVi8zK0RIdXB2WFdLQXJNcWdF +YThWTXBXbytXRkdVcURPVWkxVXh3M3BJR20yUzZ4VXgyU0FlVUc2V2xGVDUvVnN0 +S3FkVitlcWtwNHFTTHFnQlJTcmpnRzFTTlRCb0pETE10ZWpHTWEwT0hyNVg3Q3VZ +cXlPaC9WMFhwNjNJWUxTMTl4YUtlZGx0UHFsWDMzNkEwYlJhNW9nQkFBQTCBigYK +KwYBBAHWeQIEAgR8BHoAeAB2AFVZU64wlgCAbNLrUgimyZ6TGCisEFa0QhxVNhVM +X3WsAAABc6DTF3wAAAQDAEcwRQIgRsnN1miYsyCMT234C14MaMgSAgKHXmc7RrBM +a/1ovTMCIQCOc/THDvltzhZrtnoRSbjc2EYp57A0VVHvduQPa7FKBDANBgkqhkiG +9w0BAQsFAAOCAgEA8UQt5jcUeOaDkhvbLq380Oq1Jy8Vr1BO1GPisn20KRCz/NvE +56f8hhmZlZ1xXfOM+JCaGQnwVwcRBQtLQ/+6bmeT8/WM3hf9A5rP0g0ZxvaAlQtu +e6UjvgnNx02QOKNPrmxN0rW8s24kUi0OAf1ump3SY5Ab+S+ywRG7Ah+3qch+FwA8 +CYau9TgV5kvfYDRULBM84EeFhsPcwT+YJ5u7RvkGQobqNao21Ti5tupiks/9NzI8 +splBS77Z6bPdFGvZ7pJdXiiDB2+SZdyv8iqDFM6mKRbOcuwAHcTY2zVhcS46H7SO +8OU7L/2y0XQB1rMtQDarCKwdAcsAb2e+N8mYQ0glQX4k41Sf4saMXsU1EjnOCUas +YxvVgJRD+fe4JWf8EO59fElzkrQsT3guBIzV5Kg1dYaCHngCYQIakjKQM0eKxZ3d +vn4648A0vXynhJUThOSxN4jbvVA5uYYHqHDMjJtkBPDA7HtLSIxRNattshOAoeC5 +LMszAsL9th/WoXkAa2lTs2kashOHEpx+ncGactrL8tu7dvU01Yk6yP1QAjFEo1Nt +8umUG7jQQIuquB2ry4qzFuQvKpbNQZ//9RsSmq1nni+DEKd/S63N7T8M0FpioLVm +Z2OXFTCG5ORjPUOyMGjxzjEPZWmTOG+gqNOc0HKXbuBAsGZbK4dind+YdZE= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIHLzCCBRegAwIBAgIQDZXVhKBTvJ0ZjW6meNxHhTANBgkqhkiG9w0BAQsFADCB +iDELMAkGA1UEBhMCVVMxDTALBgNVBAgTBFV0YWgxDTALBgNVBAcTBExlaGkxFzAV +BgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t +MScwJQYDVQQDEx5EaWdpQ2VydCBWZXJpZmllZCBNYXJrIFJvb3QgQ0EwHhcNMTkw +OTIzMTIyNTQyWhcNMzQwOTIzMTIyNTQyWjCBkDELMAkGA1UEBhMCVVMxDTALBgNV +BAgTBFV0YWgxDTALBgNVBAcTBExlaGkxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu +MRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMS8wLQYDVQQDEyZEaWdpQ2VydCBW +ZXJpZmllZCBNYXJrIEludGVybWVkaWF0ZSBDQTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAPfhMd+2RBjNZpqq0GVUF0kKK72fQxhJbnxgYv7GFpmi69sX +dgeqH9RE07ShTDtkLks9G/GuiXsLEmjSCBDDTwfB3hpbdrZsNQFWOIRHXmU8ykuP +bCd/HVRZGULeWvbt93deEB1el5MpxP9Fs3LKjw7xytbuM/nkGJ4D2R1IHC953FoU +4BYsp+8VB1+7Gh8eKVh+HpmBeEfIB+cuq4FpZKxi+F5J7UjW5yO4SuDcTF4AMY0J +DPuKIy+Og6laNOtDS30P1CUu1N6BwLMYTbeqyYHJ7B3kLWsDceGMqIcxo8zrk1rT +sJctcXHXhB4k2PnVxt8qkQjg2Lo++kU0dFSUyrzvg3WrGypv9vphWMI+vmCjmu2K +0BZLZ4nKshoTX495R6pGbsecGaaGgACB/1NcGI7PVp7spY2ytLvHHZ+Hh446BGFy +AdM8lZCMXEhNftP8RRRVr8mwHHzyIa86r4yk0SkOlXUkNrGdTqqyMSDJ3W7DWGO/ +vCObzXiM0aq77ebD/0fE5LsZhEJYx7txF9NA1DoICgHp8zqF35i3UOp4+5IyJ8A9 +MjqYcX+LayH06B45bMgTHLmJKcsRYvXAtu+nIvIL0fk4+Ea7kJ7MNx5/udS89b2f +vSFC4hmA3OBDiJqGmldwqJL/HP7RI8O54yj10vpiH4obDo8QgfhKSVoX5HwDAgMB +AAGjggGJMIIBhTAdBgNVHQ4EFgQU6w3OZfRSdXtNs9SzPTDjUBir424wHwYDVR0j +BBgwFoAU7G8ipLME4sFjh+Z3Y+pGaU7u/OswDgYDVR0PAQH/BAQDAgGGMBMGA1Ud +JQQMMAoGCCsGAQUFBwMfMBIGA1UdEwEB/wQIMAYBAf8CAQAwfAYIKwYBBQUHAQEE +cDBuMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wRgYIKwYB +BQUHMAKGOmh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFZlcmlm +aWVkTWFya1Jvb3RDQS5jcnQwSAYDVR0fBEEwPzA9oDugOYY3aHR0cDovL2NybDMu +ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VmVyaWZpZWRNYXJrUm9vdENBLmNybDBCBgNV +HSAEOzA5MDcGCWCGSAGG/WwKATAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5k +aWdpY2VydC5jb20vQ1BTMA0GCSqGSIb3DQEBCwUAA4ICAQA6v371ixHAA9WyGlFr ++RvmmqkZZg9pf6+j5sKImeTFAHfYz3TJa2wmDRpZxSRYy9VUFOMPVDEavsJ2i5Ua +jEpkJ/7VHbX60joKBxQHKCbMpbwGen6pTXRaeE6CET2zCMbyiIqT2E6OiBZL8cWT +sgLbdhVKspoOi+c3JwTR4khR24J9IQVxK90Nq3zeciYgBvM3G+ZJtZgkA58CRdex +xVO6O57bwe4ti4rlRgWOGgAFpFrJSD1jqhhHD3MWg17NI4k0ciBPoDHHBAI8hiqg +jPM+y6aEGww7BFSfkp5tl/Aq9uGXCwxNLOc3UlUd8Cc0qH1KfkvjrumVMmJhsk9I +88YefbCuGPZ5Q8V2LIz7LrNDh0VRq/HSENXn1sBGlAFahgUTk/cWJ8nKA+8mHMlE +NGmx205Rtbf9lVL1CbH3QFwlvGEfqoMXw6G9JW2hFQVTKpBKuzjQw43CEw12lstP +oa96ixNAXXVGJsdKpYCqTIWJ0x1DssvG2shvzdHxawvYQ3C+/jaEoQ6bxSIdanI2 +NMtBdy9Q0TjDc7uf/eaUYKkP4wskNc1Os23oHllFHVm++8wdDltNulc7B1TXIQ+2 +oD5EoULMFSVUHX8gtyd463GgOQtBDwf3aZ4Xe6eDrhdfI/4IW098kVcg+qFO841L +qzFkAKWjJj4KjfrbZX4C0Spfxw== +-----END CERTIFICATE-----'; + + $x509 = new X509(); + $r = $x509->loadX509($a); + + $this->assertIsArray($r); + } } diff --git a/tests/Unit/Math/BigInteger/TestCase.php b/tests/Unit/Math/BigInteger/TestCase.php index efef0a7d6..5c17c15ee 100644 --- a/tests/Unit/Math/BigInteger/TestCase.php +++ b/tests/Unit/Math/BigInteger/TestCase.php @@ -399,7 +399,7 @@ public function testDebugInfo() { $num = $this->getInstance(50); $str = print_r($num, true); - $this->assertContains('[value] => 0x32', $str); + $this->assertStringContainsString('[value] => 0x32', $str); return $str; } diff --git a/tests/Unit/Net/SFTPStreamTest.php b/tests/Unit/Net/SFTPStreamUnitTest.php similarity index 92% rename from tests/Unit/Net/SFTPStreamTest.php rename to tests/Unit/Net/SFTPStreamUnitTest.php index 6f98bb0cb..80c3f63c8 100644 --- a/tests/Unit/Net/SFTPStreamTest.php +++ b/tests/Unit/Net/SFTPStreamUnitTest.php @@ -7,7 +7,7 @@ use phpseclib\Net\SFTP\Stream; -class Unit_Net_SFTPStreamTest extends PhpseclibTestCase +class Unit_Net_SFTPStreamUnitTest extends PhpseclibTestCase { public function testRegisterWithoutArgument() { diff --git a/tests/Unit/Net/SSH2Test.php b/tests/Unit/Net/SSH2UnitTest.php similarity index 76% rename from tests/Unit/Net/SSH2Test.php rename to tests/Unit/Net/SSH2UnitTest.php index 54695d5b2..aefcc5443 100644 --- a/tests/Unit/Net/SSH2Test.php +++ b/tests/Unit/Net/SSH2UnitTest.php @@ -6,7 +6,7 @@ * @license http://www.opensource.org/licenses/mit-license.html MIT License */ -class Unit_Net_SSH2Test extends PhpseclibTestCase +class Unit_Net_SSH2UnitTest extends PhpseclibTestCase { public function formatLogDataProvider() { @@ -46,25 +46,25 @@ public function testGenerateIdentifier() } if (extension_loaded('openssl')) { - $this->assertContains('openssl', $identifier); - $this->assertNotContains('mcrypt', $identifier); + $this->assertStringContainsString('openssl', $identifier); + $this->assertStringNotContainsString('mcrypt', $identifier); } elseif (extension_loaded('mcrypt')) { - $this->assertNotContains('openssl', $identifier); - $this->assertContains('mcrypt', $identifier); + $this->assertStringNotContainsString('openssl', $identifier); + $this->assertStringContainsString('mcrypt', $identifier); } else { - $this->assertNotContains('openssl', $identifier); - $this->assertNotContains('mcrypt', $identifier); + $this->assertStringNotContainsString('openssl', $identifier); + $this->assertStringNotContainsString('mcrypt', $identifier); } if (extension_loaded('gmp')) { - $this->assertContains('gmp', $identifier); - $this->assertNotContains('bcmath', $identifier); + $this->assertStringContainsString('gmp', $identifier); + $this->assertStringNotContainsString('bcmath', $identifier); } elseif (extension_loaded('bcmath')) { - $this->assertNotContains('gmp', $identifier); - $this->assertContains('bcmath', $identifier); + $this->assertStringNotContainsString('gmp', $identifier); + $this->assertStringContainsString('bcmath', $identifier); } else { - $this->assertNotContains('gmp', $identifier); - $this->assertNotContains('bcmath', $identifier); + $this->assertStringNotContainsString('gmp', $identifier); + $this->assertStringNotContainsString('bcmath', $identifier); } } diff --git a/travis/run-phpunit.sh b/travis/run-phpunit.sh index ce393a772..31b98afcf 100755 --- a/travis/run-phpunit.sh +++ b/travis/run-phpunit.sh @@ -20,6 +20,23 @@ then PHPUNIT_ARGS="$PHPUNIT_ARGS -d zend.enable_gc=0" fi +if $PHPUNIT --atleast-version 9 +then + find tests -type f -name "*.php" -print0 | xargs -0 sed -i 's/n setUpBeforeClass()/n setUpBeforeClass(): void/g' + find tests -type f -name "*.php" -print0 | xargs -0 sed -i 's/n setUp()/n setUp(): void/g' + find tests -type f -name "*.php" -print0 | xargs -0 sed -i 's/n tearDown()/n tearDown(): void/g' + find tests -type f -name "*.php" -print0 | xargs -0 sed -i 's/\(n assertIsArray([^)]*)\)/\1: void/g' + find tests -type f -name "*.php" -print0 | xargs -0 sed -i 's/\(n assertIsString([^)]*)\)/\1: void/g' + find tests -type f -name "*.php" -print0 | xargs -0 sed -i 's/\(n assertStringContainsString([^)]*)\)/\1: void/g' + find tests -type f -name "*.php" -print0 | xargs -0 sed -i 's/\(n assertStringNotContainsString([^)]*)\)/\1: void/g' + find tests -type f -name "*.php" -print0 | xargs -0 sed -i 's/^class Unit_Crypt_\(AES\|Hash\|RSA\)_/class /g' + find tests -type f -name "*.php" -print0 | xargs -0 sed -i 's/^class Unit_File_\(X509\)_/class /g' + find tests -type f -name "*.php" -print0 | xargs -0 sed -i 's/^class Unit_Math_\(BigInteger\)_/class /g' + find tests -type f -name "*.php" -print0 | xargs -0 sed -i 's/^class Unit_\(Crypt\|File\|Math\|Net\)_/class /g' + find tests -type f -name "*.php" -print0 | xargs -0 sed -i 's/^class Functional_Net_/class /g' + find tests -type f -name "*.php" -print0 | xargs -0 sed -i 's/extends Unit_Crypt_Hash_\(SHA512Test\|SHA256Test\)/extends \1/g' +fi + if [ "$TRAVIS_PHP_VERSION" = 'hhvm' -o `php -r "echo (int) version_compare(PHP_VERSION, '7.0', '>=');"` = "1" ] then find tests -type f -name "*Test.php" | \