Skip to content

Commit 8dd5a36

Browse files
committed
Added AES Encryption with DiffieHellman
1 parent 1184eca commit 8dd5a36

File tree

7 files changed

+197
-16
lines changed

7 files changed

+197
-16
lines changed

MLAPI/Data/NetworkedClient.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ public class NetworkedClient
88
public int ClientId;
99
public GameObject PlayerObject;
1010
public List<NetworkedObject> OwnedObjects = new List<NetworkedObject>();
11+
public byte[] AesKey;
1112
}
1213
}

MLAPI/Data/NetworkingConfiguration.cs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public class NetworkingConfiguration
1313
public List<string> MessageTypes = new List<string>();
1414
public List<string> PassthroughMessageTypes = new List<string>();
1515
internal HashSet<ushort> RegisteredPassthroughMessageTypes = new HashSet<ushort>();
16+
public HashSet<int> EncryptedChannels = new HashSet<int>();
1617
public List<string> RegisteredScenes = new List<string>();
1718
public int MessageBufferSize = 65535;
1819
public int ReceiveTickrate = 64;
@@ -28,11 +29,8 @@ public class NetworkingConfiguration
2829
public byte[] ConnectionData = new byte[0];
2930
public float SecondsHistory = 5;
3031
public bool HandleObjectSpawning = true;
31-
//TODO
32-
public bool CompressMessages = false;
33-
//Should only be used for dedicated servers and will require the servers RSA keypair being hard coded into clients in order to exchange a AES key
34-
//TODO
35-
public bool EncryptMessages = false;
32+
33+
public bool EnableEncryption = true;
3634
public bool AllowPassthroughMessages = true;
3735
public bool EnableSceneSwitching = false;
3836

@@ -72,8 +70,7 @@ public byte[] GetConfig(bool cache = true)
7270
}
7371
}
7472
writer.Write(HandleObjectSpawning);
75-
writer.Write(CompressMessages);
76-
writer.Write(EncryptMessages);
73+
writer.Write(EnableEncryption);
7774
writer.Write(AllowPassthroughMessages);
7875
writer.Write(EnableSceneSwitching);
7976
}

MLAPI/MLAPI.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@
6565
<Compile Include="Data\TrackedPointData.cs" />
6666
<Compile Include="MonoBehaviours\Prototyping\NetworkedAnimator.cs" />
6767
<Compile Include="MonoBehaviours\Prototyping\NetworkedNavMeshAgent.cs" />
68+
<Compile Include="NetworkingManagerComponents\CryptographyHelper.cs" />
69+
<Compile Include="NetworkingManagerComponents\DiffieHellman.cs" />
70+
<Compile Include="NetworkingManagerComponents\EllipticCurve.cs" />
6871
<Compile Include="NetworkingManagerComponents\LagCompensationManager.cs" />
6972
<Compile Include="MonoBehaviours\Core\NetworkedBehaviour.cs" />
7073
<Compile Include="Data\NetworkedClient.cs" />

MLAPI/MonoBehaviours/Core/NetworkingManager.cs

Lines changed: 137 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ public bool isHost
5151

5252
public NetworkingConfiguration NetworkConfig;
5353

