static RandomProvider() { psuedoRandom = new SystemRandomProvider(); cryptographicRandom = new CryptoRandomProvider(); SelectedRandomizationType = RandomizationType.Pseudorandom; }
public static void Main(string[] args) { // Create a client session manager and allow sessions to remain valid for up to 5 minutes of inactivity (300 seconds) SessionManager manager = new SessionManager(300 * TimeSpan.TicksPerSecond, 20); SetConsoleCtrlHandler(i => { db.Flush(); // Ensures that the database is flushed before the program exits return(false); }, true); // Create a secure random provider and start getting RSA stuff CryptoRandomProvider random = new CryptoRandomProvider(); Task <RSA> t = new Task <RSA>(() => { RSA rsa = new RSA(Resources.e_0x100, Resources.n_0x100, Resources.d_0x100); if (rsa == null) { Output.Fatal("No RSA keys found! Server identity will not be verifiable!"); Output.Info("Generating session-specific RSA-keys..."); rsa = new RSA(128, 8, 7, 5); rsa.Save("0x100"); Output.Info("Done!"); } return(rsa); }); t.Start(); // Local methods to simplify common operations bool ParseDataPair(string cmd, out string user, out string pass) { int idx = cmd.IndexOf(':'); user = ""; pass = ""; if (idx == -1) { return(false); } user = cmd.Substring(0, idx); try { user = user.FromBase64String(); pass = cmd.Substring(idx + 1).FromBase64String(); } catch { Output.Error($"Recieved problematic username or password! (User: \"{user}\")"); return(false); } return(true); } int ParseDataSet(string cmd, out string[] data) { List <string> gen = new List <string>(); int idx; while ((idx = cmd.IndexOf(':')) != -1) { try { gen.Add(cmd.Substring(0, idx).FromBase64String()); } catch { data = null; return(-1); // Hard error } cmd = cmd.Substring(idx + 1); } try { gen.Add(cmd.FromBase64String()); } catch { data = null; return(-1); // Hard error } data = gen.ToArray(); return(gen.Count); } string[] ParseCommand(string cmd, out long id) { int idx = cmd.IndexOf(':'), idx1; string sub; if (idx == -1 || !(sub = cmd.Substring(idx + 1)).Contains(':') || !long.TryParse(sub.Substring(0, idx1 = sub.IndexOf(':')), out id)) { id = 0; return(null); } return(new string[] { cmd.Substring(0, idx), sub.Substring(idx1 + 1) }); } string GenerateResponse(long id, dynamic d) => id + ":" + d.ToString(); string ErrorResponse(long id, string i18n = null) => GenerateResponse(id, $"ERROR{(i18n==null?"":":"+VERBOSE_RESPONSE)}{i18n??""}"); bool GetUser(string sid, out Database.User user) { user = manager.GetUser(sid); bool exists = user != null; if (exists) { user = db.GetUser(user.Name); } return(exists && user != null); } bool GetAccount(string name, Database.User user, out Database.Account acc) { acc = user.accounts.FirstOrDefault(a => a.name.Equals(name)); return(acc != null); } // Create server NetServer server = new NetServer( EllipticDiffieHellman.Curve25519(EllipticDiffieHellman.Curve25519_GeneratePrivate(random)), 80, (string r, Dictionary <string, string> associations, ref bool s) => { string[] cmd = ParseCommand(r, out long id); // Handle corrupt or badly formatted messages from client if (cmd == null) { return(ErrorResponse(-1, "corrupt")); } // Server endpoints switch (cmd[0]) { case "RmUsr": { if (!GetUser(cmd[1], out var user)) { if (verbosity > 0) { Output.Error($"Could not delete user from session as session isn't valid. (SessionID=\"{cmd[1]}\")"); } return(ErrorResponse(id, "badsession")); } manager.Expire(user); db.RemoveUser(user); if (verbosity > 0) { Output.Info($"Removed user \"{user.Name}\" (SessionID={cmd[1]})"); } return(GenerateResponse(id, true)); } case "Auth": // Log in to a user account (get a session id) { if (!ParseDataPair(cmd[1], out string user, out string pass)) { if (verbosity > 0) { Output.Error($"Recieved problematic username or password! (User: \"{user}\")"); } return(ErrorResponse(id)); } Database.User usr = db.GetUser(user); if (usr == null || !usr.Authenticate(pass)) { if (verbosity > 0) { Output.Error("Authentcation failure for user: "******"ERROR"); Output.Positive("Authentication success for user: "******"\nSession: " + sess); associations["session"] = sess; return(GenerateResponse(id, sess)); }
static void Main(string[] args) { Console.Write("Enter your personal password: "******"Generating authentication code..."); // Generate a password-based Message Authentication Code (with salt to prevent rainbowtables) to verify the user identity against the server // This string will be used as our database key: a (pseudo-)unique identifier which, for all intents and purposes, can be as public as we like since it doesn't do an attacker any good to know it string auth = Support.ToHexString(KDF.PBKDF2(KDF.HMAC_SHA1, key.ToUTF8Bytes(), "NoRainbow".ToUTF8Bytes(), 8192, 128)); // Create private encryption/decryption algorithm for processing private (encrypted) data stored on server Rijndael128 privCrypt = new Rijndael128(key); RandomProvider provider = new CryptoRandomProvider(); bool connected = false; bool load = false; // AES key used for communication is randomly chosen by generating anywhere between 1 and 511 random bytes as the password for PBKDF2-HMAC-SHA1 NetClient client = new NetClient(new Rijndael128(provider.GetBytes(provider.NextInt(511) + 1).ToUTF8String()), ParseIP(), ParsePort(), (string message, out bool keepAlive) => { if (message.StartsWith("M-")) { // Handle a blank response if (message.Length == 2) { Console.WriteLine("No messages exist with your password"); } else { string[] msgs = null; try { msgs = Support.DeserializeString(message.Substring(2)); } catch (Exception) { Console.WriteLine("The server seems to have sent an incorrect message. The stored messages could not be read!"); } foreach (var cmsg in msgs) { try { // Decrypt each message with the supplied decryptor byte[] messages_pad = privCrypt.Decrypt(Convert.FromBase64String(cmsg)); int len = Support.ReadInt(messages_pad, 0); string messages = messages_pad.SubArray(4, len + 4).ToUTF8String(); Console.WriteLine(messages); } catch (Exception) { /* Ignore corrupt message (maybe do something else here?) */ } } Console.WriteLine("\nPress any key to continue..."); Console.ReadKey(); Console.Clear(); } load = false; } // Tell the client object to keep the connection alive keepAlive = true; // Don't respond return(null); }, cli => { Console.WriteLine("Connected to server!"); connected = true; }); // Server-connection-attempt loop (mostly just UI/UX stuff) do { try { Console.WriteLine("Connecting to server..."); client.Connect(); // <----- Only important line in this entire loop break; } catch (Exception) { Console.Write("The server rejected the connection (probably because the server isn't running). Try again? (Y/n): "); if (Console.In.ReadYNBool("n")) { Console.WriteLine("OK. Exiting..."); Thread.Sleep(2500); Environment.Exit(0); } Console.Clear(); } } while (true); while (!connected) { Thread.Sleep(125); } Console.WriteLine(); bool alive = true; while (alive) { // Show selection menu switch (DoSelect()) { case 1: { // Get and send a message to the server Console.Clear(); Console.Write("Message to send to server: "); string message = Console.ReadLine(); if (message.Length == 0) { message = "\0"; // Blank messages are parsed as a null byte } // Encrypt the message with our personal AES object (which hopefully only we know) byte[] toSend = privCrypt.Encrypt(NetSupport.WithHeader(message.ToUTF8Bytes())); // Send to the server if (!client.TrySend("S-" + auth + "-" + Convert.ToBase64String(toSend))) { Console.WriteLine("Unfortunately, an error ocurred when attempting to send your message to the server :("); } break; } case 2: { // Send a request to the server for a list of all messages associated with the hex key we generated in the beginning Console.Clear(); Console.WriteLine("Loading messages..."); // Send the "Load" command along with our db key if (!client.TrySend("L-" + auth)) { Console.WriteLine("Unfortunately, an error ocurred when attempting to send your message to the server :("); } load = true; while (load) { Thread.Sleep(125); } break; } case 3: Console.WriteLine("Exiting..."); // Await client disconnection try { client.Disconnect(); } catch (Exception) { } // Stop program Environment.Exit(0); break; } } while (client.IsAlive) { Thread.Sleep(250); } }