Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public override void Decrypt(byte[] input, int cipherTextOffset, int cipherTextL

try
{
_aesGcm.Decrypt(_nonce, cipherText, tag, output.AsSpan(plainTextOffset), associatedData);
_aesGcm.Decrypt(_nonce, cipherText, tag, output.AsSpan(plainTextOffset, cipherTextLength), associatedData);
}
catch (AuthenticationTagMismatchException ex)
{
Expand Down
41 changes: 23 additions & 18 deletions src/Renci.SshNet/Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ public sealed class Session : ISession
private Socket _socket;

private ArrayBuffer _receiveBuffer = new(4 * 1024);
private byte[] _plaintextReceiveBuffer = new byte[4 * 1024];

/// <summary>
/// Gets the session semaphore that controls session channels.
Expand Down Expand Up @@ -1319,19 +1320,24 @@ private Message ReceiveMessage(Socket socket)

// Construct buffer for holding the payload and the inbound packet sequence as we need both in order
// to generate the hash.
var data = new byte[4 + totalPacketLength - serverMacLength];
var plaintextLength = 4 + totalPacketLength - serverMacLength;

BinaryPrimitives.WriteUInt32BigEndian(data, _inboundPacketSequence);
if (_plaintextReceiveBuffer.Length < plaintextLength)
{
Array.Resize(ref _plaintextReceiveBuffer, Math.Max(plaintextLength, 2 * _plaintextReceiveBuffer.Length));
}

BinaryPrimitives.WriteUInt32BigEndian(_plaintextReceiveBuffer, _inboundPacketSequence);

plainFirstBlock.AsSpan().CopyTo(data.AsSpan(4));
plainFirstBlock.AsSpan().CopyTo(_plaintextReceiveBuffer.AsSpan(4));

if (_serverMac != null && _serverEtm)
{
// ETM mac = MAC(key, sequence_number || packet_length || encrypted_packet)

// sequence_number
_ = _serverMac.TransformBlock(
inputBuffer: data,
inputBuffer: _plaintextReceiveBuffer,
inputOffset: 0,
inputCount: 4,
outputBuffer: null,
Expand Down Expand Up @@ -1363,21 +1369,23 @@ private Message ReceiveMessage(Socket socket)
input: _receiveBuffer.DangerousGetUnderlyingBuffer(),
offset: _receiveBuffer.ActiveStartOffset + blockSize,
length: numberOfBytesToDecrypt,
output: data,
output: _plaintextReceiveBuffer,
outputOffset: 4 + blockSize);

Debug.Assert(numberOfBytesDecrypted == numberOfBytesToDecrypt);
}
else
{
_receiveBuffer.ActiveReadOnlySpan.Slice(blockSize, numberOfBytesToDecrypt).CopyTo(data.AsSpan(4 + blockSize));
_receiveBuffer.ActiveReadOnlySpan
.Slice(blockSize, numberOfBytesToDecrypt)
.CopyTo(_plaintextReceiveBuffer.AsSpan(4 + blockSize));
}

if (_serverMac != null && !_serverEtm)
{
// non-ETM mac = MAC(key, sequence_number || unencrypted_packet)

var clientHash = _serverMac.ComputeHash(data);
var clientHash = _serverMac.ComputeHash(_plaintextReceiveBuffer, 0, plaintextLength);

if (!CryptoAbstraction.FixedTimeEquals(clientHash, _receiveBuffer.ActiveSpan.Slice(totalPacketLength - serverMacLength, serverMacLength)))
{
Expand All @@ -1387,19 +1395,16 @@ private Message ReceiveMessage(Socket socket)

_receiveBuffer.Discard(totalPacketLength);

var paddingLength = data[inboundPacketSequenceLength + packetLengthFieldLength];
var messagePayloadLength = packetLength - paddingLength - paddingLengthFieldLength;
var messagePayloadOffset = inboundPacketSequenceLength + packetLengthFieldLength + paddingLengthFieldLength;
var paddingLength = _plaintextReceiveBuffer[inboundPacketSequenceLength + packetLengthFieldLength];

ArraySegment<byte> payload = new(
_plaintextReceiveBuffer,
offset: inboundPacketSequenceLength + packetLengthFieldLength + paddingLengthFieldLength,
count: packetLength - paddingLength - paddingLengthFieldLength);

if (_serverDecompression != null)
{
data = _serverDecompression.Decompress(data, messagePayloadOffset, messagePayloadLength);

// Data now only contains the decompressed payload, and as such the offset is reset to zero
messagePayloadOffset = 0;

// The length of the payload is now the complete decompressed content
messagePayloadLength = data.Length;
payload = new(_serverDecompression.Decompress(payload.Array, payload.Offset, payload.Count));
}

_inboundPacketSequence++;
Expand All @@ -1411,7 +1416,7 @@ private Message ReceiveMessage(Socket socket)
throw new SshConnectionException("Inbound packet sequence number is about to wrap during initial key exchange.", DisconnectReason.KeyExchangeFailed);
}

return LoadMessage(data, messagePayloadOffset, messagePayloadLength);
return LoadMessage(payload.Array, payload.Offset, payload.Count);
}

private void TrySendDisconnect(DisconnectReason reasonCode, string message)
Expand Down