Subversion Repositories oidplus

Rev

Rev 846 | Rev 1042 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /**
  4.  * Pure-PHP implementation of SSHv2.
  5.  *
  6.  * PHP version 5
  7.  *
  8.  * Here are some examples of how to use this library:
  9.  * <code>
  10.  * <?php
  11.  *    include 'vendor/autoload.php';
  12.  *
  13.  *    $ssh = new \phpseclib3\Net\SSH2('www.domain.tld');
  14.  *    if (!$ssh->login('username', 'password')) {
  15.  *        exit('Login Failed');
  16.  *    }
  17.  *
  18.  *    echo $ssh->exec('pwd');
  19.  *    echo $ssh->exec('ls -la');
  20.  * ?>
  21.  * </code>
  22.  *
  23.  * <code>
  24.  * <?php
  25.  *    include 'vendor/autoload.php';
  26.  *
  27.  *    $key = \phpseclib3\Crypt\PublicKeyLoader::load('...', '(optional) password');
  28.  *
  29.  *    $ssh = new \phpseclib3\Net\SSH2('www.domain.tld');
  30.  *    if (!$ssh->login('username', $key)) {
  31.  *        exit('Login Failed');
  32.  *    }
  33.  *
  34.  *    echo $ssh->read('username@username:~$');
  35.  *    $ssh->write("ls -la\n");
  36.  *    echo $ssh->read('username@username:~$');
  37.  * ?>
  38.  * </code>
  39.  *
  40.  * @category  Net
  41.  * @package   SSH2
  42.  * @author    Jim Wigginton <terrafrost@php.net>
  43.  * @copyright 2007 Jim Wigginton
  44.  * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  45.  * @link      http://phpseclib.sourceforge.net
  46.  */
  47.  
  48. namespace phpseclib3\Net;
  49.  
  50. use phpseclib3\Common\Functions\Strings;
  51. use phpseclib3\Crypt\Blowfish;
  52. use phpseclib3\Crypt\ChaCha20;
  53. use phpseclib3\Crypt\Common\AsymmetricKey;
  54. use phpseclib3\Crypt\Common\PrivateKey;
  55. use phpseclib3\Crypt\Common\PublicKey;
  56. use phpseclib3\Crypt\Common\SymmetricKey;
  57. use phpseclib3\Crypt\DH;
  58. use phpseclib3\Crypt\DSA;
  59. use phpseclib3\Crypt\EC;
  60. use phpseclib3\Crypt\Hash;
  61. use phpseclib3\Crypt\Random;
  62. use phpseclib3\Crypt\RC4;
  63. use phpseclib3\Crypt\Rijndael;
  64. use phpseclib3\Crypt\RSA;
  65. use phpseclib3\Crypt\TripleDES; // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification.
  66. use phpseclib3\Crypt\Twofish;
  67. use phpseclib3\Exception\ConnectionClosedException;
  68. use phpseclib3\Exception\InsufficientSetupException;
  69. use phpseclib3\Exception\NoSupportedAlgorithmsException;
  70. use phpseclib3\Exception\UnableToConnectException;
  71. use phpseclib3\Exception\UnsupportedAlgorithmException;
  72. use phpseclib3\Exception\UnsupportedCurveException;
  73. use phpseclib3\Math\BigInteger;
  74. use phpseclib3\System\SSH\Agent;
  75.  
  76. /**
  77.  * Pure-PHP implementation of SSHv2.
  78.  *
  79.  * @package SSH2
  80.  * @author  Jim Wigginton <terrafrost@php.net>
  81.  * @access  public
  82.  */
  83. class SSH2
  84. {
  85.     /**#@+
  86.      * Compression Types
  87.      *
  88.      * @access private
  89.      */
  90.     /**
  91.      * No compression
  92.      */
  93.     const NET_SSH2_COMPRESSION_NONE = 1;
  94.     /**
  95.      * zlib compression
  96.      */
  97.     const NET_SSH2_COMPRESSION_ZLIB = 2;
  98.     /**
  99.      * zlib@openssh.com
  100.      */
  101.     const NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH = 3;
  102.     /**#@-*/
  103.  
  104.     // Execution Bitmap Masks
  105.     const MASK_CONSTRUCTOR   = 0x00000001;
  106.     const MASK_CONNECTED     = 0x00000002;
  107.     const MASK_LOGIN_REQ     = 0x00000004;
  108.     const MASK_LOGIN         = 0x00000008;
  109.     const MASK_SHELL         = 0x00000010;
  110.     const MASK_WINDOW_ADJUST = 0x00000020;
  111.  
  112.     /*
  113.      * Channel constants
  114.      *
  115.      * RFC4254 refers not to client and server channels but rather to sender and recipient channels.  we don't refer
  116.      * to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with
  117.      * a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a
  118.      * recipient channel.  at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
  119.      * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snippet:
  120.      *     The 'recipient channel' is the channel number given in the original
  121.      *     open request, and 'sender channel' is the channel number allocated by
  122.      *     the other side.
  123.      *
  124.      * @see \phpseclib3\Net\SSH2::send_channel_packet()
  125.      * @see \phpseclib3\Net\SSH2::get_channel_packet()
  126.      * @access private
  127.      */
  128.     const CHANNEL_EXEC          = 1; // PuTTy uses 0x100
  129.     const CHANNEL_SHELL         = 2;
  130.     const CHANNEL_SUBSYSTEM     = 3;
  131.     const CHANNEL_AGENT_FORWARD = 4;
  132.     const CHANNEL_KEEP_ALIVE    = 5;
  133.  
  134.     /**
  135.      * Returns the message numbers
  136.      *
  137.      * @access public
  138.      * @see \phpseclib3\Net\SSH2::getLog()
  139.      */
  140.     const LOG_SIMPLE = 1;
  141.     /**
  142.      * Returns the message content
  143.      *
  144.      * @access public
  145.      * @see \phpseclib3\Net\SSH2::getLog()
  146.      */
  147.     const LOG_COMPLEX = 2;
  148.     /**
  149.      * Outputs the content real-time
  150.      *
  151.      * @access public
  152.      * @see \phpseclib3\Net\SSH2::getLog()
  153.      */
  154.     const LOG_REALTIME = 3;
  155.     /**
  156.      * Dumps the content real-time to a file
  157.      *
  158.      * @access public
  159.      * @see \phpseclib3\Net\SSH2::getLog()
  160.      */
  161.     const LOG_REALTIME_FILE = 4;
  162.     /**
  163.      * Make sure that the log never gets larger than this
  164.      *
  165.      * @access public
  166.      * @see \phpseclib3\Net\SSH2::getLog()
  167.      */
  168.     const LOG_MAX_SIZE = 1048576; // 1024 * 1024
  169.  
  170.     /**
  171.      * Returns when a string matching $expect exactly is found
  172.      *
  173.      * @access public
  174.      * @see \phpseclib3\Net\SSH2::read()
  175.      */
  176.     const READ_SIMPLE = 1;
  177.     /**
  178.      * Returns when a string matching the regular expression $expect is found
  179.      *
  180.      * @access public
  181.      * @see \phpseclib3\Net\SSH2::read()
  182.      */
  183.     const READ_REGEX = 2;
  184.     /**
  185.      * Returns whenever a data packet is received.
  186.      *
  187.      * Some data packets may only contain a single character so it may be necessary
  188.      * to call read() multiple times when using this option
  189.      *
  190.      * @access public
  191.      * @see \phpseclib3\Net\SSH2::read()
  192.      */
  193.     const READ_NEXT = 3;
  194.  
  195.     /**
  196.      * The SSH identifier
  197.      *
  198.      * @var string
  199.      * @access private
  200.      */
  201.     private $identifier;
  202.  
  203.     /**
  204.      * The Socket Object
  205.      *
  206.      * @var resource|closed-resource|null
  207.      * @access private
  208.      */
  209.     public $fsock;
  210.  
  211.     /**
  212.      * Execution Bitmap
  213.      *
  214.      * The bits that are set represent functions that have been called already.  This is used to determine
  215.      * if a requisite function has been successfully executed.  If not, an error should be thrown.
  216.      *
  217.      * @var int
  218.      * @access private
  219.      */
  220.     protected $bitmap = 0;
  221.  
  222.     /**
  223.      * Error information
  224.      *
  225.      * @see self::getErrors()
  226.      * @see self::getLastError()
  227.      * @var array
  228.      * @access private
  229.      */
  230.     private $errors = [];
  231.  
  232.     /**
  233.      * Server Identifier
  234.      *
  235.      * @see self::getServerIdentification()
  236.      * @var string|false
  237.      * @access private
  238.      */
  239.     protected $server_identifier = false;
  240.  
  241.     /**
  242.      * Key Exchange Algorithms
  243.      *
  244.      * @see self::getKexAlgorithims()
  245.      * @var array|false
  246.      * @access private
  247.      */
  248.     private $kex_algorithms = false;
  249.  
  250.     /**
  251.      * Key Exchange Algorithm
  252.      *
  253.      * @see self::getMethodsNegotiated()
  254.      * @var string|false
  255.      * @access private
  256.      */
  257.     private $kex_algorithm = false;
  258.  
  259.     /**
  260.      * Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
  261.      *
  262.      * @see self::_key_exchange()
  263.      * @var int
  264.      * @access private
  265.      */
  266.     private $kex_dh_group_size_min = 1536;
  267.  
  268.     /**
  269.      * Preferred Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
  270.      *
  271.      * @see self::_key_exchange()
  272.      * @var int
  273.      * @access private
  274.      */
  275.     private $kex_dh_group_size_preferred = 2048;
  276.  
  277.     /**
  278.      * Maximum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
  279.      *
  280.      * @see self::_key_exchange()
  281.      * @var int
  282.      * @access private
  283.      */
  284.     private $kex_dh_group_size_max = 4096;
  285.  
  286.     /**
  287.      * Server Host Key Algorithms
  288.      *
  289.      * @see self::getServerHostKeyAlgorithms()
  290.      * @var array|false
  291.      * @access private
  292.      */
  293.     private $server_host_key_algorithms = false;
  294.  
  295.     /**
  296.      * Encryption Algorithms: Client to Server
  297.      *
  298.      * @see self::getEncryptionAlgorithmsClient2Server()
  299.      * @var array|false
  300.      * @access private
  301.      */
  302.     private $encryption_algorithms_client_to_server = false;
  303.  
  304.     /**
  305.      * Encryption Algorithms: Server to Client
  306.      *
  307.      * @see self::getEncryptionAlgorithmsServer2Client()
  308.      * @var array|false
  309.      * @access private
  310.      */
  311.     private $encryption_algorithms_server_to_client = false;
  312.  
  313.     /**
  314.      * MAC Algorithms: Client to Server
  315.      *
  316.      * @see self::getMACAlgorithmsClient2Server()
  317.      * @var array|false
  318.      * @access private
  319.      */
  320.     private $mac_algorithms_client_to_server = false;
  321.  
  322.     /**
  323.      * MAC Algorithms: Server to Client
  324.      *
  325.      * @see self::getMACAlgorithmsServer2Client()
  326.      * @var array|false
  327.      * @access private
  328.      */
  329.     private $mac_algorithms_server_to_client = false;
  330.  
  331.     /**
  332.      * Compression Algorithms: Client to Server
  333.      *
  334.      * @see self::getCompressionAlgorithmsClient2Server()
  335.      * @var array|false
  336.      * @access private
  337.      */
  338.     private $compression_algorithms_client_to_server = false;
  339.  
  340.     /**
  341.      * Compression Algorithms: Server to Client
  342.      *
  343.      * @see self::getCompressionAlgorithmsServer2Client()
  344.      * @var array|false
  345.      * @access private
  346.      */
  347.     private $compression_algorithms_server_to_client = false;
  348.  
  349.     /**
  350.      * Languages: Server to Client
  351.      *
  352.      * @see self::getLanguagesServer2Client()
  353.      * @var array|false
  354.      * @access private
  355.      */
  356.     private $languages_server_to_client = false;
  357.  
  358.     /**
  359.      * Languages: Client to Server
  360.      *
  361.      * @see self::getLanguagesClient2Server()
  362.      * @var array|false
  363.      * @access private
  364.      */
  365.     private $languages_client_to_server = false;
  366.  
  367.     /**
  368.      * Preferred Algorithms
  369.      *
  370.      * @see self::setPreferredAlgorithms()
  371.      * @var array
  372.      * @access private
  373.      */
  374.     private $preferred = [];
  375.  
  376.     /**
  377.      * Block Size for Server to Client Encryption
  378.      *
  379.      * "Note that the length of the concatenation of 'packet_length',
  380.      *  'padding_length', 'payload', and 'random padding' MUST be a multiple
  381.      *  of the cipher block size or 8, whichever is larger.  This constraint
  382.      *  MUST be enforced, even when using stream ciphers."
  383.      *
  384.      *  -- http://tools.ietf.org/html/rfc4253#section-6
  385.      *
  386.      * @see self::__construct()
  387.      * @see self::_send_binary_packet()
  388.      * @var int
  389.      * @access private
  390.      */
  391.     private $encrypt_block_size = 8;
  392.  
  393.     /**
  394.      * Block Size for Client to Server Encryption
  395.      *
  396.      * @see self::__construct()
  397.      * @see self::_get_binary_packet()
  398.      * @var int
  399.      * @access private
  400.      */
  401.     private $decrypt_block_size = 8;
  402.  
  403.     /**
  404.      * Server to Client Encryption Object
  405.      *
  406.      * @see self::_get_binary_packet()
  407.      * @var SymmetricKey|false
  408.      * @access private
  409.      */
  410.     private $decrypt = false;
  411.  
  412.     /**
  413.      * Decryption Algorithm Name
  414.      *
  415.      * @var string|null
  416.      * @access private
  417.      */
  418.     private $decryptName;
  419.  
  420.     /**
  421.      * Decryption Invocation Counter
  422.      *
  423.      * Used by GCM
  424.      *
  425.      * @var string|null
  426.      * @access private
  427.      */
  428.     private $decryptInvocationCounter;
  429.  
  430.     /**
  431.      * Fixed Part of Nonce
  432.      *
  433.      * Used by GCM
  434.      *
  435.      * @var string|null
  436.      * @access private
  437.      */
  438.     private $decryptFixedPart;
  439.  
  440.     /**
  441.      * Server to Client Length Encryption Object
  442.      *
  443.      * @see self::_get_binary_packet()
  444.      * @var object
  445.      * @access private
  446.      */
  447.     private $lengthDecrypt = false;
  448.  
  449.     /**
  450.      * Client to Server Encryption Object
  451.      *
  452.      * @see self::_send_binary_packet()
  453.      * @var SymmetricKey|false
  454.      * @access private
  455.      */
  456.     private $encrypt = false;
  457.  
  458.     /**
  459.      * Encryption Algorithm Name
  460.      *
  461.      * @var string|null
  462.      * @access private
  463.      */
  464.     private $encryptName;
  465.  
  466.     /**
  467.      * Encryption Invocation Counter
  468.      *
  469.      * Used by GCM
  470.      *
  471.      * @var string|null
  472.      * @access private
  473.      */
  474.     private $encryptInvocationCounter;
  475.  
  476.     /**
  477.      * Fixed Part of Nonce
  478.      *
  479.      * Used by GCM
  480.      *
  481.      * @var string|null
  482.      * @access private
  483.      */
  484.     private $encryptFixedPart;
  485.  
  486.     /**
  487.      * Client to Server Length Encryption Object
  488.      *
  489.      * @see self::_send_binary_packet()
  490.      * @var object
  491.      * @access private
  492.      */
  493.     private $lengthEncrypt = false;
  494.  
  495.     /**
  496.      * Client to Server HMAC Object
  497.      *
  498.      * @see self::_send_binary_packet()
  499.      * @var object
  500.      * @access private
  501.      */
  502.     private $hmac_create = false;
  503.  
  504.     /**
  505.      * Client to Server HMAC Name
  506.      *
  507.      * @var string|false
  508.      * @access private
  509.      */
  510.     private $hmac_create_name;
  511.  
  512.     /**
  513.      * Client to Server ETM
  514.      *
  515.      * @var int|false
  516.      * @access private
  517.      */
  518.     private $hmac_create_etm;
  519.  
  520.     /**
  521.      * Server to Client HMAC Object
  522.      *
  523.      * @see self::_get_binary_packet()
  524.      * @var object
  525.      * @access private
  526.      */
  527.     private $hmac_check = false;
  528.  
  529.     /**
  530.      * Server to Client HMAC Name
  531.      *
  532.      * @var string|false
  533.      * @access private
  534.      */
  535.     private $hmac_check_name;
  536.  
  537.     /**
  538.      * Server to Client ETM
  539.      *
  540.      * @var int|false
  541.      * @access private
  542.      */
  543.     private $hmac_check_etm;
  544.  
  545.     /**
  546.      * Size of server to client HMAC
  547.      *
  548.      * We need to know how big the HMAC will be for the server to client direction so that we know how many bytes to read.
  549.      * For the client to server side, the HMAC object will make the HMAC as long as it needs to be.  All we need to do is
  550.      * append it.
  551.      *
  552.      * @see self::_get_binary_packet()
  553.      * @var int
  554.      * @access private
  555.      */
  556.     private $hmac_size = false;
  557.  
  558.     /**
  559.      * Server Public Host Key
  560.      *
  561.      * @see self::getServerPublicHostKey()
  562.      * @var string
  563.      * @access private
  564.      */
  565.     private $server_public_host_key;
  566.  
  567.     /**
  568.      * Session identifier
  569.      *
  570.      * "The exchange hash H from the first key exchange is additionally
  571.      *  used as the session identifier, which is a unique identifier for
  572.      *  this connection."
  573.      *
  574.      *  -- http://tools.ietf.org/html/rfc4253#section-7.2
  575.      *
  576.      * @see self::_key_exchange()
  577.      * @var string
  578.      * @access private
  579.      */
  580.     private $session_id = false;
  581.  
  582.     /**
  583.      * Exchange hash
  584.      *
  585.      * The current exchange hash
  586.      *
  587.      * @see self::_key_exchange()
  588.      * @var string
  589.      * @access private
  590.      */
  591.     private $exchange_hash = false;
  592.  
  593.     /**
  594.      * Message Numbers
  595.      *
  596.      * @see self::__construct()
  597.      * @var array
  598.      * @access private
  599.      */
  600.     private $message_numbers = [];
  601.  
  602.     /**
  603.      * Disconnection Message 'reason codes' defined in RFC4253
  604.      *
  605.      * @see self::__construct()
  606.      * @var array
  607.      * @access private
  608.      */
  609.     private $disconnect_reasons = [];
  610.  
  611.     /**
  612.      * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254
  613.      *
  614.      * @see self::__construct()
  615.      * @var array
  616.      * @access private
  617.      */
  618.     private $channel_open_failure_reasons = [];
  619.  
  620.     /**
  621.      * Terminal Modes
  622.      *
  623.      * @link http://tools.ietf.org/html/rfc4254#section-8
  624.      * @see self::__construct()
  625.      * @var array
  626.      * @access private
  627.      */
  628.     private $terminal_modes = [];
  629.  
  630.     /**
  631.      * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes
  632.      *
  633.      * @link http://tools.ietf.org/html/rfc4254#section-5.2
  634.      * @see self::__construct()
  635.      * @var array
  636.      * @access private
  637.      */
  638.     private $channel_extended_data_type_codes = [];
  639.  
  640.     /**
  641.      * Send Sequence Number
  642.      *
  643.      * See 'Section 6.4.  Data Integrity' of rfc4253 for more info.
  644.      *
  645.      * @see self::_send_binary_packet()
  646.      * @var int
  647.      * @access private
  648.      */
  649.     private $send_seq_no = 0;
  650.  
  651.     /**
  652.      * Get Sequence Number
  653.      *
  654.      * See 'Section 6.4.  Data Integrity' of rfc4253 for more info.
  655.      *
  656.      * @see self::_get_binary_packet()
  657.      * @var int
  658.      * @access private
  659.      */
  660.     private $get_seq_no = 0;
  661.  
  662.     /**
  663.      * Server Channels
  664.      *
  665.      * Maps client channels to server channels
  666.      *
  667.      * @see self::get_channel_packet()
  668.      * @see self::exec()
  669.      * @var array
  670.      * @access private
  671.      */
  672.     protected $server_channels = [];
  673.  
  674.     /**
  675.      * Channel Buffers
  676.      *
  677.      * If a client requests a packet from one channel but receives two packets from another those packets should
  678.      * be placed in a buffer
  679.      *
  680.      * @see self::get_channel_packet()
  681.      * @see self::exec()
  682.      * @var array
  683.      * @access private
  684.      */
  685.     private $channel_buffers = [];
  686.  
  687.     /**
  688.      * Channel Status
  689.      *
  690.      * Contains the type of the last sent message
  691.      *
  692.      * @see self::get_channel_packet()
  693.      * @var array
  694.      * @access private
  695.      */
  696.     protected $channel_status = [];
  697.  
  698.     /**
  699.      * Packet Size
  700.      *
  701.      * Maximum packet size indexed by channel
  702.      *
  703.      * @see self::send_channel_packet()
  704.      * @var array
  705.      * @access private
  706.      */
  707.     private $packet_size_client_to_server = [];
  708.  
  709.     /**
  710.      * Message Number Log
  711.      *
  712.      * @see self::getLog()
  713.      * @var array
  714.      * @access private
  715.      */
  716.     private $message_number_log = [];
  717.  
  718.     /**
  719.      * Message Log
  720.      *
  721.      * @see self::getLog()
  722.      * @var array
  723.      * @access private
  724.      */
  725.     private $message_log = [];
  726.  
  727.     /**
  728.      * The Window Size
  729.      *
  730.      * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 2GB)
  731.      *
  732.      * @var int
  733.      * @see self::send_channel_packet()
  734.      * @see self::exec()
  735.      * @access private
  736.      */
  737.     protected $window_size = 0x7FFFFFFF;
  738.  
  739.     /**
  740.      * What we resize the window to
  741.      *
  742.      * When PuTTY resizes the window it doesn't add an additional 0x7FFFFFFF bytes - it adds 0x40000000 bytes.
  743.      * Some SFTP clients (GoAnywhere) don't support adding 0x7FFFFFFF to the window size after the fact so
  744.      * we'll just do what PuTTY does
  745.      *
  746.      * @var int
  747.      * @see self::_send_channel_packet()
  748.      * @see self::exec()
  749.      * @access private
  750.      */
  751.     private $window_resize = 0x40000000;
  752.  
  753.     /**
  754.      * Window size, server to client
  755.      *
  756.      * Window size indexed by channel
  757.      *
  758.      * @see self::send_channel_packet()
  759.      * @var array
  760.      * @access private
  761.      */
  762.     protected $window_size_server_to_client = [];
  763.  
  764.     /**
  765.      * Window size, client to server
  766.      *
  767.      * Window size indexed by channel
  768.      *
  769.      * @see self::get_channel_packet()
  770.      * @var array
  771.      * @access private
  772.      */
  773.     private $window_size_client_to_server = [];
  774.  
  775.     /**
  776.      * Server signature
  777.      *
  778.      * Verified against $this->session_id
  779.      *
  780.      * @see self::getServerPublicHostKey()
  781.      * @var string
  782.      * @access private
  783.      */
  784.     private $signature = '';
  785.  
  786.     /**
  787.      * Server signature format
  788.      *
  789.      * ssh-rsa or ssh-dss.
  790.      *
  791.      * @see self::getServerPublicHostKey()
  792.      * @var string
  793.      * @access private
  794.      */
  795.     private $signature_format = '';
  796.  
  797.     /**
  798.      * Interactive Buffer
  799.      *
  800.      * @see self::read()
  801.      * @var string
  802.      * @access private
  803.      */
  804.     private $interactiveBuffer = '';
  805.  
  806.     /**
  807.      * Current log size
  808.      *
  809.      * Should never exceed self::LOG_MAX_SIZE
  810.      *
  811.      * @see self::_send_binary_packet()
  812.      * @see self::_get_binary_packet()
  813.      * @var int
  814.      * @access private
  815.      */
  816.     private $log_size;
  817.  
  818.     /**
  819.      * Timeout
  820.      *
  821.      * @see self::setTimeout()
  822.      * @access private
  823.      */
  824.     protected $timeout;
  825.  
  826.     /**
  827.      * Current Timeout
  828.      *
  829.      * @see self::get_channel_packet()
  830.      * @access private
  831.      */
  832.     protected $curTimeout;
  833.  
  834.     /**
  835.      * Keep Alive Interval
  836.      *
  837.      * @see self::setKeepAlive()
  838.      * @access private
  839.      */
  840.     private $keepAlive;
  841.  
  842.     /**
  843.      * Real-time log file pointer
  844.      *
  845.      * @see self::_append_log()
  846.      * @var resource|closed-resource
  847.      * @access private
  848.      */
  849.     private $realtime_log_file;
  850.  
  851.     /**
  852.      * Real-time log file size
  853.      *
  854.      * @see self::_append_log()
  855.      * @var int
  856.      * @access private
  857.      */
  858.     private $realtime_log_size;
  859.  
  860.     /**
  861.      * Has the signature been validated?
  862.      *
  863.      * @see self::getServerPublicHostKey()
  864.      * @var bool
  865.      * @access private
  866.      */
  867.     private $signature_validated = false;
  868.  
  869.     /**
  870.      * Real-time log file wrap boolean
  871.      *
  872.      * @see self::_append_log()
  873.      * @access private
  874.      */
  875.     private $realtime_log_wrap;
  876.  
  877.     /**
  878.      * Flag to suppress stderr from output
  879.      *
  880.      * @see self::enableQuietMode()
  881.      * @access private
  882.      */
  883.     private $quiet_mode = false;
  884.  
  885.     /**
  886.      * Time of first network activity
  887.      *
  888.      * @var float
  889.      * @access private
  890.      */
  891.     private $last_packet;
  892.  
  893.     /**
  894.      * Exit status returned from ssh if any
  895.      *
  896.      * @var int
  897.      * @access private
  898.      */
  899.     private $exit_status;
  900.  
  901.     /**
  902.      * Flag to request a PTY when using exec()
  903.      *
  904.      * @var bool
  905.      * @see self::enablePTY()
  906.      * @access private
  907.      */
  908.     private $request_pty = false;
  909.  
  910.     /**
  911.      * Flag set while exec() is running when using enablePTY()
  912.      *
  913.      * @var bool
  914.      * @access private
  915.      */
  916.     private $in_request_pty_exec = false;
  917.  
  918.     /**
  919.      * Flag set after startSubsystem() is called
  920.      *
  921.      * @var bool
  922.      * @access private
  923.      */
  924.     private $in_subsystem;
  925.  
  926.     /**
  927.      * Contents of stdError
  928.      *
  929.      * @var string
  930.      * @access private
  931.      */
  932.     private $stdErrorLog;
  933.  
  934.     /**
  935.      * The Last Interactive Response
  936.      *
  937.      * @see self::_keyboard_interactive_process()
  938.      * @var string
  939.      * @access private
  940.      */
  941.     private $last_interactive_response = '';
  942.  
  943.     /**
  944.      * Keyboard Interactive Request / Responses
  945.      *
  946.      * @see self::_keyboard_interactive_process()
  947.      * @var array
  948.      * @access private
  949.      */
  950.     private $keyboard_requests_responses = [];
  951.  
  952.     /**
  953.      * Banner Message
  954.      *
  955.      * Quoting from the RFC, "in some jurisdictions, sending a warning message before
  956.      * authentication may be relevant for getting legal protection."
  957.      *
  958.      * @see self::_filter()
  959.      * @see self::getBannerMessage()
  960.      * @var string
  961.      * @access private
  962.      */
  963.     private $banner_message = '';
  964.  
  965.     /**
  966.      * Did read() timeout or return normally?
  967.      *
  968.      * @see self::isTimeout()
  969.      * @var bool
  970.      * @access private
  971.      */
  972.     private $is_timeout = false;
  973.  
  974.     /**
  975.      * Log Boundary
  976.      *
  977.      * @see self::_format_log()
  978.      * @var string
  979.      * @access private
  980.      */
  981.     private $log_boundary = ':';
  982.  
  983.     /**
  984.      * Log Long Width
  985.      *
  986.      * @see self::_format_log()
  987.      * @var int
  988.      * @access private
  989.      */
  990.     private $log_long_width = 65;
  991.  
  992.     /**
  993.      * Log Short Width
  994.      *
  995.      * @see self::_format_log()
  996.      * @var int
  997.      * @access private
  998.      */
  999.     private $log_short_width = 16;
  1000.  
  1001.     /**
  1002.      * Hostname
  1003.      *
  1004.      * @see self::__construct()
  1005.      * @see self::_connect()
  1006.      * @var string
  1007.      * @access private
  1008.      */
  1009.     private $host;
  1010.  
  1011.     /**
  1012.      * Port Number
  1013.      *
  1014.      * @see self::__construct()
  1015.      * @see self::_connect()
  1016.      * @var int
  1017.      * @access private
  1018.      */
  1019.     private $port;
  1020.  
  1021.     /**
  1022.      * Number of columns for terminal window size
  1023.      *
  1024.      * @see self::getWindowColumns()
  1025.      * @see self::setWindowColumns()
  1026.      * @see self::setWindowSize()
  1027.      * @var int
  1028.      * @access private
  1029.      */
  1030.     private $windowColumns = 80;
  1031.  
  1032.     /**
  1033.      * Number of columns for terminal window size
  1034.      *
  1035.      * @see self::getWindowRows()
  1036.      * @see self::setWindowRows()
  1037.      * @see self::setWindowSize()
  1038.      * @var int
  1039.      * @access private
  1040.      */
  1041.     private $windowRows = 24;
  1042.  
  1043.     /**
  1044.      * Crypto Engine
  1045.      *
  1046.      * @see self::setCryptoEngine()
  1047.      * @see self::_key_exchange()
  1048.      * @var int
  1049.      * @access private
  1050.      */
  1051.     private static $crypto_engine = false;
  1052.  
  1053.     /**
  1054.      * A System_SSH_Agent for use in the SSH2 Agent Forwarding scenario
  1055.      *
  1056.      * @var Agent
  1057.      * @access private
  1058.      */
  1059.     private $agent;
  1060.  
  1061.     /**
  1062.      * Connection storage to replicates ssh2 extension functionality:
  1063.      * {@link http://php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-examples}
  1064.      *
  1065.      * @var array<string, SSH2|\WeakReference<SSH2>>
  1066.      */
  1067.     private static $connections;
  1068.  
  1069.     /**
  1070.      * Send the identification string first?
  1071.      *
  1072.      * @var bool
  1073.      * @access private
  1074.      */
  1075.     private $send_id_string_first = true;
  1076.  
  1077.     /**
  1078.      * Send the key exchange initiation packet first?
  1079.      *
  1080.      * @var bool
  1081.      * @access private
  1082.      */
  1083.     private $send_kex_first = true;
  1084.  
  1085.     /**
  1086.      * Some versions of OpenSSH incorrectly calculate the key size
  1087.      *
  1088.      * @var bool
  1089.      * @access private
  1090.      */
  1091.     private $bad_key_size_fix = false;
  1092.  
  1093.     /**
  1094.      * Should we try to re-connect to re-establish keys?
  1095.      *
  1096.      * @var bool
  1097.      * @access private
  1098.      */
  1099.     private $retry_connect = false;
  1100.  
  1101.     /**
  1102.      * Binary Packet Buffer
  1103.      *
  1104.      * @var string|false
  1105.      * @access private
  1106.      */
  1107.     private $binary_packet_buffer = false;
  1108.  
  1109.     /**
  1110.      * Preferred Signature Format
  1111.      *
  1112.      * @var string|false
  1113.      * @access private
  1114.      */
  1115.     protected $preferred_signature_format = false;
  1116.  
  1117.     /**
  1118.      * Authentication Credentials
  1119.      *
  1120.      * @var array
  1121.      * @access private
  1122.      */
  1123.     protected $auth = [];
  1124.  
  1125.     /**
  1126.      * Terminal
  1127.      *
  1128.      * @var string
  1129.      * @access private
  1130.      */
  1131.     private $term = 'vt100';
  1132.  
  1133.     /**
  1134.      * The authentication methods that may productively continue authentication.
  1135.      *
  1136.      * @see https://tools.ietf.org/html/rfc4252#section-5.1
  1137.      * @var array|null
  1138.      * @access private
  1139.      */
  1140.     private $auth_methods_to_continue = null;
  1141.  
  1142.     /**
  1143.      * Compression method
  1144.      *
  1145.      * @var int
  1146.      * @access private
  1147.      */
  1148.     private $compress = self::NET_SSH2_COMPRESSION_NONE;
  1149.  
  1150.     /**
  1151.      * Decompression method
  1152.      *
  1153.      * @var int
  1154.      * @access private
  1155.      */
  1156.     private $decompress = self::NET_SSH2_COMPRESSION_NONE;
  1157.  
  1158.     /**
  1159.      * Compression context
  1160.      *
  1161.      * @var resource|false|null
  1162.      * @access private
  1163.      */
  1164.     private $compress_context;
  1165.  
  1166.     /**
  1167.      * Decompression context
  1168.      *
  1169.      * @var resource|object
  1170.      * @access private
  1171.      */
  1172.     private $decompress_context;
  1173.  
  1174.     /**
  1175.      * Regenerate Compression Context
  1176.      *
  1177.      * @var bool
  1178.      * @access private
  1179.      */
  1180.     private $regenerate_compression_context = false;
  1181.  
  1182.     /**
  1183.      * Regenerate Decompression Context
  1184.      *
  1185.      * @var bool
  1186.      * @access private
  1187.      */
  1188.     private $regenerate_decompression_context = false;
  1189.  
  1190.     /**
  1191.      * Smart multi-factor authentication flag
  1192.      *
  1193.      * @var bool
  1194.      * @access private
  1195.      */
  1196.     private $smartMFA = true;
  1197.  
  1198.     /**
  1199.      * Default Constructor.
  1200.      *
  1201.      * $host can either be a string, representing the host, or a stream resource.
  1202.      *
  1203.      * @param mixed $host
  1204.      * @param int $port
  1205.      * @param int $timeout
  1206.      * @see self::login()
  1207.      * @access public
  1208.      */
  1209.     public function __construct($host, $port = 22, $timeout = 10)
  1210.     {
  1211.         $this->message_numbers = [
  1212.             1 => 'NET_SSH2_MSG_DISCONNECT',
  1213.             2 => 'NET_SSH2_MSG_IGNORE',
  1214.             3 => 'NET_SSH2_MSG_UNIMPLEMENTED',
  1215.             4 => 'NET_SSH2_MSG_DEBUG',
  1216.             5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
  1217.             6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
  1218.             20 => 'NET_SSH2_MSG_KEXINIT',
  1219.             21 => 'NET_SSH2_MSG_NEWKEYS',
  1220.             30 => 'NET_SSH2_MSG_KEXDH_INIT',
  1221.             31 => 'NET_SSH2_MSG_KEXDH_REPLY',
  1222.             50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
  1223.             51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
  1224.             52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
  1225.             53 => 'NET_SSH2_MSG_USERAUTH_BANNER',
  1226.  
  1227.             80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
  1228.             81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
  1229.             82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
  1230.             90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
  1231.             91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
  1232.             92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
  1233.             93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
  1234.             94 => 'NET_SSH2_MSG_CHANNEL_DATA',
  1235.             95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
  1236.             96 => 'NET_SSH2_MSG_CHANNEL_EOF',
  1237.             97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
  1238.             98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
  1239.             99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
  1240.             100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
  1241.         ];
  1242.         $this->disconnect_reasons = [
  1243.             1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
  1244.             2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
  1245.             3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
  1246.             4 => 'NET_SSH2_DISCONNECT_RESERVED',
  1247.             5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
  1248.             6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
  1249.             7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
  1250.             8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
  1251.             9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
  1252.             10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
  1253.             11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
  1254.             12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
  1255.             13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
  1256.             14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
  1257.             15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
  1258.         ];
  1259.         $this->channel_open_failure_reasons = [
  1260.             1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
  1261.         ];
  1262.         $this->terminal_modes = [
  1263.             0 => 'NET_SSH2_TTY_OP_END'
  1264.         ];
  1265.         $this->channel_extended_data_type_codes = [
  1266.             1 => 'NET_SSH2_EXTENDED_DATA_STDERR'
  1267.         ];
  1268.  
  1269.         $this->define_array(
  1270.             $this->message_numbers,
  1271.             $this->disconnect_reasons,
  1272.             $this->channel_open_failure_reasons,
  1273.             $this->terminal_modes,
  1274.             $this->channel_extended_data_type_codes,
  1275.             [60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'],
  1276.             [60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'],
  1277.             [60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
  1278.                   61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'],
  1279.             // RFC 4419 - diffie-hellman-group-exchange-sha{1,256}
  1280.             [30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD',
  1281.                   31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP',
  1282.                   32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT',
  1283.                   33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY',
  1284.                   34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'],
  1285.             // RFC 5656 - Elliptic Curves (for curve25519-sha256@libssh.org)
  1286.             [30 => 'NET_SSH2_MSG_KEX_ECDH_INIT',
  1287.                   31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY']
  1288.         );
  1289.  
  1290.         /**
  1291.          * Typehint is required due to a bug in Psalm: https://github.com/vimeo/psalm/issues/7508
  1292.          * @var \WeakReference<SSH2>|SSH2
  1293.          */
  1294.         self::$connections[$this->getResourceId()] = class_exists('WeakReference')
  1295.             ? \WeakReference::create($this)
  1296.             : $this;
  1297.  
  1298.         if (is_resource($host)) {
  1299.             $this->fsock = $host;
  1300.             return;
  1301.         }
  1302.  
  1303.         if (Strings::is_stringable($host)) {
  1304.             $this->host = $host;
  1305.             $this->port = $port;
  1306.             $this->timeout = $timeout;
  1307.         }
  1308.     }
  1309.  
  1310.     /**
  1311.      * Set Crypto Engine Mode
  1312.      *
  1313.      * Possible $engine values:
  1314.      * OpenSSL, mcrypt, Eval, PHP
  1315.      *
  1316.      * @param int $engine
  1317.      * @access public
  1318.      */
  1319.     public static function setCryptoEngine($engine)
  1320.     {
  1321.         self::$crypto_engine = $engine;
  1322.     }
  1323.  
  1324.     /**
  1325.      * Send Identification String First
  1326.      *
  1327.      * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
  1328.      * both sides MUST send an identification string". It does not say which side sends it first. In
  1329.      * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
  1330.      *
  1331.      * @access public
  1332.      */
  1333.     public function sendIdentificationStringFirst()
  1334.     {
  1335.         $this->send_id_string_first = true;
  1336.     }
  1337.  
  1338.     /**
  1339.      * Send Identification String Last
  1340.      *
  1341.      * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
  1342.      * both sides MUST send an identification string". It does not say which side sends it first. In
  1343.      * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
  1344.      *
  1345.      * @access public
  1346.      */
  1347.     public function sendIdentificationStringLast()
  1348.     {
  1349.         $this->send_id_string_first = false;
  1350.     }
  1351.  
  1352.     /**
  1353.      * Send SSH_MSG_KEXINIT First
  1354.      *
  1355.      * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
  1356.      * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
  1357.      * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
  1358.      *
  1359.      * @access public
  1360.      */
  1361.     public function sendKEXINITFirst()
  1362.     {
  1363.         $this->send_kex_first = true;
  1364.     }
  1365.  
  1366.     /**
  1367.      * Send SSH_MSG_KEXINIT Last
  1368.      *
  1369.      * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
  1370.      * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
  1371.      * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
  1372.      *
  1373.      * @access public
  1374.      */
  1375.     public function sendKEXINITLast()
  1376.     {
  1377.         $this->send_kex_first = false;
  1378.     }
  1379.  
  1380.     /**
  1381.      * Connect to an SSHv2 server
  1382.      *
  1383.      * @throws \UnexpectedValueException on receipt of unexpected packets
  1384.      * @throws \RuntimeException on other errors
  1385.      * @access private
  1386.      */
  1387.     private function connect()
  1388.     {
  1389.         if ($this->bitmap & self::MASK_CONSTRUCTOR) {
  1390.             return;
  1391.         }
  1392.  
  1393.         $this->bitmap |= self::MASK_CONSTRUCTOR;
  1394.  
  1395.         $this->curTimeout = $this->timeout;
  1396.  
  1397.         $this->last_packet = microtime(true);
  1398.  
  1399.         if (!is_resource($this->fsock)) {
  1400.             $start = microtime(true);
  1401.             // with stream_select a timeout of 0 means that no timeout takes place;
  1402.             // with fsockopen a timeout of 0 means that you instantly timeout
  1403.             // to resolve this incompatibility a timeout of 100,000 will be used for fsockopen if timeout is 0
  1404.             $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout == 0 ? 100000 : $this->curTimeout);
  1405.             if (!$this->fsock) {
  1406.                 $host = $this->host . ':' . $this->port;
  1407.                 throw new UnableToConnectException(rtrim("Cannot connect to $host. Error $errno. $errstr"));
  1408.             }
  1409.             $elapsed = microtime(true) - $start;
  1410.  
  1411.             if ($this->curTimeout) {
  1412.                 $this->curTimeout -= $elapsed;
  1413.                 if ($this->curTimeout < 0) {
  1414.                     throw new \RuntimeException('Connection timed out whilst attempting to open socket connection');
  1415.                 }
  1416.             }
  1417.         }
  1418.  
  1419.         $this->identifier = $this->generate_identifier();
  1420.  
  1421.         if ($this->send_id_string_first) {
  1422.             fputs($this->fsock, $this->identifier . "\r\n");
  1423.         }
  1424.  
  1425.         /* According to the SSH2 specs,
  1426.  
  1427.           "The server MAY send other lines of data before sending the version
  1428.            string.  Each line SHOULD be terminated by a Carriage Return and Line
  1429.            Feed.  Such lines MUST NOT begin with "SSH-", and SHOULD be encoded
  1430.            in ISO-10646 UTF-8 [RFC3629] (language is not specified).  Clients
  1431.            MUST be able to process such lines." */
  1432.         $data = '';
  1433.         while (!feof($this->fsock) && !preg_match('#(.*)^(SSH-(\d\.\d+).*)#ms', $data, $matches)) {
  1434.             $line = '';
  1435.             while (true) {
  1436.                 if ($this->curTimeout) {
  1437.                     if ($this->curTimeout < 0) {
  1438.                         throw new \RuntimeException('Connection timed out whilst receiving server identification string');
  1439.                     }
  1440.                     $read = [$this->fsock];
  1441.                     $write = $except = null;
  1442.                     $start = microtime(true);
  1443.                     $sec = (int) floor($this->curTimeout);
  1444.                     $usec = (int) (1000000 * ($this->curTimeout - $sec));
  1445.                     if (@stream_select($read, $write, $except, $sec, $usec) === false) {
  1446.                         throw new \RuntimeException('Connection timed out whilst receiving server identification string');
  1447.                     }
  1448.                     $elapsed = microtime(true) - $start;
  1449.                     $this->curTimeout -= $elapsed;
  1450.                 }
  1451.  
  1452.                 $temp = stream_get_line($this->fsock, 255, "\n");
  1453.                 if ($temp === false) {
  1454.                     throw new \RuntimeException('Error reading from socket');
  1455.                 }
  1456.                 if (strlen($temp) == 255) {
  1457.                     continue;
  1458.                 }
  1459.  
  1460.                 $line .= "$temp\n";
  1461.  
  1462.                 // quoting RFC4253, "Implementers who wish to maintain
  1463.                 // compatibility with older, undocumented versions of this protocol may
  1464.                 // want to process the identification string without expecting the
  1465.                 // presence of the carriage return character for reasons described in
  1466.                 // Section 5 of this document."
  1467.  
  1468.                 //if (substr($line, -2) == "\r\n") {
  1469.                 //    break;
  1470.                 //}
  1471.  
  1472.                 break;
  1473.             }
  1474.  
  1475.             $data .= $line;
  1476.         }
  1477.  
  1478.         if (feof($this->fsock)) {
  1479.             $this->bitmap = 0;
  1480.             throw new ConnectionClosedException('Connection closed by server');
  1481.         }
  1482.  
  1483.         $extra = $matches[1];
  1484.  
  1485.         if (defined('NET_SSH2_LOGGING')) {
  1486.             $this->append_log('<-', $matches[0]);
  1487.             $this->append_log('->', $this->identifier . "\r\n");
  1488.         }
  1489.  
  1490.         $this->server_identifier = trim($temp, "\r\n");
  1491.         if (strlen($extra)) {
  1492.             $this->errors[] = $data;
  1493.         }
  1494.  
  1495.         if (version_compare($matches[3], '1.99', '<')) {
  1496.             $this->bitmap = 0;
  1497.             throw new UnableToConnectException("Cannot connect to SSH $matches[3] servers");
  1498.         }
  1499.  
  1500.         if (!$this->send_id_string_first) {
  1501.             fputs($this->fsock, $this->identifier . "\r\n");
  1502.         }
  1503.  
  1504.         if (!$this->send_kex_first) {
  1505.             $response = $this->get_binary_packet();
  1506.  
  1507.             if (is_bool($response) || !strlen($response) || ord($response[0]) != NET_SSH2_MSG_KEXINIT) {
  1508.                 $this->bitmap = 0;
  1509.                 throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT');
  1510.             }
  1511.  
  1512.             $this->key_exchange($response);
  1513.         }
  1514.  
  1515.         if ($this->send_kex_first) {
  1516.             $this->key_exchange();
  1517.         }
  1518.  
  1519.         $this->bitmap |= self::MASK_CONNECTED;
  1520.  
  1521.         return true;
  1522.     }
  1523.  
  1524.     /**
  1525.      * Generates the SSH identifier
  1526.      *
  1527.      * You should overwrite this method in your own class if you want to use another identifier
  1528.      *
  1529.      * @access protected
  1530.      * @return string
  1531.      */
  1532.     private function generate_identifier()
  1533.     {
  1534.         $identifier = 'SSH-2.0-phpseclib_3.0';
  1535.  
  1536.         $ext = [];
  1537.         if (extension_loaded('sodium')) {
  1538.             $ext[] = 'libsodium';
  1539.         }
  1540.  
  1541.         if (extension_loaded('openssl')) {
  1542.             $ext[] = 'openssl';
  1543.         } elseif (extension_loaded('mcrypt')) {
  1544.             $ext[] = 'mcrypt';
  1545.         }
  1546.  
  1547.         if (extension_loaded('gmp')) {
  1548.             $ext[] = 'gmp';
  1549.         } elseif (extension_loaded('bcmath')) {
  1550.             $ext[] = 'bcmath';
  1551.         }
  1552.  
  1553.         if (!empty($ext)) {
  1554.             $identifier .= ' (' . implode(', ', $ext) . ')';
  1555.         }
  1556.  
  1557.         return $identifier;
  1558.     }
  1559.  
  1560.     /**
  1561.      * Key Exchange
  1562.      *
  1563.      * @return bool
  1564.      * @param string|bool $kexinit_payload_server optional
  1565.      * @throws \UnexpectedValueException on receipt of unexpected packets
  1566.      * @throws \RuntimeException on other errors
  1567.      * @throws \phpseclib3\Exception\NoSupportedAlgorithmsException when none of the algorithms phpseclib has loaded are compatible
  1568.      * @access private
  1569.      */
  1570.     private function key_exchange($kexinit_payload_server = false)
  1571.     {
  1572.         $preferred = $this->preferred;
  1573.         $send_kex = true;
  1574.  
  1575.         $kex_algorithms = isset($preferred['kex']) ?
  1576.             $preferred['kex'] :
  1577.             SSH2::getSupportedKEXAlgorithms();
  1578.         $server_host_key_algorithms = isset($preferred['hostkey']) ?
  1579.             $preferred['hostkey'] :
  1580.             SSH2::getSupportedHostKeyAlgorithms();
  1581.         $s2c_encryption_algorithms = isset($preferred['server_to_client']['crypt']) ?
  1582.             $preferred['server_to_client']['crypt'] :
  1583.             SSH2::getSupportedEncryptionAlgorithms();
  1584.         $c2s_encryption_algorithms = isset($preferred['client_to_server']['crypt']) ?
  1585.             $preferred['client_to_server']['crypt'] :
  1586.             SSH2::getSupportedEncryptionAlgorithms();
  1587.         $s2c_mac_algorithms = isset($preferred['server_to_client']['mac']) ?
  1588.             $preferred['server_to_client']['mac'] :
  1589.             SSH2::getSupportedMACAlgorithms();
  1590.         $c2s_mac_algorithms = isset($preferred['client_to_server']['mac']) ?
  1591.             $preferred['client_to_server']['mac'] :
  1592.             SSH2::getSupportedMACAlgorithms();
  1593.         $s2c_compression_algorithms = isset($preferred['server_to_client']['comp']) ?
  1594.             $preferred['server_to_client']['comp'] :
  1595.             SSH2::getSupportedCompressionAlgorithms();
  1596.         $c2s_compression_algorithms = isset($preferred['client_to_server']['comp']) ?
  1597.             $preferred['client_to_server']['comp'] :
  1598.             SSH2::getSupportedCompressionAlgorithms();
  1599.  
  1600.         // some SSH servers have buggy implementations of some of the above algorithms
  1601.         switch (true) {
  1602.             case $this->server_identifier == 'SSH-2.0-SSHD':
  1603.             case substr($this->server_identifier, 0, 13) == 'SSH-2.0-DLINK':
  1604.                 if (!isset($preferred['server_to_client']['mac'])) {
  1605.                     $s2c_mac_algorithms = array_values(array_diff(
  1606.                         $s2c_mac_algorithms,
  1607.                         ['hmac-sha1-96', 'hmac-md5-96']
  1608.                     ));
  1609.                 }
  1610.                 if (!isset($preferred['client_to_server']['mac'])) {
  1611.                     $c2s_mac_algorithms = array_values(array_diff(
  1612.                         $c2s_mac_algorithms,
  1613.                         ['hmac-sha1-96', 'hmac-md5-96']
  1614.                     ));
  1615.                 }
  1616.         }
  1617.  
  1618.         $client_cookie = Random::string(16);
  1619.  
  1620.         $kexinit_payload_client = pack('Ca*', NET_SSH2_MSG_KEXINIT, $client_cookie);
  1621.         $kexinit_payload_client .= Strings::packSSH2(
  1622.             'L10bN',
  1623.             $kex_algorithms,
  1624.             $server_host_key_algorithms,
  1625.             $c2s_encryption_algorithms,
  1626.             $s2c_encryption_algorithms,
  1627.             $c2s_mac_algorithms,
  1628.             $s2c_mac_algorithms,
  1629.             $c2s_compression_algorithms,
  1630.             $s2c_compression_algorithms,
  1631.             [], // language, client to server
  1632.             [], // language, server to client
  1633.             false, // first_kex_packet_follows
  1634.             0 // reserved for future extension
  1635.         );
  1636.  
  1637.         if ($kexinit_payload_server === false) {
  1638.             $this->send_binary_packet($kexinit_payload_client);
  1639.  
  1640.             $kexinit_payload_server = $this->get_binary_packet();
  1641.  
  1642.             if (
  1643.                 is_bool($kexinit_payload_server)
  1644.                 || !strlen($kexinit_payload_server)
  1645.                 || ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT
  1646.             ) {
  1647.                 $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR);
  1648.                 throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT');
  1649.             }
  1650.  
  1651.             $send_kex = false;
  1652.         }
  1653.  
  1654.         $response = $kexinit_payload_server;
  1655.         Strings::shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT)
  1656.         $server_cookie = Strings::shift($response, 16);
  1657.  
  1658.         list(
  1659.             $this->kex_algorithms,
  1660.             $this->server_host_key_algorithms,
  1661.             $this->encryption_algorithms_client_to_server,
  1662.             $this->encryption_algorithms_server_to_client,
  1663.             $this->mac_algorithms_client_to_server,
  1664.             $this->mac_algorithms_server_to_client,
  1665.             $this->compression_algorithms_client_to_server,
  1666.             $this->compression_algorithms_server_to_client,
  1667.             $this->languages_client_to_server,
  1668.             $this->languages_server_to_client,
  1669.             $first_kex_packet_follows
  1670.         ) = Strings::unpackSSH2('L10C', $response);
  1671.  
  1672.         if ($send_kex) {
  1673.             $this->send_binary_packet($kexinit_payload_client);
  1674.         }
  1675.  
  1676.         // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange
  1677.  
  1678.         // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
  1679.         // diffie-hellman key exchange as fast as possible
  1680.         $decrypt = self::array_intersect_first($s2c_encryption_algorithms, $this->encryption_algorithms_server_to_client);
  1681.         $decryptKeyLength = $this->encryption_algorithm_to_key_size($decrypt);
  1682.         if ($decryptKeyLength === null) {
  1683.             $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1684.             throw new NoSupportedAlgorithmsException('No compatible server to client encryption algorithms found');
  1685.         }
  1686.  
  1687.         $encrypt = self::array_intersect_first($c2s_encryption_algorithms, $this->encryption_algorithms_client_to_server);
  1688.         $encryptKeyLength = $this->encryption_algorithm_to_key_size($encrypt);
  1689.         if ($encryptKeyLength === null) {
  1690.             $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1691.             throw new NoSupportedAlgorithmsException('No compatible client to server encryption algorithms found');
  1692.         }
  1693.  
  1694.         // through diffie-hellman key exchange a symmetric key is obtained
  1695.         $this->kex_algorithm = self::array_intersect_first($kex_algorithms, $this->kex_algorithms);
  1696.         if ($this->kex_algorithm === false) {
  1697.             $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1698.             throw new NoSupportedAlgorithmsException('No compatible key exchange algorithms found');
  1699.         }
  1700.  
  1701.         $server_host_key_algorithm = self::array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms);
  1702.         if ($server_host_key_algorithm === false) {
  1703.             $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1704.             throw new NoSupportedAlgorithmsException('No compatible server host key algorithms found');
  1705.         }
  1706.  
  1707.         $mac_algorithm_out = self::array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server);
  1708.         if ($mac_algorithm_out === false) {
  1709.             $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1710.             throw new NoSupportedAlgorithmsException('No compatible client to server message authentication algorithms found');
  1711.         }
  1712.  
  1713.         $mac_algorithm_in = self::array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client);
  1714.         if ($mac_algorithm_in === false) {
  1715.             $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1716.             throw new NoSupportedAlgorithmsException('No compatible server to client message authentication algorithms found');
  1717.         }
  1718.  
  1719.         $compression_map = [
  1720.             'none' => self::NET_SSH2_COMPRESSION_NONE,
  1721.             'zlib' => self::NET_SSH2_COMPRESSION_ZLIB,
  1722.             'zlib@openssh.com' => self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH
  1723.         ];
  1724.  
  1725.         $compression_algorithm_in = self::array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_server_to_client);
  1726.         if ($compression_algorithm_in === false) {
  1727.             $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1728.             throw new NoSupportedAlgorithmsException('No compatible server to client compression algorithms found');
  1729.         }
  1730.         $this->decompress = $compression_map[$compression_algorithm_in];
  1731.  
  1732.         $compression_algorithm_out = self::array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server);
  1733.         if ($compression_algorithm_out === false) {
  1734.             $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1735.             throw new NoSupportedAlgorithmsException('No compatible client to server compression algorithms found');
  1736.         }
  1737.         $this->compress = $compression_map[$compression_algorithm_out];
  1738.  
  1739.         switch ($this->kex_algorithm) {
  1740.             case 'diffie-hellman-group15-sha512':
  1741.             case 'diffie-hellman-group16-sha512':
  1742.             case 'diffie-hellman-group17-sha512':
  1743.             case 'diffie-hellman-group18-sha512':
  1744.             case 'ecdh-sha2-nistp521':
  1745.                 $kexHash = new Hash('sha512');
  1746.                 break;
  1747.             case 'ecdh-sha2-nistp384':
  1748.                 $kexHash = new Hash('sha384');
  1749.                 break;
  1750.             case 'diffie-hellman-group-exchange-sha256':
  1751.             case 'diffie-hellman-group14-sha256':
  1752.             case 'ecdh-sha2-nistp256':
  1753.             case 'curve25519-sha256@libssh.org':
  1754.             case 'curve25519-sha256':
  1755.                 $kexHash = new Hash('sha256');
  1756.                 break;
  1757.             default:
  1758.                 $kexHash = new Hash('sha1');
  1759.         }
  1760.  
  1761.         // Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty.
  1762.  
  1763.         $exchange_hash_rfc4419 = '';
  1764.  
  1765.         if (strpos($this->kex_algorithm, 'curve25519-sha256') === 0 || strpos($this->kex_algorithm, 'ecdh-sha2-nistp') === 0) {
  1766.             $curve = strpos($this->kex_algorithm, 'curve25519-sha256') === 0 ?
  1767.                 'Curve25519' :
  1768.                 substr($this->kex_algorithm, 10);
  1769.             $ourPrivate = EC::createKey($curve);
  1770.             $ourPublicBytes = $ourPrivate->getPublicKey()->getEncodedCoordinates();
  1771.             $clientKexInitMessage = 'NET_SSH2_MSG_KEX_ECDH_INIT';
  1772.             $serverKexReplyMessage = 'NET_SSH2_MSG_KEX_ECDH_REPLY';
  1773.         } else {
  1774.             if (strpos($this->kex_algorithm, 'diffie-hellman-group-exchange') === 0) {
  1775.                 $dh_group_sizes_packed = pack(
  1776.                     'NNN',
  1777.                     $this->kex_dh_group_size_min,
  1778.                     $this->kex_dh_group_size_preferred,
  1779.                     $this->kex_dh_group_size_max
  1780.                 );
  1781.                 $packet = pack(
  1782.                     'Ca*',
  1783.                     NET_SSH2_MSG_KEXDH_GEX_REQUEST,
  1784.                     $dh_group_sizes_packed
  1785.                 );
  1786.                 $this->send_binary_packet($packet);
  1787.                 $this->updateLogHistory('UNKNOWN (34)', 'NET_SSH2_MSG_KEXDH_GEX_REQUEST');
  1788.  
  1789.                 $response = $this->get_binary_packet();
  1790.  
  1791.                 list($type, $primeBytes, $gBytes) = Strings::unpackSSH2('Css', $response);
  1792.                 if ($type != NET_SSH2_MSG_KEXDH_GEX_GROUP) {
  1793.                     $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR);
  1794.                     throw new \UnexpectedValueException('Expected SSH_MSG_KEX_DH_GEX_GROUP');
  1795.                 }
  1796.                 $this->updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEXDH_GEX_GROUP');
  1797.                 $prime = new BigInteger($primeBytes, -256);
  1798.                 $g = new BigInteger($gBytes, -256);
  1799.  
  1800.                 $exchange_hash_rfc4419 = $dh_group_sizes_packed . Strings::packSSH2(
  1801.                     'ss',
  1802.                     $primeBytes,
  1803.                     $gBytes
  1804.                 );
  1805.  
  1806.                 $params = DH::createParameters($prime, $g);
  1807.                 $clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_GEX_INIT';
  1808.                 $serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_GEX_REPLY';
  1809.             } else {
  1810.                 $params = DH::createParameters($this->kex_algorithm);
  1811.                 $clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_INIT';
  1812.                 $serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_REPLY';
  1813.             }
  1814.  
  1815.             $keyLength = min($kexHash->getLengthInBytes(), max($encryptKeyLength, $decryptKeyLength));
  1816.  
  1817.             $ourPrivate = DH::createKey($params, 16 * $keyLength); // 2 * 8 * $keyLength
  1818.             $ourPublic = $ourPrivate->getPublicKey()->toBigInteger();
  1819.             $ourPublicBytes = $ourPublic->toBytes(true);
  1820.         }
  1821.  
  1822.         $data = pack('CNa*', constant($clientKexInitMessage), strlen($ourPublicBytes), $ourPublicBytes);
  1823.  
  1824.         $this->send_binary_packet($data);
  1825.  
  1826.         switch ($clientKexInitMessage) {
  1827.             case 'NET_SSH2_MSG_KEX_ECDH_INIT':
  1828.                 $this->updateLogHistory('NET_SSH2_MSG_KEXDH_INIT', 'NET_SSH2_MSG_KEX_ECDH_INIT');
  1829.                 break;
  1830.             case 'NET_SSH2_MSG_KEXDH_GEX_INIT':
  1831.                 $this->updateLogHistory('UNKNOWN (32)', 'NET_SSH2_MSG_KEXDH_GEX_INIT');
  1832.         }
  1833.  
  1834.         $response = $this->get_binary_packet();
  1835.  
  1836.         list(
  1837.             $type,
  1838.             $server_public_host_key,
  1839.             $theirPublicBytes,
  1840.             $this->signature
  1841.         ) = Strings::unpackSSH2('Csss', $response);
  1842.  
  1843.         if ($type != constant($serverKexReplyMessage)) {
  1844.             $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR);
  1845.             throw new \UnexpectedValueException("Expected $serverKexReplyMessage");
  1846.         }
  1847.         switch ($serverKexReplyMessage) {
  1848.             case 'NET_SSH2_MSG_KEX_ECDH_REPLY':
  1849.                 $this->updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEX_ECDH_REPLY');
  1850.                 break;
  1851.             case 'NET_SSH2_MSG_KEXDH_GEX_REPLY':
  1852.                 $this->updateLogHistory('UNKNOWN (33)', 'NET_SSH2_MSG_KEXDH_GEX_REPLY');
  1853.         }
  1854.  
  1855.         $this->server_public_host_key = $server_public_host_key;
  1856.         list($public_key_format) = Strings::unpackSSH2('s', $server_public_host_key);
  1857.         if (strlen($this->signature) < 4) {
  1858.             throw new \LengthException('The signature needs at least four bytes');
  1859.         }
  1860.         $temp = unpack('Nlength', substr($this->signature, 0, 4));
  1861.         $this->signature_format = substr($this->signature, 4, $temp['length']);
  1862.  
  1863.         $keyBytes = DH::computeSecret($ourPrivate, $theirPublicBytes);
  1864.         if (($keyBytes & "\xFF\x80") === "\x00\x00") {
  1865.             $keyBytes = substr($keyBytes, 1);
  1866.         } elseif (($keyBytes[0] & "\x80") === "\x80") {
  1867.             $keyBytes = "\0$keyBytes";
  1868.         }
  1869.  
  1870.         $this->exchange_hash = Strings::packSSH2(
  1871.             's5',
  1872.             $this->identifier,
  1873.             $this->server_identifier,
  1874.             $kexinit_payload_client,
  1875.             $kexinit_payload_server,
  1876.             $this->server_public_host_key
  1877.         );
  1878.         $this->exchange_hash .= $exchange_hash_rfc4419;
  1879.         $this->exchange_hash .= Strings::packSSH2(
  1880.             's3',
  1881.             $ourPublicBytes,
  1882.             $theirPublicBytes,
  1883.             $keyBytes
  1884.         );
  1885.  
  1886.         $this->exchange_hash = $kexHash->hash($this->exchange_hash);
  1887.  
  1888.         if ($this->session_id === false) {
  1889.             $this->session_id = $this->exchange_hash;
  1890.         }
  1891.  
  1892.         switch ($server_host_key_algorithm) {
  1893.             case 'rsa-sha2-256':
  1894.             case 'rsa-sha2-512':
  1895.             //case 'ssh-rsa':
  1896.                 $expected_key_format = 'ssh-rsa';
  1897.                 break;
  1898.             default:
  1899.                 $expected_key_format = $server_host_key_algorithm;
  1900.         }
  1901.         if ($public_key_format != $expected_key_format || $this->signature_format != $server_host_key_algorithm) {
  1902.             switch (true) {
  1903.                 case $this->signature_format == $server_host_key_algorithm:
  1904.                 case $server_host_key_algorithm != 'rsa-sha2-256' && $server_host_key_algorithm != 'rsa-sha2-512':
  1905.                 case $this->signature_format != 'ssh-rsa':
  1906.                     $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
  1907.                     throw new \RuntimeException('Server Host Key Algorithm Mismatch (' . $this->signature_format . ' vs ' . $server_host_key_algorithm . ')');
  1908.             }
  1909.         }
  1910.  
  1911.         $packet = pack('C', NET_SSH2_MSG_NEWKEYS);
  1912.         $this->send_binary_packet($packet);
  1913.  
  1914.         $response = $this->get_binary_packet();
  1915.  
  1916.         if ($response === false) {
  1917.             $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST);
  1918.             throw new ConnectionClosedException('Connection closed by server');
  1919.         }
  1920.  
  1921.         list($type) = Strings::unpackSSH2('C', $response);
  1922.         if ($type != NET_SSH2_MSG_NEWKEYS) {
  1923.             $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR);
  1924.             throw new \UnexpectedValueException('Expected SSH_MSG_NEWKEYS');
  1925.         }
  1926.  
  1927.         $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
  1928.  
  1929.         $this->encrypt = self::encryption_algorithm_to_crypt_instance($encrypt);
  1930.         if ($this->encrypt) {
  1931.             if (self::$crypto_engine) {
  1932.                 $this->encrypt->setPreferredEngine(self::$crypto_engine);
  1933.             }
  1934.             if ($this->encrypt->getBlockLengthInBytes()) {
  1935.                 $this->encrypt_block_size = $this->encrypt->getBlockLengthInBytes();
  1936.             }
  1937.             $this->encrypt->disablePadding();
  1938.  
  1939.             if ($this->encrypt->usesIV()) {
  1940.                 $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id);
  1941.                 while ($this->encrypt_block_size > strlen($iv)) {
  1942.                     $iv .= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
  1943.                 }
  1944.                 $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
  1945.             }
  1946.  
  1947.             switch ($encrypt) {
  1948.                 case 'aes128-gcm@openssh.com':
  1949.                 case 'aes256-gcm@openssh.com':
  1950.                     $nonce = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id);
  1951.                     $this->encryptFixedPart = substr($nonce, 0, 4);
  1952.                     $this->encryptInvocationCounter = substr($nonce, 4, 8);
  1953.                     // fall-through
  1954.                 case 'chacha20-poly1305@openssh.com':
  1955.                     break;
  1956.                 default:
  1957.                     $this->encrypt->enableContinuousBuffer();
  1958.             }
  1959.  
  1960.             $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id);
  1961.             while ($encryptKeyLength > strlen($key)) {
  1962.                 $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
  1963.             }
  1964.             switch ($encrypt) {
  1965.                 case 'chacha20-poly1305@openssh.com':
  1966.                     $encryptKeyLength = 32;
  1967.                     $this->lengthEncrypt = self::encryption_algorithm_to_crypt_instance($encrypt);
  1968.                     $this->lengthEncrypt->setKey(substr($key, 32, 32));
  1969.             }
  1970.             $this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
  1971.             $this->encryptName = $encrypt;
  1972.         }
  1973.  
  1974.         $this->decrypt = self::encryption_algorithm_to_crypt_instance($decrypt);
  1975.         if ($this->decrypt) {
  1976.             if (self::$crypto_engine) {
  1977.                 $this->decrypt->setPreferredEngine(self::$crypto_engine);
  1978.             }
  1979.             if ($this->decrypt->getBlockLengthInBytes()) {
  1980.                 $this->decrypt_block_size = $this->decrypt->getBlockLengthInBytes();
  1981.             }
  1982.             $this->decrypt->disablePadding();
  1983.  
  1984.             if ($this->decrypt->usesIV()) {
  1985.                 $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id);
  1986.                 while ($this->decrypt_block_size > strlen($iv)) {
  1987.                     $iv .= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
  1988.                 }
  1989.                 $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
  1990.             }
  1991.  
  1992.             switch ($decrypt) {
  1993.                 case 'aes128-gcm@openssh.com':
  1994.                 case 'aes256-gcm@openssh.com':
  1995.                     // see https://tools.ietf.org/html/rfc5647#section-7.1
  1996.                     $nonce = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id);
  1997.                     $this->decryptFixedPart = substr($nonce, 0, 4);
  1998.                     $this->decryptInvocationCounter = substr($nonce, 4, 8);
  1999.                     // fall-through
  2000.                 case 'chacha20-poly1305@openssh.com':
  2001.                     break;
  2002.                 default:
  2003.                     $this->decrypt->enableContinuousBuffer();
  2004.             }
  2005.  
  2006.             $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id);
  2007.             while ($decryptKeyLength > strlen($key)) {
  2008.                 $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
  2009.             }
  2010.             switch ($decrypt) {
  2011.                 case 'chacha20-poly1305@openssh.com':
  2012.                     $decryptKeyLength = 32;
  2013.                     $this->lengthDecrypt = self::encryption_algorithm_to_crypt_instance($decrypt);
  2014.                     $this->lengthDecrypt->setKey(substr($key, 32, 32));
  2015.             }
  2016.             $this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
  2017.             $this->decryptName = $decrypt;
  2018.         }
  2019.  
  2020.         /* The "arcfour128" algorithm is the RC4 cipher, as described in
  2021.            [SCHNEIER], using a 128-bit key.  The first 1536 bytes of keystream
  2022.            generated by the cipher MUST be discarded, and the first byte of the
  2023.            first encrypted packet MUST be encrypted using the 1537th byte of
  2024.            keystream.
  2025.  
  2026.            -- http://tools.ietf.org/html/rfc4345#section-4 */
  2027.         if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') {
  2028.             $this->encrypt->encrypt(str_repeat("\0", 1536));
  2029.         }
  2030.         if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') {
  2031.             $this->decrypt->decrypt(str_repeat("\0", 1536));
  2032.         }
  2033.  
  2034.         if (!$this->encrypt->usesNonce()) {
  2035.             list($this->hmac_create, $createKeyLength) = self::mac_algorithm_to_hash_instance($mac_algorithm_out);
  2036.         } else {
  2037.             $this->hmac_create = new \stdClass();
  2038.             $this->hmac_create_name = $mac_algorithm_out;
  2039.             //$mac_algorithm_out = 'none';
  2040.             $createKeyLength = 0;
  2041.         }
  2042.  
  2043.         if ($this->hmac_create instanceof Hash) {
  2044.             $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id);
  2045.             while ($createKeyLength > strlen($key)) {
  2046.                 $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
  2047.             }
  2048.             $this->hmac_create->setKey(substr($key, 0, $createKeyLength));
  2049.             $this->hmac_create_name = $mac_algorithm_out;
  2050.             $this->hmac_create_etm = preg_match('#-etm@openssh\.com$#', $mac_algorithm_out);
  2051.         }
  2052.  
  2053.         if (!$this->decrypt->usesNonce()) {
  2054.             list($this->hmac_check, $checkKeyLength) = self::mac_algorithm_to_hash_instance($mac_algorithm_in);
  2055.             $this->hmac_size = $this->hmac_check->getLengthInBytes();
  2056.         } else {
  2057.             $this->hmac_check = new \stdClass();
  2058.             $this->hmac_check_name = $mac_algorithm_in;
  2059.             //$mac_algorithm_in = 'none';
  2060.             $checkKeyLength = 0;
  2061.             $this->hmac_size = 0;
  2062.         }
  2063.  
  2064.         if ($this->hmac_check instanceof Hash) {
  2065.             $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id);
  2066.             while ($checkKeyLength > strlen($key)) {
  2067.                 $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
  2068.             }
  2069.             $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
  2070.             $this->hmac_check_name = $mac_algorithm_in;
  2071.             $this->hmac_check_etm = preg_match('#-etm@openssh\.com$#', $mac_algorithm_in);
  2072.         }
  2073.  
  2074.         $this->regenerate_compression_context = $this->regenerate_decompression_context = true;
  2075.  
  2076.         return true;
  2077.     }
  2078.  
  2079.     /**
  2080.      * Maps an encryption algorithm name to the number of key bytes.
  2081.      *
  2082.      * @param string $algorithm Name of the encryption algorithm
  2083.      * @return int|null Number of bytes as an integer or null for unknown
  2084.      * @access private
  2085.      */
  2086.     private function encryption_algorithm_to_key_size($algorithm)
  2087.     {
  2088.         if ($this->bad_key_size_fix && self::bad_algorithm_candidate($algorithm)) {
  2089.             return 16;
  2090.         }
  2091.  
  2092.         switch ($algorithm) {
  2093.             case 'none':
  2094.                 return 0;
  2095.             case 'aes128-gcm@openssh.com':
  2096.             case 'aes128-cbc':
  2097.             case 'aes128-ctr':
  2098.             case 'arcfour':
  2099.             case 'arcfour128':
  2100.             case 'blowfish-cbc':
  2101.             case 'blowfish-ctr':
  2102.             case 'twofish128-cbc':
  2103.             case 'twofish128-ctr':
  2104.                 return 16;
  2105.             case '3des-cbc':
  2106.             case '3des-ctr':
  2107.             case 'aes192-cbc':
  2108.             case 'aes192-ctr':
  2109.             case 'twofish192-cbc':
  2110.             case 'twofish192-ctr':
  2111.                 return 24;
  2112.             case 'aes256-gcm@openssh.com':
  2113.             case 'aes256-cbc':
  2114.             case 'aes256-ctr':
  2115.             case 'arcfour256':
  2116.             case 'twofish-cbc':
  2117.             case 'twofish256-cbc':
  2118.             case 'twofish256-ctr':
  2119.                 return 32;
  2120.             case 'chacha20-poly1305@openssh.com':
  2121.                 return 64;
  2122.         }
  2123.         return null;
  2124.     }
  2125.  
  2126.     /**
  2127.      * Maps an encryption algorithm name to an instance of a subclass of
  2128.      * \phpseclib3\Crypt\Common\SymmetricKey.
  2129.      *
  2130.      * @param string $algorithm Name of the encryption algorithm
  2131.      * @return SymmetricKey|null
  2132.      * @access private
  2133.      */
  2134.     private static function encryption_algorithm_to_crypt_instance($algorithm)
  2135.     {
  2136.         switch ($algorithm) {
  2137.             case '3des-cbc':
  2138.                 return new TripleDES('cbc');
  2139.             case '3des-ctr':
  2140.                 return new TripleDES('ctr');
  2141.             case 'aes256-cbc':
  2142.             case 'aes192-cbc':
  2143.             case 'aes128-cbc':
  2144.                 return new Rijndael('cbc');
  2145.             case 'aes256-ctr':
  2146.             case 'aes192-ctr':
  2147.             case 'aes128-ctr':
  2148.                 return new Rijndael('ctr');
  2149.             case 'blowfish-cbc':
  2150.                 return new Blowfish('cbc');
  2151.             case 'blowfish-ctr':
  2152.                 return new Blowfish('ctr');
  2153.             case 'twofish128-cbc':
  2154.             case 'twofish192-cbc':
  2155.             case 'twofish256-cbc':
  2156.             case 'twofish-cbc':
  2157.                 return new Twofish('cbc');
  2158.             case 'twofish128-ctr':
  2159.             case 'twofish192-ctr':
  2160.             case 'twofish256-ctr':
  2161.                 return new Twofish('ctr');
  2162.             case 'arcfour':
  2163.             case 'arcfour128':
  2164.             case 'arcfour256':
  2165.                 return new RC4();
  2166.             case 'aes128-gcm@openssh.com':
  2167.             case 'aes256-gcm@openssh.com':
  2168.                 return new Rijndael('gcm');
  2169.             case 'chacha20-poly1305@openssh.com':
  2170.                 return new ChaCha20();
  2171.         }
  2172.         return null;
  2173.     }
  2174.  
  2175.     /**
  2176.      * Maps an encryption algorithm name to an instance of a subclass of
  2177.      * \phpseclib3\Crypt\Hash.
  2178.      *
  2179.      * @param string $algorithm Name of the encryption algorithm
  2180.      * @return array{Hash, int}|null
  2181.      * @access private
  2182.      */
  2183.     private static function mac_algorithm_to_hash_instance($algorithm)
  2184.     {
  2185.         switch ($algorithm) {
  2186.             case 'umac-64@openssh.com':
  2187.             case 'umac-64-etm@openssh.com':
  2188.                 return [new Hash('umac-64'), 16];
  2189.             case 'umac-128@openssh.com':
  2190.             case 'umac-128-etm@openssh.com':
  2191.                 return [new Hash('umac-128'), 16];
  2192.             case 'hmac-sha2-512':
  2193.             case 'hmac-sha2-512-etm@openssh.com':
  2194.                 return [new Hash('sha512'), 64];
  2195.             case 'hmac-sha2-256':
  2196.             case 'hmac-sha2-256-etm@openssh.com':
  2197.                 return [new Hash('sha256'), 32];
  2198.             case 'hmac-sha1':
  2199.             case 'hmac-sha1-etm@openssh.com':
  2200.                 return [new Hash('sha1'), 20];
  2201.             case 'hmac-sha1-96':
  2202.                 return [new Hash('sha1-96'), 20];
  2203.             case 'hmac-md5':
  2204.                 return [new Hash('md5'), 16];
  2205.             case 'hmac-md5-96':
  2206.                 return [new Hash('md5-96'), 16];
  2207.         }
  2208.     }
  2209.  
  2210.     /*
  2211.      * Tests whether or not proposed algorithm has a potential for issues
  2212.      *
  2213.      * @link https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ssh2-aesctr-openssh.html
  2214.      * @link https://bugzilla.mindrot.org/show_bug.cgi?id=1291
  2215.      * @param string $algorithm Name of the encryption algorithm
  2216.      * @return bool
  2217.      * @access private
  2218.      */
  2219.     private static function bad_algorithm_candidate($algorithm)
  2220.     {
  2221.         switch ($algorithm) {
  2222.             case 'arcfour256':
  2223.             case 'aes192-ctr':
  2224.             case 'aes256-ctr':
  2225.                 return true;
  2226.         }
  2227.  
  2228.         return false;
  2229.     }
  2230.  
  2231.     /**
  2232.      * Login
  2233.      *
  2234.      * The $password parameter can be a plaintext password, a \phpseclib3\Crypt\RSA|EC|DSA object, a \phpseclib3\System\SSH\Agent object or an array
  2235.      *
  2236.      * @param string $username
  2237.      * @param string|AsymmetricKey|array[]|Agent|null ...$args
  2238.      * @return bool
  2239.      * @see self::_login()
  2240.      * @access public
  2241.      */
  2242.     public function login($username, ...$args)
  2243.     {
  2244.         $this->auth[] = func_get_args();
  2245.  
  2246.         // try logging with 'none' as an authentication method first since that's what
  2247.         // PuTTY does
  2248.         if (substr($this->server_identifier, 0, 15) != 'SSH-2.0-CoreFTP' && $this->auth_methods_to_continue === null) {
  2249.             if ($this->sublogin($username)) {
  2250.                 return true;
  2251.             }
  2252.             if (!count($args)) {
  2253.                 return false;
  2254.             }
  2255.         }
  2256.         return $this->sublogin($username, ...$args);
  2257.     }
  2258.  
  2259.     /**
  2260.      * Login Helper
  2261.      *
  2262.      * @param string $username
  2263.      * @param string ...$args
  2264.      * @return bool
  2265.      * @see self::_login_helper()
  2266.      * @access private
  2267.      */
  2268.     protected function sublogin($username, ...$args)
  2269.     {
  2270.         if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
  2271.             $this->connect();
  2272.         }
  2273.  
  2274.         if (empty($args)) {
  2275.             return $this->login_helper($username);
  2276.         }
  2277.  
  2278.         foreach ($args as $arg) {
  2279.             switch (true) {
  2280.                 case $arg instanceof PublicKey:
  2281.                     throw new \UnexpectedValueException('A PublicKey object was passed to the login method instead of a PrivateKey object');
  2282.                 case $arg instanceof PrivateKey:
  2283.                 case $arg instanceof Agent:
  2284.                 case is_array($arg):
  2285.                 case Strings::is_stringable($arg):
  2286.                     break;
  2287.                 default:
  2288.                     throw new \UnexpectedValueException('$password needs to either be an instance of \phpseclib3\Crypt\Common\PrivateKey, \System\SSH\Agent, an array or a string');
  2289.             }
  2290.         }
  2291.  
  2292.         while (count($args)) {
  2293.             if (!$this->auth_methods_to_continue || !$this->smartMFA) {
  2294.                 $newargs = $args;
  2295.                 $args = [];
  2296.             } else {
  2297.                 $newargs = [];
  2298.                 foreach ($this->auth_methods_to_continue as $method) {
  2299.                     switch ($method) {
  2300.                         case 'publickey':
  2301.                             foreach ($args as $key => $arg) {
  2302.                                 if ($arg instanceof PrivateKey || $arg instanceof Agent) {
  2303.                                     $newargs[] = $arg;
  2304.                                     unset($args[$key]);
  2305.                                     break;
  2306.                                 }
  2307.                             }
  2308.                             break;
  2309.                         case 'keyboard-interactive':
  2310.                             $hasArray = $hasString = false;
  2311.                             foreach ($args as $arg) {
  2312.                                 if ($hasArray || is_array($arg)) {
  2313.                                     $hasArray = true;
  2314.                                     break;
  2315.                                 }
  2316.                                 if ($hasString || Strings::is_stringable($arg)) {
  2317.                                     $hasString = true;
  2318.                                     break;
  2319.                                 }
  2320.                             }
  2321.                             if ($hasArray && $hasString) {
  2322.                                 foreach ($args as $key => $arg) {
  2323.                                     if (is_array($arg)) {
  2324.                                         $newargs[] = $arg;
  2325.                                         break 2;
  2326.                                     }
  2327.                                 }
  2328.                             }
  2329.                             // fall-through
  2330.                         case 'password':
  2331.                             foreach ($args as $key => $arg) {
  2332.                                 $newargs[] = $arg;
  2333.                                 unset($args[$key]);
  2334.                                 break;
  2335.                             }
  2336.                     }
  2337.                 }
  2338.             }
  2339.  
  2340.             if (!count($newargs)) {
  2341.                 return false;
  2342.             }
  2343.  
  2344.             foreach ($newargs as $arg) {
  2345.                 if ($this->login_helper($username, $arg)) {
  2346.                     return true;
  2347.                 }
  2348.             }
  2349.         }
  2350.         return false;
  2351.     }
  2352.  
  2353.     /**
  2354.      * Login Helper
  2355.      *
  2356.      * {@internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
  2357.      *           by sending dummy SSH_MSG_IGNORE messages.}
  2358.      *
  2359.      * @param string $username
  2360.      * @param string|AsymmetricKey|array[]|Agent|null ...$args
  2361.      * @return bool
  2362.      * @throws \UnexpectedValueException on receipt of unexpected packets
  2363.      * @throws \RuntimeException on other errors
  2364.      * @access private
  2365.      */
  2366.     private function login_helper($username, $password = null)
  2367.     {
  2368.         if (!($this->bitmap & self::MASK_CONNECTED)) {
  2369.             return false;
  2370.         }
  2371.  
  2372.         if (!($this->bitmap & self::MASK_LOGIN_REQ)) {
  2373.             $packet = Strings::packSSH2('Cs', NET_SSH2_MSG_SERVICE_REQUEST, 'ssh-userauth');
  2374.             $this->send_binary_packet($packet);
  2375.  
  2376.             try {
  2377.                 $response = $this->get_binary_packet();
  2378.             } catch (\Exception $e) {
  2379.                 if ($this->retry_connect) {
  2380.                     $this->retry_connect = false;
  2381.                     $this->connect();
  2382.                     return $this->login_helper($username, $password);
  2383.                 }
  2384.                 $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST);
  2385.                 throw new ConnectionClosedException('Connection closed by server');
  2386.             }
  2387.  
  2388.             list($type, $service) = Strings::unpackSSH2('Cs', $response);
  2389.             if ($type != NET_SSH2_MSG_SERVICE_ACCEPT || $service != 'ssh-userauth') {
  2390.                 $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR);
  2391.                 throw new \UnexpectedValueException('Expected SSH_MSG_SERVICE_ACCEPT');
  2392.             }
  2393.             $this->bitmap |= self::MASK_LOGIN_REQ;
  2394.         }
  2395.  
  2396.         if (strlen($this->last_interactive_response)) {
  2397.             return !Strings::is_stringable($password) && !is_array($password) ? false : $this->keyboard_interactive_process($password);
  2398.         }
  2399.  
  2400.         if ($password instanceof PrivateKey) {
  2401.             return $this->privatekey_login($username, $password);
  2402.         }
  2403.  
  2404.         if ($password instanceof Agent) {
  2405.             return $this->ssh_agent_login($username, $password);
  2406.         }
  2407.  
  2408.         if (is_array($password)) {
  2409.             if ($this->keyboard_interactive_login($username, $password)) {
  2410.                 $this->bitmap |= self::MASK_LOGIN;
  2411.                 return true;
  2412.             }
  2413.             return false;
  2414.         }
  2415.  
  2416.         if (!isset($password)) {
  2417.             $packet = Strings::packSSH2(
  2418.                 'Cs3',
  2419.                 NET_SSH2_MSG_USERAUTH_REQUEST,
  2420.                 $username,
  2421.                 'ssh-connection',
  2422.                 'none'
  2423.             );
  2424.  
  2425.             $this->send_binary_packet($packet);
  2426.  
  2427.             $response = $this->get_binary_packet();
  2428.  
  2429.             list($type) = Strings::unpackSSH2('C', $response);
  2430.             switch ($type) {
  2431.                 case NET_SSH2_MSG_USERAUTH_SUCCESS:
  2432.                     $this->bitmap |= self::MASK_LOGIN;
  2433.                     return true;
  2434.                 case NET_SSH2_MSG_USERAUTH_FAILURE:
  2435.                     list($auth_methods) = Strings::unpackSSH2('L', $response);
  2436.                     $this->auth_methods_to_continue = $auth_methods;
  2437.                     // fall-through
  2438.                 default:
  2439.                     return false;
  2440.             }
  2441.         }
  2442.  
  2443.         $packet = Strings::packSSH2(
  2444.             'Cs3bs',
  2445.             NET_SSH2_MSG_USERAUTH_REQUEST,
  2446.             $username,
  2447.             'ssh-connection',
  2448.             'password',
  2449.             false,
  2450.             $password
  2451.         );
  2452.  
  2453.         // remove the username and password from the logged packet
  2454.         if (!defined('NET_SSH2_LOGGING')) {
  2455.             $logged = null;
  2456.         } else {
  2457.             $logged = Strings::packSSH2(
  2458.                 'Cs3bs',
  2459.                 NET_SSH2_MSG_USERAUTH_REQUEST,
  2460.                 $username,
  2461.                 'ssh-connection',
  2462.                 'password',
  2463.                 false,
  2464.                 'password'
  2465.             );
  2466.         }
  2467.  
  2468.         $this->send_binary_packet($packet, $logged);
  2469.  
  2470.         $response = $this->get_binary_packet();
  2471.  
  2472.         list($type) = Strings::unpackSSH2('C', $response);
  2473.         switch ($type) {
  2474.             case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed
  2475.                 $this->updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ');
  2476.  
  2477.                 list($message) = Strings::unpackSSH2('s', $response);
  2478.                 $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . $message;
  2479.  
  2480.                 return $this->disconnect_helper(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
  2481.             case NET_SSH2_MSG_USERAUTH_FAILURE:
  2482.                 // can we use keyboard-interactive authentication?  if not then either the login is bad or the server employees
  2483.                 // multi-factor authentication
  2484.                 list($auth_methods, $partial_success) = Strings::unpackSSH2('Lb', $response);
  2485.                 $this->auth_methods_to_continue = $auth_methods;
  2486.                 if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) {
  2487.                     if ($this->keyboard_interactive_login($username, $password)) {
  2488.                         $this->bitmap |= self::MASK_LOGIN;
  2489.                         return true;
  2490.                     }
  2491.                     return false;
  2492.                 }
  2493.                 return false;
  2494.             case NET_SSH2_MSG_USERAUTH_SUCCESS:
  2495.                 $this->bitmap |= self::MASK_LOGIN;
  2496.                 return true;
  2497.         }
  2498.  
  2499.         return false;
  2500.     }
  2501.  
  2502.     /**
  2503.      * Login via keyboard-interactive authentication
  2504.      *
  2505.      * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details.  This is not a full-featured keyboard-interactive authenticator.
  2506.      *
  2507.      * @param string $username
  2508.      * @param string|array $password
  2509.      * @return bool
  2510.      * @access private
  2511.      */
  2512.     private function keyboard_interactive_login($username, $password)
  2513.     {
  2514.         $packet = Strings::packSSH2(
  2515.             'Cs5',
  2516.             NET_SSH2_MSG_USERAUTH_REQUEST,
  2517.             $username,
  2518.             'ssh-connection',
  2519.             'keyboard-interactive',
  2520.             '', // language tag
  2521.             '' // submethods
  2522.         );
  2523.         $this->send_binary_packet($packet);
  2524.  
  2525.         return $this->keyboard_interactive_process($password);
  2526.     }
  2527.  
  2528.     /**
  2529.      * Handle the keyboard-interactive requests / responses.
  2530.      *
  2531.      * @param string|array ...$responses
  2532.      * @return bool
  2533.      * @throws \RuntimeException on connection error
  2534.      * @access private
  2535.      */
  2536.     private function keyboard_interactive_process(...$responses)
  2537.     {
  2538.         if (strlen($this->last_interactive_response)) {
  2539.             $response = $this->last_interactive_response;
  2540.         } else {
  2541.             $orig = $response = $this->get_binary_packet();
  2542.         }
  2543.  
  2544.         list($type) = Strings::unpackSSH2('C', $response);
  2545.         switch ($type) {
  2546.             case NET_SSH2_MSG_USERAUTH_INFO_REQUEST:
  2547.                 list(
  2548.                     , // name; may be empty
  2549.                     , // instruction; may be empty
  2550.                     , // language tag; may be empty
  2551.                     $num_prompts
  2552.                 ) = Strings::unpackSSH2('s3N', $response);
  2553.  
  2554.                 for ($i = 0; $i < count($responses); $i++) {
  2555.                     if (is_array($responses[$i])) {
  2556.                         foreach ($responses[$i] as $key => $value) {
  2557.                             $this->keyboard_requests_responses[$key] = $value;
  2558.                         }
  2559.                         unset($responses[$i]);
  2560.                     }
  2561.                 }
  2562.                 $responses = array_values($responses);
  2563.  
  2564.                 if (isset($this->keyboard_requests_responses)) {
  2565.                     for ($i = 0; $i < $num_prompts; $i++) {
  2566.                         list(
  2567.                             $prompt, // prompt - ie. "Password: "; must not be empty
  2568.                             // echo
  2569.                         ) = Strings::unpackSSH2('sC', $response);
  2570.                         foreach ($this->keyboard_requests_responses as $key => $value) {
  2571.                             if (substr($prompt, 0, strlen($key)) == $key) {
  2572.                                 $responses[] = $value;
  2573.                                 break;
  2574.                             }
  2575.                         }
  2576.                     }
  2577.                 }
  2578.  
  2579.                 // see http://tools.ietf.org/html/rfc4256#section-3.2
  2580.                 if (strlen($this->last_interactive_response)) {
  2581.                     $this->last_interactive_response = '';
  2582.                 } else {
  2583.                     $this->updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST');
  2584.                 }
  2585.  
  2586.                 if (!count($responses) && $num_prompts) {
  2587.                     $this->last_interactive_response = $orig;
  2588.                     return false;
  2589.                 }
  2590.  
  2591.                 /*
  2592.                    After obtaining the requested information from the user, the client
  2593.                    MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message.
  2594.                 */
  2595.                 // see http://tools.ietf.org/html/rfc4256#section-3.4
  2596.                 $packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses));
  2597.                 for ($i = 0; $i < count($responses); $i++) {
  2598.                     $packet .= Strings::packSSH2('s', $responses[$i]);
  2599.                     $logged .= Strings::packSSH2('s', 'dummy-answer');
  2600.                 }
  2601.  
  2602.                 $this->send_binary_packet($packet, $logged);
  2603.  
  2604.                 $this->updateLogHistory('UNKNOWN (61)', 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE');
  2605.  
  2606.                 /*
  2607.                    After receiving the response, the server MUST send either an
  2608.                    SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another
  2609.                    SSH_MSG_USERAUTH_INFO_REQUEST message.
  2610.                 */
  2611.                 // maybe phpseclib should force close the connection after x request / responses?  unless something like that is done
  2612.                 // there could be an infinite loop of request / responses.
  2613.                 return $this->keyboard_interactive_process();
  2614.             case NET_SSH2_MSG_USERAUTH_SUCCESS:
  2615.                 return true;
  2616.             case NET_SSH2_MSG_USERAUTH_FAILURE:
  2617.                 list($auth_methods) = Strings::unpackSSH2('L', $response);
  2618.                 $this->auth_methods_to_continue = $auth_methods;
  2619.                 return false;
  2620.         }
  2621.  
  2622.         return false;
  2623.     }
  2624.  
  2625.     /**
  2626.      * Login with an ssh-agent provided key
  2627.      *
  2628.      * @param string $username
  2629.      * @param \phpseclib3\System\SSH\Agent $agent
  2630.      * @return bool
  2631.      * @access private
  2632.      */
  2633.     private function ssh_agent_login($username, Agent $agent)
  2634.     {
  2635.         $this->agent = $agent;
  2636.         $keys = $agent->requestIdentities();
  2637.         foreach ($keys as $key) {
  2638.             if ($this->privatekey_login($username, $key)) {
  2639.                 return true;
  2640.             }
  2641.         }
  2642.  
  2643.         return false;
  2644.     }
  2645.  
  2646.     /**
  2647.      * Login with an RSA private key
  2648.      *
  2649.      * {@internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
  2650.      *           by sending dummy SSH_MSG_IGNORE messages.}
  2651.      *
  2652.      * @param string $username
  2653.      * @param \phpseclib3\Crypt\Common\PrivateKey $privatekey
  2654.      * @return bool
  2655.      * @throws \RuntimeException on connection error
  2656.      * @access private
  2657.      */
  2658.     private function privatekey_login($username, PrivateKey $privatekey)
  2659.     {
  2660.         $publickey = $privatekey->getPublicKey();
  2661.  
  2662.         if ($publickey instanceof RSA) {
  2663.             $privatekey = $privatekey->withPadding(RSA::SIGNATURE_PKCS1);
  2664.             $algos = ['rsa-sha2-256', 'rsa-sha2-512', 'ssh-rsa'];
  2665.             if (isset($this->preferred['hostkey'])) {
  2666.                 $algos = array_intersect($this->preferred['hostkey'], $algos);
  2667.             }
  2668.             $algo = self::array_intersect_first($algos, $this->server_host_key_algorithms);
  2669.             switch ($algo) {
  2670.                 case 'rsa-sha2-512':
  2671.                     $hash = 'sha512';
  2672.                     $signatureType = 'rsa-sha2-512';
  2673.                     break;
  2674.                 case 'rsa-sha2-256':
  2675.                     $hash = 'sha256';
  2676.                     $signatureType = 'rsa-sha2-256';
  2677.                     break;
  2678.                 //case 'ssh-rsa':
  2679.                 default:
  2680.                     $hash = 'sha1';
  2681.                     $signatureType = 'ssh-rsa';
  2682.             }
  2683.         } elseif ($publickey instanceof EC) {
  2684.             $privatekey = $privatekey->withSignatureFormat('SSH2');
  2685.             $curveName = $privatekey->getCurve();
  2686.             switch ($curveName) {
  2687.                 case 'Ed25519':
  2688.                     $hash = 'sha512';
  2689.                     $signatureType = 'ssh-ed25519';
  2690.                     break;
  2691.                 case 'secp256r1': // nistp256
  2692.                     $hash = 'sha256';
  2693.                     $signatureType = 'ecdsa-sha2-nistp256';
  2694.                     break;
  2695.                 case 'secp384r1': // nistp384
  2696.                     $hash = 'sha384';
  2697.                     $signatureType = 'ecdsa-sha2-nistp384';
  2698.                     break;
  2699.                 case 'secp521r1': // nistp521
  2700.                     $hash = 'sha512';
  2701.                     $signatureType = 'ecdsa-sha2-nistp521';
  2702.                     break;
  2703.                 default:
  2704.                     if (is_array($curveName)) {
  2705.                         throw new UnsupportedCurveException('Specified Curves are not supported by SSH2');
  2706.                     }
  2707.                     throw new UnsupportedCurveException('Named Curve of ' . $curveName . ' is not supported by phpseclib3\'s SSH2 implementation');
  2708.             }
  2709.         } elseif ($publickey instanceof DSA) {
  2710.             $privatekey = $privatekey->withSignatureFormat('SSH2');
  2711.             $hash = 'sha1';
  2712.             $signatureType = 'ssh-dss';
  2713.         } else {
  2714.             throw new UnsupportedAlgorithmException('Please use either an RSA key, an EC one or a DSA key');
  2715.         }
  2716.  
  2717.         $publickeyStr = $publickey->toString('OpenSSH', ['binary' => true]);
  2718.  
  2719.         $part1 = Strings::packSSH2(
  2720.             'Csss',
  2721.             NET_SSH2_MSG_USERAUTH_REQUEST,
  2722.             $username,
  2723.             'ssh-connection',
  2724.             'publickey'
  2725.         );
  2726.         $part2 = Strings::packSSH2('ss', $signatureType, $publickeyStr);
  2727.  
  2728.         $packet = $part1 . chr(0) . $part2;
  2729.         $this->send_binary_packet($packet);
  2730.  
  2731.         $response = $this->get_binary_packet();
  2732.  
  2733.         list($type) = Strings::unpackSSH2('C', $response);
  2734.         switch ($type) {
  2735.             case NET_SSH2_MSG_USERAUTH_FAILURE:
  2736.                 list($auth_methods) = Strings::unpackSSH2('L', $response);
  2737.                 $this->auth_methods_to_continue = $auth_methods;
  2738.                 $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE';
  2739.                 return false;
  2740.             case NET_SSH2_MSG_USERAUTH_PK_OK:
  2741.                 // we'll just take it on faith that the public key blob and the public key algorithm name are as
  2742.                 // they should be
  2743.                 $this->updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PK_OK');
  2744.                 break;
  2745.             case NET_SSH2_MSG_USERAUTH_SUCCESS:
  2746.                 $this->bitmap |= self::MASK_LOGIN;
  2747.                 return true;
  2748.             default:
  2749.                 $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
  2750.                 throw new ConnectionClosedException('Unexpected response to publickey authentication pt 1');
  2751.         }
  2752.  
  2753.         $packet = $part1 . chr(1) . $part2;
  2754.         $privatekey = $privatekey->withHash($hash);
  2755.         $signature = $privatekey->sign(Strings::packSSH2('s', $this->session_id) . $packet);
  2756.         if ($publickey instanceof RSA) {
  2757.             $signature = Strings::packSSH2('ss', $signatureType, $signature);
  2758.         }
  2759.         $packet .= Strings::packSSH2('s', $signature);
  2760.  
  2761.         $this->send_binary_packet($packet);
  2762.  
  2763.         $response = $this->get_binary_packet();
  2764.  
  2765.         list($type) = Strings::unpackSSH2('C', $response);
  2766.         switch ($type) {
  2767.             case NET_SSH2_MSG_USERAUTH_FAILURE:
  2768.                 // either the login is bad or the server employs multi-factor authentication
  2769.                 list($auth_methods) = Strings::unpackSSH2('L', $response);
  2770.                 $this->auth_methods_to_continue = $auth_methods;
  2771.                 return false;
  2772.             case NET_SSH2_MSG_USERAUTH_SUCCESS:
  2773.                 $this->bitmap |= self::MASK_LOGIN;
  2774.                 return true;
  2775.         }
  2776.  
  2777.         $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
  2778.         throw new ConnectionClosedException('Unexpected response to publickey authentication pt 2');
  2779.     }
  2780.  
  2781.     /**
  2782.      * Set Timeout
  2783.      *
  2784.      * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely.  setTimeout() makes it so it'll timeout.
  2785.      * Setting $timeout to false or 0 will mean there is no timeout.
  2786.      *
  2787.      * @param mixed $timeout
  2788.      * @access public
  2789.      */
  2790.     public function setTimeout($timeout)
  2791.     {
  2792.         $this->timeout = $this->curTimeout = $timeout;
  2793.     }
  2794.  
  2795.     /**
  2796.      * Set Keep Alive
  2797.      *
  2798.      * Sends an SSH2_MSG_IGNORE message every x seconds, if x is a positive non-zero number.
  2799.      *
  2800.      * @param int $interval
  2801.      * @access public
  2802.      */
  2803.     public function setKeepAlive($interval)
  2804.     {
  2805.         $this->keepAlive = $interval;
  2806.     }
  2807.  
  2808.     /**
  2809.      * Get the output from stdError
  2810.      *
  2811.      * @access public
  2812.      */
  2813.     public function getStdError()
  2814.     {
  2815.         return $this->stdErrorLog;
  2816.     }
  2817.  
  2818.     /**
  2819.      * Execute Command
  2820.      *
  2821.      * If $callback is set to false then \phpseclib3\Net\SSH2::get_channel_packet(self::CHANNEL_EXEC) will need to be called manually.
  2822.      * In all likelihood, this is not a feature you want to be taking advantage of.
  2823.      *
  2824.      * @param string $command
  2825.      * @return string|bool
  2826.      * @psalm-return ($callback is callable ? bool : string|bool)
  2827.      * @throws \RuntimeException on connection error
  2828.      * @access public
  2829.      */
  2830.     public function exec($command, callable $callback = null)
  2831.     {
  2832.         $this->curTimeout = $this->timeout;
  2833.         $this->is_timeout = false;
  2834.         $this->stdErrorLog = '';
  2835.  
  2836.         if (!$this->isAuthenticated()) {
  2837.             return false;
  2838.         }
  2839.  
  2840.         if ($this->in_request_pty_exec) {
  2841.             throw new \RuntimeException('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.');
  2842.         }
  2843.  
  2844.         // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
  2845.         // be adjusted".  0x7FFFFFFF is, at 2GB, the max size.  technically, it should probably be decremented, but,
  2846.         // honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway.
  2847.         // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
  2848.         $this->window_size_server_to_client[self::CHANNEL_EXEC] = $this->window_size;
  2849.         // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
  2850.         // uses 0x4000, that's what will be used here, as well.
  2851.         $packet_size = 0x4000;
  2852.  
  2853.         $packet = Strings::packSSH2(
  2854.             'CsN3',
  2855.             NET_SSH2_MSG_CHANNEL_OPEN,
  2856.             'session',
  2857.             self::CHANNEL_EXEC,
  2858.             $this->window_size_server_to_client[self::CHANNEL_EXEC],
  2859.             $packet_size
  2860.         );
  2861.         $this->send_binary_packet($packet);
  2862.  
  2863.         $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN;
  2864.  
  2865.         $this->get_channel_packet(self::CHANNEL_EXEC);
  2866.  
  2867.         if ($this->request_pty === true) {
  2868.             $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
  2869.             $packet = Strings::packSSH2(
  2870.                 'CNsCsN4s',
  2871.                 NET_SSH2_MSG_CHANNEL_REQUEST,
  2872.                 $this->server_channels[self::CHANNEL_EXEC],
  2873.                 'pty-req',
  2874.                 1,
  2875.                 $this->term,
  2876.                 $this->windowColumns,
  2877.                 $this->windowRows,
  2878.                 0,
  2879.                 0,
  2880.                 $terminal_modes
  2881.             );
  2882.  
  2883.             $this->send_binary_packet($packet);
  2884.  
  2885.             $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
  2886.             if (!$this->get_channel_packet(self::CHANNEL_EXEC)) {
  2887.                 $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
  2888.                 throw new \RuntimeException('Unable to request pseudo-terminal');
  2889.             }
  2890.  
  2891.             $this->in_request_pty_exec = true;
  2892.         }
  2893.  
  2894.         // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things
  2895.         // down.  the one place where it might be desirable is if you're doing something like \phpseclib3\Net\SSH2::exec('ping localhost &').
  2896.         // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then
  2897.         // then immediately terminate.  without such a request exec() will loop indefinitely.  the ping process won't end but
  2898.         // neither will your script.
  2899.  
  2900.         // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by
  2901.         // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the
  2902.         // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA.  RFC4254#section-5.2 corroborates.
  2903.         $packet = Strings::packSSH2(
  2904.             'CNsCs',
  2905.             NET_SSH2_MSG_CHANNEL_REQUEST,
  2906.             $this->server_channels[self::CHANNEL_EXEC],
  2907.             'exec',
  2908.             1,
  2909.             $command
  2910.         );
  2911.         $this->send_binary_packet($packet);
  2912.  
  2913.         $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
  2914.  
  2915.         if (!$this->get_channel_packet(self::CHANNEL_EXEC)) {
  2916.             return false;
  2917.         }
  2918.  
  2919.         $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA;
  2920.  
  2921.         if ($callback === false || $this->in_request_pty_exec) {
  2922.             return true;
  2923.         }
  2924.  
  2925.         $output = '';
  2926.         while (true) {
  2927.             $temp = $this->get_channel_packet(self::CHANNEL_EXEC);
  2928.             switch (true) {
  2929.                 case $temp === true:
  2930.                     return is_callable($callback) ? true : $output;
  2931.                 case $temp === false:
  2932.                     return false;
  2933.                 default:
  2934.                     if (is_callable($callback)) {
  2935.                         if ($callback($temp) === true) {
  2936.                             $this->close_channel(self::CHANNEL_EXEC);
  2937.                             return true;
  2938.                         }
  2939.                     } else {
  2940.                         $output .= $temp;
  2941.                     }
  2942.             }
  2943.         }
  2944.     }
  2945.  
  2946.     /**
  2947.      * Creates an interactive shell
  2948.      *
  2949.      * @see self::read()
  2950.      * @see self::write()
  2951.      * @return bool
  2952.      * @throws \UnexpectedValueException on receipt of unexpected packets
  2953.      * @throws \RuntimeException on other errors
  2954.      * @access private
  2955.      */
  2956.     private function initShell()
  2957.     {
  2958.         if ($this->in_request_pty_exec === true) {
  2959.             return true;
  2960.         }
  2961.  
  2962.         $this->window_size_server_to_client[self::CHANNEL_SHELL] = $this->window_size;
  2963.         $packet_size = 0x4000;
  2964.  
  2965.         $packet = Strings::packSSH2(
  2966.             'CsN3',
  2967.             NET_SSH2_MSG_CHANNEL_OPEN,
  2968.             'session',
  2969.             self::CHANNEL_SHELL,
  2970.             $this->window_size_server_to_client[self::CHANNEL_SHELL],
  2971.             $packet_size
  2972.         );
  2973.  
  2974.         $this->send_binary_packet($packet);
  2975.  
  2976.         $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN;
  2977.  
  2978.         $this->get_channel_packet(self::CHANNEL_SHELL);
  2979.  
  2980.         $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
  2981.         $packet = Strings::packSSH2(
  2982.             'CNsbsN4s',
  2983.             NET_SSH2_MSG_CHANNEL_REQUEST,
  2984.             $this->server_channels[self::CHANNEL_SHELL],
  2985.             'pty-req',
  2986.             true, // want reply
  2987.             $this->term,
  2988.             $this->windowColumns,
  2989.             $this->windowRows,
  2990.             0,
  2991.             0,
  2992.             $terminal_modes
  2993.         );
  2994.  
  2995.         $this->send_binary_packet($packet);
  2996.  
  2997.         $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;
  2998.  
  2999.         if (!$this->get_channel_packet(self::CHANNEL_SHELL)) {
  3000.             throw new \RuntimeException('Unable to request pty');
  3001.         }
  3002.  
  3003.         $packet = Strings::packSSH2(
  3004.             'CNsb',
  3005.             NET_SSH2_MSG_CHANNEL_REQUEST,
  3006.             $this->server_channels[self::CHANNEL_SHELL],
  3007.             'shell',
  3008.             true // want reply
  3009.         );
  3010.         $this->send_binary_packet($packet);
  3011.  
  3012.         $response = $this->get_channel_packet(self::CHANNEL_SHELL);
  3013.         if ($response === false) {
  3014.             throw new \RuntimeException('Unable to request shell');
  3015.         }
  3016.  
  3017.         $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA;
  3018.  
  3019.         $this->bitmap |= self::MASK_SHELL;
  3020.  
  3021.         return true;
  3022.     }
  3023.  
  3024.     /**
  3025.      * Return the channel to be used with read() / write()
  3026.      *
  3027.      * @see self::read()
  3028.      * @see self::write()
  3029.      * @return int
  3030.      * @access public
  3031.      */
  3032.     private function get_interactive_channel()
  3033.     {
  3034.         switch (true) {
  3035.             case $this->in_subsystem:
  3036.                 return self::CHANNEL_SUBSYSTEM;
  3037.             case $this->in_request_pty_exec:
  3038.                 return self::CHANNEL_EXEC;
  3039.             default:
  3040.                 return self::CHANNEL_SHELL;
  3041.         }
  3042.     }
  3043.  
  3044.     /**
  3045.      * Return an available open channel
  3046.      *
  3047.      * @return int
  3048.      * @access public
  3049.      */
  3050.     private function get_open_channel()
  3051.     {
  3052.         $channel = self::CHANNEL_EXEC;
  3053.         do {
  3054.             if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_OPEN) {
  3055.                 return $channel;
  3056.             }
  3057.         } while ($channel++ < self::CHANNEL_SUBSYSTEM);
  3058.  
  3059.         return false;
  3060.     }
  3061.  
  3062.     /**
  3063.      * Request agent forwarding of remote server
  3064.      *
  3065.      * @return bool
  3066.      * @access public
  3067.      */
  3068.     public function requestAgentForwarding()
  3069.     {
  3070.         $request_channel = $this->get_open_channel();
  3071.         if ($request_channel === false) {
  3072.             return false;
  3073.         }
  3074.  
  3075.         $packet = Strings::packSSH2(
  3076.             'CNsC',
  3077.             NET_SSH2_MSG_CHANNEL_REQUEST,
  3078.             $this->server_channels[$request_channel],
  3079.             'auth-agent-req@openssh.com',
  3080.             1
  3081.         );
  3082.  
  3083.         $this->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_REQUEST;
  3084.  
  3085.         $this->send_binary_packet($packet);
  3086.  
  3087.         if (!$this->get_channel_packet($request_channel)) {
  3088.             return false;
  3089.         }
  3090.  
  3091.         $this->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_OPEN;
  3092.  
  3093.         return true;
  3094.     }
  3095.  
  3096.     /**
  3097.      * Returns the output of an interactive shell
  3098.      *
  3099.      * Returns when there's a match for $expect, which can take the form of a string literal or,
  3100.      * if $mode == self::READ_REGEX, a regular expression.
  3101.      *
  3102.      * @see self::write()
  3103.      * @param string $expect
  3104.      * @param int $mode
  3105.      * @return string|bool|null
  3106.      * @throws \RuntimeException on connection error
  3107.      * @access public
  3108.      */
  3109.     public function read($expect = '', $mode = self::READ_SIMPLE)
  3110.     {
  3111.         $this->curTimeout = $this->timeout;
  3112.         $this->is_timeout = false;
  3113.  
  3114.         if (!$this->isAuthenticated()) {
  3115.             throw new InsufficientSetupException('Operation disallowed prior to login()');
  3116.         }
  3117.  
  3118.         if (!($this->bitmap & self::MASK_SHELL) && !$this->initShell()) {
  3119.             throw new \RuntimeException('Unable to initiate an interactive shell session');
  3120.         }
  3121.  
  3122.         $channel = $this->get_interactive_channel();
  3123.  
  3124.         if ($mode == self::READ_NEXT) {
  3125.             return $this->get_channel_packet($channel);
  3126.         }
  3127.  
  3128.         $match = $expect;
  3129.         while (true) {
  3130.             if ($mode == self::READ_REGEX) {
  3131.                 preg_match($expect, substr($this->interactiveBuffer, -1024), $matches);
  3132.                 $match = isset($matches[0]) ? $matches[0] : '';
  3133.             }
  3134.             $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false;
  3135.             if ($pos !== false) {
  3136.                 return Strings::shift($this->interactiveBuffer, $pos + strlen($match));
  3137.             }
  3138.             $response = $this->get_channel_packet($channel);
  3139.             if ($response === true) {
  3140.                 $this->in_request_pty_exec = false;
  3141.                 return Strings::shift($this->interactiveBuffer, strlen($this->interactiveBuffer));
  3142.             }
  3143.  
  3144.             $this->interactiveBuffer .= $response;
  3145.         }
  3146.     }
  3147.  
  3148.     /**
  3149.      * Inputs a command into an interactive shell.
  3150.      *
  3151.      * @see SSH2::read()
  3152.      * @param string $cmd
  3153.      * @return void
  3154.      * @throws \RuntimeException on connection error
  3155.      */
  3156.     public function write($cmd)
  3157.     {
  3158.         if (!$this->isAuthenticated()) {
  3159.             throw new InsufficientSetupException('Operation disallowed prior to login()');
  3160.         }
  3161.  
  3162.         if (!($this->bitmap & self::MASK_SHELL) && !$this->initShell()) {
  3163.             throw new \RuntimeException('Unable to initiate an interactive shell session');
  3164.         }
  3165.  
  3166.         $this->send_channel_packet($this->get_interactive_channel(), $cmd);
  3167.     }
  3168.  
  3169.     /**
  3170.      * Start a subsystem.
  3171.      *
  3172.      * Right now only one subsystem at a time is supported. To support multiple subsystem's stopSubsystem() could accept
  3173.      * a string that contained the name of the subsystem, but at that point, only one subsystem of each type could be opened.
  3174.      * To support multiple subsystem's of the same name maybe it'd be best if startSubsystem() generated a new channel id and
  3175.      * returns that and then that that was passed into stopSubsystem() but that'll be saved for a future date and implemented
  3176.      * if there's sufficient demand for such a feature.
  3177.      *
  3178.      * @see self::stopSubsystem()
  3179.      * @param string $subsystem
  3180.      * @return bool
  3181.      * @access public
  3182.      */
  3183.     public function startSubsystem($subsystem)
  3184.     {
  3185.         $this->window_size_server_to_client[self::CHANNEL_SUBSYSTEM] = $this->window_size;
  3186.  
  3187.         $packet = Strings::packSSH2(
  3188.             'CsN3',
  3189.             NET_SSH2_MSG_CHANNEL_OPEN,
  3190.             'session',
  3191.             self::CHANNEL_SUBSYSTEM,
  3192.             $this->window_size,
  3193.             0x4000
  3194.         );
  3195.  
  3196.         $this->send_binary_packet($packet);
  3197.  
  3198.         $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN;
  3199.  
  3200.         $this->get_channel_packet(self::CHANNEL_SUBSYSTEM);
  3201.  
  3202.         $packet = Strings::packSSH2(
  3203.             'CNsCs',
  3204.             NET_SSH2_MSG_CHANNEL_REQUEST,
  3205.             $this->server_channels[self::CHANNEL_SUBSYSTEM],
  3206.             'subsystem',
  3207.             1,
  3208.             $subsystem
  3209.         );
  3210.         $this->send_binary_packet($packet);
  3211.  
  3212.         $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_REQUEST;
  3213.  
  3214.         if (!$this->get_channel_packet(self::CHANNEL_SUBSYSTEM)) {
  3215.             return false;
  3216.         }
  3217.  
  3218.         $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA;
  3219.  
  3220.         $this->bitmap |= self::MASK_SHELL;
  3221.         $this->in_subsystem = true;
  3222.  
  3223.         return true;
  3224.     }
  3225.  
  3226.     /**
  3227.      * Stops a subsystem.
  3228.      *
  3229.      * @see self::startSubsystem()
  3230.      * @return bool
  3231.      * @access public
  3232.      */
  3233.     public function stopSubsystem()
  3234.     {
  3235.         $this->in_subsystem = false;
  3236.         $this->close_channel(self::CHANNEL_SUBSYSTEM);
  3237.         return true;
  3238.     }
  3239.  
  3240.     /**
  3241.      * Closes a channel
  3242.      *
  3243.      * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call
  3244.      *
  3245.      * @access public
  3246.      */
  3247.     public function reset()
  3248.     {
  3249.         $this->close_channel($this->get_interactive_channel());
  3250.     }
  3251.  
  3252.     /**
  3253.      * Is timeout?
  3254.      *
  3255.      * Did exec() or read() return because they timed out or because they encountered the end?
  3256.      *
  3257.      * @access public
  3258.      */
  3259.     public function isTimeout()
  3260.     {
  3261.         return $this->is_timeout;
  3262.     }
  3263.  
  3264.     /**
  3265.      * Disconnect
  3266.      *
  3267.      * @access public
  3268.      */
  3269.     public function disconnect()
  3270.     {
  3271.         $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
  3272.         if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) {
  3273.             fclose($this->realtime_log_file);
  3274.         }
  3275.         unset(self::$connections[$this->getResourceId()]);
  3276.     }
  3277.  
  3278.     /**
  3279.      * Destructor.
  3280.      *
  3281.      * Will be called, automatically, if you're supporting just PHP5.  If you're supporting PHP4, you'll need to call
  3282.      * disconnect().
  3283.      *
  3284.      * @access public
  3285.      */
  3286.     public function __destruct()
  3287.     {
  3288.         $this->disconnect();
  3289.     }
  3290.  
  3291.     /**
  3292.      * Is the connection still active?
  3293.      *
  3294.      * @return bool
  3295.      * @access public
  3296.      */
  3297.     public function isConnected()
  3298.     {
  3299.         return (bool) ($this->bitmap & self::MASK_CONNECTED);
  3300.     }
  3301.  
  3302.     /**
  3303.      * Have you successfully been logged in?
  3304.      *
  3305.      * @return bool
  3306.      * @access public
  3307.      */
  3308.     public function isAuthenticated()
  3309.     {
  3310.         return (bool) ($this->bitmap & self::MASK_LOGIN);
  3311.     }
  3312.  
  3313.     /**
  3314.      * Pings a server connection, or tries to reconnect if the connection has gone down
  3315.      *
  3316.      * Inspired by http://php.net/manual/en/mysqli.ping.php
  3317.      *
  3318.      * @return bool
  3319.      */
  3320.     public function ping()
  3321.     {
  3322.         if (!$this->isAuthenticated()) {
  3323.             if (!empty($this->auth)) {
  3324.                 return $this->reconnect();
  3325.             }
  3326.             return false;
  3327.         }
  3328.  
  3329.         $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE] = $this->window_size;
  3330.         $packet_size = 0x4000;
  3331.         $packet = Strings::packSSH2(
  3332.             'CsN3',
  3333.             NET_SSH2_MSG_CHANNEL_OPEN,
  3334.             'session',
  3335.             self::CHANNEL_KEEP_ALIVE,
  3336.             $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE],
  3337.             $packet_size
  3338.         );
  3339.  
  3340.         try {
  3341.             $this->send_binary_packet($packet);
  3342.  
  3343.             $this->channel_status[self::CHANNEL_KEEP_ALIVE] = NET_SSH2_MSG_CHANNEL_OPEN;
  3344.  
  3345.             $response = $this->get_channel_packet(self::CHANNEL_KEEP_ALIVE);
  3346.         } catch (\RuntimeException $e) {
  3347.             return $this->reconnect();
  3348.         }
  3349.  
  3350.         $this->close_channel(self::CHANNEL_KEEP_ALIVE);
  3351.         return true;
  3352.     }
  3353.  
  3354.     /**
  3355.      * In situ reconnect method
  3356.      *
  3357.      * @return boolean
  3358.      */
  3359.     private function reconnect()
  3360.     {
  3361.         $this->reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST);
  3362.         $this->retry_connect = true;
  3363.         $this->connect();
  3364.         foreach ($this->auth as $auth) {
  3365.             $result = $this->login(...$auth);
  3366.         }
  3367.         return $result;
  3368.     }
  3369.  
  3370.     /**
  3371.      * Resets a connection for re-use
  3372.      *
  3373.      * @param int $reason
  3374.      * @access private
  3375.      */
  3376.     protected function reset_connection($reason)
  3377.     {
  3378.         $this->disconnect_helper($reason);
  3379.         $this->decrypt = $this->encrypt = false;
  3380.         $this->decrypt_block_size = $this->encrypt_block_size = 8;
  3381.         $this->hmac_check = $this->hmac_create = false;
  3382.         $this->hmac_size = false;
  3383.         $this->session_id = false;
  3384.         $this->retry_connect = true;
  3385.         $this->get_seq_no = $this->send_seq_no = 0;
  3386.     }
  3387.  
  3388.     /**
  3389.      * Gets Binary Packets
  3390.      *
  3391.      * See '6. Binary Packet Protocol' of rfc4253 for more info.
  3392.      *
  3393.      * @see self::_send_binary_packet()
  3394.      * @param bool $skip_channel_filter
  3395.      * @return bool|string
  3396.      * @access private
  3397.      */
  3398.     private function get_binary_packet($skip_channel_filter = false)
  3399.     {
  3400.         if ($skip_channel_filter) {
  3401.             if (!is_resource($this->fsock)) {
  3402.                 throw new \InvalidArgumentException('fsock is not a resource.');
  3403.             }
  3404.             $read = [$this->fsock];
  3405.             $write = $except = null;
  3406.  
  3407.             if (!$this->curTimeout) {
  3408.                 if ($this->keepAlive <= 0) {
  3409.                     @stream_select($read, $write, $except, null);
  3410.                 } else {
  3411.                     if (!@stream_select($read, $write, $except, $this->keepAlive)) {
  3412.                         $this->send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0));
  3413.                         return $this->get_binary_packet(true);
  3414.                     }
  3415.                 }
  3416.             } else {
  3417.                 if ($this->curTimeout < 0) {
  3418.                     $this->is_timeout = true;
  3419.                     return true;
  3420.                 }
  3421.  
  3422.                 $start = microtime(true);
  3423.  
  3424.                 if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) {
  3425.                     if (!@stream_select($read, $write, $except, $this->keepAlive)) {
  3426.                         $this->send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0));
  3427.                         $elapsed = microtime(true) - $start;
  3428.                         $this->curTimeout -= $elapsed;
  3429.                         return $this->get_binary_packet(true);
  3430.                     }
  3431.                     $elapsed = microtime(true) - $start;
  3432.                     $this->curTimeout -= $elapsed;
  3433.                 }
  3434.  
  3435.                 $sec = (int) floor($this->curTimeout);
  3436.                 $usec = (int) (1000000 * ($this->curTimeout - $sec));
  3437.  
  3438.                 // this can return a "stream_select(): unable to select [4]: Interrupted system call" error
  3439.                 if (!@stream_select($read, $write, $except, $sec, $usec)) {
  3440.                     $this->is_timeout = true;
  3441.                     return true;
  3442.                 }
  3443.                 $elapsed = microtime(true) - $start;
  3444.                 $this->curTimeout -= $elapsed;
  3445.             }
  3446.         }
  3447.  
  3448.         if (!is_resource($this->fsock) || feof($this->fsock)) {
  3449.             $this->bitmap = 0;
  3450.             throw new ConnectionClosedException('Connection closed (by server) prematurely ' . $elapsed . 's');
  3451.         }
  3452.  
  3453.         $start = microtime(true);
  3454.         $raw = stream_get_contents($this->fsock, $this->decrypt_block_size);
  3455.  
  3456.         if (!strlen($raw)) {
  3457.             $this->bitmap = 0;
  3458.             throw new ConnectionClosedException('No data received from server');
  3459.         }
  3460.  
  3461.         if ($this->decrypt) {
  3462.             switch ($this->decryptName) {
  3463.                 case 'aes128-gcm@openssh.com':
  3464.                 case 'aes256-gcm@openssh.com':
  3465.                     $this->decrypt->setNonce(
  3466.                         $this->decryptFixedPart .
  3467.                         $this->decryptInvocationCounter
  3468.                     );
  3469.                     Strings::increment_str($this->decryptInvocationCounter);
  3470.                     $this->decrypt->setAAD($temp = Strings::shift($raw, 4));
  3471.                     extract(unpack('Npacket_length', $temp));
  3472.                     /**
  3473.                      * @var integer $packet_length
  3474.                      */
  3475.  
  3476.                     $raw .= $this->read_remaining_bytes($packet_length - $this->decrypt_block_size + 4);
  3477.                     $stop = microtime(true);
  3478.                     $tag = stream_get_contents($this->fsock, $this->decrypt_block_size);
  3479.                     $this->decrypt->setTag($tag);
  3480.                     $raw = $this->decrypt->decrypt($raw);
  3481.                     $raw = $temp . $raw;
  3482.                     $remaining_length = 0;
  3483.                     break;
  3484.                 case 'chacha20-poly1305@openssh.com':
  3485.                     // This should be impossible, but we are checking anyway to narrow the type for Psalm.
  3486.                     if (!($this->decrypt instanceof ChaCha20)) {
  3487.                         throw new \LogicException('$this->decrypt is not a ' . ChaCha20::class);
  3488.                     }
  3489.  
  3490.                     $nonce = pack('N2', 0, $this->get_seq_no);
  3491.  
  3492.                     $this->lengthDecrypt->setNonce($nonce);
  3493.                     $temp = $this->lengthDecrypt->decrypt($aad = Strings::shift($raw, 4));
  3494.                     extract(unpack('Npacket_length', $temp));
  3495.                     /**
  3496.                      * @var integer $packet_length
  3497.                      */
  3498.  
  3499.                     $raw .= $this->read_remaining_bytes($packet_length - $this->decrypt_block_size + 4);
  3500.                     $stop = microtime(true);
  3501.                     $tag = stream_get_contents($this->fsock, 16);
  3502.  
  3503.                     $this->decrypt->setNonce($nonce);
  3504.                     $this->decrypt->setCounter(0);
  3505.                     // this is the same approach that's implemented in Salsa20::createPoly1305Key()
  3506.                     // but we don't want to use the same AEAD construction that RFC8439 describes
  3507.                     // for ChaCha20-Poly1305 so we won't rely on it (see Salsa20::poly1305())
  3508.                     $this->decrypt->setPoly1305Key(
  3509.                         $this->decrypt->encrypt(str_repeat("\0", 32))
  3510.                     );
  3511.                     $this->decrypt->setAAD($aad);
  3512.                     $this->decrypt->setCounter(1);
  3513.                     $this->decrypt->setTag($tag);
  3514.                     $raw = $this->decrypt->decrypt($raw);
  3515.                     $raw = $temp . $raw;
  3516.                     $remaining_length = 0;
  3517.                     break;
  3518.                 default:
  3519.                     if (!$this->hmac_check instanceof Hash || !$this->hmac_check_etm) {
  3520.                         $raw = $this->decrypt->decrypt($raw);
  3521.                         break;
  3522.                     }
  3523.                     extract(unpack('Npacket_length', $temp = Strings::shift($raw, 4)));
  3524.                     /**
  3525.                      * @var integer $packet_length
  3526.                      */
  3527.                     $raw .= $this->read_remaining_bytes($packet_length - $this->decrypt_block_size + 4);
  3528.                     $stop = microtime(true);
  3529.                     $encrypted = $temp . $raw;
  3530.                     $raw = $temp . $this->decrypt->decrypt($raw);
  3531.                     $remaining_length = 0;
  3532.             }
  3533.         }
  3534.  
  3535.         if (strlen($raw) < 5) {
  3536.             $this->bitmap = 0;
  3537.             throw new \RuntimeException('Plaintext is too short');
  3538.         }
  3539.         extract(unpack('Npacket_length/Cpadding_length', Strings::shift($raw, 5)));
  3540.         /**
  3541.          * @var integer $packet_length
  3542.          * @var integer $padding_length
  3543.          */
  3544.  
  3545.         if (!isset($remaining_length)) {
  3546.             $remaining_length = $packet_length + 4 - $this->decrypt_block_size;
  3547.         }
  3548.  
  3549.         $buffer = $this->read_remaining_bytes($remaining_length);
  3550.  
  3551.         if (!isset($stop)) {
  3552.             $stop = microtime(true);
  3553.         }
  3554.         if (strlen($buffer)) {
  3555.             $raw .= $this->decrypt ? $this->decrypt->decrypt($buffer) : $buffer;
  3556.         }
  3557.  
  3558.         $payload = Strings::shift($raw, $packet_length - $padding_length - 1);
  3559.         $padding = Strings::shift($raw, $padding_length); // should leave $raw empty
  3560.  
  3561.         if ($this->hmac_check instanceof Hash) {
  3562.             $hmac = stream_get_contents($this->fsock, $this->hmac_size);
  3563.             if ($hmac === false || strlen($hmac) != $this->hmac_size) {
  3564.                 $this->disconnect_helper(NET_SSH2_DISCONNECT_MAC_ERROR);
  3565.                 throw new \RuntimeException('Error reading socket');
  3566.             }
  3567.  
  3568.             $reconstructed = !$this->hmac_check_etm ?
  3569.                 pack('NCa*', $packet_length, $padding_length, $payload . $padding) :
  3570.                 $encrypted;
  3571.             if (($this->hmac_check->getHash() & "\xFF\xFF\xFF\xFF") == 'umac') {
  3572.                 $this->hmac_check->setNonce("\0\0\0\0" . pack('N', $this->get_seq_no));
  3573.                 if ($hmac != $this->hmac_check->hash($reconstructed)) {
  3574.                     $this->disconnect_helper(NET_SSH2_DISCONNECT_MAC_ERROR);
  3575.                     throw new \RuntimeException('Invalid UMAC');
  3576.                 }
  3577.             } else {
  3578.                 if ($hmac != $this->hmac_check->hash(pack('Na*', $this->get_seq_no, $reconstructed))) {
  3579.                     $this->disconnect_helper(NET_SSH2_DISCONNECT_MAC_ERROR);
  3580.                     throw new \RuntimeException('Invalid HMAC');
  3581.                 }
  3582.             }
  3583.         }
  3584.  
  3585.         switch ($this->decompress) {
  3586.             case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH:
  3587.                 if (!$this->isAuthenticated()) {
  3588.                     break;
  3589.                 }
  3590.                 // fall-through
  3591.             case self::NET_SSH2_COMPRESSION_ZLIB:
  3592.                 if ($this->regenerate_decompression_context) {
  3593.                     $this->regenerate_decompression_context = false;
  3594.  
  3595.                     $cmf = ord($payload[0]);
  3596.                     $cm = $cmf & 0x0F;
  3597.                     if ($cm != 8) { // deflate
  3598.                         user_error("Only CM = 8 ('deflate') is supported ($cm)");
  3599.                     }
  3600.                     $cinfo = ($cmf & 0xF0) >> 4;
  3601.                     if ($cinfo > 7) {
  3602.                         user_error("CINFO above 7 is not allowed ($cinfo)");
  3603.                     }
  3604.                     $windowSize = 1 << ($cinfo + 8);
  3605.  
  3606.                     $flg = ord($payload[1]);
  3607.                     //$fcheck = $flg && 0x0F;
  3608.                     if ((($cmf << 8) | $flg) % 31) {
  3609.                         user_error('fcheck failed');
  3610.                     }
  3611.                     $fdict = boolval($flg & 0x20);
  3612.                     $flevel = ($flg & 0xC0) >> 6;
  3613.  
  3614.                     $this->decompress_context = inflate_init(ZLIB_ENCODING_RAW, ['window' => $cinfo + 8]);
  3615.                     $payload = substr($payload, 2);
  3616.                 }
  3617.                 if ($this->decompress_context) {
  3618.                     $payload = inflate_add($this->decompress_context, $payload, ZLIB_PARTIAL_FLUSH);
  3619.                 }
  3620.         }
  3621.  
  3622.         $this->get_seq_no++;
  3623.  
  3624.         if (defined('NET_SSH2_LOGGING')) {
  3625.             $current = microtime(true);
  3626.             $message_number = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')';
  3627.             $message_number = '<- ' . $message_number .
  3628.                               ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
  3629.             $this->append_log($message_number, $payload);
  3630.             $this->last_packet = $current;
  3631.         }
  3632.  
  3633.         return $this->filter($payload, $skip_channel_filter);
  3634.     }
  3635.  
  3636.     /**
  3637.      * Read Remaining Bytes
  3638.      *
  3639.      * @see self::get_binary_packet()
  3640.      * @param int $remaining_length
  3641.      * @return string
  3642.      * @access private
  3643.      */
  3644.     private function read_remaining_bytes($remaining_length)
  3645.     {
  3646.         if (!$remaining_length) {
  3647.             return '';
  3648.         }
  3649.  
  3650.         $adjustLength = false;
  3651.         if ($this->decrypt) {
  3652.             switch (true) {
  3653.                 case $this->decryptName == 'aes128-gcm@openssh.com':
  3654.                 case $this->decryptName == 'aes256-gcm@openssh.com':
  3655.                 case $this->decryptName == 'chacha20-poly1305@openssh.com':
  3656.                 case $this->hmac_check instanceof Hash && $this->hmac_check_etm:
  3657.                     $remaining_length += $this->decrypt_block_size - 4;
  3658.                     $adjustLength = true;
  3659.             }
  3660.         }
  3661.  
  3662.         // quoting <http://tools.ietf.org/html/rfc4253#section-6.1>,
  3663.         // "implementations SHOULD check that the packet length is reasonable"
  3664.         // PuTTY uses 0x9000 as the actual max packet size and so to shall we
  3665.         // don't do this when GCM mode is used since GCM mode doesn't encrypt the length
  3666.         if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) {
  3667.             if (!$this->bad_key_size_fix && self::bad_algorithm_candidate($this->decrypt ? $this->decryptName : '') && !($this->bitmap & SSH2::MASK_LOGIN)) {
  3668.                 $this->bad_key_size_fix = true;
  3669.                 $this->reset_connection(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  3670.                 return false;
  3671.             }
  3672.             throw new \RuntimeException('Invalid size');
  3673.         }
  3674.  
  3675.         if ($adjustLength) {
  3676.             $remaining_length -= $this->decrypt_block_size - 4;
  3677.         }
  3678.  
  3679.         $buffer = '';
  3680.         while ($remaining_length > 0) {
  3681.             $temp = stream_get_contents($this->fsock, $remaining_length);
  3682.             if ($temp === false || feof($this->fsock)) {
  3683.                 $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST);
  3684.                 throw new \RuntimeException('Error reading from socket');
  3685.             }
  3686.             $buffer .= $temp;
  3687.             $remaining_length -= strlen($temp);
  3688.         }
  3689.  
  3690.         return $buffer;
  3691.     }
  3692.  
  3693.     /**
  3694.      * Filter Binary Packets
  3695.      *
  3696.      * Because some binary packets need to be ignored...
  3697.      *
  3698.      * @see self::_get_binary_packet()
  3699.      * @param string $payload
  3700.      * @param bool $skip_channel_filter
  3701.      * @return string|bool
  3702.      * @access private
  3703.      */
  3704.     private function filter($payload, $skip_channel_filter)
  3705.     {
  3706.         switch (ord($payload[0])) {
  3707.             case NET_SSH2_MSG_DISCONNECT:
  3708.                 Strings::shift($payload, 1);
  3709.                 list($reason_code, $message) = Strings::unpackSSH2('Ns', $payload);
  3710.                 $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n$message";
  3711.                 $this->bitmap = 0;
  3712.                 return false;
  3713.             case NET_SSH2_MSG_IGNORE:
  3714.                 $payload = $this->get_binary_packet($skip_channel_filter);
  3715.                 break;
  3716.             case NET_SSH2_MSG_DEBUG:
  3717.                 Strings::shift($payload, 2); // second byte is "always_display"
  3718.                 list($message) = Strings::unpackSSH2('s', $payload);
  3719.                 $this->errors[] = "SSH_MSG_DEBUG: $message";
  3720.                 $payload = $this->get_binary_packet($skip_channel_filter);
  3721.                 break;
  3722.             case NET_SSH2_MSG_UNIMPLEMENTED:
  3723.                 return false;
  3724.             case NET_SSH2_MSG_KEXINIT:
  3725.                 if ($this->session_id !== false) {
  3726.                     if (!$this->key_exchange($payload)) {
  3727.                         $this->bitmap = 0;
  3728.                         return false;
  3729.                     }
  3730.                     $payload = $this->get_binary_packet($skip_channel_filter);
  3731.                 }
  3732.         }
  3733.  
  3734.         // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in
  3735.         if (($this->bitmap & self::MASK_CONNECTED) && !$this->isAuthenticated() && !is_bool($payload) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
  3736.             Strings::shift($payload, 1);
  3737.             list($this->banner_message) = Strings::unpackSSH2('s', $payload);
  3738.             $payload = $this->get_binary_packet();
  3739.         }
  3740.  
  3741.         // only called when we've already logged in
  3742.         if (($this->bitmap & self::MASK_CONNECTED) && $this->isAuthenticated()) {
  3743.             if (is_bool($payload)) {
  3744.                 return $payload;
  3745.             }
  3746.  
  3747.             switch (ord($payload[0])) {
  3748.                 case NET_SSH2_MSG_CHANNEL_REQUEST:
  3749.                     if (strlen($payload) == 31) {
  3750.                         extract(unpack('cpacket_type/Nchannel/Nlength', $payload));
  3751.                         if (substr($payload, 9, $length) == 'keepalive@openssh.com' && isset($this->server_channels[$channel])) {
  3752.                             if (ord(substr($payload, 9 + $length))) { // want reply
  3753.                                 $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_SUCCESS, $this->server_channels[$channel]));
  3754.                             }
  3755.                             $payload = $this->get_binary_packet($skip_channel_filter);
  3756.                         }
  3757.                     }
  3758.                     break;
  3759.                 case NET_SSH2_MSG_CHANNEL_DATA:
  3760.                 case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
  3761.                 case NET_SSH2_MSG_CHANNEL_CLOSE:
  3762.                 case NET_SSH2_MSG_CHANNEL_EOF:
  3763.                     if (!$skip_channel_filter && !empty($this->server_channels)) {
  3764.                         $this->binary_packet_buffer = $payload;
  3765.                         $this->get_channel_packet(true);
  3766.                         $payload = $this->get_binary_packet();
  3767.                     }
  3768.                     break;
  3769.                 case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
  3770.                     Strings::shift($payload, 1);
  3771.                     list($request_name) = Strings::unpackSSH2('s', $payload);
  3772.                     $this->errors[] = "SSH_MSG_GLOBAL_REQUEST: $request_name";
  3773.  
  3774.                     try {
  3775.                         $this->send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE));
  3776.                     } catch (\RuntimeException $e) {
  3777.                         return $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
  3778.                     }
  3779.  
  3780.                     $payload = $this->get_binary_packet($skip_channel_filter);
  3781.                     break;
  3782.                 case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1
  3783.                     Strings::shift($payload, 1);
  3784.                     list($data, $server_channel) = Strings::unpackSSH2('sN', $payload);
  3785.                     switch ($data) {
  3786.                         case 'auth-agent':
  3787.                         case 'auth-agent@openssh.com':
  3788.                             if (isset($this->agent)) {
  3789.                                 $new_channel = self::CHANNEL_AGENT_FORWARD;
  3790.  
  3791.                                 list(
  3792.                                     $remote_window_size,
  3793.                                     $remote_maximum_packet_size
  3794.                                 ) = Strings::unpackSSH2('NN', $payload);
  3795.  
  3796.                                 $this->packet_size_client_to_server[$new_channel] = $remote_window_size;
  3797.                                 $this->window_size_server_to_client[$new_channel] = $remote_maximum_packet_size;
  3798.                                 $this->window_size_client_to_server[$new_channel] = $this->window_size;
  3799.  
  3800.                                 $packet_size = 0x4000;
  3801.  
  3802.                                 $packet = pack(
  3803.                                     'CN4',
  3804.                                     NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION,
  3805.                                     $server_channel,
  3806.                                     $new_channel,
  3807.                                     $packet_size,
  3808.                                     $packet_size
  3809.                                 );
  3810.  
  3811.                                 $this->server_channels[$new_channel] = $server_channel;
  3812.                                 $this->channel_status[$new_channel] = NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION;
  3813.                                 $this->send_binary_packet($packet);
  3814.                             }
  3815.                             break;
  3816.                         default:
  3817.                             $packet = Strings::packSSH2(
  3818.                                 'CN2ss',
  3819.                                 NET_SSH2_MSG_CHANNEL_OPEN_FAILURE,
  3820.                                 $server_channel,
  3821.                                 NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED,
  3822.                                 '', // description
  3823.                                 '' // language tag
  3824.                             );
  3825.  
  3826.                             try {
  3827.                                 $this->send_binary_packet($packet);
  3828.                             } catch (\RuntimeException $e) {
  3829.                                 return $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
  3830.                             }
  3831.                     }
  3832.  
  3833.                     $payload = $this->get_binary_packet($skip_channel_filter);
  3834.                     break;
  3835.                 case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
  3836.                     Strings::shift($payload, 1);
  3837.                     list($channel, $window_size) = Strings::unpackSSH2('NN', $payload);
  3838.  
  3839.                     $this->window_size_client_to_server[$channel] += $window_size;
  3840.  
  3841.                     $payload = ($this->bitmap & self::MASK_WINDOW_ADJUST) ? true : $this->get_binary_packet($skip_channel_filter);
  3842.             }
  3843.         }
  3844.  
  3845.         return $payload;
  3846.     }
  3847.  
  3848.     /**
  3849.      * Enable Quiet Mode
  3850.      *
  3851.      * Suppress stderr from output
  3852.      *
  3853.      * @access public
  3854.      */
  3855.     public function enableQuietMode()
  3856.     {
  3857.         $this->quiet_mode = true;
  3858.     }
  3859.  
  3860.     /**
  3861.      * Disable Quiet Mode
  3862.      *
  3863.      * Show stderr in output
  3864.      *
  3865.      * @access public
  3866.      */
  3867.     public function disableQuietMode()
  3868.     {
  3869.         $this->quiet_mode = false;
  3870.     }
  3871.  
  3872.     /**
  3873.      * Returns whether Quiet Mode is enabled or not
  3874.      *
  3875.      * @see self::enableQuietMode()
  3876.      * @see self::disableQuietMode()
  3877.      * @access public
  3878.      * @return bool
  3879.      */
  3880.     public function isQuietModeEnabled()
  3881.     {
  3882.         return $this->quiet_mode;
  3883.     }
  3884.  
  3885.     /**
  3886.      * Enable request-pty when using exec()
  3887.      *
  3888.      * @access public
  3889.      */
  3890.     public function enablePTY()
  3891.     {
  3892.         $this->request_pty = true;
  3893.     }
  3894.  
  3895.     /**
  3896.      * Disable request-pty when using exec()
  3897.      *
  3898.      * @access public
  3899.      */
  3900.     public function disablePTY()
  3901.     {
  3902.         if ($this->in_request_pty_exec) {
  3903.             $this->close_channel(self::CHANNEL_EXEC);
  3904.             $this->in_request_pty_exec = false;
  3905.         }
  3906.         $this->request_pty = false;
  3907.     }
  3908.  
  3909.     /**
  3910.      * Returns whether request-pty is enabled or not
  3911.      *
  3912.      * @see self::enablePTY()
  3913.      * @see self::disablePTY()
  3914.      * @access public
  3915.      * @return bool
  3916.      */
  3917.     public function isPTYEnabled()
  3918.     {
  3919.         return $this->request_pty;
  3920.     }
  3921.  
  3922.     /**
  3923.      * Gets channel data
  3924.      *
  3925.      * Returns the data as a string. bool(true) is returned if:
  3926.      *
  3927.      * - the server closes the channel
  3928.      * - if the connection times out
  3929.      * - if the channel status is CHANNEL_OPEN and the response was CHANNEL_OPEN_CONFIRMATION
  3930.      * - if the channel status is CHANNEL_REQUEST and the response was CHANNEL_SUCCESS
  3931.      *
  3932.      * bool(false) is returned if:
  3933.      *
  3934.      * - if the channel status is CHANNEL_REQUEST and the response was CHANNEL_FAILURE
  3935.      *
  3936.      * @param int $client_channel
  3937.      * @param bool $skip_extended
  3938.      * @return mixed
  3939.      * @throws \RuntimeException on connection error
  3940.      * @access private
  3941.      */
  3942.     protected function get_channel_packet($client_channel, $skip_extended = false)
  3943.     {
  3944.         if (!empty($this->channel_buffers[$client_channel])) {
  3945.             switch ($this->channel_status[$client_channel]) {
  3946.                 case NET_SSH2_MSG_CHANNEL_REQUEST:
  3947.                     foreach ($this->channel_buffers[$client_channel] as $i => $packet) {
  3948.                         switch (ord($packet[0])) {
  3949.                             case NET_SSH2_MSG_CHANNEL_SUCCESS:
  3950.                             case NET_SSH2_MSG_CHANNEL_FAILURE:
  3951.                                 unset($this->channel_buffers[$client_channel][$i]);
  3952.                                 return substr($packet, 1);
  3953.                         }
  3954.                     }
  3955.                     break;
  3956.                 default:
  3957.                     return substr(array_shift($this->channel_buffers[$client_channel]), 1);
  3958.             }
  3959.         }
  3960.  
  3961.         while (true) {
  3962.             if ($this->binary_packet_buffer !== false) {
  3963.                 $response = $this->binary_packet_buffer;
  3964.                 $this->binary_packet_buffer = false;
  3965.             } else {
  3966.                 $response = $this->get_binary_packet(true);
  3967.                 if ($response === true && $this->is_timeout) {
  3968.                     if ($client_channel == self::CHANNEL_EXEC && !$this->request_pty) {
  3969.                         $this->close_channel($client_channel);
  3970.                     }
  3971.                     return true;
  3972.                 }
  3973.                 if ($response === false) {
  3974.                     $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST);
  3975.                     throw new ConnectionClosedException('Connection closed by server');
  3976.                 }
  3977.             }
  3978.  
  3979.             if ($client_channel == -1 && $response === true) {
  3980.                 return true;
  3981.             }
  3982.             list($type, $channel) = Strings::unpackSSH2('CN', $response);
  3983.  
  3984.             // will not be setup yet on incoming channel open request
  3985.             if (isset($channel) && isset($this->channel_status[$channel]) && isset($this->window_size_server_to_client[$channel])) {
  3986.                 $this->window_size_server_to_client[$channel] -= strlen($response);
  3987.  
  3988.                 // resize the window, if appropriate
  3989.                 if ($this->window_size_server_to_client[$channel] < 0) {
  3990.                 // PuTTY does something more analogous to the following:
  3991.                 //if ($this->window_size_server_to_client[$channel] < 0x3FFFFFFF) {
  3992.                     $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_resize);
  3993.                     $this->send_binary_packet($packet);
  3994.                     $this->window_size_server_to_client[$channel] += $this->window_resize;
  3995.                 }
  3996.  
  3997.                 switch ($type) {
  3998.                     case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
  3999.                         /*
  4000.                         if ($client_channel == self::CHANNEL_EXEC) {
  4001.                             $this->send_channel_packet($client_channel, chr(0));
  4002.                         }
  4003.                         */
  4004.                         // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR
  4005.                         list($data_type_code, $data) = Strings::unpackSSH2('Ns', $response);
  4006.                         $this->stdErrorLog .= $data;
  4007.                         if ($skip_extended || $this->quiet_mode) {
  4008.                             continue 2;
  4009.                         }
  4010.                         if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) {
  4011.                             return $data;
  4012.                         }
  4013.                         $this->channel_buffers[$channel][] = chr($type) . $data;
  4014.  
  4015.                         continue 2;
  4016.                     case NET_SSH2_MSG_CHANNEL_REQUEST:
  4017.                         if ($this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_CLOSE) {
  4018.                             continue 2;
  4019.                         }
  4020.                         list($value) = Strings::unpackSSH2('s', $response);
  4021.                         switch ($value) {
  4022.                             case 'exit-signal':
  4023.                                 list(
  4024.                                     , // FALSE
  4025.                                     $signal_name,
  4026.                                     , // core dumped
  4027.                                     $error_message
  4028.                                 ) = Strings::unpackSSH2('bsbs', $response);
  4029.  
  4030.                                 $this->errors[] = "SSH_MSG_CHANNEL_REQUEST (exit-signal): $signal_name";
  4031.                                 if (strlen($error_message)) {
  4032.                                     $this->errors[count($this->errors) - 1] .= "\r\n$error_message";
  4033.                                 }
  4034.  
  4035.                                 $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
  4036.                                 $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
  4037.  
  4038.                                 $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF;
  4039.  
  4040.                                 continue 3;
  4041.                             case 'exit-status':
  4042.                                 list(, $this->exit_status) = Strings::unpackSSH2('CN', $response);
  4043.  
  4044.                                 // "The client MAY ignore these messages."
  4045.                                 // -- http://tools.ietf.org/html/rfc4254#section-6.10
  4046.  
  4047.                                 continue 3;
  4048.                             default:
  4049.                                 // "Some systems may not implement signals, in which case they SHOULD ignore this message."
  4050.                                 //  -- http://tools.ietf.org/html/rfc4254#section-6.9
  4051.                                 continue 3;
  4052.                         }
  4053.                 }
  4054.  
  4055.                 switch ($this->channel_status[$channel]) {
  4056.                     case NET_SSH2_MSG_CHANNEL_OPEN:
  4057.                         switch ($type) {
  4058.                             case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
  4059.                                 list(
  4060.                                     $this->server_channels[$channel],
  4061.                                     $window_size,
  4062.                                     $this->packet_size_client_to_server[$channel]
  4063.                                 ) = Strings::unpackSSH2('NNN', $response);
  4064.  
  4065.                                 if ($window_size < 0) {
  4066.                                     $window_size &= 0x7FFFFFFF;
  4067.                                     $window_size += 0x80000000;
  4068.                                 }
  4069.                                 $this->window_size_client_to_server[$channel] = $window_size;
  4070.                                 $result = $client_channel == $channel ? true : $this->get_channel_packet($client_channel, $skip_extended);
  4071.                                 $this->on_channel_open();
  4072.                                 return $result;
  4073.                             case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
  4074.                                 $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
  4075.                                 throw new \RuntimeException('Unable to open channel');
  4076.                             default:
  4077.                                 if ($client_channel == $channel) {
  4078.                                     $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
  4079.                                     throw new \RuntimeException('Unexpected response to open request');
  4080.                                 }
  4081.                                 return $this->get_channel_packet($client_channel, $skip_extended);
  4082.                         }
  4083.                         break;
  4084.                     case NET_SSH2_MSG_CHANNEL_REQUEST:
  4085.                         switch ($type) {
  4086.                             case NET_SSH2_MSG_CHANNEL_SUCCESS:
  4087.                                 return true;
  4088.                             case NET_SSH2_MSG_CHANNEL_FAILURE:
  4089.                                 return false;
  4090.                             case NET_SSH2_MSG_CHANNEL_DATA:
  4091.                                 list($data) = Strings::unpackSSH2('s', $response);
  4092.                                 $this->channel_buffers[$channel][] = chr($type) . $data;
  4093.                                 return $this->get_channel_packet($client_channel, $skip_extended);
  4094.                             default:
  4095.                                 $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
  4096.                                 throw new \RuntimeException('Unable to fulfill channel request');
  4097.                         }
  4098.                     case NET_SSH2_MSG_CHANNEL_CLOSE:
  4099.                         return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->get_channel_packet($client_channel, $skip_extended);
  4100.                 }
  4101.             }
  4102.  
  4103.             // ie. $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA
  4104.  
  4105.             switch ($type) {
  4106.                 case NET_SSH2_MSG_CHANNEL_DATA:
  4107.                     /*
  4108.                     if ($channel == self::CHANNEL_EXEC) {
  4109.                         // SCP requires null packets, such as this, be sent.  further, in the case of the ssh.com SSH server
  4110.                         // this actually seems to make things twice as fast.  more to the point, the message right after
  4111.                         // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise.
  4112.                         // in OpenSSH it slows things down but only by a couple thousandths of a second.
  4113.                         $this->send_channel_packet($channel, chr(0));
  4114.                     }
  4115.                     */
  4116.                     list($data) = Strings::unpackSSH2('s', $response);
  4117.  
  4118.                     if ($channel == self::CHANNEL_AGENT_FORWARD) {
  4119.                         $agent_response = $this->agent->forwardData($data);
  4120.                         if (!is_bool($agent_response)) {
  4121.                             $this->send_channel_packet($channel, $agent_response);
  4122.                         }
  4123.                         break;
  4124.                     }
  4125.  
  4126.                     if ($client_channel == $channel) {
  4127.                         return $data;
  4128.                     }
  4129.                     $this->channel_buffers[$channel][] = chr($type) . $data;
  4130.                     break;
  4131.                 case NET_SSH2_MSG_CHANNEL_CLOSE:
  4132.                     $this->curTimeout = 5;
  4133.  
  4134.                     if ($this->bitmap & self::MASK_SHELL) {
  4135.                         $this->bitmap &= ~self::MASK_SHELL;
  4136.                     }
  4137.                     if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) {
  4138.                         $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
  4139.                     }
  4140.  
  4141.                     $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
  4142.                     if ($client_channel == $channel) {
  4143.                         return true;
  4144.                     }
  4145.                     // fall-through
  4146.                 case NET_SSH2_MSG_CHANNEL_EOF:
  4147.                     break;
  4148.                 default:
  4149.                     $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
  4150.                     throw new \RuntimeException("Error reading channel data ($type)");
  4151.             }
  4152.         }
  4153.     }
  4154.  
  4155.     /**
  4156.      * Sends Binary Packets
  4157.      *
  4158.      * See '6. Binary Packet Protocol' of rfc4253 for more info.
  4159.      *
  4160.      * @param string $data
  4161.      * @param string $logged
  4162.      * @see self::_get_binary_packet()
  4163.      * @return void
  4164.      * @access private
  4165.      */
  4166.     protected function send_binary_packet($data, $logged = null)
  4167.     {
  4168.         if (!is_resource($this->fsock) || feof($this->fsock)) {
  4169.             $this->bitmap = 0;
  4170.             throw new ConnectionClosedException('Connection closed prematurely');
  4171.         }
  4172.  
  4173.         if (!isset($logged)) {
  4174.             $logged = $data;
  4175.         }
  4176.  
  4177.         switch ($this->compress) {
  4178.             case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH:
  4179.                 if (!$this->isAuthenticated()) {
  4180.                     break;
  4181.                 }
  4182.                 // fall-through
  4183.             case self::NET_SSH2_COMPRESSION_ZLIB:
  4184.                 if (!$this->regenerate_compression_context) {
  4185.                     $header = '';
  4186.                 } else {
  4187.                     $this->regenerate_compression_context = false;
  4188.                     $this->compress_context = deflate_init(ZLIB_ENCODING_RAW, ['window' => 15]);
  4189.                     $header = "\x78\x9C";
  4190.                 }
  4191.                 if ($this->compress_context) {
  4192.                     $data = $header . deflate_add($this->compress_context, $data, ZLIB_PARTIAL_FLUSH);
  4193.                 }
  4194.         }
  4195.  
  4196.         // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
  4197.         $packet_length = strlen($data) + 9;
  4198.         if ($this->encrypt && $this->encrypt->usesNonce()) {
  4199.             $packet_length -= 4;
  4200.         }
  4201.         // round up to the nearest $this->encrypt_block_size
  4202.         $packet_length += (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size;
  4203.         // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length
  4204.         $padding_length = $packet_length - strlen($data) - 5;
  4205.         switch (true) {
  4206.             case $this->encrypt && $this->encrypt->usesNonce():
  4207.             case $this->hmac_create instanceof Hash && $this->hmac_create_etm:
  4208.                 $padding_length += 4;
  4209.                 $packet_length += 4;
  4210.         }
  4211.  
  4212.         $padding = Random::string($padding_length);
  4213.  
  4214.         // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself
  4215.         $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding);
  4216.  
  4217.         $hmac = '';
  4218.         if ($this->hmac_create instanceof Hash && !$this->hmac_create_etm) {
  4219.             if (($this->hmac_create->getHash() & "\xFF\xFF\xFF\xFF") == 'umac') {
  4220.                 $this->hmac_create->setNonce("\0\0\0\0" . pack('N', $this->send_seq_no));
  4221.                 $hmac = $this->hmac_create->hash($packet);
  4222.             } else {
  4223.                 $hmac = $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet));
  4224.             }
  4225.         }
  4226.  
  4227.         if ($this->encrypt) {
  4228.             switch ($this->encryptName) {
  4229.                 case 'aes128-gcm@openssh.com':
  4230.                 case 'aes256-gcm@openssh.com':
  4231.                     $this->encrypt->setNonce(
  4232.                         $this->encryptFixedPart .
  4233.                         $this->encryptInvocationCounter
  4234.                     );
  4235.                     Strings::increment_str($this->encryptInvocationCounter);
  4236.                     $this->encrypt->setAAD($temp = ($packet & "\xFF\xFF\xFF\xFF"));
  4237.                     $packet = $temp . $this->encrypt->encrypt(substr($packet, 4));
  4238.                     break;
  4239.                 case 'chacha20-poly1305@openssh.com':
  4240.                     // This should be impossible, but we are checking anyway to narrow the type for Psalm.
  4241.                     if (!($this->encrypt instanceof ChaCha20)) {
  4242.                         throw new \LogicException('$this->encrypt is not a ' . ChaCha20::class);
  4243.                     }
  4244.  
  4245.                     $nonce = pack('N2', 0, $this->send_seq_no);
  4246.  
  4247.                     $this->encrypt->setNonce($nonce);
  4248.                     $this->lengthEncrypt->setNonce($nonce);
  4249.  
  4250.                     $length = $this->lengthEncrypt->encrypt($packet & "\xFF\xFF\xFF\xFF");
  4251.  
  4252.                     $this->encrypt->setCounter(0);
  4253.                     // this is the same approach that's implemented in Salsa20::createPoly1305Key()
  4254.                     // but we don't want to use the same AEAD construction that RFC8439 describes
  4255.                     // for ChaCha20-Poly1305 so we won't rely on it (see Salsa20::poly1305())
  4256.                     $this->encrypt->setPoly1305Key(
  4257.                         $this->encrypt->encrypt(str_repeat("\0", 32))
  4258.                     );
  4259.                     $this->encrypt->setAAD($length);
  4260.                     $this->encrypt->setCounter(1);
  4261.                     $packet = $length . $this->encrypt->encrypt(substr($packet, 4));
  4262.                     break;
  4263.                 default:
  4264.                     $packet = $this->hmac_create instanceof Hash && $this->hmac_create_etm ?
  4265.                         ($packet & "\xFF\xFF\xFF\xFF") . $this->encrypt->encrypt(substr($packet, 4)) :
  4266.                         $this->encrypt->encrypt($packet);
  4267.             }
  4268.         }
  4269.  
  4270.         if ($this->hmac_create instanceof Hash && $this->hmac_create_etm) {
  4271.             if (($this->hmac_create->getHash() & "\xFF\xFF\xFF\xFF") == 'umac') {
  4272.                 $this->hmac_create->setNonce("\0\0\0\0" . pack('N', $this->send_seq_no));
  4273.                 $hmac = $this->hmac_create->hash($packet);
  4274.             } else {
  4275.                 $hmac = $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet));
  4276.             }
  4277.         }
  4278.  
  4279.         $this->send_seq_no++;
  4280.  
  4281.         $packet .= $this->encrypt && $this->encrypt->usesNonce() ? $this->encrypt->getTag() : $hmac;
  4282.  
  4283.         $start = microtime(true);
  4284.         $sent = @fputs($this->fsock, $packet);
  4285.         $stop = microtime(true);
  4286.  
  4287.         if (defined('NET_SSH2_LOGGING')) {
  4288.             $current = microtime(true);
  4289.             $message_number = isset($this->message_numbers[ord($logged[0])]) ? $this->message_numbers[ord($logged[0])] : 'UNKNOWN (' . ord($logged[0]) . ')';
  4290.             $message_number = '-> ' . $message_number .
  4291.                               ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
  4292.             $this->append_log($message_number, $logged);
  4293.             $this->last_packet = $current;
  4294.         }
  4295.  
  4296.         if (strlen($packet) != $sent) {
  4297.             $this->bitmap = 0;
  4298.             throw new \RuntimeException("Only $sent of " . strlen($packet) . " bytes were sent");
  4299.         }
  4300.     }
  4301.  
  4302.     /**
  4303.      * Logs data packets
  4304.      *
  4305.      * Makes sure that only the last 1MB worth of packets will be logged
  4306.      *
  4307.      * @param string $message_number
  4308.      * @param string $message
  4309.      * @access private
  4310.      */
  4311.     private function append_log($message_number, $message)
  4312.     {
  4313.         // remove the byte identifying the message type from all but the first two messages (ie. the identification strings)
  4314.         if (strlen($message_number) > 2) {
  4315.             Strings::shift($message);
  4316.         }
  4317.  
  4318.         switch (NET_SSH2_LOGGING) {
  4319.             // useful for benchmarks
  4320.             case self::LOG_SIMPLE:
  4321.                 $this->message_number_log[] = $message_number;
  4322.                 break;
  4323.             // the most useful log for SSH2
  4324.             case self::LOG_COMPLEX:
  4325.                 $this->message_number_log[] = $message_number;
  4326.                 $this->log_size += strlen($message);
  4327.                 $this->message_log[] = $message;
  4328.                 while ($this->log_size > self::LOG_MAX_SIZE) {
  4329.                     $this->log_size -= strlen(array_shift($this->message_log));
  4330.                     array_shift($this->message_number_log);
  4331.                 }
  4332.                 break;
  4333.             // dump the output out realtime; packets may be interspersed with non packets,
  4334.             // passwords won't be filtered out and select other packets may not be correctly
  4335.             // identified
  4336.             case self::LOG_REALTIME:
  4337.                 switch (PHP_SAPI) {
  4338.                     case 'cli':
  4339.                         $start = $stop = "\r\n";
  4340.                         break;
  4341.                     default:
  4342.                         $start = '<pre>';
  4343.                         $stop = '</pre>';
  4344.                 }
  4345.                 echo $start . $this->format_log([$message], [$message_number]) . $stop;
  4346.                 @flush();
  4347.                 @ob_flush();
  4348.                 break;
  4349.             // basically the same thing as self::LOG_REALTIME with the caveat that NET_SSH2_LOG_REALTIME_FILENAME
  4350.             // needs to be defined and that the resultant log file will be capped out at self::LOG_MAX_SIZE.
  4351.             // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily
  4352.             // at the beginning of the file
  4353.             case self::LOG_REALTIME_FILE:
  4354.                 if (!isset($this->realtime_log_file)) {
  4355.                     // PHP doesn't seem to like using constants in fopen()
  4356.                     $filename = NET_SSH2_LOG_REALTIME_FILENAME;
  4357.                     $fp = fopen($filename, 'w');
  4358.                     $this->realtime_log_file = $fp;
  4359.                 }
  4360.                 if (!is_resource($this->realtime_log_file)) {
  4361.                     break;
  4362.                 }
  4363.                 $entry = $this->format_log([$message], [$message_number]);
  4364.                 if ($this->realtime_log_wrap) {
  4365.                     $temp = "<<< START >>>\r\n";
  4366.                     $entry .= $temp;
  4367.                     fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp));
  4368.                 }
  4369.                 $this->realtime_log_size += strlen($entry);
  4370.                 if ($this->realtime_log_size > self::LOG_MAX_SIZE) {
  4371.                     fseek($this->realtime_log_file, 0);
  4372.                     $this->realtime_log_size = strlen($entry);
  4373.                     $this->realtime_log_wrap = true;
  4374.                 }
  4375.                 fputs($this->realtime_log_file, $entry);
  4376.         }
  4377.     }
  4378.  
  4379.     /**
  4380.      * Sends channel data
  4381.      *
  4382.      * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate
  4383.      *
  4384.      * @param int $client_channel
  4385.      * @param string $data
  4386.      * @return void
  4387.      */
  4388.     protected function send_channel_packet($client_channel, $data)
  4389.     {
  4390.         while (strlen($data)) {
  4391.             if (!$this->window_size_client_to_server[$client_channel]) {
  4392.                 $this->bitmap ^= self::MASK_WINDOW_ADJUST;
  4393.                 // using an invalid channel will let the buffers be built up for the valid channels
  4394.                 $this->get_channel_packet(-1);
  4395.                 $this->bitmap ^= self::MASK_WINDOW_ADJUST;
  4396.             }
  4397.  
  4398.             /* The maximum amount of data allowed is determined by the maximum
  4399.                packet size for the channel, and the current window size, whichever
  4400.                is smaller.
  4401.                  -- http://tools.ietf.org/html/rfc4254#section-5.2 */
  4402.             $max_size = min(
  4403.                 $this->packet_size_client_to_server[$client_channel],
  4404.                 $this->window_size_client_to_server[$client_channel]
  4405.             );
  4406.  
  4407.             $temp = Strings::shift($data, $max_size);
  4408.             $packet = Strings::packSSH2(
  4409.                 'CNs',
  4410.                 NET_SSH2_MSG_CHANNEL_DATA,
  4411.                 $this->server_channels[$client_channel],
  4412.                 $temp
  4413.             );
  4414.             $this->window_size_client_to_server[$client_channel] -= strlen($temp);
  4415.             $this->send_binary_packet($packet);
  4416.         }
  4417.     }
  4418.  
  4419.     /**
  4420.      * Closes and flushes a channel
  4421.      *
  4422.      * \phpseclib3\Net\SSH2 doesn't properly close most channels.  For exec() channels are normally closed by the server
  4423.      * and for SFTP channels are presumably closed when the client disconnects.  This functions is intended
  4424.      * for SCP more than anything.
  4425.      *
  4426.      * @param int $client_channel
  4427.      * @param bool $want_reply
  4428.      * @return void
  4429.      * @access private
  4430.      */
  4431.     private function close_channel($client_channel, $want_reply = false)
  4432.     {
  4433.         // see http://tools.ietf.org/html/rfc4254#section-5.3
  4434.  
  4435.         $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
  4436.  
  4437.         if (!$want_reply) {
  4438.             $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
  4439.         }
  4440.  
  4441.         $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
  4442.  
  4443.         $this->curTimeout = 5;
  4444.  
  4445.         while (!is_bool($this->get_channel_packet($client_channel))) {
  4446.         }
  4447.  
  4448.         if ($this->is_timeout) {
  4449.             $this->disconnect();
  4450.         }
  4451.  
  4452.         if ($want_reply) {
  4453.             $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
  4454.         }
  4455.  
  4456.         if ($this->bitmap & self::MASK_SHELL) {
  4457.             $this->bitmap &= ~self::MASK_SHELL;
  4458.         }
  4459.     }
  4460.  
  4461.     /**
  4462.      * Disconnect
  4463.      *
  4464.      * @param int $reason
  4465.      * @return false
  4466.      * @access protected
  4467.      */
  4468.     protected function disconnect_helper($reason)
  4469.     {
  4470.         if ($this->bitmap & self::MASK_CONNECTED) {
  4471.             $data = Strings::packSSH2('CNss', NET_SSH2_MSG_DISCONNECT, $reason, '', '');
  4472.             try {
  4473.                 $this->send_binary_packet($data);
  4474.             } catch (\Exception $e) {
  4475.             }
  4476.         }
  4477.  
  4478.         $this->bitmap = 0;
  4479.         if (is_resource($this->fsock) && get_resource_type($this->fsock) === 'stream') {
  4480.             fclose($this->fsock);
  4481.         }
  4482.  
  4483.         return false;
  4484.     }
  4485.  
  4486.     /**
  4487.      * Define Array
  4488.      *
  4489.      * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
  4490.      * named constants from it, using the value as the name of the constant and the index as the value of the constant.
  4491.      * If any of the constants that would be defined already exists, none of the constants will be defined.
  4492.      *
  4493.      * @param mixed[] ...$args
  4494.      * @access protected
  4495.      */
  4496.     protected function define_array(...$args)
  4497.     {
  4498.         foreach ($args as $arg) {
  4499.             foreach ($arg as $key => $value) {
  4500.                 if (!defined($value)) {
  4501.                     define($value, $key);
  4502.                 } else {
  4503.                     break 2;
  4504.                 }
  4505.             }
  4506.         }
  4507.     }
  4508.  
  4509.     /**
  4510.      * Returns a log of the packets that have been sent and received.
  4511.      *
  4512.      * Returns a string if NET_SSH2_LOGGING == self::LOG_COMPLEX, an array if NET_SSH2_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING')
  4513.      *
  4514.      * @access public
  4515.      * @return array|false|string
  4516.      */
  4517.     public function getLog()
  4518.     {
  4519.         if (!defined('NET_SSH2_LOGGING')) {
  4520.             return false;
  4521.         }
  4522.  
  4523.         switch (NET_SSH2_LOGGING) {
  4524.             case self::LOG_SIMPLE:
  4525.                 return $this->message_number_log;
  4526.             case self::LOG_COMPLEX:
  4527.                 $log = $this->format_log($this->message_log, $this->message_number_log);
  4528.                 return PHP_SAPI == 'cli' ? $log : '<pre>' . $log . '</pre>';
  4529.             default:
  4530.                 return false;
  4531.         }
  4532.     }
  4533.  
  4534.     /**
  4535.      * Formats a log for printing
  4536.      *
  4537.      * @param array $message_log
  4538.      * @param array $message_number_log
  4539.      * @access private
  4540.      * @return string
  4541.      */
  4542.     protected function format_log($message_log, $message_number_log)
  4543.     {
  4544.         $output = '';
  4545.         for ($i = 0; $i < count($message_log); $i++) {
  4546.             $output .= $message_number_log[$i] . "\r\n";
  4547.             $current_log = $message_log[$i];
  4548.             $j = 0;
  4549.             do {
  4550.                 if (strlen($current_log)) {
  4551.                     $output .= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0  ';
  4552.                 }
  4553.                 $fragment = Strings::shift($current_log, $this->log_short_width);
  4554.                 $hex = substr(preg_replace_callback('#.#s', function ($matches) {
  4555.                     return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT);
  4556.                 }, $fragment), strlen($this->log_boundary));
  4557.                 // replace non ASCII printable characters with dots
  4558.                 // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
  4559.                 // also replace < with a . since < messes up the output on web browsers
  4560.                 $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
  4561.                 $output .= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n";
  4562.                 $j++;
  4563.             } while (strlen($current_log));
  4564.             $output .= "\r\n";
  4565.         }
  4566.  
  4567.         return $output;
  4568.     }
  4569.  
  4570.     /**
  4571.      * Helper function for agent->on_channel_open()
  4572.      *
  4573.      * Used when channels are created to inform agent
  4574.      * of said channel opening. Must be called after
  4575.      * channel open confirmation received
  4576.      *
  4577.      * @access private
  4578.      */
  4579.     private function on_channel_open()
  4580.     {
  4581.         if (isset($this->agent)) {
  4582.             $this->agent->registerChannelOpen($this);
  4583.         }
  4584.     }
  4585.  
  4586.     /**
  4587.      * Returns the first value of the intersection of two arrays or false if
  4588.      * the intersection is empty. The order is defined by the first parameter.
  4589.      *
  4590.      * @param array $array1
  4591.      * @param array $array2
  4592.      * @return mixed False if intersection is empty, else intersected value.
  4593.      * @access private
  4594.      */
  4595.     private static function array_intersect_first($array1, $array2)
  4596.     {
  4597.         foreach ($array1 as $value) {
  4598.             if (in_array($value, $array2)) {
  4599.                 return $value;
  4600.             }
  4601.         }
  4602.         return false;
  4603.     }
  4604.  
  4605.     /**
  4606.      * Returns all errors
  4607.      *
  4608.      * @return string[]
  4609.      * @access public
  4610.      */
  4611.     public function getErrors()
  4612.     {
  4613.         return $this->errors;
  4614.     }
  4615.  
  4616.     /**
  4617.      * Returns the last error
  4618.      *
  4619.      * @return string
  4620.      * @access public
  4621.      */
  4622.     public function getLastError()
  4623.     {
  4624.         $count = count($this->errors);
  4625.  
  4626.         if ($count > 0) {
  4627.             return $this->errors[$count - 1];
  4628.         }
  4629.     }
  4630.  
  4631.     /**
  4632.      * Return the server identification.
  4633.      *
  4634.      * @return string|false
  4635.      * @access public
  4636.      */
  4637.     public function getServerIdentification()
  4638.     {
  4639.         $this->connect();
  4640.  
  4641.         return $this->server_identifier;
  4642.     }
  4643.  
  4644.     /**
  4645.      * Returns a list of algorithms the server supports
  4646.      *
  4647.      * @return array
  4648.      * @access public
  4649.      */
  4650.     public function getServerAlgorithms()
  4651.     {
  4652.         $this->connect();
  4653.  
  4654.         return [
  4655.             'kex' => $this->kex_algorithms,
  4656.             'hostkey' => $this->server_host_key_algorithms,
  4657.             'client_to_server' => [
  4658.                 'crypt' => $this->encryption_algorithms_client_to_server,
  4659.                 'mac' => $this->mac_algorithms_client_to_server,
  4660.                 'comp' => $this->compression_algorithms_client_to_server,
  4661.                 'lang' => $this->languages_client_to_server
  4662.             ],
  4663.             'server_to_client' => [
  4664.                 'crypt' => $this->encryption_algorithms_server_to_client,
  4665.                 'mac' => $this->mac_algorithms_server_to_client,
  4666.                 'comp' => $this->compression_algorithms_server_to_client,
  4667.                 'lang' => $this->languages_server_to_client
  4668.             ]
  4669.         ];
  4670.     }
  4671.  
  4672.     /**
  4673.      * Returns a list of KEX algorithms that phpseclib supports
  4674.      *
  4675.      * @return array
  4676.      * @access public
  4677.      */
  4678.     public static function getSupportedKEXAlgorithms()
  4679.     {
  4680.         $kex_algorithms = [
  4681.             // Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using
  4682.             // Curve25519. See doc/curve25519-sha256@libssh.org.txt in the
  4683.             // libssh repository for more information.
  4684.             'curve25519-sha256',
  4685.             'curve25519-sha256@libssh.org',
  4686.  
  4687.             'ecdh-sha2-nistp256', // RFC 5656
  4688.             'ecdh-sha2-nistp384', // RFC 5656
  4689.             'ecdh-sha2-nistp521', // RFC 5656
  4690.  
  4691.             'diffie-hellman-group-exchange-sha256',// RFC 4419
  4692.             'diffie-hellman-group-exchange-sha1',  // RFC 4419
  4693.  
  4694.             // Diffie-Hellman Key Agreement (DH) using integer modulo prime
  4695.             // groups.
  4696.             'diffie-hellman-group14-sha256',
  4697.             'diffie-hellman-group14-sha1', // REQUIRED
  4698.             'diffie-hellman-group15-sha512',
  4699.             'diffie-hellman-group16-sha512',
  4700.             'diffie-hellman-group17-sha512',
  4701.             'diffie-hellman-group18-sha512',
  4702.  
  4703.             'diffie-hellman-group1-sha1', // REQUIRED
  4704.         ];
  4705.  
  4706.         return $kex_algorithms;
  4707.     }
  4708.  
  4709.     /**
  4710.      * Returns a list of host key algorithms that phpseclib supports
  4711.      *
  4712.      * @return array
  4713.      * @access public
  4714.      */
  4715.     public static function getSupportedHostKeyAlgorithms()
  4716.     {
  4717.         return [
  4718.             'ssh-ed25519', // https://tools.ietf.org/html/draft-ietf-curdle-ssh-ed25519-02
  4719.             'ecdsa-sha2-nistp256', // RFC 5656
  4720.             'ecdsa-sha2-nistp384', // RFC 5656
  4721.             'ecdsa-sha2-nistp521', // RFC 5656
  4722.             'rsa-sha2-256', // RFC 8332
  4723.             'rsa-sha2-512', // RFC 8332
  4724.             'ssh-rsa', // RECOMMENDED  sign   Raw RSA Key
  4725.             'ssh-dss'  // REQUIRED     sign   Raw DSS Key
  4726.         ];
  4727.     }
  4728.  
  4729.     /**
  4730.      * Returns a list of symmetric key algorithms that phpseclib supports
  4731.      *
  4732.      * @return array
  4733.      * @access public
  4734.      */
  4735.     public static function getSupportedEncryptionAlgorithms()
  4736.     {
  4737.         $algos = [
  4738.             // from <https://tools.ietf.org/html/rfc5647>:
  4739.             'aes128-gcm@openssh.com',
  4740.             'aes256-gcm@openssh.com',
  4741.  
  4742.             // from <http://tools.ietf.org/html/rfc4345#section-4>:
  4743.             'arcfour256',
  4744.             'arcfour128',
  4745.  
  4746.             //'arcfour',      // OPTIONAL          the ARCFOUR stream cipher with a 128-bit key
  4747.  
  4748.             // CTR modes from <http://tools.ietf.org/html/rfc4344#section-4>:
  4749.             'aes128-ctr',     // RECOMMENDED       AES (Rijndael) in SDCTR mode, with 128-bit key
  4750.             'aes192-ctr',     // RECOMMENDED       AES with 192-bit key
  4751.             'aes256-ctr',     // RECOMMENDED       AES with 256-bit key
  4752.  
  4753.             // from <https://git.io/fhxOl>:
  4754.             // one of the big benefits of chacha20-poly1305 is speed. the problem is...
  4755.             // libsodium doesn't generate the poly1305 keys in the way ssh does and openssl's PHP bindings don't even
  4756.             // seem to support poly1305 currently. so even if libsodium or openssl are being used for the chacha20
  4757.             // part, pure-PHP has to be used for the poly1305 part and that's gonna cause a big slow down.
  4758.             // speed-wise it winds up being faster to use AES (when openssl or mcrypt are available) and some HMAC
  4759.             // (which is always gonna be super fast to compute thanks to the hash extension, which
  4760.             // "is bundled and compiled into PHP by default")
  4761.             'chacha20-poly1305@openssh.com',
  4762.  
  4763.             'twofish128-ctr', // OPTIONAL          Twofish in SDCTR mode, with 128-bit key
  4764.             'twofish192-ctr', // OPTIONAL          Twofish with 192-bit key
  4765.             'twofish256-ctr', // OPTIONAL          Twofish with 256-bit key
  4766.  
  4767.             'aes128-cbc',     // RECOMMENDED       AES with a 128-bit key
  4768.             'aes192-cbc',     // OPTIONAL          AES with a 192-bit key
  4769.             'aes256-cbc',     // OPTIONAL          AES in CBC mode, with a 256-bit key
  4770.  
  4771.             'twofish128-cbc', // OPTIONAL          Twofish with a 128-bit key
  4772.             'twofish192-cbc', // OPTIONAL          Twofish with a 192-bit key
  4773.             'twofish256-cbc',
  4774.             'twofish-cbc',    // OPTIONAL          alias for "twofish256-cbc"
  4775.                               //                   (this is being retained for historical reasons)
  4776.  
  4777.             'blowfish-ctr',   // OPTIONAL          Blowfish in SDCTR mode
  4778.  
  4779.             'blowfish-cbc',   // OPTIONAL          Blowfish in CBC mode
  4780.  
  4781.             '3des-ctr',       // RECOMMENDED       Three-key 3DES in SDCTR mode
  4782.  
  4783.             '3des-cbc',       // REQUIRED          three-key 3DES in CBC mode
  4784.  
  4785.              //'none'           // OPTIONAL          no encryption; NOT RECOMMENDED
  4786.         ];
  4787.  
  4788.         if (self::$crypto_engine) {
  4789.             $engines = [self::$crypto_engine];
  4790.         } else {
  4791.             $engines = [
  4792.                 'libsodium',
  4793.                 'OpenSSL (GCM)',
  4794.                 'OpenSSL',
  4795.                 'mcrypt',
  4796.                 'Eval',
  4797.                 'PHP'
  4798.             ];
  4799.         }
  4800.  
  4801.         $ciphers = [];
  4802.  
  4803.         foreach ($engines as $engine) {
  4804.             foreach ($algos as $algo) {
  4805.                 $obj = self::encryption_algorithm_to_crypt_instance($algo);
  4806.                 if ($obj instanceof Rijndael) {
  4807.                     $obj->setKeyLength(preg_replace('#[^\d]#', '', $algo));
  4808.                 }
  4809.                 switch ($algo) {
  4810.                     case 'chacha20-poly1305@openssh.com':
  4811.                     case 'arcfour128':
  4812.                     case 'arcfour256':
  4813.                         if ($engine != 'Eval') {
  4814.                             continue 2;
  4815.                         }
  4816.                         break;
  4817.                     case 'aes128-gcm@openssh.com':
  4818.                     case 'aes256-gcm@openssh.com':
  4819.                         if ($engine == 'OpenSSL') {
  4820.                             continue 2;
  4821.                         }
  4822.                         $obj->setNonce('dummydummydu');
  4823.                 }
  4824.                 if ($obj->isValidEngine($engine)) {
  4825.                     $algos = array_diff($algos, [$algo]);
  4826.                     $ciphers[] = $algo;
  4827.                 }
  4828.             }
  4829.         }
  4830.  
  4831.         return $ciphers;
  4832.     }
  4833.  
  4834.     /**
  4835.      * Returns a list of MAC algorithms that phpseclib supports
  4836.      *
  4837.      * @return array
  4838.      * @access public
  4839.      */
  4840.     public static function getSupportedMACAlgorithms()
  4841.     {
  4842.         return [
  4843.             'hmac-sha2-256-etm@openssh.com',
  4844.             'hmac-sha2-512-etm@openssh.com',
  4845.             'umac-64-etm@openssh.com',
  4846.             'umac-128-etm@openssh.com',
  4847.             'hmac-sha1-etm@openssh.com',
  4848.  
  4849.             // from <http://www.ietf.org/rfc/rfc6668.txt>:
  4850.             'hmac-sha2-256',// RECOMMENDED     HMAC-SHA256 (digest length = key length = 32)
  4851.             'hmac-sha2-512',// OPTIONAL        HMAC-SHA512 (digest length = key length = 64)
  4852.  
  4853.             // from <https://tools.ietf.org/html/draft-miller-secsh-umac-01>:
  4854.             'umac-64@openssh.com',
  4855.             'umac-128@openssh.com',
  4856.  
  4857.             'hmac-sha1-96', // RECOMMENDED     first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
  4858.             'hmac-sha1',    // REQUIRED        HMAC-SHA1 (digest length = key length = 20)
  4859.             'hmac-md5-96',  // OPTIONAL        first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
  4860.             'hmac-md5',     // OPTIONAL        HMAC-MD5 (digest length = key length = 16)
  4861.             //'none'          // OPTIONAL        no MAC; NOT RECOMMENDED
  4862.         ];
  4863.     }
  4864.  
  4865.     /**
  4866.      * Returns a list of compression algorithms that phpseclib supports
  4867.      *
  4868.      * @return array
  4869.      * @access public
  4870.      */
  4871.     public static function getSupportedCompressionAlgorithms()
  4872.     {
  4873.         $algos = ['none']; // REQUIRED        no compression
  4874.         if (function_exists('deflate_init')) {
  4875.             $algos[] = 'zlib@openssh.com'; // https://datatracker.ietf.org/doc/html/draft-miller-secsh-compression-delayed
  4876.             $algos[] = 'zlib';
  4877.         }
  4878.         return $algos;
  4879.     }
  4880.  
  4881.     /**
  4882.      * Return list of negotiated algorithms
  4883.      *
  4884.      * Uses the same format as https://www.php.net/ssh2-methods-negotiated
  4885.      *
  4886.      * @return array
  4887.      * @access public
  4888.      */
  4889.     public function getAlgorithmsNegotiated()
  4890.     {
  4891.         $this->connect();
  4892.  
  4893.         $compression_map = [
  4894.             self::NET_SSH2_COMPRESSION_NONE => 'none',
  4895.             self::NET_SSH2_COMPRESSION_ZLIB => 'zlib',
  4896.             self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH => 'zlib@openssh.com'
  4897.         ];
  4898.  
  4899.         return [
  4900.             'kex' => $this->kex_algorithm,
  4901.             'hostkey' => $this->signature_format,
  4902.             'client_to_server' => [
  4903.                 'crypt' => $this->encryptName,
  4904.                 'mac' => $this->hmac_create_name,
  4905.                 'comp' => $compression_map[$this->compress],
  4906.             ],
  4907.             'server_to_client' => [
  4908.                 'crypt' => $this->decryptName,
  4909.                 'mac' => $this->hmac_check_name,
  4910.                 'comp' => $compression_map[$this->decompress],
  4911.             ]
  4912.         ];
  4913.     }
  4914.  
  4915.     /**
  4916.      * Allows you to set the terminal
  4917.      *
  4918.      * @param string $term
  4919.      * @access public
  4920.      */
  4921.     public function setTerminal($term)
  4922.     {
  4923.         $this->term = $term;
  4924.     }
  4925.  
  4926.     /**
  4927.      * Accepts an associative array with up to four parameters as described at
  4928.      * <https://www.php.net/manual/en/function.ssh2-connect.php>
  4929.      *
  4930.      * @param array $methods
  4931.      * @access public
  4932.      */
  4933.     public function setPreferredAlgorithms(array $methods)
  4934.     {
  4935.         $preferred = $methods;
  4936.  
  4937.         if (isset($preferred['kex'])) {
  4938.             $preferred['kex'] = array_intersect(
  4939.                 $preferred['kex'],
  4940.                 static::getSupportedKEXAlgorithms()
  4941.             );
  4942.         }
  4943.  
  4944.         if (isset($preferred['hostkey'])) {
  4945.             $preferred['hostkey'] = array_intersect(
  4946.                 $preferred['hostkey'],
  4947.                 static::getSupportedHostKeyAlgorithms()
  4948.             );
  4949.         }
  4950.  
  4951.         $keys = ['client_to_server', 'server_to_client'];
  4952.         foreach ($keys as $key) {
  4953.             if (isset($preferred[$key])) {
  4954.                 $a = &$preferred[$key];
  4955.                 if (isset($a['crypt'])) {
  4956.                     $a['crypt'] = array_intersect(
  4957.                         $a['crypt'],
  4958.                         static::getSupportedEncryptionAlgorithms()
  4959.                     );
  4960.                 }
  4961.                 if (isset($a['comp'])) {
  4962.                     $a['comp'] = array_intersect(
  4963.                         $a['comp'],
  4964.                         static::getSupportedCompressionAlgorithms()
  4965.                     );
  4966.                 }
  4967.                 if (isset($a['mac'])) {
  4968.                     $a['mac'] = array_intersect(
  4969.                         $a['mac'],
  4970.                         static::getSupportedMACAlgorithms()
  4971.                     );
  4972.                 }
  4973.             }
  4974.         }
  4975.  
  4976.         $keys = [
  4977.             'kex',
  4978.             'hostkey',
  4979.             'client_to_server/crypt',
  4980.             'client_to_server/comp',
  4981.             'client_to_server/mac',
  4982.             'server_to_client/crypt',
  4983.             'server_to_client/comp',
  4984.             'server_to_client/mac',
  4985.         ];
  4986.         foreach ($keys as $key) {
  4987.             $p = $preferred;
  4988.             $m = $methods;
  4989.  
  4990.             $subkeys = explode('/', $key);
  4991.             foreach ($subkeys as $subkey) {
  4992.                 if (!isset($p[$subkey])) {
  4993.                     continue 2;
  4994.                 }
  4995.                 $p = $p[$subkey];
  4996.                 $m = $m[$subkey];
  4997.             }
  4998.  
  4999.             if (count($p) != count($m)) {
  5000.                 $diff = array_diff($m, $p);
  5001.                 $msg = count($diff) == 1 ?
  5002.                     ' is not a supported algorithm' :
  5003.                     ' are not supported algorithms';
  5004.                 throw new UnsupportedAlgorithmException(implode(', ', $diff) . $msg);
  5005.             }
  5006.         }
  5007.  
  5008.         $this->preferred = $preferred;
  5009.     }
  5010.  
  5011.     /**
  5012.      * Returns the banner message.
  5013.      *
  5014.      * Quoting from the RFC, "in some jurisdictions, sending a warning message before
  5015.      * authentication may be relevant for getting legal protection."
  5016.      *
  5017.      * @return string
  5018.      * @access public
  5019.      */
  5020.     public function getBannerMessage()
  5021.     {
  5022.         return $this->banner_message;
  5023.     }
  5024.  
  5025.     /**
  5026.      * Returns the server public host key.
  5027.      *
  5028.      * Caching this the first time you connect to a server and checking the result on subsequent connections
  5029.      * is recommended.  Returns false if the server signature is not signed correctly with the public host key.
  5030.      *
  5031.      * @return string|false
  5032.      * @throws \RuntimeException on badly formatted keys
  5033.      * @throws \phpseclib3\Exception\NoSupportedAlgorithmsException when the key isn't in a supported format
  5034.      * @access public
  5035.      */
  5036.     public function getServerPublicHostKey()
  5037.     {
  5038.         if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
  5039.             $this->connect();
  5040.         }
  5041.  
  5042.         $signature = $this->signature;
  5043.         $server_public_host_key = base64_encode($this->server_public_host_key);
  5044.  
  5045.         if ($this->signature_validated) {
  5046.             return $this->bitmap ?
  5047.                 $this->signature_format . ' ' . $server_public_host_key :
  5048.                 false;
  5049.         }
  5050.  
  5051.         $this->signature_validated = true;
  5052.  
  5053.         switch ($this->signature_format) {
  5054.             case 'ssh-ed25519':
  5055.             case 'ecdsa-sha2-nistp256':
  5056.             case 'ecdsa-sha2-nistp384':
  5057.             case 'ecdsa-sha2-nistp521':
  5058.                 $key = EC::loadFormat('OpenSSH', $server_public_host_key)
  5059.                     ->withSignatureFormat('SSH2');
  5060.                 switch ($this->signature_format) {
  5061.                     case 'ssh-ed25519':
  5062.                         $hash = 'sha512';
  5063.                         break;
  5064.                     case 'ecdsa-sha2-nistp256':
  5065.                         $hash = 'sha256';
  5066.                         break;
  5067.                     case 'ecdsa-sha2-nistp384':
  5068.                         $hash = 'sha384';
  5069.                         break;
  5070.                     case 'ecdsa-sha2-nistp521':
  5071.                         $hash = 'sha512';
  5072.                 }
  5073.                 $key = $key->withHash($hash);
  5074.                 break;
  5075.             case 'ssh-dss':
  5076.                 $key = DSA::loadFormat('OpenSSH', $server_public_host_key)
  5077.                     ->withSignatureFormat('SSH2')
  5078.                     ->withHash('sha1');
  5079.                 break;
  5080.             case 'ssh-rsa':
  5081.             case 'rsa-sha2-256':
  5082.             case 'rsa-sha2-512':
  5083.                 // could be ssh-rsa, rsa-sha2-256, rsa-sha2-512
  5084.                 // we don't check here because we already checked in key_exchange
  5085.                 // some signatures have the type embedded within the message and some don't
  5086.                 list(, $signature) = Strings::unpackSSH2('ss', $signature);
  5087.  
  5088.                 $key = RSA::loadFormat('OpenSSH', $server_public_host_key)
  5089.                     ->withPadding(RSA::SIGNATURE_PKCS1);
  5090.                 switch ($this->signature_format) {
  5091.                     case 'rsa-sha2-512':
  5092.                         $hash = 'sha512';
  5093.                         break;
  5094.                     case 'rsa-sha2-256':
  5095.                         $hash = 'sha256';
  5096.                         break;
  5097.                     //case 'ssh-rsa':
  5098.                     default:
  5099.                         $hash = 'sha1';
  5100.                 }
  5101.                 $key = $key->withHash($hash);
  5102.                 break;
  5103.             default:
  5104.                 $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
  5105.                 throw new NoSupportedAlgorithmsException('Unsupported signature format');
  5106.         }
  5107.  
  5108.         if (!$key->verify($this->exchange_hash, $signature)) {
  5109.             return $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
  5110.         };
  5111.  
  5112.         return $this->signature_format . ' ' . $server_public_host_key;
  5113.     }
  5114.  
  5115.     /**
  5116.      * Returns the exit status of an SSH command or false.
  5117.      *
  5118.      * @return false|int
  5119.      * @access public
  5120.      */
  5121.     public function getExitStatus()
  5122.     {
  5123.         if (is_null($this->exit_status)) {
  5124.             return false;
  5125.         }
  5126.         return $this->exit_status;
  5127.     }
  5128.  
  5129.     /**
  5130.      * Returns the number of columns for the terminal window size.
  5131.      *
  5132.      * @return int
  5133.      * @access public
  5134.      */
  5135.     public function getWindowColumns()
  5136.     {
  5137.         return $this->windowColumns;
  5138.     }
  5139.  
  5140.     /**
  5141.      * Returns the number of rows for the terminal window size.
  5142.      *
  5143.      * @return int
  5144.      * @access public
  5145.      */
  5146.     public function getWindowRows()
  5147.     {
  5148.         return $this->windowRows;
  5149.     }
  5150.  
  5151.     /**
  5152.      * Sets the number of columns for the terminal window size.
  5153.      *
  5154.      * @param int $value
  5155.      * @access public
  5156.      */
  5157.     public function setWindowColumns($value)
  5158.     {
  5159.         $this->windowColumns = $value;
  5160.     }
  5161.  
  5162.     /**
  5163.      * Sets the number of rows for the terminal window size.
  5164.      *
  5165.      * @param int $value
  5166.      * @access public
  5167.      */
  5168.     public function setWindowRows($value)
  5169.     {
  5170.         $this->windowRows = $value;
  5171.     }
  5172.  
  5173.     /**
  5174.      * Sets the number of columns and rows for the terminal window size.
  5175.      *
  5176.      * @param int $columns
  5177.      * @param int $rows
  5178.      * @access public
  5179.      */
  5180.     public function setWindowSize($columns = 80, $rows = 24)
  5181.     {
  5182.         $this->windowColumns = $columns;
  5183.         $this->windowRows = $rows;
  5184.     }
  5185.  
  5186.     /**
  5187.      * To String Magic Method
  5188.      *
  5189.      * @return string
  5190.      * @access public
  5191.      */
  5192.     #[\ReturnTypeWillChange]
  5193.    public function __toString()
  5194.     {
  5195.         return $this->getResourceId();
  5196.     }
  5197.  
  5198.     /**
  5199.      * Get Resource ID
  5200.      *
  5201.      * We use {} because that symbols should not be in URL according to
  5202.      * {@link http://tools.ietf.org/html/rfc3986#section-2 RFC}.
  5203.      * It will safe us from any conflicts, because otherwise regexp will
  5204.      * match all alphanumeric domains.
  5205.      *
  5206.      * @return string
  5207.      */
  5208.     public function getResourceId()
  5209.     {
  5210.         return '{' . spl_object_hash($this) . '}';
  5211.     }
  5212.  
  5213.     /**
  5214.      * Return existing connection
  5215.      *
  5216.      * @param string $id
  5217.      *
  5218.      * @return bool|SSH2 will return false if no such connection
  5219.      */
  5220.     public static function getConnectionByResourceId($id)
  5221.     {
  5222.         if (isset(self::$connections[$id])) {
  5223.             return self::$connections[$id] instanceof \WeakReference ? self::$connections[$id]->get() : self::$connections[$id];
  5224.         }
  5225.         return false;
  5226.     }
  5227.  
  5228.     /**
  5229.      * Return all excising connections
  5230.      *
  5231.      * @return array<string, SSH2>
  5232.      */
  5233.     public static function getConnections()
  5234.     {
  5235.         if (!class_exists('WeakReference')) {
  5236.             /** @var array<string, SSH2> */
  5237.             return self::$connections;
  5238.         }
  5239.         $temp = [];
  5240.         foreach (self::$connections as $key => $ref) {
  5241.             $temp[$key] = $ref->get();
  5242.         }
  5243.         return $temp;
  5244.     }
  5245.  
  5246.     /*
  5247.      * Update packet types in log history
  5248.      *
  5249.      * @param string $old
  5250.      * @param string $new
  5251.      * @access private
  5252.      */
  5253.     private function updateLogHistory($old, $new)
  5254.     {
  5255.         if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) {
  5256.             $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
  5257.                 $old,
  5258.                 $new,
  5259.                 $this->message_number_log[count($this->message_number_log) - 1]
  5260.             );
  5261.         }
  5262.     }
  5263.  
  5264.     /**
  5265.      * Return the list of authentication methods that may productively continue authentication.
  5266.      *
  5267.      * @see https://tools.ietf.org/html/rfc4252#section-5.1
  5268.      * @return array|null
  5269.      */
  5270.     public function getAuthMethodsToContinue()
  5271.     {
  5272.         return $this->auth_methods_to_continue;
  5273.     }
  5274.  
  5275.     /**
  5276.      * Enables "smart" multi-factor authentication (MFA)
  5277.      */
  5278.     public function enableSmartMFA()
  5279.     {
  5280.         $this->smartMFA = true;
  5281.     }
  5282.  
  5283.     /**
  5284.      * Disables "smart" multi-factor authentication (MFA)
  5285.      */
  5286.     public function disableSmartMFA()
  5287.     {
  5288.         $this->smartMFA = false;
  5289.     }
  5290. }
  5291.