diff --git a/MTSC-TestServer/MTSC-TestServer.csproj b/MTSC-TestServer/MTSC-TestServer.csproj
new file mode 100644
index 0000000..fd5f0aa
--- /dev/null
+++ b/MTSC-TestServer/MTSC-TestServer.csproj
@@ -0,0 +1,13 @@
+
+
+
+ Exe
+ netcoreapp2.1
+ MTSC_TestServer
+
+
+
+
+
+
+
diff --git a/MTSC-TestServer/Program.cs b/MTSC-TestServer/Program.cs
new file mode 100644
index 0000000..f18dbde
--- /dev/null
+++ b/MTSC-TestServer/Program.cs
@@ -0,0 +1,14 @@
+using MTSC.Server;
+using System;
+
+namespace MTSC_TestServer
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ Server server = new Server(555);
+ server.
+ }
+ }
+}
diff --git a/MTSC.sln b/MTSC.sln
index dff394d..3f20e1d 100644
--- a/MTSC.sln
+++ b/MTSC.sln
@@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29102.190
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MTSC", "MTSC\MTSC.csproj", "{869DAEBC-5884-43C9-BA0B-2C4376A7ED1C}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MTSC", "MTSC\MTSC.csproj", "{869DAEBC-5884-43C9-BA0B-2C4376A7ED1C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MTSC-TestServer", "MTSC-TestServer\MTSC-TestServer.csproj", "{08A53F8F-BA6B-4C1C-B24B-CFD2626DE6C3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -15,6 +17,10 @@ Global
{869DAEBC-5884-43C9-BA0B-2C4376A7ED1C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{869DAEBC-5884-43C9-BA0B-2C4376A7ED1C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{869DAEBC-5884-43C9-BA0B-2C4376A7ED1C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {08A53F8F-BA6B-4C1C-B24B-CFD2626DE6C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {08A53F8F-BA6B-4C1C-B24B-CFD2626DE6C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {08A53F8F-BA6B-4C1C-B24B-CFD2626DE6C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {08A53F8F-BA6B-4C1C-B24B-CFD2626DE6C3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/MTSC/HelperFunctions.cs b/MTSC/HelperFunctions.cs
new file mode 100644
index 0000000..449dd8e
--- /dev/null
+++ b/MTSC/HelperFunctions.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.Security.Cryptography;
+using System.Text;
+using System.Xml;
+
+namespace MTSC
+{
+ static class HelperFunctions
+ {
+ #region XML
+
+ public static void FromXmlString(this RSA rsa, string xmlString)
+ {
+ RSAParameters parameters = new RSAParameters();
+
+ XmlDocument xmlDoc = new XmlDocument();
+ xmlDoc.LoadXml(xmlString);
+
+ if (xmlDoc.DocumentElement.Name.Equals("RSAKeyValue"))
+ {
+ foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes)
+ {
+ switch (node.Name)
+ {
+ case "Modulus": parameters.Modulus = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
+ case "Exponent": parameters.Exponent = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
+ case "P": parameters.P = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
+ case "Q": parameters.Q = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
+ case "DP": parameters.DP = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
+ case "DQ": parameters.DQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
+ case "InverseQ": parameters.InverseQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
+ case "D": parameters.D = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
+ }
+ }
+ }
+ else
+ {
+ throw new Exception("Invalid XML RSA key.");
+ }
+
+ rsa.ImportParameters(parameters);
+ }
+
+ public static string ToXmlString(this RSA rsa, bool includePrivateParameters)
+ {
+ RSAParameters parameters = rsa.ExportParameters(includePrivateParameters);
+
+ return string.Format("{0}{1}{2}
{3}
{4}{5}{6}{7}",
+ parameters.Modulus != null ? Convert.ToBase64String(parameters.Modulus) : null,
+ parameters.Exponent != null ? Convert.ToBase64String(parameters.Exponent) : null,
+ parameters.P != null ? Convert.ToBase64String(parameters.P) : null,
+ parameters.Q != null ? Convert.ToBase64String(parameters.Q) : null,
+ parameters.DP != null ? Convert.ToBase64String(parameters.DP) : null,
+ parameters.DQ != null ? Convert.ToBase64String(parameters.DQ) : null,
+ parameters.InverseQ != null ? Convert.ToBase64String(parameters.InverseQ) : null,
+ parameters.D != null ? Convert.ToBase64String(parameters.D) : null);
+ }
+
+ #endregion
+ }
+}
diff --git a/MTSC/Server/Handlers/EncryptionHandler.cs b/MTSC/Server/Handlers/EncryptionHandler.cs
new file mode 100644
index 0000000..aad1f8f
--- /dev/null
+++ b/MTSC/Server/Handlers/EncryptionHandler.cs
@@ -0,0 +1,147 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Security.Cryptography;
+using System.Text;
+using MTSC;
+
+namespace MTSC.Server.Handlers
+{
+ ///
+ /// Handler that encrypts the communication.
+ ///
+ public class EncryptionHandler : IHandler
+ {
+ private enum ClientState
+ {
+ Initial,
+ Negotiating,
+ Encrypted
+ }
+ private struct AdditionalData
+ {
+ public byte[] Key;
+ public ClientState ClientState;
+ }
+ private Dictionary additionalData;
+ private RSA rsa;
+ private string privateKey, publicKey;
+ ///
+ /// Creates an instance of EncryptionHandler.
+ ///
+ /// Symmetrical algorithm to be used for end-to-end encryption.
+ public EncryptionHandler(RSA rsa)
+ {
+ additionalData = new Dictionary();
+ privateKey = rsa.ToXmlString(true);
+ publicKey = rsa.ToXmlString(false);
+ }
+
+ public void ClientRemoved(ClientStruct client)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool HandleClient(ClientStruct client)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool HandleMessage(ClientStruct client, Message message)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool PreHandleMessage(ClientStruct client, ref Message message)
+ {
+ if(additionalData[client].ClientState == ClientState.Encrypted)
+ {
+ /*
+ * Decrypt message before returning.
+ */
+ byte[] encryptedBytes = message.MessageBytes;
+ byte[] decryptedBytes = DecryptBytes(additionalData[client].Key, encryptedBytes);
+ message = new Message((uint)decryptedBytes.Length, decryptedBytes);
+ return false;
+ }
+ else
+ {
+ /*
+ * If the state of the client is not encrypted, there's nothing to decrypt.
+ */
+ return false;
+ }
+ }
+
+
+ private byte[] EncryptBytes(byte[] clientKey, byte[] bytesToBeEncrypted)
+ {
+ byte[] encryptedBytes = null;
+
+ // Set your salt here, change it to meet your flavor:
+ // The salt bytes must be at least 8 bytes.
+ byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
+
+ using (MemoryStream ms = new MemoryStream())
+ {
+ using (RijndaelManaged AES = new RijndaelManaged())
+ {
+ AES.KeySize = 256;
+ AES.BlockSize = 128;
+
+ AES.Mode = CipherMode.CBC;
+
+ var key = new Rfc2898DeriveBytes(clientKey, saltBytes, 1000);
+ AES.Key = key.GetBytes(AES.KeySize / 8);
+ AES.IV = key.GetBytes(AES.BlockSize / 8);
+
+
+
+ using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
+ {
+ cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
+ cs.Close();
+ }
+ encryptedBytes = ms.ToArray();
+ }
+ }
+
+ return encryptedBytes;
+ }
+
+ private byte[] DecryptBytes(byte[] clientKey, byte[] bytesToBeDecrypted)
+ {
+ byte[] decryptedBytes = null;
+
+ // Set your salt here, change it to meet your flavor:
+ // The salt bytes must be at least 8 bytes.
+ byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
+
+ using (MemoryStream ms = new MemoryStream())
+ {
+ using (RijndaelManaged AES = new RijndaelManaged())
+ {
+ AES.KeySize = 256;
+ AES.BlockSize = 128;
+
+ AES.Mode = CipherMode.CBC;
+
+ var key = new Rfc2898DeriveBytes(clientKey, saltBytes, 1000);
+ AES.Key = key.GetBytes(AES.KeySize / 8);
+ AES.IV = key.GetBytes(AES.BlockSize / 8);
+
+
+
+ using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
+ {
+ cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
+ cs.Close();
+ }
+ decryptedBytes = ms.ToArray();
+ }
+ }
+
+ return decryptedBytes;
+ }
+ }
+}
diff --git a/MTSC/Server/Handlers/IHandler.cs b/MTSC/Server/Handlers/IHandler.cs
index cc7b950..2196c51 100644
--- a/MTSC/Server/Handlers/IHandler.cs
+++ b/MTSC/Server/Handlers/IHandler.cs
@@ -23,7 +23,7 @@ namespace MTSC.Server.Handlers
/// Client structure.
/// Message to be preprocessed.
/// True if the message has been preprocessed and no other handler should handle it anymore.
- bool PreHandleMessage(ClientStruct client, out Message message);
+ bool PreHandleMessage(ClientStruct client, ref Message message);
///
/// Handles the received message.
///
diff --git a/MTSC/Server/Server.cs b/MTSC/Server/Server.cs
index e5b85f9..189efd2 100644
--- a/MTSC/Server/Server.cs
+++ b/MTSC/Server/Server.cs
@@ -134,7 +134,7 @@ namespace MTSC.Server
}
foreach (IHandler handler in handlers)
{
- if (handler.PreHandleMessage(client, out message))
+ if (handler.PreHandleMessage(client, ref message))
{
break;
}