/// <summary> /// Extracts SSH packet from the internal buffer. /// </summary> /// <returns> /// true if one SSH packet has been extracted. /// in this case, _packetImage contains payload part of the SSH packet. /// </returns> private bool ConstructPacket() { const int SEQUENCE_NUMBER_FIELD_LEN = 4; const int PACKET_LENGTH_FIELD_LEN = 4; const int PADDING_LENGTH_FIELD_LEN = 1; lock (_cipherSync) { if (_packetLength < 0) { int headLen = (_cipher != null) ? _cipher.BlockSize : 4; if (_inputBuffer.Length < headLen) { return(false); } _packetImage.Clear(); _packetImage.WriteUInt32(_sequence); _packetImage.Append(_inputBuffer, 0, headLen); _inputBuffer.RemoveHead(headLen); int headOffset = _packetImage.RawBufferOffset + SEQUENCE_NUMBER_FIELD_LEN; if (_cipher != null) { // decrypt first block _cipher.Decrypt( _packetImage.RawBuffer, headOffset, headLen, _packetImage.RawBuffer, headOffset); } uint packetLength = SSHUtil.ReadUInt32(_packetImage.RawBuffer, headOffset); if (packetLength < MIN_PACKET_LENGTH || packetLength >= MAX_PACKET_LENGTH) { throw new SSHException(String.Format("invalid packet length : {0}", packetLength)); } _packetLength = (int)packetLength; } int packetHeadLen = _packetImage.Length; // size already read in int requiredLength = SEQUENCE_NUMBER_FIELD_LEN + PACKET_LENGTH_FIELD_LEN + _packetLength + _macLength - packetHeadLen; if (_inputBuffer.Length < requiredLength) { return(false); } _packetImage.Append(_inputBuffer, 0, requiredLength); _inputBuffer.RemoveHead(requiredLength); if (_cipher != null) { // decrypt excluding MAC int headOffset = _packetImage.RawBufferOffset + packetHeadLen; _cipher.Decrypt( _packetImage.RawBuffer, headOffset, requiredLength - _macLength, _packetImage.RawBuffer, headOffset); } int paddingLength = _packetImage[SEQUENCE_NUMBER_FIELD_LEN + PACKET_LENGTH_FIELD_LEN]; if (paddingLength < 4) { throw new SSHException(String.Format("invalid padding length : {0}", paddingLength)); } int payloadLength = _packetLength - PADDING_LENGTH_FIELD_LEN - paddingLength; if (_checkMAC && _mac != null) { int contentLen = SEQUENCE_NUMBER_FIELD_LEN + PACKET_LENGTH_FIELD_LEN + _packetLength; byte[] result = _mac.ComputeHash(_packetImage.RawBuffer, _packetImage.RawBufferOffset, contentLen); if (result.Length != _macLength || !SSHUtil.ByteArrayEqual(result, 0, _packetImage.RawBuffer, _packetImage.RawBufferOffset + contentLen, _macLength)) { throw new SSHException("MAC mismatch"); } } // retain only payload _packetImage.RemoveHead(SEQUENCE_NUMBER_FIELD_LEN + PACKET_LENGTH_FIELD_LEN + PADDING_LENGTH_FIELD_LEN); _packetImage.RemoveTail(_macLength + paddingLength); // sanity check if (_packetImage.Length != payloadLength) { throw new InvalidOperationException(); } // prepare for the next packet ++_sequence; _packetLength = -1; return(true); } }
// Derived class can override this method to modify the buffer. public virtual DataFragment Close(Cipher cipher, MAC mac, int sequence) { if (!_isOpen) throw new SSHException("internal state error"); int blocksize = cipher == null ? 8 : cipher.BlockSize; int payloadLength = _writer.Length - (SEQUENCE_MARGIN + LENGTH_MARGIN + PADDING_MARGIN); int paddingLength = 11 - payloadLength % blocksize; while (paddingLength < 4) paddingLength += blocksize; int packetLength = PADDING_MARGIN + payloadLength + paddingLength; int imageLength = packetLength + LENGTH_MARGIN; //fill padding byte[] tmp = new byte[4]; Rng rng = RngManager.GetSecureRng(); for (int i = 0; i < paddingLength; i += 4) { rng.GetBytes(tmp); _writer.Write(tmp); } //manipulate stream byte[] rawbuf = _writer.UnderlyingBuffer; SSHUtil.WriteIntToByteArray(rawbuf, 0, sequence); SSHUtil.WriteIntToByteArray(rawbuf, SEQUENCE_MARGIN, packetLength); rawbuf[SEQUENCE_MARGIN + LENGTH_MARGIN] = (byte)paddingLength; //mac if (mac != null) { byte[] macCode = mac.ComputeHash(rawbuf, 0, packetLength + LENGTH_MARGIN + SEQUENCE_MARGIN); Array.Copy(macCode, 0, rawbuf, packetLength + LENGTH_MARGIN + SEQUENCE_MARGIN, macCode.Length); imageLength += macCode.Length; } //encrypt if (cipher != null) cipher.Encrypt(rawbuf, SEQUENCE_MARGIN, packetLength + LENGTH_MARGIN, rawbuf, SEQUENCE_MARGIN); _dataFragment.Init(rawbuf, SEQUENCE_MARGIN, imageLength); _isOpen = false; return _dataFragment; }
public void Close(Cipher cipher, Random rnd, MAC mac, int sequence, DataFragment result) { if(!_is_open) throw new SSHException("internal state error"); int blocksize = cipher==null? 8 : cipher.BlockSize; int payload_length = _writer.Length - (SEQUENCE_MARGIN + LENGTH_MARGIN + PADDING_MARGIN); int r = 11 - payload_length % blocksize; while(r < 4) r += blocksize; _paddingLength = r; _packetLength = PADDING_MARGIN + payload_length + _paddingLength; int image_length = _packetLength + LENGTH_MARGIN; //fill padding for(int i=0; i<_paddingLength; i+=4) _writer.Write(rnd.Next()); //manipulate stream byte[] rawbuf = _writer.UnderlyingBuffer; SSHUtil.WriteIntToByteArray(rawbuf, 0, sequence); SSHUtil.WriteIntToByteArray(rawbuf, SEQUENCE_MARGIN, _packetLength); rawbuf[SEQUENCE_MARGIN + LENGTH_MARGIN] = (byte)_paddingLength; //mac if(mac!=null) { _mac = mac.ComputeHash(rawbuf, 0, _packetLength+LENGTH_MARGIN+SEQUENCE_MARGIN); Array.Copy(_mac, 0, rawbuf, _packetLength+LENGTH_MARGIN+SEQUENCE_MARGIN, _mac.Length); image_length += _mac.Length; } //encrypt if(cipher!=null) cipher.Encrypt(rawbuf, SEQUENCE_MARGIN, _packetLength+LENGTH_MARGIN, rawbuf, SEQUENCE_MARGIN); result.Init(rawbuf, SEQUENCE_MARGIN, image_length); _is_open = false; }