Skip to content

Commit a49db87

Browse files
committed
regular aes support to accomodate Mac until .NET 8's November release (and our upgrade to .NET 8)
1 parent 007d78d commit a49db87

File tree

7 files changed

+295
-57
lines changed

7 files changed

+295
-57
lines changed

Intersect (Core)/Network/Packets/HailPacket.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ public partial class HailPacket : ConnectionPacket
2020
[IgnoreMember]
2121
private RSAParameters mRsaParameters;
2222

23+
private byte _symmetricVersion = (byte)(AesGcm.IsSupported ? 1 : 0);
24+
2325
public HailPacket()
2426
{
2527
}
@@ -53,6 +55,13 @@ public RSAParameters RsaParameters
5355
set => mRsaParameters = value;
5456
}
5557

58+
[IgnoreMember]
59+
public byte SymmetricVersion
60+
{
61+
get => _symmetricVersion;
62+
set => _symmetricVersion = value;
63+
}
64+
5665
public override bool Encrypt()
5766
{
5867
using (var buffer = new MemoryBuffer())
@@ -75,6 +84,7 @@ public override bool Encrypt()
7584
buffer.Write(bits);
7685
buffer.Write(RsaParameters.Exponent, 3);
7786
buffer.Write(RsaParameters.Modulus, bits >> 3);
87+
buffer.Write(SymmetricVersion);
7888

7989
#if INTERSECT_DIAGNOSTIC
8090
DumpKey(RsaParameters, true);
@@ -151,6 +161,11 @@ public override bool Decrypt(RSA rsa)
151161
return false;
152162
}
153163

