Skip to content

Commit a7dfddf

Browse files
committed
Added AES Encryption with DiffieHellman
1 parent b618ec3 commit a7dfddf

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
@@ -49,6 +49,10 @@ public bool isHost
4949

5050
public NetworkingConfiguration NetworkConfig;
5151

52+
private EllipticDiffieHellman clientDiffieHellman;
53+
private Dictionary<int, byte[]> diffieHellmanPublicKeys;
54+
private byte[] clientAesKey;
55+
5256
private void OnValidate()
5357
{
5458
if (SpawnablePrefabs != null)
@@ -86,6 +90,7 @@ private ConnectionConfig Init(NetworkingConfiguration netConfig)
8690
pendingClients = new HashSet<int>();
8791
connectedClients = new Dictionary<int, NetworkedClient>();
8892
messageBuffer = new byte[NetworkConfig.MessageBufferSize];
93+
diffieHellmanPublicKeys = new Dictionary<int, byte[]>();
8994
MessageManager.channels = new Dictionary<string, int>();
9095
MessageManager.messageTypes = new Dictionary<string, ushort>();
9196
MessageManager.messageCallbacks = new Dictionary<ushort, Dictionary<int, Action<int, byte[]>>>();
@@ -372,15 +377,29 @@ private void Update()
372377
}
373378
else
374379
{
380+
byte[] diffiePublic = new byte[0];
381+
if(NetworkConfig.EnableEncryption)
382+
{
383+
clientDiffieHellman = new EllipticDiffieHellman(EllipticDiffieHellman.DEFAULT_CURVE, EllipticDiffieHellman.DEFAULT_GENERATOR, EllipticDiffieHellman.DEFAULT_ORDER);
384+
diffiePublic = clientDiffieHellman.GetPublicKey();
385+
}
386+
375387
int sizeOfStream = 32;
376388
if (NetworkConfig.ConnectionApproval)
377389
sizeOfStream += 2 + NetworkConfig.ConnectionData.Length;
390+
if (NetworkConfig.EnableEncryption)
391+
sizeOfStream += 2 + diffiePublic.Length;
378392

379393
using (MemoryStream writeStream = new MemoryStream(sizeOfStream))
380394
{
381395
using (BinaryWriter writer = new BinaryWriter(writeStream))
382396
{
383397
writer.Write(NetworkConfig.GetConfig());
398+
if (NetworkConfig.EnableEncryption)
399+
{
400+
writer.Write((ushort)diffiePublic.Length);
401+
writer.Write(diffiePublic);
402+
}
384403
if (NetworkConfig.ConnectionApproval)
385404
{
386405
writer.Write((ushort)NetworkConfig.ConnectionData.Length);
@@ -469,6 +488,14 @@ private void HandleIncomingData(int clientId, byte[] data, int channelId)
469488

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

473500
if (isServer && isPassthrough && !NetworkConfig.RegisteredPassthroughMessageTypes.Contains(messageType))
474501
{
@@ -550,6 +577,18 @@ private void HandleIncomingData(int clientId, byte[] data, int channelId)
550577
DisconnectClient(clientId);
551578
return;
552579
}
580+
byte[] aesKey = new byte[0];
581+
if(NetworkConfig.EnableEncryption)
582+
{
583+
ushort diffiePublicSize = reader.ReadUInt16();
584+
byte[] diffiePublic = reader.ReadBytes(diffiePublicSize);
585+
diffieHellmanPublicKeys.Add(clientId, diffiePublic);
586+
/*
587+
EllipticDiffieHellman diffieHellman = new EllipticDiffieHellman(EllipticDiffieHellman.DEFAULT_CURVE, EllipticDiffieHellman.DEFAULT_GENERATOR, EllipticDiffieHellman.DEFAULT_ORDER);
588+
aesKey = diffieHellman.GetSharedSecret(diffiePublic);
589+
*/
590+
591+
}
553592
if (NetworkConfig.ConnectionApproval)
554593
{
555594
ushort bufferSize = messageReader.ReadUInt16();
@@ -578,6 +617,12 @@ private void HandleIncomingData(int clientId, byte[] data, int channelId)
578617
sceneIndex = messageReader.ReadUInt32();
579618
}
580619

620+
if (NetworkConfig.EnableEncryption)
621+
{
622+
ushort keyLength = reader.ReadUInt16();
623+
clientAesKey = clientDiffieHellman.GetSharedSecret(reader.ReadBytes(keyLength));
624+
}
625+
581626
float netTime = messageReader.ReadSingle();
582627
int remoteStamp = messageReader.ReadInt32();
583628
int msDelay = NetworkTransport.GetRemoteDelayTimeMS(hostId, clientId, remoteStamp, out error);
@@ -894,8 +939,18 @@ internal void PassthroughSend(int targetId, int sourceId, ushort messageType, in
894939
writer.Write(orderId.Value);
895940
writer.Write(true);
896941
writer.Write(sourceId);
897-
writer.Write((ushort)data.Length);
898-
writer.Write(data);
942+
if(NetworkConfig.EncryptedChannels.Contains(channelId))
943+
{
944+
//Encrypted message
945+
byte[] encrypted = CryptographyHelper.Encrypt(data, connectedClients[targetId].AesKey);
946+
writer.Write((ushort)encrypted.Length);
947+
writer.Write(encrypted);
948+
}
949+
else
950+
{
951+
writer.Write((ushort)data.Length);
952+
writer.Write(data);
953+
}
899954
}
900955
NetworkTransport.QueueMessageForSending(hostId, targetId, channelId, stream.GetBuffer(), sizeOfStream, out error);
901956
}
@@ -944,8 +999,25 @@ internal void Send(int clientId, string messageType, string channelName, byte[]
944999
writer.Write(isPassthrough);
9451000
if (isPassthrough)
9461001
writer.Write(clientId);
947-
writer.Write((ushort)data.Length);
948-
writer.Write(data);
1002+
1003+
if (NetworkConfig.EncryptedChannels.Contains(MessageManager.channels[channelName]))
1004+
{
1005+
//This is an encrypted message.
1006+
byte[] encrypted;
1007+
if (isServer)
1008+
encrypted = CryptographyHelper.Encrypt(data, connectedClients[clientId].AesKey);
1009+
else
1010+
encrypted = CryptographyHelper.Encrypt(data, clientAesKey);
1011+
1012+
writer.Write((ushort)encrypted.Length);
1013+
writer.Write(encrypted);
1014+
}
1015+
else
1016+
{
1017+
//Send in plaintext.
1018+
writer.Write((ushort)data.Length);
1019+
writer.Write(data);
1020+
}
9491021
}
9501022
if (isPassthrough)
9511023
clientId = serverClientId;
@@ -958,7 +1030,12 @@ internal void Send(int clientId, string messageType, string channelName, byte[]
9581030

9591031
internal void Send(int[] clientIds, string messageType, string channelName, byte[] data, uint? networkId = null, ushort? orderId = null)
9601032
{
961-
int sizeOfStream = 6;
1033+
if (NetworkConfig.EncryptedChannels.Contains(MessageManager.channels[channelName]))
1034+
{
1035+
Debug.LogWarning("MLAPI: Cannot send messages over encrypted channel to multiple clients.");
1036+
return;
1037+
}
1038+
int sizeOfStream = 6;
9621039
if (networkId != null)
9631040
sizeOfStream += 4;
9641041
if (orderId != null)
@@ -1000,6 +1077,12 @@ internal void Send(int[] clientIds, string messageType, string channelName, byte
10001077

10011078
internal void Send(List<int> clientIds, string messageType, string channelName, byte[] data, uint? networkId = null, ushort? orderId = null)
10021079
{
1080+
if (NetworkConfig.EncryptedChannels.Contains(MessageManager.channels[channelName]))
1081+
{
1082+
Debug.LogWarning("MLAPI: Cannot send messages over encrypted channel to multiple clients.");
1083+
return;
1084+
}
1085+
10031086
//2 bytes for messageType, 2 bytes for buffer length and one byte for target bool
10041087
int sizeOfStream = 6;
10051088
if (networkId != null)
@@ -1043,6 +1126,12 @@ internal void Send(List<int> clientIds, string messageType, string channelName,
10431126

10441127
internal void Send(string messageType, string channelName, byte[] data, uint? networkId = null, ushort? orderId = null)
10451128
{
1129+
if (NetworkConfig.EncryptedChannels.Contains(MessageManager.channels[channelName]))
1130+
{
1131+
Debug.LogWarning("MLAPI: Cannot send messages over encrypted channel to multiple clients.");
1132+
return;
1133+
}
1134+
10461135
//2 bytes for messageType, 2 bytes for buffer length and one byte for target bool
10471136
int sizeOfStream = 6;
10481137
if (networkId != null)
@@ -1087,6 +1176,12 @@ internal void Send(string messageType, string channelName, byte[] data, uint? ne
10871176

10881177
internal void Send(string messageType, string channelName, byte[] data, int clientIdToIgnore, uint? networkId = null, ushort? orderId = null)
10891178
{
1179+
if (NetworkConfig.EncryptedChannels.Contains(MessageManager.channels[channelName]))
1180+
{
1181+
Debug.LogWarning("MLAPI: Cannot send messages over encrypted channel to multiple clients.");
1182+
return;
1183+
}
1184+
10901185
//2 bytes for messageType, 2 bytes for buffer length and one byte for target bool
10911186
int sizeOfStream = 5;
10921187
if (networkId != null)
@@ -1134,10 +1229,16 @@ private void DisconnectClient(int clientId)
11341229
{
11351230
if (!isServer)
11361231
return;
1232+
11371233
if (pendingClients.Contains(clientId))
11381234
pendingClients.Remove(clientId);
1235+
11391236
if (connectedClients.ContainsKey(clientId))
11401237
connectedClients.Remove(clientId);
1238+
1239+
if (diffieHellmanPublicKeys.ContainsKey(clientId))
1240+
diffieHellmanPublicKeys.Remove(clientId);
1241+
11411242
NetworkTransport.Disconnect(hostId, clientId, out error);
11421243
}
11431244

@@ -1180,9 +1281,23 @@ private void HandleApproval(int clientId, bool approved)
11801281
//Inform new client it got approved
11811282
if (pendingClients.Contains(clientId))
11821283
pendingClients.Remove(clientId);
1284+
1285+
byte[] aesKey = new byte[0];
1286+
byte[] publicKey = new byte[0];
1287+
if (NetworkConfig.EnableEncryption)
1288+
{
1289+
EllipticDiffieHellman diffieHellman = new EllipticDiffieHellman(EllipticDiffieHellman.DEFAULT_CURVE, EllipticDiffieHellman.DEFAULT_GENERATOR, EllipticDiffieHellman.DEFAULT_ORDER);
1290+
aesKey = diffieHellman.GetSharedSecret(diffieHellmanPublicKeys[clientId]);
1291+
publicKey = diffieHellman.GetPublicKey();
1292+
1293+
if (diffieHellmanPublicKeys.ContainsKey(clientId))
1294+
diffieHellmanPublicKeys.Remove(clientId);
1295+
}
1296+
11831297
NetworkedClient client = new NetworkedClient()
11841298
{
1185-
ClientId = clientId
1299+
ClientId = clientId,
1300+
AesKey = aesKey
11861301
};
11871302
connectedClients.Add(clientId, client);
11881303

@@ -1193,7 +1308,6 @@ private void HandleApproval(int clientId, bool approved)
11931308
connectedClients[clientId].PlayerObject = go;
11941309
}
11951310

1196-
11971311
int sizeOfStream = 16 + ((connectedClients.Count - 1) * 4);
11981312

11991313
int amountOfObjectsToSend = SpawnManager.spawnedObjects.Values.Count(x => x.ServerOnly == false);
@@ -1203,6 +1317,10 @@ private void HandleApproval(int clientId, bool approved)
12031317
sizeOfStream += 4;
12041318
sizeOfStream += 14 * amountOfObjectsToSend;
12051319
}
1320+
if(NetworkConfig.EnableEncryption)
1321+
{
1322+
sizeOfStream += 2 + publicKey.Length;
1323+
}
12061324
if(NetworkConfig.EnableSceneSwitching)
12071325
{
12081326
sizeOfStream += 4;
@@ -1217,8 +1335,16 @@ private void HandleApproval(int clientId, bool approved)
12171335
{
12181336
writer.Write(NetworkSceneManager.CurrentSceneIndex);
12191337
}
1338+
1339+
if(NetworkConfig.EnableEncryption)
1340+
{
1341+
writer.Write((ushort)publicKey.Length);
1342+
writer.Write(publicKey);
1343+
}
1344+
12201345
writer.Write(NetworkTime);
12211346
writer.Write(NetworkTransport.GetNetworkTimestamp());
1347+
12221348
writer.Write(connectedClients.Count - 1);
12231349
foreach (KeyValuePair<int, NetworkedClient> item in connectedClients)
12241350
{
@@ -1284,6 +1410,10 @@ private void HandleApproval(int clientId, bool approved)
12841410
{
12851411
if (pendingClients.Contains(clientId))
12861412
pendingClients.Remove(clientId);
1413+
1414+
if (diffieHellmanPublicKeys.ContainsKey(clientId))
1415+
diffieHellmanPublicKeys.Remove(clientId);
1416+
12871417
NetworkTransport.Disconnect(hostId, clientId, out error);
12881418
}
12891419
}
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)