This commit is contained in:
EonaCat 2025-08-25 19:33:04 +02:00
parent 0c659e9d34
commit 7e9d8a0d47
4 changed files with 86 additions and 25 deletions

View File

@ -11,7 +11,7 @@
<Copyright>EonaCat (Jeroen Saey)</Copyright>
<PackageReadmeFile>readme.md</PackageReadmeFile>
<PackageId>EonaCat.Connections</PackageId>
<Version>1.0.5</Version>
<Version>1.0.6</Version>
<Authors>EonaCat (Jeroen Saey)</Authors>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageIcon>EonaCat.png</PackageIcon>

View File

@ -7,24 +7,24 @@ namespace EonaCat.Connections.Helpers
public static class AesKeyExchange
{
private const int SaltSize = 16;
private const int KeySize = 32;
private const int IvSize = 16;
private const int HmacSize = 32;
private const int Pbkdf2Iterations = 100_000;
private const int _saltSize = 16;
private const int _keySize = 32;
private const int _ivSize = 16;
private const int _hmacSize = 32;
private const int _pbkdf2Iterations = 100_000;
// Returns an AES object derived from the password and salt
public static async Task<Aes> ReceiveAesKeyAsync(Stream stream, string password)
{
// Read salt
byte[] salt = new byte[SaltSize];
await stream.ReadExactlyAsync(salt, 0, SaltSize);
byte[] salt = new byte[_saltSize];
await stream.ReadExactlyAsync(salt, 0, _saltSize);
// Derive key
byte[] key;
using (var kdf = new Rfc2898DeriveBytes(password, salt, Pbkdf2Iterations, HashAlgorithmName.SHA256))
using (var kdf = new Rfc2898DeriveBytes(password, salt, _pbkdf2Iterations, HashAlgorithmName.SHA256))
{
key = kdf.GetBytes(KeySize);
key = kdf.GetBytes(_keySize);
}
var aes = Aes.Create();
@ -41,7 +41,7 @@ namespace EonaCat.Connections.Helpers
public static async Task SendAesKeyAsync(Stream stream, Aes aes, string password)
{
// Generate random salt
byte[] salt = new byte[SaltSize];
byte[] salt = new byte[_saltSize];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(salt);
@ -49,9 +49,9 @@ namespace EonaCat.Connections.Helpers
// Derive AES key
byte[] key;
using (var kdf = new Rfc2898DeriveBytes(password, salt, Pbkdf2Iterations, HashAlgorithmName.SHA256))
using (var kdf = new Rfc2898DeriveBytes(password, salt, _pbkdf2Iterations, HashAlgorithmName.SHA256))
{
key = kdf.GetBytes(KeySize);
key = kdf.GetBytes(_keySize);
}
aes.Key = key;
@ -66,7 +66,11 @@ namespace EonaCat.Connections.Helpers
while (read < count)
{
int readBytes = await stream.ReadAsync(buffer, offset + read, count - read);
if (readBytes == 0) throw new EndOfStreamException();
if (readBytes == 0)
{
throw new EndOfStreamException();
}
read += readBytes;
}
}

View File

@ -45,9 +45,13 @@ namespace EonaCat.Connections
_cancellation = new CancellationTokenSource();
if (_config.Protocol == ProtocolType.TCP)
{
await ConnectTcpAsync();
}
else
{
await ConnectUdpAsync();
}
}
private async Task ConnectTcpAsync()
@ -65,9 +69,13 @@ namespace EonaCat.Connections
{
var sslStream = new SslStream(stream, false, _config.GetRemoteCertificateValidationCallback());
if (_config.Certificate != null)
{
sslStream.AuthenticateAsClient(_config.Host, new X509CertificateCollection { _config.Certificate }, _config.CheckCertificateRevocation);
}
else
{
sslStream.AuthenticateAsClient(_config.Host);
}
stream = sslStream;
}
@ -136,9 +144,16 @@ namespace EonaCat.Connections
if (_config.UseAesEncryption && _aesEncryption != null)
{
var lengthBuffer = new byte[4];
if (await ReadExactAsync(_stream, lengthBuffer, 4, ct) == 0) break;
if (await ReadExactAsync(_stream, lengthBuffer, 4, ct) == 0)
{
break;
}
if (BitConverter.IsLittleEndian)
{
Array.Reverse(lengthBuffer);
}
if (BitConverter.IsLittleEndian) Array.Reverse(lengthBuffer);
int length = BitConverter.ToInt32(lengthBuffer, 0);
var encrypted = new byte[length];
@ -150,7 +165,10 @@ namespace EonaCat.Connections
{
data = new byte[_config.BufferSize];
int bytesRead = await _stream.ReadAsync(data, 0, data.Length, ct);
if (bytesRead == 0) break;
if (bytesRead == 0)
{
break;
}
if (bytesRead < data.Length)
{
@ -180,7 +198,11 @@ namespace EonaCat.Connections
while (offset < length)
{
int read = await stream.ReadAsync(buffer, offset, length - offset, ct);
if (read == 0) return 0;
if (read == 0)
{
return 0;
}
offset += read;
}
return offset;
@ -236,7 +258,10 @@ namespace EonaCat.Connections
public async Task SendAsync(byte[] data)
{
if (!_isConnected) return;
if (!_isConnected)
{
return;
}
try
{
@ -245,7 +270,10 @@ namespace EonaCat.Connections
data = await AesCryptoHelpers.EncryptDataAsync(data, _aesEncryption);
var lengthPrefix = BitConverter.GetBytes(data.Length);
if (BitConverter.IsLittleEndian) Array.Reverse(lengthPrefix);
if (BitConverter.IsLittleEndian)
{
Array.Reverse(lengthPrefix);
}
var framed = new byte[lengthPrefix.Length + data.Length];
Buffer.BlockCopy(lengthPrefix, 0, framed, 0, lengthPrefix.Length);
@ -275,7 +303,10 @@ namespace EonaCat.Connections
private async Task AutoReconnectAsync()
{
if (!_config.EnableAutoReconnect || IsAutoReconnecting) return;
if (!_config.EnableAutoReconnect || IsAutoReconnecting)
{
return;
}
int attempt = 0;
@ -300,7 +331,9 @@ namespace EonaCat.Connections
}
if (!_isConnected)
{
OnGeneralError?.Invoke(this, new ErrorEventArgs { Message = "Failed to reconnect" });
}
}
public async Task DisconnectAsync()

View File

@ -58,9 +58,13 @@ namespace EonaCat.Connections
_serverCancellation = new CancellationTokenSource();
if (_config.Protocol == ProtocolType.TCP)
{
await StartTcpServerAsync();
}
else
{
await StartUdpServerAsync();
}
}
private async Task StartTcpServerAsync()
@ -121,7 +125,9 @@ namespace EonaCat.Connections
{
tcpClient.NoDelay = !_config.EnableNagle;
if (_config.EnableKeepAlive)
{
tcpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
}
Stream stream = tcpClient.GetStream();
@ -225,8 +231,16 @@ namespace EonaCat.Connections
if (client.IsEncrypted && client.AesEncryption != null)
{
if (await ReadExactAsync(client.Stream, lengthBuffer, 4, client.CancellationToken.Token) == 0) break;
if (BitConverter.IsLittleEndian) Array.Reverse(lengthBuffer);
if (await ReadExactAsync(client.Stream, lengthBuffer, 4, client.CancellationToken.Token) == 0)
{
break;
}
if (BitConverter.IsLittleEndian)
{
Array.Reverse(lengthBuffer);
}
int length = BitConverter.ToInt32(lengthBuffer, 0);
var encrypted = new byte[length];
@ -238,7 +252,11 @@ namespace EonaCat.Connections
{
data = new byte[_config.BufferSize];
int bytesRead = await client.Stream.ReadAsync(data, 0, data.Length, client.CancellationToken.Token);
if (bytesRead == 0) break;
if (bytesRead == 0)
{
break;
}
if (bytesRead < data.Length)
{
var tmp = new byte[bytesRead];
@ -263,7 +281,11 @@ namespace EonaCat.Connections
while (offset < length)
{
int read = await stream.ReadAsync(buffer, offset, length - offset, ct);
if (read == 0) return 0;
if (read == 0)
{
return 0;
}
offset += read;
}
return offset;
@ -334,7 +356,9 @@ namespace EonaCat.Connections
// Prepend 4-byte length (big-endian) for framing
var lengthPrefix = BitConverter.GetBytes(data.Length);
if (BitConverter.IsLittleEndian)
{
Array.Reverse(lengthPrefix);
}
var framed = new byte[lengthPrefix.Length + data.Length];
Buffer.BlockCopy(lengthPrefix, 0, framed, 0, lengthPrefix.Length);