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> <Copyright>EonaCat (Jeroen Saey)</Copyright>
<PackageReadmeFile>readme.md</PackageReadmeFile> <PackageReadmeFile>readme.md</PackageReadmeFile>
<PackageId>EonaCat.Connections</PackageId> <PackageId>EonaCat.Connections</PackageId>
<Version>1.0.5</Version> <Version>1.0.6</Version>
<Authors>EonaCat (Jeroen Saey)</Authors> <Authors>EonaCat (Jeroen Saey)</Authors>
<PackageLicenseFile>LICENSE</PackageLicenseFile> <PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageIcon>EonaCat.png</PackageIcon> <PackageIcon>EonaCat.png</PackageIcon>

View File

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

View File

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

View File

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