@@ -176,6 +176,10 @@ public class Session : ISession
176176
177177 private HashAlgorithm _clientMac ;
178178
179+ private bool _serverEtm ;
180+
181+ private bool _clientEtm ;
182+
179183 private Cipher _clientCipher ;
180184
181185 private Cipher _serverCipher ;
@@ -1054,7 +1058,7 @@ internal void SendMessage(Message message)
10541058 DiagnosticAbstraction . Log ( string . Format ( "[{0}] Sending message '{1}' to server: '{2}'." , ToHex ( SessionId ) , message . GetType ( ) . Name , message ) ) ;
10551059
10561060 var paddingMultiplier = _clientCipher is null ? ( byte ) 8 : Math . Max ( ( byte ) 8 , _serverCipher . MinimumSize ) ;
1057- var packetData = message . GetPacket ( paddingMultiplier , _clientCompression ) ;
1061+ var packetData = message . GetPacket ( paddingMultiplier , _clientCompression , _clientMac != null && _clientEtm ) ;
10581062
10591063 // take a write lock to ensure the outbound packet sequence number is incremented
10601064 // atomically, and only after the packet has actually been sent
@@ -1063,7 +1067,7 @@ internal void SendMessage(Message message)
10631067 byte [ ] hash = null ;
10641068 var packetDataOffset = 4 ; // first four bytes are reserved for outbound packet sequence
10651069
1066- if ( _clientMac != null )
1070+ if ( _clientMac != null && ! _clientEtm )
10671071 {
10681072 // write outbound packet sequence to start of packet data
10691073 Pack . UInt32ToBigEndian ( _outboundPacketSequence , packetData ) ;
@@ -1075,8 +1079,29 @@ internal void SendMessage(Message message)
10751079 // Encrypt packet data
10761080 if ( _clientCipher != null )
10771081 {
1078- packetData = _clientCipher . Encrypt ( packetData , packetDataOffset , packetData . Length - packetDataOffset ) ;
1079- packetDataOffset = 0 ;
1082+ if ( _clientMac != null && _clientEtm )
1083+ {
1084+ // The length of the "packet length" field in bytes
1085+ const int packetLengthFieldLength = 4 ;
1086+
1087+ var encryptedData = _clientCipher . Encrypt ( packetData , packetDataOffset + packetLengthFieldLength , packetData . Length - packetDataOffset - packetLengthFieldLength ) ;
1088+
1089+ Array . Resize ( ref packetData , packetDataOffset + packetLengthFieldLength + encryptedData . Length ) ;
1090+
1091+ // write outbound packet sequence to start of packet data
1092+ Pack . UInt32ToBigEndian ( _outboundPacketSequence , packetData ) ;
1093+
1094+ // write encrypted data
1095+ Buffer . BlockCopy ( encryptedData , 0 , packetData , packetDataOffset + packetLengthFieldLength , encryptedData . Length ) ;
1096+
1097+ // calculate packet hash
1098+ hash = _clientMac . ComputeHash ( packetData ) ;
1099+ }
1100+ else
1101+ {
1102+ packetData = _clientCipher . Encrypt ( packetData , packetDataOffset , packetData . Length - packetDataOffset ) ;
1103+ packetDataOffset = 0 ;
1104+ }
10801105 }
10811106
10821107 if ( packetData . Length > MaximumSshPacketSize )
@@ -1194,8 +1219,22 @@ private Message ReceiveMessage(Socket socket)
11941219 // The length of the "padding length" field in bytes
11951220 const int paddingLengthFieldLength = 1 ;
11961221
1197- // Determine the size of the first block, which is 8 or cipher block size (whichever is larger) bytes
1198- var blockSize = _serverCipher is null ? ( byte ) 8 : Math . Max ( ( byte ) 8 , _serverCipher . MinimumSize ) ;
1222+ int blockSize ;
1223+
1224+ // Determine the size of the first block which is 8 or cipher block size (whichever is larger) bytes
1225+ // The "packet length" field is not encrypted in ETM.
1226+ if ( _serverMac != null && _serverEtm )
1227+ {
1228+ blockSize = ( byte ) 4 ;
1229+ }
1230+ else if ( _serverCipher != null )
1231+ {
1232+ blockSize = Math . Max ( ( byte ) 8 , _serverCipher . MinimumSize ) ;
1233+ }
1234+ else
1235+ {
1236+ blockSize = ( byte ) 8 ;
1237+ }
11991238
12001239 var serverMacLength = _serverMac != null ? _serverMac . HashSize / 8 : 0 ;
12011240
@@ -1215,7 +1254,7 @@ private Message ReceiveMessage(Socket socket)
12151254 return null ;
12161255 }
12171256
1218- if ( _serverCipher != null )
1257+ if ( _serverCipher != null && ( _serverMac == null || ! _serverEtm ) )
12191258 {
12201259 firstBlock = _serverCipher . Decrypt ( firstBlock ) ;
12211260 }
@@ -1257,6 +1296,20 @@ private Message ReceiveMessage(Socket socket)
12571296 }
12581297 }
12591298
1299+ // validate encrypted message against MAC
1300+ if ( _serverMac != null && _serverEtm )
1301+ {
1302+ var clientHash = _serverMac . ComputeHash ( data , 0 , data . Length - serverMacLength ) ;
1303+ var serverHash = data . Take ( data . Length - serverMacLength , serverMacLength ) ;
1304+
1305+ // TODO Add IsEqualTo overload that takes left+right index and number of bytes to compare.
1306+ // TODO That way we can eliminate the extra allocation of the Take above.
1307+ if ( ! serverHash . IsEqualTo ( clientHash ) )
1308+ {
1309+ throw new SshConnectionException ( "MAC error" , DisconnectReason . MacError ) ;
1310+ }
1311+ }
1312+
12601313 if ( _serverCipher != null )
12611314 {
12621315 var numberOfBytesToDecrypt = data . Length - ( blockSize + inboundPacketSequenceLength + serverMacLength ) ;
@@ -1271,8 +1324,8 @@ private Message ReceiveMessage(Socket socket)
12711324 var messagePayloadLength = ( int ) packetLength - paddingLength - paddingLengthFieldLength ;
12721325 var messagePayloadOffset = inboundPacketSequenceLength + packetLengthFieldLength + paddingLengthFieldLength ;
12731326
1274- // validate message against MAC
1275- if ( _serverMac != null )
1327+ // validate decrypted message against MAC
1328+ if ( _serverMac != null && ! _serverEtm )
12761329 {
12771330 var clientHash = _serverMac . ComputeHash ( data , 0 , data . Length - serverMacLength ) ;
12781331 var serverHash = data . Take ( data . Length - serverMacLength , serverMacLength ) ;
@@ -1472,8 +1525,8 @@ internal void OnNewKeysReceived(NewKeysMessage message)
14721525 // Update negotiated algorithms
14731526 _serverCipher = _keyExchange . CreateServerCipher ( ) ;
14741527 _clientCipher = _keyExchange . CreateClientCipher ( ) ;
1475- _serverMac = _keyExchange . CreateServerHash ( ) ;
1476- _clientMac = _keyExchange . CreateClientHash ( ) ;
1528+ _serverMac = _keyExchange . CreateServerHash ( out _serverEtm ) ;
1529+ _clientMac = _keyExchange . CreateClientHash ( out _clientEtm ) ;
14771530 _clientCompression = _keyExchange . CreateCompressor ( ) ;
14781531 _serverDecompression = _keyExchange . CreateDecompressor ( ) ;
14791532
0 commit comments