|  | @@ -176,6 +176,10 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          private HashAlgorithm _clientMac;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        private bool _serverEtm;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private bool _clientEtm;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          private Cipher _clientCipher;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          private Cipher _serverCipher;
 | 
	
	
		
			
				|  | @@ -1054,7 +1058,7 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |              DiagnosticAbstraction.Log(string.Format("[{0}] Sending message '{1}' to server: '{2}'.", ToHex(SessionId), message.GetType().Name, message));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              var paddingMultiplier = _clientCipher is null ? (byte) 8 : Math.Max((byte) 8, _serverCipher.MinimumSize);
 | 
	
		
			
				|  |  | -            var packetData = message.GetPacket(paddingMultiplier, _clientCompression);
 | 
	
		
			
				|  |  | +            var packetData = message.GetPacket(paddingMultiplier, _clientCompression, _clientMac != null && _clientEtm);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              // take a write lock to ensure the outbound packet sequence number is incremented
 | 
	
		
			
				|  |  |              // atomically, and only after the packet has actually been sent
 | 
	
	
		
			
				|  | @@ -1063,7 +1067,7 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |                  byte[] hash = null;
 | 
	
		
			
				|  |  |                  var packetDataOffset = 4; // first four bytes are reserved for outbound packet sequence
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                if (_clientMac != null)
 | 
	
		
			
				|  |  | +                if (_clientMac != null && !_clientEtm)
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  |                      // write outbound packet sequence to start of packet data
 | 
	
		
			
				|  |  |                      Pack.UInt32ToBigEndian(_outboundPacketSequence, packetData);
 | 
	
	
		
			
				|  | @@ -1075,8 +1079,29 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |                  // Encrypt packet data
 | 
	
		
			
				|  |  |                  if (_clientCipher != null)
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                    packetData = _clientCipher.Encrypt(packetData, packetDataOffset, packetData.Length - packetDataOffset);
 | 
	
		
			
				|  |  | -                    packetDataOffset = 0;
 | 
	
		
			
				|  |  | +                    if (_clientMac != null && _clientEtm)
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        // The length of the "packet length" field in bytes
 | 
	
		
			
				|  |  | +                        const int packetLengthFieldLength = 4;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                        var encryptedData = _clientCipher.Encrypt(packetData, packetDataOffset + packetLengthFieldLength, packetData.Length - packetDataOffset - packetLengthFieldLength);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                        Array.Resize(ref packetData, packetDataOffset + packetLengthFieldLength + encryptedData.Length);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                        // write outbound packet sequence to start of packet data
 | 
	
		
			
				|  |  | +                        Pack.UInt32ToBigEndian(_outboundPacketSequence, packetData);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                        // write encrypted data
 | 
	
		
			
				|  |  | +                        Buffer.BlockCopy(encryptedData, 0, packetData, packetDataOffset + packetLengthFieldLength, encryptedData.Length);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                        // calculate packet hash
 | 
	
		
			
				|  |  | +                        hash = _clientMac.ComputeHash(packetData);
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    else
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        packetData = _clientCipher.Encrypt(packetData, packetDataOffset, packetData.Length - packetDataOffset);
 | 
	
		
			
				|  |  | +                        packetDataOffset = 0;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                  if (packetData.Length > MaximumSshPacketSize)
 | 
	
	
		
			
				|  | @@ -1194,8 +1219,22 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |              // The length of the "padding length" field in bytes
 | 
	
		
			
				|  |  |              const int paddingLengthFieldLength = 1;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            // Determine the size of the first block, which is 8 or cipher block size (whichever is larger) bytes
 | 
	
		
			
				|  |  | -            var blockSize = _serverCipher is null ? (byte) 8 : Math.Max((byte) 8, _serverCipher.MinimumSize);
 | 
	
		
			
				|  |  | +            int blockSize;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // Determine the size of the first block which is 8 or cipher block size (whichever is larger) bytes
 | 
	
		
			
				|  |  | +            // The "packet length" field is not encrypted in ETM.
 | 
	
		
			
				|  |  | +            if (_serverMac != null && _serverEtm)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                blockSize = (byte) 4;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            else if (_serverCipher != null)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                blockSize = Math.Max((byte) 8, _serverCipher.MinimumSize);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            else
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                blockSize = (byte) 8;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              var serverMacLength = _serverMac != null ? _serverMac.HashSize/8 : 0;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1215,7 +1254,7 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |                      return null;
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                if (_serverCipher != null)
 | 
	
		
			
				|  |  | +                if (_serverCipher != null && (_serverMac == null || !_serverEtm))
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  |                      firstBlock = _serverCipher.Decrypt(firstBlock);
 | 
	
		
			
				|  |  |                  }
 | 
	
	
		
			
				|  | @@ -1257,6 +1296,20 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +            // validate encrypted message against MAC
 | 
	
		
			
				|  |  | +            if (_serverMac != null && _serverEtm)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                var clientHash = _serverMac.ComputeHash(data, 0, data.Length - serverMacLength);
 | 
	
		
			
				|  |  | +                var serverHash = data.Take(data.Length - serverMacLength, serverMacLength);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                // TODO Add IsEqualTo overload that takes left+right index and number of bytes to compare.
 | 
	
		
			
				|  |  | +                // TODO That way we can eliminate the extra allocation of the Take above.
 | 
	
		
			
				|  |  | +                if (!serverHash.IsEqualTo(clientHash))
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    throw new SshConnectionException("MAC error", DisconnectReason.MacError);
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |              if (_serverCipher != null)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  var numberOfBytesToDecrypt = data.Length - (blockSize + inboundPacketSequenceLength + serverMacLength);
 | 
	
	
		
			
				|  | @@ -1271,8 +1324,8 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |              var messagePayloadLength = (int) packetLength - paddingLength - paddingLengthFieldLength;
 | 
	
		
			
				|  |  |              var messagePayloadOffset = inboundPacketSequenceLength + packetLengthFieldLength + paddingLengthFieldLength;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            // validate message against MAC
 | 
	
		
			
				|  |  | -            if (_serverMac != null)
 | 
	
		
			
				|  |  | +            // validate decrypted message against MAC
 | 
	
		
			
				|  |  | +            if (_serverMac != null && !_serverEtm)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  var clientHash = _serverMac.ComputeHash(data, 0, data.Length - serverMacLength);
 | 
	
		
			
				|  |  |                  var serverHash = data.Take(data.Length - serverMacLength, serverMacLength);
 | 
	
	
		
			
				|  | @@ -1472,8 +1525,8 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |              // Update negotiated algorithms
 | 
	
		
			
				|  |  |              _serverCipher = _keyExchange.CreateServerCipher();
 | 
	
		
			
				|  |  |              _clientCipher = _keyExchange.CreateClientCipher();
 | 
	
		
			
				|  |  | -            _serverMac = _keyExchange.CreateServerHash();
 | 
	
		
			
				|  |  | -            _clientMac = _keyExchange.CreateClientHash();
 | 
	
		
			
				|  |  | +            _serverMac = _keyExchange.CreateServerHash(out _serverEtm);
 | 
	
		
			
				|  |  | +            _clientMac = _keyExchange.CreateClientHash(out _clientEtm);
 | 
	
		
			
				|  |  |              _clientCompression = _keyExchange.CreateCompressor();
 | 
	
		
			
				|  |  |              _serverDecompression = _keyExchange.CreateDecompressor();
 | 
	
		
			
				|  |  |  
 |