164+
if (!buffer.Read(out _symmetricVersion))
165+
{
166+
return false;
167+
}
168+
154169
#if INTERSECT_DIAGNOSTIC
155170
DumpKey(RsaParameters, true);
156171
#endif
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
using System.Buffers;
2+
using System.Buffers.Binary;
3+
using System.Security.Cryptography;
4+
using System.Text;
5+
using Intersect.Logging;
6+
7+
namespace Intersect.Network.LiteNetLib;
8+
9+
public sealed class AesAlgorithm : SymmetricAlgorithm
10+
{
11+
// Encode the plaindata length (1 int), the nonce length (1 byte), and the tag length (1 byte)
12+
private const int HeaderSize = sizeof(int) + sizeof(byte) * 2;
13+
14+
private static KeySizes[] LegalBlockSizes;
15+
private static KeySizes[] LegalKeySizes;
16+
17+
static AesAlgorithm()
18+
{
19+
using var aes = Aes.Create();
20+
LegalBlockSizes = aes.LegalBlockSizes;
21+
LegalKeySizes = aes.LegalKeySizes;
22+
}
23+
24+
private readonly Memory<byte> _key;
25+
26+
private MemoryHandle _keyHandle;
27+
28+
private static new bool IsValidSize(KeySizes sizes, int size)
29+
{
30+
return SymmetricAlgorithm.IsValidSize(
31+
new KeySizes(sizes.MinSize / 8, sizes.MaxSize / 8, sizes.SkipSize / 8),
32+
size
33+
);
34+
}
35+
36+
public AesAlgorithm(ReadOnlySpan<byte> key = default)
37+
{
38+
var buffer = RandomNumberGenerator.GetBytes(32);
39+
if (key != default)
40+
{
41+
var keyLength = key.Length;
42+
if (!LegalKeySizes.Any(keySizes => IsValidSize(keySizes, keyLength)))
43+
{
44+
throw new ArgumentException(
45+
$"{key.Length} is not a supported key length in bytes.",
46+
nameof(key)
47+
);
48+
}
49+
50+
if (!key.TryCopyTo(buffer))
51+
{
52+
throw new ArgumentException(
53+
"The provided key could not be copied to the internal buffer.",
54+
nameof(key)
55+
);
56+
}
57+
}
58+
59+
_key = new Memory<byte>(buffer);
60+
_keyHandle = _key.Pin();
61+
}
62+
63+
public override void Dispose()
64+
{
65+
_keyHandle.Dispose();
66+
}
67+
68+
public override bool SetKey(ReadOnlySpan<byte> key) => key.TryCopyTo(_key.Span);
69+
70+
public override EncryptionResult TryDecrypt(ReadOnlySpan<byte> cipherdata, out ReadOnlySpan<byte> plaindata)
71+
{
72+
plaindata = default;
73+
74+
try
75+
{
76+
var version = cipherdata[0];
77+
cipherdata = cipherdata[1..];
78+
79+
if (version != 0)
80+
{
81+
return EncryptionResult.InvalidVersion;
82+
}
83+
84+
using var aes = Aes.Create();
85+
aes.Key = _key.ToArray();
86+
87+
var plaindataLength = BinaryPrimitives.ReadInt32LittleEndian(cipherdata);
88+
cipherdata = cipherdata[sizeof(int)..];
89+
90+
var nonceLength = cipherdata[0];
91+
cipherdata = cipherdata[1..];
92+
93+
var nonce = cipherdata[..nonceLength];
94+
cipherdata = cipherdata[nonceLength..];
95+
96+
aes.IV = nonce.ToArray();
97+
98+
var cryptoTransform = aes.CreateDecryptor(aes.Key, aes.IV);
99+
100+
using MemoryStream memoryStream = new(cipherdata.ToArray());
101+
using CryptoStream cryptoStream = new(memoryStream, cryptoTransform, CryptoStreamMode.Read);
102+
var plaindataBuffer = new byte[plaindataLength];
103+
cryptoStream.ReadExactly(plaindataBuffer);
104+
plaindata = plaindataBuffer;
105+
106+
#if DIAGNOSTIC
107+
Log.Debug($"Decrypting {plaindata.Length} bytes:\nKey={Convert.ToHexString(aes.Key)} Nonce={Convert.ToHexString(aes.IV)}\nplaindata={Convert.ToHexString(plaindata)}\ncipherdata={Convert.ToHexString(cipherdata)}");
108+
#endif
109+
110+
return EncryptionResult.Success;
111+
}
112+
catch (Exception exception)
113+
{
114+
Log.Debug(exception);
115+
return EncryptionResult.Error;
116+
}
117+
}
118+
119+
public override EncryptionResult TryDecrypt(ReadOnlySpan<byte> cipherdata, int offset, int length, out ReadOnlySpan<byte> plaindata) =>
120+
TryDecrypt(cipherdata[offset..(offset + length)], out plaindata);
121+
122+
public override EncryptionResult TryEncrypt(ReadOnlySpan<byte> plaindata, out ReadOnlySpan<byte> cipherdata)
123+
{
124+
cipherdata = default;
125+
126+
try
127+
{
128+
using var aes = Aes.Create();
129+
aes.Key = _key.ToArray();
130+
131+
var nonce = RandomNumberGenerator.GetBytes(aes.BlockSize / 8);
132+
aes.IV = nonce;
133+
134+
using MemoryStream memoryStream = new();
135+
136+
// Version
137+
memoryStream.WriteByte(0);
138+
139+
var plaindataLengthBytes = new byte[sizeof(int)];
140+
BinaryPrimitives.WriteInt32LittleEndian(plaindataLengthBytes, plaindata.Length);
141+
memoryStream.Write(plaindataLengthBytes);
142+
143+
memoryStream.WriteByte((byte)nonce.Length);
144+
memoryStream.Write(nonce);
145+
146+
var cryptoTransform = aes.CreateEncryptor(aes.Key, aes.IV);
147+
using CryptoStream cryptoStream = new(memoryStream, cryptoTransform, CryptoStreamMode.Write, true);
148+
cryptoStream.Write(plaindata);
149+
150+
cryptoStream.Close();
151+
memoryStream.Close();
152+
153+
cipherdata = memoryStream.ToArray();
154+
155+
#if DIAGNOSTIC
156+
Log.Debug($"Encrypting {plaindata.Length} bytes:\nKey={Convert.ToHexString(aes.Key)} Nonce={Convert.ToHexString(aes.IV)}\nplaindata={Convert.ToHexString(plaindata)}\ncipherdata={Convert.ToHexString(cipherdata[^plaindata.Length..])}");
157+
#endif
158+
159+
return EncryptionResult.Success;
160+
}
161+
catch (Exception exception)
162+
{
163+
Log.Debug(exception);
164+
return EncryptionResult.Error;
165+
}
166+
}
167+
168+
public override EncryptionResult TryEncrypt(ReadOnlySpan<byte> plaindata, int offset, int length, out ReadOnlySpan<byte> cipherdata) =>
169+
TryEncrypt(plaindata[offset..(offset + length)], out cipherdata);
170+
}

Intersect.Network/LiteNetLib/AesGcmContainer.cs renamed to Intersect.Network/LiteNetLib/AesGcmAlgorithm.cs

Lines changed: 13 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
namespace Intersect.Network.LiteNetLib;
77

