locked
Simple security audit (did I do it right?) RRS feed

  • Question

  • Given that I am trying to get into the security thinking, I have created a simple Server/Client solution which implements security. But given that I don't have experience and am no expert, I thought I'd ask if you could do some quick glance over the code and tell me if I'm missing anything or if it's completely wrong, etc. It's pretty simple, all in all. Since it uses certificates and so you can test it if you want and see the complete code including the user forms, etc, I have made the entire source available here.

    In order for the application to work, the CA needs to be trusted (self-signed), so it needs to be imported into trusted root certificates. With that out of the way, here is a small list of attacks I know and I'm trying to protect against:

    - Man-in-the-middle (woman-in-the-middle?)
    - Spoofing
    - Replay attacks
    - Unintentional forwarding (I can't remember what this is called--when A sends to B and B forwards to C so that C thinks A sent to C directly).

    So I used:

    - Client and server certificates
    - Client password
    - Asymmetric and symmetric encryption
    - Nonce
    - Session key

    Of course, I am no expert in C# either, so if you spot some mistakes or ways something can be done better, then I'm all ears. Also, I know that I hard-coded both the client password in plaintext in the client (this is to simulate someone actually entering the password so I can send it to the server; normally I would absolutely not do this) and the hashed client password in the server. I realize the server password should probably be stored in a safe database, but hey, this is just a toy app, and needing a database makes everything much harder.

    Code for server:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using System.Security.Cryptography;
    using System.Net.Sockets;
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    using System.IO;
    
    namespace Introduction_to_Computer_Security_Assignment
    {
    	public partial class Server : Form
    	{
    		private TcpListener Listener;
    		private Random Rng = new Random();
    		string ClientPass = "s/f05iCMRsoL1R0E8wjOkghD1ivu20LweXluaqRpeM3zPrwavrpoNUFByoVzDaQg8oX1yjgO5eCA3i/2s8L4wA==";
    
    		class InvalidCertErr : System.Exception { };
    		class InvalidClientPassErr : System.Exception { };
    		class ClientErr : System.Exception { };
    
    		public Server()
    		{
    			InitializeComponent();
    		}
    
    		private void ListenBtn_Click(object sender, EventArgs e)
    		{
    			if (Listener != null)
    			{
    				Listener.Stop();
    				Listener = null;
    				ListenBtn.Text = "Listen";
    				PortLbl.Enabled = true;
    				PortTxt.Enabled = true;
    				SpoofsGroupBox.Enabled = true;
    				SpoofNone.Enabled = true;
    				SpoofFake.Enabled = true;
    				SpoofUntrusted.Enabled = true;
    			}
    			else
    			{
    				ListenBtn.Text = "Stop";
    				PortLbl.Enabled = false;
    				PortTxt.Enabled = false;
    				SpoofsGroupBox.Enabled = false;
    				SpoofNone.Enabled = false;
    				SpoofFake.Enabled = false;
    				SpoofUntrusted.Enabled = false;
    				new System.Threading.Thread(new System.Threading.ThreadStart(ListenHandler)).Start();
    			}
    		}
    
    		private void ListenHandler()
    		{
    			X509Certificate2 MyCert = null;
    			if (SpoofNone.Checked)
    				MyCert = new X509Certificate2("Server.p12");
    			else if (SpoofUntrusted.Checked)
    				MyCert = new X509Certificate2("UntrustedServer.p12");
    			else if (SpoofFake.Checked)
    				MyCert = new X509Certificate2("FakeServer.p12");
    
    			var UTF8Encoder = new System.Text.UTF8Encoding();
    			Listener = new TcpListener(IPAddress.Parse("127.0.0.1"), Convert.ToInt32(PortTxt.Text));
    			Listener.Start();
    
    			try
    			{
    				for (;;)
    				{
    					using (var Client = Listener.AcceptTcpClient())
    					using (var NetworkStream = Client.GetStream())
    					using (var Reader = new BinaryReader(NetworkStream))
    					using (var Writer = new BinaryWriter(NetworkStream))
    					{
    						try
    						{
    							// Exchange certificates
    							Writer.Write(Convert.ToBase64String(MyCert.Export(X509ContentType.Cert)));
    							var ClientCert = new X509Certificate2(Convert.FromBase64String(Reader.ReadString()));
    
    							// Send nonce and session key
    							var KeyGen = new AesManaged();
    							KeyGen.GenerateKey();
    							using (var Asymmetric = new AsymmetricEncryption(MyCert, ClientCert, NetworkStream, ReplayType.None))
    							using (var Symmetric = new SymmetricEncryption(KeyGen.Key, Rng.Next(), NetworkStream, ReplayType.None))
    							{
    								Asymmetric.Send(Symmetric.GetNonce(), Symmetric.GetKey());
    
    								// Wait for client OK
    								string Response = Symmetric.Receive();
    								if (Response != "CertificateOK") throw new ClientErr();
    
    								// Authorize client
    								var Verifier = new X509Chain();
    								Verifier.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
    								if (!Verifier.Build(ClientCert)) throw new InvalidCertErr();
    								Symmetric.Send("CertificateOK");
    
    								// Validate client
    								var ClientPass = Symmetric.Receive();
    								if (ClientPass != this.ClientPass) throw new InvalidClientPassErr();
    								Symmetric.Send("PassOK");
    
    								// OK: Get data
    								var Data = Symmetric.Receive();
    								MessageBox.Show("Got message:\n\n" + Data, "Got message!", MessageBoxButtons.OK, MessageBoxIcon.Information);
    							}
    						}
    						catch (System.FormatException)
    						{
    							MessageBox.Show("Error! Provided raw text was not previously encrypted or damaged during transfer.", "Server Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    						}
    						catch (InvalidCertErr)
    						{
    							MessageBox.Show("Unable to validate client!\nReason: Client certificate not valid.", "Server Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    						}
    						catch (InvalidClientPassErr)
    						{
    							MessageBox.Show("Unable to validate client!\nReason: Client sent invalid password.", "Server Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    						}
    						catch (SymmetricEncryption.InvalidNonceErr err)
    						{
    							MessageBox.Show("Unable to validate client!\nReason: Invalid nonce received. Expected: " + err.Expected + ". Got: " + err.Got, "Server Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    						}
    						catch (SymmetricEncryption.InvalidMACErr)
    						{
    							MessageBox.Show("Invalid data received from client!\nReason: Calculated hash and MAC do not agree.", "Server Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    						}
    						catch (AsymmetricEncryption.InvalidMACErr)
    						{
    							MessageBox.Show("Invalid data received from client!\nReason: Calculated hash and MAC do not agree.", "Server Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    						}
    						catch (ClientErr)
    						{
    							MessageBox.Show("The client responded with an error.", "Server Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    						}
    						catch (EndOfStreamException)
    						{
    							MessageBox.Show("The client forcefully closed the connection.", "Server Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    						}
    					}
    				}
    			}
    			catch (System.Net.Sockets.SocketException ex)
    			{
    				if (ex.ErrorCode != 10004) // 10004 occurs while forecfully closing the listener socket
    					MessageBox.Show("An error occured while listening. The error code was: " + ex.ErrorCode, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    			}
    		}
    
    		private void Server_FormClosing(object sender, FormClosingEventArgs e)
    		{
    			if (Listener != null)
    				Listener.Stop();
    		}
    	}
    }

    Code for client:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using System.Security.Cryptography;
    using System.Security.Cryptography.X509Certificates;
    using System.IO;
    using System.Diagnostics;
    
    namespace Client
    {
    	public partial class Client : Form
    	{
    		string Pass = "w%esMDt5RIjaGbUAd4l5rrsSmMf%0YXMYb$Pxyir1vevIxS^P3fjTx8XmS@msoEgvgp6OJ6EganJ7SKcfy4*RAn2DLo7h#NUdy#TZIljwCN^0N3uQ3z6mYj7g@jEpP32&j";
    		string InvalidPass = "w%esMDt5RIjaGbUAd4l5rrsSmMf%0YXMYb$Pxyir1vevIxS^P3fjTx8XmS@msoEgvgp6OJ6EganJ7SKcfy4*RAn2DLo7h#NUdy#TZIljwCN^0N3uQ3z6mYj7g@jEpP32&k";
    
    		class InvalidCertErr : System.Exception { };
    		class InvalidClientPassErr : System.Exception { };
    		class InvalidMACErr : System.Exception { };
    		class ServerErr : System.Exception { };
    
    		class InvalidServerResponseErr : System.Exception
    		{
    			public InvalidServerResponseErr(string Got, string Expected)
    			{
    				this.Got = Got;
    				this.Expected = Expected;
    			}
    
    			public string Got;
    			public string Expected;
    		};
    
    		class InvalidNonceErr : System.Exception
    		{
    			public InvalidNonceErr(int Got, int Expected)
    			{
    				this.Got = Got;
    				this.Expected = Expected;
    			}
    
    			public int Got;
    			public int Expected;
    		};
    
    		public Client()
    		{
    			InitializeComponent();
    		}
    
    		private void SendBtn_Click(object sender, EventArgs e)
    		{
    			X509Certificate2 MyCert = null;
    			if (SpoofNone.Checked || SpoofInvalidPass.Checked || SpoofReplay1.Checked || SpoofReplay2.Checked)
    				MyCert = new X509Certificate2("Client.p12");
    			else if (SpoofUntrusted.Checked)
    				MyCert = new X509Certificate2("UntrustedClient.p12");
    			else if (SpoofFake.Checked)
    				MyCert = new X509Certificate2("FakeClient.p12");
    
    			try
    			{
    				var KeyEncoder = new System.Text.UTF8Encoding();
    
    				using (var Sender = new System.Net.Sockets.TcpClient(ServerTxt.Text, Convert.ToInt32(PortTxt.Text)))
    				using (var NetworkStream = Sender.GetStream())
    				using (var Writer = new System.IO.BinaryWriter(NetworkStream))
    				using (var Reader = new System.IO.BinaryReader(NetworkStream))
    				{
    					ReplayType ReplayType;
    					if (SpoofReplay1.Checked)
    						ReplayType = ReplayType.Phase1;
    					else if (SpoofReplay2.Checked)
    						ReplayType = ReplayType.Phase2;
    					else
    						ReplayType = ReplayType.None;
    
    					if (!SpoofReplay2.Checked)
    					{
    						// Exchange certificates
    						Writer.Write(Convert.ToBase64String(MyCert.Export(X509ContentType.Cert)));
    						var ServerCert = new X509Certificate2(Convert.FromBase64String(Reader.ReadString()));
    
    						// Key and nonce exchange
    						byte[] Key = null;
    						int Nonce = 0;
    						using (var Asymmetric = new AsymmetricEncryption(MyCert, ServerCert, NetworkStream, ReplayType))
    						{
    							Asymmetric.Receive(ref Nonce, ref Key);
    							using (var Symmetric = new SymmetricEncryption(Key, Nonce, NetworkStream, ReplayType))
    							{
    
    								// Authorize server
    								var Verifier = new X509Chain();
    								Verifier.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
    								if (!Verifier.Build(ServerCert)) throw new InvalidCertErr();
    								if (ServerCert.GetNameInfo(X509NameType.SimpleName, false) != ServerTxt.Text) throw new InvalidCertErr();
    								Symmetric.Send("CertificateOK");
    
    								// Wait for server OK
    								string Response = Symmetric.Receive();
    								if (Response != "CertificateOK") throw new ServerErr();
    
    								// Send password
    								string Hash = null;
    								if (SpoofInvalidPass.Checked)
    									Hash = Convert.ToBase64String(new SHA512CryptoServiceProvider().ComputeHash(new System.Text.UTF8Encoding().GetBytes(InvalidPass)));
    								else
    									Hash = Convert.ToBase64String(new SHA512CryptoServiceProvider().ComputeHash(new System.Text.UTF8Encoding().GetBytes(Pass)));
    								Symmetric.Send(Hash);
    
    								// Wait for server OK
    								Response = Symmetric.Receive();
    								if (Response != "PassOK") throw new InvalidServerResponseErr(Response, "PassOK");
    
    								// Data exchange
    								Symmetric.Send(RawDataTxt.Text);
    							}
    						}
    					}
    					else 
    					{
    						var Asymmetric = new AsymmetricEncryption(null, null, NetworkStream, ReplayType.Phase2);
    						var Symmetric = new SymmetricEncryption(null, 0, NetworkStream, ReplayType.Phase2);
    						Writer.Write(Convert.ToBase64String(MyCert.Export(X509ContentType.Cert))); // Send certificate
    						//Asymmetric.SendLoggedPacket(); // Send certificate
    						Symmetric.SendLoggedPacket(); // Certificate was OK
    						Symmetric.SendLoggedPacket(); // Send password
    						Symmetric.SendLoggedPacket(); // Send data
    					}
    				}
    			}
    			catch (InvalidCertErr)
    			{
    				MessageBox.Show("Unable to validate server!\nReason: Server certificate not valid.", "Client Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    			}
    			catch (InvalidClientPassErr)
    			{
    				MessageBox.Show("Unable to validate server!\nReason: Server sent invalid password.", "Client Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    			}
    			catch (InvalidNonceErr err)
    			{
    				MessageBox.Show("Unable to validate server!\nReason: Invalid nonce received. Expected: " + err.Expected + ". Got: " + err.Got, "Client Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    			}
    			catch (InvalidMACErr)
    			{
    				MessageBox.Show("Invalid data received from server!\nReason: Calculated hash and MAC do not agree.", "Client Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    			}
    			catch (InvalidServerResponseErr err)
    			{
    				MessageBox.Show("Unable to validate server!\nReason: Invalid server response. Expected: " + err.Expected + ". Got: " + err.Got, "Client Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    			}
    			catch (ServerErr)
    			{
    				MessageBox.Show("The server responded with an error.", "Client Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    			}
    			catch (EndOfStreamException)
    			{
    				MessageBox.Show("The server forcefully closed the connection.", "Client Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    			}
    		}
    	}
    }

    Code for asymmetric encryption:

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Security.Cryptography;
    using System.Security.Cryptography.X509Certificates;
    
    public class AsymmetricEncryption : IDisposable
    {
    	BinaryReader m_NetworkReader;
    	BinaryWriter m_NetworkWriter;
    	RSACryptoServiceProvider m_MyRSA;
    	RSACryptoServiceProvider m_TargetRSA;
    	X509Certificate2 m_MyCert;
    	X509Certificate2 m_TargetCert;
    	FileStream m_Logger;
    	BinaryReader m_LogReader;
    	BinaryWriter m_LogWriter;
    
    	public class InvalidMACErr : System.Exception { };
    
    	public AsymmetricEncryption(X509Certificate2 MyCertificate, X509Certificate2 TargetCertificate, Stream NetworkStream, ReplayType ReplayType)
    	{
    		m_NetworkReader = new BinaryReader(NetworkStream);
    		m_NetworkWriter = new BinaryWriter(NetworkStream);
    		if (ReplayType == ReplayType.Phase1)
    		{
    			m_Logger = new FileStream("AsymmetricPackets.log", FileMode.Create);
    			m_LogWriter = new BinaryWriter(m_Logger);
    		}
    		else if (ReplayType == ReplayType.Phase2)
    		{
    			m_Logger = new FileStream("AsymmetricPackets.log", FileMode.Open);
    			m_LogReader = new BinaryReader(m_Logger);
    			return;
    		}
    		m_MyCert = MyCertificate;
    		m_TargetCert = TargetCertificate;
    		m_MyRSA = (RSACryptoServiceProvider)m_MyCert.PrivateKey;
    		m_TargetRSA = (RSACryptoServiceProvider)m_TargetCert.PublicKey.Key;
    	}
    
    	public void Receive(ref int Nonce, ref byte[] Key)
    	{
    		byte[] BytesToDecrypt = Convert.FromBase64String(m_NetworkReader.ReadString());
    		var BytesToDecryptSlice = new byte[m_MyRSA.KeySize / 8];
    		var DecryptedData = new List<byte>();
    
    		for (int i = 0; i < BytesToDecrypt.Length; i += BytesToDecryptSlice.Length)
    		{
    			Array.Copy(BytesToDecrypt, i, BytesToDecryptSlice, 0, BytesToDecryptSlice.Length);
    			DecryptedData.AddRange(m_MyRSA.Decrypt(BytesToDecryptSlice, true));
    		}
    
    		using (var MemoryStream = new MemoryStream(DecryptedData.ToArray()))
    		using (var MemoryReader = new BinaryReader(MemoryStream))
    		{
    			Nonce = MemoryReader.ReadInt32();
    			Key = Convert.FromBase64String(MemoryReader.ReadString());
    			var ReceiverKey = MemoryReader.ReadString();
    			int PlaintextLength = (int)MemoryStream.Position;
    			var Signature = MemoryReader.ReadString();
    
    			if (ReceiverKey != this.m_MyCert.PublicKey.Key.ToXmlString(false))
    				throw new InvalidMACErr();
    
    			var Plaintext = new List<byte>(MemoryStream.ToArray()).GetRange(0, PlaintextLength);
    			if (!m_TargetRSA.VerifyData(Plaintext.ToArray(), "SHA1", Convert.FromBase64String(Signature)))
    				throw new InvalidMACErr();
    		}
    	}
    
    	public void Send(int Nonce, byte[] Key)
    	{
    		using (var MemoryStream = new MemoryStream())
    		using (var MemoryWriter = new BinaryWriter(MemoryStream))
    		{
    			MemoryWriter.Write(Nonce); // Write nonce
    			MemoryWriter.Write(Convert.ToBase64String(Key)); // Write payload
    			MemoryWriter.Write(m_TargetCert.PublicKey.Key.ToXmlString(false)); // Write receiver's public key
    			MemoryWriter.Write(Convert.ToBase64String(m_MyRSA.SignData(MemoryStream.ToArray(), "SHA1"))); // Write digital signature
    
    			// Encrypt packet
    			const int NumBytesToEncrypt = 400;
    			List<byte> BytesToEncrypt = new List<byte>(MemoryStream.ToArray());
    			var BytesToEncryptSlice = new List<byte>();
    			var EncryptedData = new List<byte>();
    
    			for (int i = 0; i < BytesToEncrypt.Count; i += NumBytesToEncrypt)
    			{
    				var BytesLeft = Math.Min(BytesToEncrypt.Count - i, NumBytesToEncrypt);
    				BytesToEncryptSlice.AddRange(BytesToEncrypt.GetRange(i, BytesLeft));
    				EncryptedData.AddRange(m_TargetRSA.Encrypt(BytesToEncryptSlice.ToArray(), true));
    				BytesToEncryptSlice.Clear();
    			}
    
    			// Send packet
    			var Data = Convert.ToBase64String(EncryptedData.ToArray());
    			m_NetworkWriter.Write(Data);
    			if (m_Logger != null)
    				m_LogWriter.Write(Data);
    		}
    	}
    
    	public void SendLoggedPacket()
    	{
    		Debug.Assert(m_Logger != null);
    		var Data = m_LogReader.ReadString();
    		m_NetworkWriter.Write(Data);
    	}
    
    	public void Dispose()
    	{
    		if (m_Logger != null) m_Logger.Dispose();
    		if (m_NetworkReader != null) m_NetworkReader.Dispose();
    		if (m_NetworkWriter != null) m_NetworkWriter.Dispose();
    		if (m_LogReader != null) m_LogReader.Dispose();
    		if (m_LogWriter != null) m_LogWriter.Dispose();
    		//if (m_MyRSA != null) m_MyRSA.Dispose();
    		//if (m_TargetRSA != null) m_TargetRSA.Dispose();
    	}
    }

    Code for symmetric encryption:

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Security.Cryptography;
    using System.Security.Cryptography.X509Certificates;
    
    public enum ReplayType
    {
    	None,
    	Phase1,
    	Phase2
    }
    
    public class SymmetricEncryption : IDisposable
    {
    	BinaryReader m_NetworkReader;
    	BinaryWriter m_NetworkWriter;
    	BinaryReader m_LogReader;
    	BinaryWriter m_LogWriter;
    	HMACSHA512 m_Hasher;
    	FileStream m_Logger;
    	byte[] m_Key;
    	int m_Nonce;
    
    	public class InvalidMACErr : System.Exception { };
    	public class InvalidNonceErr : System.Exception
    	{
    		public InvalidNonceErr(int Got, int Expected)
    		{
    			this.Got = Got;
    			this.Expected = Expected;
    		}
    
    		public int Got;
    		public int Expected;
    	};
    
    	public SymmetricEncryption(byte[] Key, int InitialNonce, Stream NetworkStream, ReplayType ReplayType)
    	{
    		m_NetworkReader = new BinaryReader(NetworkStream);
    		m_NetworkWriter = new BinaryWriter(NetworkStream);
    		m_Key = Key;
    		m_Nonce = InitialNonce;
    		m_Hasher = new HMACSHA512(Key);
    		if (ReplayType == ReplayType.Phase1)
    		{
    			m_Logger = new FileStream("SymmetricPackets.log", FileMode.Create);
    			m_LogWriter = new BinaryWriter(m_Logger);
    		}
    		else if (ReplayType == ReplayType.Phase2)
    		{
    			m_Logger = new FileStream("SymmetricPackets.log", FileMode.Open);
    			m_LogReader = new BinaryReader(m_Logger);
    		}
    	}
    
    	public int GetNonce() { return m_Nonce; }
    	public byte[] GetKey() { return m_Key; }
    
    	public string Receive()
    	{
    		var DecryptedData = Encryptamajig.AesEncryptamajig.Decrypt(m_NetworkReader.ReadString(), Convert.ToBase64String(m_Key));
    		using (var DataStream = new System.IO.MemoryStream(Convert.FromBase64String(DecryptedData)))
    		using (var DataReader = new System.IO.BinaryReader(DataStream))
    		{
    			int Nonce = DataReader.ReadInt32();
    			var Data = DataReader.ReadString();
    			string MAC = DataReader.ReadString();
    			string CalculatedMAC = Convert.ToBase64String(m_Hasher.ComputeHash(new System.Text.UTF8Encoding().GetBytes(Data)));
    			if (Nonce != m_Nonce) throw new InvalidNonceErr(Nonce, m_Nonce);
    			if (MAC != CalculatedMAC) throw new InvalidMACErr();
    			m_Nonce++;
    			return Data;
    		}
    	}
    
    	public void Send(string DataToSend)
    	{
    		var MemoryStream = new MemoryStream();
    		using (var MemoryWriter = new BinaryWriter(MemoryStream))
    		{
    			MemoryWriter.Write(m_Nonce++);
    			MemoryWriter.Write(DataToSend);
    			MemoryWriter.Write(Convert.ToBase64String(m_Hasher.ComputeHash(new System.Text.UTF8Encoding().GetBytes(DataToSend))));
    			var EncryptedData = Encryptamajig.AesEncryptamajig.Encrypt(Convert.ToBase64String(MemoryStream.ToArray()), Convert.ToBase64String(m_Key));
    			m_NetworkWriter.Write(EncryptedData);
    			if (m_Logger != null)
    				m_LogWriter.Write(EncryptedData);
    		}
    	}
    
    	public void SendLoggedPacket()
    	{
    		Debug.Assert(m_Logger != null);
    		var Data = m_LogReader.ReadString();
    		m_NetworkWriter.Write(Data);
    	}
    
    	public void Dispose()
    	{
    		if (m_Logger != null) m_Logger.Dispose();
    		if (m_NetworkReader != null) m_NetworkReader.Dispose();
    		if (m_NetworkWriter != null) m_NetworkWriter.Dispose();
    		if (m_LogReader != null) m_LogReader.Dispose();
    		if (m_LogWriter != null) m_LogWriter.Dispose();
    		if (m_Hasher != null) m_Hasher.Dispose();
    		m_Key = null;
    	}
    }

    3rd party library encryptor/decryptor for symmetric encryption that I'm using (why did they make it so difficult?):

    namespace Encryptamajig
    {
    	using System;
    	using System.IO;
    	using System.Linq;
    	using System.Security.Cryptography;
    
    	/// <summary>
    	/// A simple wrapper to the AesManaged class and the AES algorithm.
    	/// Requires a securely stored key which should be a random string of characters that an attacker could never guess.
    	/// Make sure to save the Key if you want to decrypt your data later!
    	/// If you're using this with a Web app, put the key in the web.config and encrypt the web.config.
    	/// </summary>
    	public class AesEncryptamajig
    	{
    		private static readonly int _saltSize = 32;
    
    		/// <summary>
    		/// Encrypts the plainText input using the given Key.
    		/// A 128 bit random salt will be generated and prepended to the ciphertext before it is base64 encoded.
    		/// </summary>
    		/// <param name="plainText">The plain text to encrypt.</param>
    		/// <param name="key">The plain text encryption key.</param>
    		/// <returns>The salt and the ciphertext, Base64 encoded for convenience.</returns>
    		public static string Encrypt(string plainText, string key)
    		{
    			if (string.IsNullOrEmpty(plainText))
    				throw new ArgumentNullException("plainText");
    			if (string.IsNullOrEmpty(key))
    				throw new ArgumentNullException("key");
    
    			// Derive a new Salt and IV from the Key
    			using (var keyDerivationFunction = new Rfc2898DeriveBytes(key, _saltSize))
    			{
    				var saltBytes = keyDerivationFunction.Salt;
    				var keyBytes = keyDerivationFunction.GetBytes(32);
    				var ivBytes = keyDerivationFunction.GetBytes(16);
    
    				// Create an encryptor to perform the stream transform.
    				// Create the streams used for encryption.
    				using (var aesManaged = new AesManaged())
    				using (var encryptor = aesManaged.CreateEncryptor(keyBytes, ivBytes))
    				using (var memoryStream = new MemoryStream())
    				{
    					using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
    					using (var streamWriter = new StreamWriter(cryptoStream))
    					{
    						// Send the data through the StreamWriter, through the CryptoStream, to the underlying MemoryStream
    						streamWriter.Write(plainText);
    					}
    
    					// Return the encrypted bytes from the memory stream, in Base64 form so we can send it right to a database (if we want).
    					var cipherTextBytes = memoryStream.ToArray();
    					Array.Resize(ref saltBytes, saltBytes.Length + cipherTextBytes.Length);
    					Array.Copy(cipherTextBytes, 0, saltBytes, _saltSize, cipherTextBytes.Length);
    
    					return Convert.ToBase64String(saltBytes);
    				}
    			}
    		}
    
    		/// <summary>
    		/// Decrypts the ciphertext using the Key.
    		/// </summary>
    		/// <param name="ciphertext">The ciphertext to decrypt.</param>
    		/// <param name="key">The plain text encryption key.</param>
    		/// <returns>The decrypted text.</returns>
    		public static string Decrypt(string ciphertext, string key)
    		{
    			if (string.IsNullOrEmpty(ciphertext))
    				throw new ArgumentNullException("cipherText");
    			if (string.IsNullOrEmpty(key))
    				throw new ArgumentNullException("key");
    
    			// Extract the salt from our ciphertext
    			var allTheBytes = Convert.FromBase64String(ciphertext);
    			var saltBytes = allTheBytes.Take(_saltSize).ToArray();
    			var ciphertextBytes = allTheBytes.Skip(_saltSize).Take(allTheBytes.Length - _saltSize).ToArray();
    
    			using (var keyDerivationFunction = new Rfc2898DeriveBytes(key, saltBytes))
    			{
    				// Derive the previous IV from the Key and Salt
    				var keyBytes = keyDerivationFunction.GetBytes(32);
    				var ivBytes = keyDerivationFunction.GetBytes(16);
    
    				// Create a decrytor to perform the stream transform.
    				// Create the streams used for decryption.
    				// The default Cipher Mode is CBC and the Padding is PKCS7 which are both good
    				using (var aesManaged = new AesManaged())
    				using (var decryptor = aesManaged.CreateDecryptor(keyBytes, ivBytes))
    				using (var memoryStream = new MemoryStream(ciphertextBytes))
    				using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
    				using (var streamReader = new StreamReader(cryptoStream))
    				{
    					// Return the decrypted bytes from the decrypting stream.
    					return streamReader.ReadToEnd();
    				}
    			}
    		}
    	}
    }

    And that's it. I appreciate if someone takes a quick look and can offer their opinions on what's good and what is not and how it can be improved.


    Monday, May 5, 2014 12:16 AM