54+
private EllipticDiffieHellman clientDiffieHellman;
55+
private Dictionary<int, byte[]> diffieHellmanPublicKeys;
56+
private byte[] clientAesKey;
57+
5458
private void OnValidate()
5559
{
5660
if (SpawnablePrefabs != null)
@@ -88,6 +92,7 @@ private ConnectionConfig Init(NetworkingConfiguration netConfig)
8892
pendingClients = new HashSet<int>();
8993
connectedClients = new Dictionary<int, NetworkedClient>();
9094
messageBuffer = new byte[NetworkConfig.MessageBufferSize];
95+
diffieHellmanPublicKeys = new Dictionary<int, byte[]>();
9196
MessageManager.channels = new Dictionary<string, int>();
9297
MessageManager.messageTypes = new Dictionary<string, ushort>();
9398
MessageManager.messageCallbacks = new Dictionary<ushort, Dictionary<int, Action<int, byte[]>>>();
@@ -374,15 +379,29 @@ private void Update()
374379
}
375380
else
376381
{
382+
byte[] diffiePublic = new byte[0];
383+
if(NetworkConfig.EnableEncryption)
384+
{
385+
clientDiffieHellman = new EllipticDiffieHellman(EllipticDiffieHellman.DEFAULT_CURVE, EllipticDiffieHellman.DEFAULT_GENERATOR, EllipticDiffieHellman.DEFAULT_ORDER);
386+
diffiePublic = clientDiffieHellman.GetPublicKey();
387+
}
388+
377389
int sizeOfStream = 32;
378390
if (NetworkConfig.ConnectionApproval)
379391
sizeOfStream += 2 + NetworkConfig.ConnectionData.Length;
392+
if (NetworkConfig.EnableEncryption)
393+
sizeOfStream += 2 + diffiePublic.Length;
380394

381395
using (MemoryStream writeStream = new MemoryStream(sizeOfStream))
382396
{
383397
using (BinaryWriter writer = new BinaryWriter(writeStream))
384398
{
385399
writer.Write(NetworkConfig.GetConfig());
400+
if (NetworkConfig.EnableEncryption)
401+
{
402+
writer.Write((ushort)diffiePublic.Length);
403+
writer.Write(diffiePublic);
404+
}
386405
if (NetworkConfig.ConnectionApproval)
387406
{
388407
writer.Write((ushort)NetworkConfig.ConnectionData.Length);
@@ -471,6 +490,14 @@ private void HandleIncomingData(int clientId, byte[] data, int channelId)
471490

472491
ushort bytesToRead = reader.ReadUInt16();
473492
byte[] incommingData = reader.ReadBytes(bytesToRead);
493+
if(NetworkConfig.EncryptedChannels.Contains(channelId))
494+
{
495+
//Encrypted message
496+
if (isServer)
497+
incommingData = CryptographyHelper.Decrypt(incommingData, connectedClients[clientId].AesKey);
498+
else
499+
incommingData = CryptographyHelper.Decrypt(incommingData, clientAesKey);
500+
}
474501

475502
if (isServer && isPassthrough && !NetworkConfig.RegisteredPassthroughMessageTypes.Contains(messageType))
476503
{
@@ -555,6 +582,18 @@ private void HandleIncomingData(int clientId, byte[] data, int channelId)
555582
DisconnectClient(clientId);
556583
return;
557584
}
585+
byte[] aesKey = new byte[0];
586+
if(NetworkConfig.EnableEncryption)
587+
{
588+
ushort diffiePublicSize = reader.ReadUInt16();
589+
byte[] diffiePublic = reader.ReadBytes(diffiePublicSize);
590+
diffieHellmanPublicKeys.Add(clientId, diffiePublic);
591+
/*
592+
EllipticDiffieHellman diffieHellman = new EllipticDiffieHellman(EllipticDiffieHellman.DEFAULT_CURVE, EllipticDiffieHellman.DEFAULT_GENERATOR, EllipticDiffieHellman.DEFAULT_ORDER);
593+
aesKey = diffieHellman.GetSharedSecret(diffiePublic);
594+
*/
595+
596+
}
558597
if (NetworkConfig.ConnectionApproval)
559598
{
560599
ushort bufferSize = messageReader.ReadUInt16();
@@ -583,6 +622,12 @@ private void HandleIncomingData(int clientId, byte[] data, int channelId)
583622
sceneIndex = messageReader.ReadUInt32();
584623
}
585624

625+
if (NetworkConfig.EnableEncryption)
626+
{
627+
ushort keyLength = reader.ReadUInt16();
628+
clientAesKey = clientDiffieHellman.GetSharedSecret(reader.ReadBytes(keyLength));
629+
}
630+
586631
float netTime = messageReader.ReadSingle();
587632
int remoteStamp = messageReader.ReadInt32();
588633
int msDelay = NetworkTransport.GetRemoteDelayTimeMS(hostId, clientId, remoteStamp, out error);
@@ -901,8 +946,18 @@ internal void PassthroughSend(int targetId, int sourceId, ushort messageType, in
901946
writer.Write(orderId.Value);
902947
writer.Write(true);
903948
writer.Write(sourceId);
904-
writer.Write((ushort)data.Length);
905-
writer.Write(data);
949+
if(NetworkConfig.EncryptedChannels.Contains(channelId))
950+
{
951+
//Encrypted message
952+
byte[] encrypted = CryptographyHelper.Encrypt(data, connectedClients[targetId].AesKey);
953+
writer.Write((ushort)encrypted.Length);
954+
writer.Write(encrypted);
955+
}
956+
else
957+
{
958+
writer.Write((ushort)data.Length);
959+
writer.Write(data);
960+
}
906961
}
907962
NetworkTransport.QueueMessageForSending(hostId, targetId, channelId, stream.GetBuffer(), sizeOfStream, out error);
908963
}
@@ -951,8 +1006,25 @@ internal void Send(int clientId, string messageType, string channelName, byte[]
9511006
writer.Write(isPassthrough);
9521007
if (isPassthrough)
9531008
writer.Write(clientId);
954-
writer.Write((ushort)data.Length);
955-
writer.Write(data);
1009+
1010+
if (NetworkConfig.EncryptedChannels.Contains(MessageManager.channels[channelName]))
1011+
{
1012+
//This is an encrypted message.
1013+
byte[] encrypted;
1014+
if (isServer)
1015+
encrypted = CryptographyHelper.Encrypt(data, connectedClients[clientId].AesKey);
1016+
else
1017+
encrypted = CryptographyHelper.Encrypt(data, clientAesKey);
1018+
1019+
writer.Write((ushort)encrypted.Length);
1020+
writer.Write(encrypted);
1021+
}
1022+
else
1023+
{
1024+
//Send in plaintext.
1025+
writer.Write((ushort)data.Length);
1026+
writer.Write(data);
1027+
}
9561028
}
9571029
if (isPassthrough)
9581030
clientId = serverClientId;
@@ -965,7 +1037,12 @@ internal void Send(int clientId, string messageType, string channelName, byte[]
9651037

9661038
internal void Send(int[] clientIds, string messageType, string channelName, byte[] data, uint? networkId = null, ushort? orderId = null)
9671039
{
968-
int sizeOfStream = 6;
1040+
if (NetworkConfig.EncryptedChannels.Contains(MessageManager.channels[channelName]))
1041+
{
1042+
Debug.LogWarning("MLAPI: Cannot send messages over encrypted channel to multiple clients.");
1043+
return;
1044+
}
1045+
int sizeOfStream = 6;
9691046
if (networkId != null)
9701047
sizeOfStream += 4;
9711048
if (orderId != null)
@@ -1007,6 +1084,12 @@ internal void Send(int[] clientIds, string messageType, string channelName, byte
10071084

10081085
internal void Send(List<int> clientIds, string messageType, string channelName, byte[] data, uint? networkId = null, ushort? orderId = null)
10091086
{
1087+
if (NetworkConfig.EncryptedChannels.Contains(MessageManager.channels[channelName]))
1088+
{
1089+
Debug.LogWarning("MLAPI: Cannot send messages over encrypted channel to multiple clients.");
1090+
return;
1091+
}
1092+
10101093
//2 bytes for messageType, 2 bytes for buffer length and one byte for target bool
10111094
int sizeOfStream = 6;
10121095
if (networkId != null)
@@ -1050,6 +1133,12 @@ internal void Send(List<int> clientIds, string messageType, string channelName,
10501133

10511134
internal void Send(string messageType, string channelName, byte[] data, uint? networkId = null, ushort? orderId = null)
10521135
{
1136+
if (NetworkConfig.EncryptedChannels.Contains(MessageManager.channels[channelName]))
1137+
{
1138+
Debug.LogWarning("MLAPI: Cannot send messages over encrypted channel to multiple clients.");
1139+
return;
1140+
}
1141+
10531142
//2 bytes for messageType, 2 bytes for buffer length and one byte for target bool
10541143
int sizeOfStream = 6;
10551144
if (networkId != null)
@@ -1094,6 +1183,12 @@ internal void Send(string messageType, string channelName, byte[] data, uint? ne
10941183

10951184
internal void Send(string messageType, string channelName, byte[] data, int clientIdToIgnore, uint? networkId = null, ushort? orderId = null)
10961185
{
1186+
if (NetworkConfig.EncryptedChannels.Contains(MessageManager.channels[channelName]))
1187+
{
1188+
Debug.LogWarning("MLAPI: Cannot send messages over encrypted channel to multiple clients.");
1189+
return;
1190+
}
1191+
10971192
//2 bytes for messageType, 2 bytes for buffer length and one byte for target bool
10981193
int sizeOfStream = 5;
10991194
if (networkId != null)
@@ -1142,10 +1237,16 @@ private void DisconnectClient(int clientId)
11421237
{
11431238
if (!isServer)
11441239
return;
1240+
11451241
if (pendingClients.Contains(clientId))
11461242
pendingClients.Remove(clientId);
1243+
11471244
if (connectedClients.ContainsKey(clientId))
11481245
connectedClients.Remove(clientId);
1246+
1247+
if (diffieHellmanPublicKeys.ContainsKey(clientId))
1248+
diffieHellmanPublicKeys.Remove(clientId);
1249+
11491250
NetworkTransport.Disconnect(hostId, clientId, out error);
11501251
}
11511252

@@ -1188,9 +1289,23 @@ private void HandleApproval(int clientId, bool approved)
11881289
//Inform new client it got approved
11891290
if (pendingClients.Contains(clientId))
11901291
pendingClients.Remove(clientId);
1292+
1293+
byte[] aesKey = new byte[0];
1294+
byte[] publicKey = new byte[0];
1295+
if (NetworkConfig.EnableEncryption)
1296+
{
1297+
EllipticDiffieHellman diffieHellman = new EllipticDiffieHellman(EllipticDiffieHellman.DEFAULT_CURVE, EllipticDiffieHellman.DEFAULT_GENERATOR, EllipticDiffieHellman.DEFAULT_ORDER);
1298+
aesKey = diffieHellman.GetSharedSecret(diffieHellmanPublicKeys[clientId]);
1299+
publicKey = diffieHellman.GetPublicKey();
1300+
1301+
if (diffieHellmanPublicKeys.ContainsKey(clientId))
1302+
diffieHellmanPublicKeys.Remove(clientId);
1303+
}
1304+
11911305
NetworkedClient client = new NetworkedClient()
11921306
{
1193-
ClientId = clientId
1307+
ClientId = clientId,
1308+
AesKey = aesKey
11941309
};
11951310
connectedClients.Add(clientId, client);
11961311

@@ -1201,7 +1316,6 @@ private void HandleApproval(int clientId, bool approved)
12011316
connectedClients[clientId].PlayerObject = go;
12021317
}
12031318

1204-
12051319
int sizeOfStream = 16 + ((connectedClients.Count - 1) * 4);
12061320

12071321
int amountOfObjectsToSend = SpawnManager.spawnedObjects.Values.Count(x => x.ServerOnly == false);
@@ -1211,6 +1325,10 @@ private void HandleApproval(int clientId, bool approved)
12111325
sizeOfStream += 4;
12121326
sizeOfStream += 14 * amountOfObjectsToSend;
12131327
}
1328+
if(NetworkConfig.EnableEncryption)
1329+
{
1330+
sizeOfStream += 2 + publicKey.Length;
1331+
}
12141332
if(NetworkConfig.EnableSceneSwitching)
12151333
{
12161334
sizeOfStream += 4;
@@ -1225,8 +1343,16 @@ private void HandleApproval(int clientId, bool approved)
12251343
{
12261344
writer.Write(NetworkSceneManager.CurrentSceneIndex);
12271345
}
1346+
1347+
if(NetworkConfig.EnableEncryption)
1348+
{
1349+
writer.Write((ushort)publicKey.Length);
1350+
writer.Write(publicKey);
1351+
}
1352+
12281353
writer.Write(NetworkTime);
12291354
writer.Write(NetworkTransport.GetNetworkTimestamp());
1355+
12301356
writer.Write(connectedClients.Count - 1);
12311357
foreach (KeyValuePair<int, NetworkedClient> item in connectedClients)
12321358
{
@@ -1292,6 +1418,10 @@ private void HandleApproval(int clientId, bool approved)
12921418
{
12931419
if (pendingClients.Contains(clientId))
12941420
pendingClients.Remove(clientId);
1421+
1422+
if (diffieHellmanPublicKeys.ContainsKey(clientId))
1423+
diffieHellmanPublicKeys.Remove(clientId);
1424+
12951425
NetworkTransport.Disconnect(hostId, clientId, out error);
12961426
}
12971427
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System;
2+
using System.Security.Cryptography;
3+
using System.IO;
4+
5+
namespace MLAPI.NetworkingManagerComponents
6+
{
7+
public static class CryptographyHelper
8+
{
9+
public static byte[] Decrypt(byte[] encryptedBuffer, byte[] key)
10+
{
11+
byte[] iv = new byte[16];
12+
Array.Copy(encryptedBuffer, 0, iv, 0, 16);
13+
14+
using (MemoryStream stream = new MemoryStream())
15+
{
16+
using (RijndaelManaged aes = new RijndaelManaged())
17+
{
18+
aes.IV = iv;
19+
aes.Key = key;
20+
using (CryptoStream cs = new CryptoStream(stream, aes.CreateDecryptor(), CryptoStreamMode.Write))
21+
{
22+
cs.Write(encryptedBuffer, 16, encryptedBuffer.Length - 16);
23+
}
24+
return stream.ToArray();
25+
}
26+
}
27+
}
28+
29+
public static byte[] Encrypt(byte[] clearBuffer, byte[] key)
30+
{
31+
using (MemoryStream stream = new MemoryStream())
32+
{
33+
using (RijndaelManaged aes = new RijndaelManaged())
34+
{
35+
aes.Key = key;
36+
aes.GenerateIV();
37+
using (CryptoStream cs = new CryptoStream(stream, aes.CreateEncryptor(), CryptoStreamMode.Write))
38+
{
39+
cs.Write(clearBuffer, 0, clearBuffer.Length);
40+
}
41+
byte[] encrypted = stream.ToArray();
42+
byte[] final = new byte[encrypted.Length + 16];
43+
Array.Copy(aes.IV, final, 16);
44+
Array.Copy(encrypted, 0, final, 16, encrypted.Length);
45+
return final;
46+
}
47+
}
48+
}
49+
}
50+
}

MLAPI/NetworkingManagerComponents/DiffieHellman.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using System.Text;
44
using System.Security.Cryptography;
55

6-
namespace ECDH
6+
namespace MLAPI.NetworkingManagerComponents
77
{
88
public class EllipticDiffieHellman
99
{

0 commit comments

Comments
 (0)