8-
public sealed class AesGcmContainer : IDisposable
8+
public sealed class AesGcmAlgorithm : SymmetricAlgorithm
99
{
1010
// Encode the version (1 byte), plaindata length (1 int), the nonce length (1 byte), and the tag length (1 byte)
1111
private const int HeaderSize = sizeof(int) + sizeof(byte) * 3;
@@ -15,7 +15,7 @@ public sealed class AesGcmContainer : IDisposable
1515
private AesGcm _aesGcm;
1616
private MemoryHandle _keyHandle;
1717

18-
public AesGcmContainer(ReadOnlySpan<byte> key = default)
18+
public AesGcmAlgorithm(ReadOnlySpan<byte> key = default)
1919
{
2020
var buffer = RandomNumberGenerator.GetBytes(32);
2121
if (key != default)
@@ -31,7 +31,7 @@ public AesGcmContainer(ReadOnlySpan<byte> key = default)
3131
if (!key.TryCopyTo(buffer))
3232
{
3333
throw new ArgumentException(
34-
"The provided key could not be copied to the internal buffer.",
34+
"The provided key could not be copied to the internal buffer.",
3535
nameof(key)
3636
);
3737
}
@@ -42,53 +42,24 @@ public AesGcmContainer(ReadOnlySpan<byte> key = default)
4242
_aesGcm = new AesGcm(_key.Span);
4343
}
4444

45-
public bool SetKey(ReadOnlySpan<byte> key)
45+
public override void Dispose()
4646
{
47-
if (!key.TryCopyTo(_key.Span))
48-
{
49-
return false;
50-
}
51-
52-
_aesGcm = new AesGcm(_key.Span);
53-
return true;
54-
55-
}
56-
57-
private static bool IsValidSize(KeySizes sizes, int size)
58-
{
59-
if (size == sizes.MinSize || size == sizes.MaxSize)
60-
{
61-
return true;
62-
}
63-
64-
if (size < sizes.MinSize || size > sizes.MaxSize)
65-
{
66-
return false;
67-
}
68-
69-
return (size - sizes.MinSize) % sizes.SkipSize == 0;
47+
_keyHandle.Dispose();
48+
_aesGcm.Dispose();
7049
}
7150

72-
private static bool TryPickValidSize(KeySizes sizes, out byte size)
51+
public override bool SetKey(ReadOnlySpan<byte> key)
7352
{
74-
if (sizes.MinSize > byte.MaxValue)
53+
if (!key.TryCopyTo(_key.Span))
7554
{
76-
size = default;
7755
return false;
7856
}
7957

80-
var currentSize = sizes.MaxSize;
81-
82-
while (currentSize > byte.MaxValue)
83-
{
84-
currentSize -= sizes.SkipSize;
85-
}
86-
87-
size = (byte)currentSize;
58+
_aesGcm = new AesGcm(_key.Span);
8859
return true;
8960
}
9061

91-
public EncryptionResult TryDecrypt(ReadOnlySpan<byte> cipherdata, out ReadOnlySpan<byte> plaindata)
62+
public override EncryptionResult TryDecrypt(ReadOnlySpan<byte> cipherdata, out ReadOnlySpan<byte> plaindata)
9263
{
9364
if (cipherdata.Length < HeaderSize)
9465
{
@@ -169,10 +140,10 @@ public EncryptionResult TryDecrypt(ReadOnlySpan<byte> cipherdata, out ReadOnlySp
169140
}
170141
}
171142

172-
public EncryptionResult TryDecrypt(ReadOnlySpan<byte> cipherdata, int offset, int length, out ReadOnlySpan<byte> plaindata) =>
143+
public override EncryptionResult TryDecrypt(ReadOnlySpan<byte> cipherdata, int offset, int length, out ReadOnlySpan<byte> plaindata) =>
173144
TryDecrypt(cipherdata[offset..(offset + length)], out plaindata);
174145

175-
public EncryptionResult TryEncrypt(ReadOnlySpan<byte> plaindata, out ReadOnlySpan<byte> cipherdata)
146+
public override EncryptionResult TryEncrypt(ReadOnlySpan<byte> plaindata, out ReadOnlySpan<byte> cipherdata)
176147
{
177148
if (plaindata.Length < 1)
178149
{
@@ -242,12 +213,6 @@ public EncryptionResult TryEncrypt(ReadOnlySpan<byte> plaindata, out ReadOnlySpa
242213
}
243214
}
244215

245-
public EncryptionResult TryEncrypt(ReadOnlySpan<byte> plaindata, int offset, int length, out ReadOnlySpan<byte> cipherdata) =>
216+
public override EncryptionResult TryEncrypt(ReadOnlySpan<byte> plaindata, int offset, int length, out ReadOnlySpan<byte> cipherdata) =>
246217
TryEncrypt(plaindata[offset..(offset + length)], out cipherdata);
247-
248-
public void Dispose()
249-
{
250-
_keyHandle.Dispose();
251-
_aesGcm.Dispose();
252-
}
253218
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace Intersect.Network.LiteNetLib;
2+
3+
public interface ISymmetricAlgorithm : IDisposable
4+
{
5+
bool SetKey(ReadOnlySpan<byte> key);
6+
EncryptionResult TryDecrypt(ReadOnlySpan<byte> cipherdata, out ReadOnlySpan<byte> plaindata);
7+
EncryptionResult TryDecrypt(ReadOnlySpan<byte> cipherdata, int offset, int length, out ReadOnlySpan<byte> plaindata);
8+
EncryptionResult TryEncrypt(ReadOnlySpan<byte> plaindata, out ReadOnlySpan<byte> cipherdata);
9+
EncryptionResult TryEncrypt(ReadOnlySpan<byte> plaindata, int offset, int length, out ReadOnlySpan<byte> cipherdata);
10+
}

0 commit comments

Comments
 (0)