ReceiveEvent HandleNetworkEvent() { ReceiveEvent e; using( BitReader reader = GetPooledReader() ) { DataStream stream = reader.GetStream(); int connectionId; ChannelIndex channel; e = PollReceive( stream, out connectionId, out channel ); if( e == ReceiveEvent.Error || e == ReceiveEvent.NoMoreEvents ) { return e; } // on the server, the connectionId is one to one with the client index NodeIndex client = new NodeIndex( (uint)connectionId ); switch( e ) { case ReceiveEvent.Connect: //_profiler.RecordEvent(TickType.Receive, (uint)receivedSize, MessageManager.reverseChannels[channelId], "TRANSPORT_CONNECT"); Log.Info( $"Client {client.GetClientIndex()} sent initial connection packet" ); ClientConnection c; if( _commonSettings.enableEncryption ) { // This client is required to complete the crypto-hail exchange. EllipticDiffieHellman keyExchange = SendCryptoHail(); c = new ClientConnection( client, ClientConnection.Status.PendingConnectionChallengeResponse, keyExchange ); } else { c = new ClientConnection( client, ClientConnection.Status.PendingConnectionRequest, null ); } _connection.Add( c.GetIndex(), c ); // TODO: cozeroff NO, NO, NO, check for timeouts of clients manually //StartCoroutine(ApprovalTimeout(clientId)); break; case ReceiveEvent.Message: HandleMessage( reader, client, channel ); break; case ReceiveEvent.Disconnect: //_profiler.RecordEvent(TickType.Receive, 0, "NONE", "TRANSPORT_DISCONNECT"); Log.Info( "Disconnect Event From " + client.GetClientIndex() ); // TODO: cozeroff //OnClientDisconnectServer( client ); if( _onClientDisconnect != null ) { _onClientDisconnect.Invoke( client ); } break; } } return e; }
public ClientConnection( NodeIndex id, Status state, EllipticDiffieHellman keyExchange ) { _id = id; _state = state; _keyExchange = keyExchange; _sharedSecretKey = null; _playerAvatar = null; _ownedEntity = new EntitySet( AlpacaConstant.CLIENT_OWNER_LIMIT ); }
// Runs on client internal static void HandleHailRequest(uint clientId, Stream stream, int channelId) { X509Certificate2 certificate = null; byte[] serverDiffieHellmanPublicPart = null; using (PooledBitReader reader = PooledBitReader.Get(stream)) { if (netManager.NetworkConfig.EnableEncryption) { // Read the certificate if (netManager.NetworkConfig.SignKeyExchange) { // Allocation justification: This runs on client and only once, at initial connection certificate = new X509Certificate2(reader.ReadByteArray()); if (CryptographyHelper.VerifyCertificate(certificate, netManager.ConnectedHostname)) { // The certificate is not valid :( // Man in the middle. if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Invalid certificate. Disconnecting"); } } netManager.StopClient(); return; } else { netManager.NetworkConfig.ServerX509Certificate = certificate; } } // Read the ECDH // Allocation justification: This runs on client and only once, at initial connection serverDiffieHellmanPublicPart = reader.ReadByteArray(); // Verify the key exchange if (netManager.NetworkConfig.SignKeyExchange) { byte[] serverDiffieHellmanPublicPartSignature = reader.ReadByteArray(); RSACryptoServiceProvider rsa = certificate.PublicKey.Key as RSACryptoServiceProvider; if (rsa != null) { using (SHA256Managed sha = new SHA256Managed()) { if (!rsa.VerifyData(serverDiffieHellmanPublicPart, sha, serverDiffieHellmanPublicPartSignature)) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Invalid signature. Disconnecting"); } } netManager.StopClient(); return; } } } } } } using (PooledBitStream outStream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(outStream)) { if (netManager.NetworkConfig.EnableEncryption) { // Create a ECDH key EllipticDiffieHellman diffieHellman = new EllipticDiffieHellman(EllipticDiffieHellman.DEFAULT_CURVE, EllipticDiffieHellman.DEFAULT_GENERATOR, EllipticDiffieHellman.DEFAULT_ORDER); netManager.clientAesKey = diffieHellman.GetSharedSecret(serverDiffieHellmanPublicPart); byte[] diffieHellmanPublicKey = diffieHellman.GetPublicKey(); writer.WriteByteArray(diffieHellmanPublicKey); if (netManager.NetworkConfig.SignKeyExchange) { RSACryptoServiceProvider rsa = certificate.PublicKey.Key as RSACryptoServiceProvider; if (rsa != null) { using (SHA256CryptoServiceProvider sha = new SHA256CryptoServiceProvider()) { writer.WriteByteArray(rsa.Encrypt(sha.ComputeHash(diffieHellmanPublicKey), false)); } } else { throw new CryptographicException("[MLAPI] Only RSA certificates are supported. No valid RSA key was found"); } } } } // Send HailResponse InternalMessageHandler.Send(NetworkingManager.Singleton.ServerClientId, MLAPIConstants.MLAPI_CERTIFICATE_HAIL_RESPONSE, "MLAPI_INTERNAL", outStream, SecuritySendFlags.None, true); } }
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)); }