internal static void HandleClientRPCRequest(ulong clientId, Stream stream, string channelName, SecuritySendFlags security) { using (PooledBitReader reader = PooledBitReader.Get(stream)) { ulong networkId = reader.ReadUInt64Packed(); ushort behaviourId = reader.ReadUInt16Packed(); ulong hash = reader.ReadUInt64Packed(); ulong responseId = reader.ReadUInt64Packed(); if (SpawnManager.SpawnedObjects.ContainsKey(networkId)) { NetworkedBehaviour behaviour = SpawnManager.SpawnedObjects[networkId].GetBehaviourAtOrderIndex(behaviourId); if (behaviour != null) { object result = behaviour.OnRemoteClientRPC(hash, clientId, stream); using (PooledBitStream responseStream = PooledBitStream.Get()) { using (PooledBitWriter responseWriter = PooledBitWriter.Get(responseStream)) { responseWriter.WriteUInt64Packed(responseId); responseWriter.WriteObjectPacked(result); } InternalMessageHandler.Send(clientId, MLAPIConstants.MLAPI_CLIENT_RPC_RESPONSE, channelName, responseStream, security, null); } } } } }
// 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); } }
// Ran on server internal static void HandleHailResponse(uint clientId, Stream stream, int channelId) { if (!netManager.PendingClients.ContainsKey(clientId) || netManager.PendingClients[clientId].ConnectionState != PendingClient.State.PendingHail) { return; } if (!netManager.NetworkConfig.EnableEncryption) { return; } using (PooledBitReader reader = PooledBitReader.Get(stream)) { if (NetworkingManager.Singleton.PendingClients[clientId].KeyExchange != null) { byte[] diffieHellmanPublic = reader.ReadByteArray(); netManager.PendingClients[clientId].AesKey = netManager.PendingClients[clientId].KeyExchange.GetSharedSecret(diffieHellmanPublic); if (netManager.NetworkConfig.SignKeyExchange) { byte[] diffieHellmanPublicSignature = reader.ReadByteArray(); X509Certificate2 certificate = netManager.NetworkConfig.ServerX509Certificate; RSACryptoServiceProvider rsa = certificate.PrivateKey as RSACryptoServiceProvider; if (rsa != null) { using (SHA256Managed sha = new SHA256Managed()) { byte[] clientHash = rsa.Decrypt(diffieHellmanPublicSignature, false); byte[] serverHash = sha.ComputeHash(diffieHellmanPublic); if (clientHash.Length != serverHash.Length) { //Man in the middle. if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Signature length doesnt match for the key exchange public part. Disconnecting"); } } netManager.DisconnectClient(clientId); return; } for (int i = 0; i < clientHash.Length; i++) { if (clientHash[i] != serverHash[i]) { //Man in the middle. if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Signature doesnt match for the key exchange public part. Disconnecting"); } } netManager.DisconnectClient(clientId); return; } } } } else { throw new CryptographicException("[MLAPI] Only RSA certificates are supported. No valid RSA key was found"); } } } } netManager.PendingClients[clientId].ConnectionState = PendingClient.State.PendingConnection; netManager.PendingClients[clientId].KeyExchange = null; // Give to GC // Send greetings, they have passed all the handshakes using (PooledBitStream outStream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(outStream)) { writer.WriteInt64Packed(DateTime.Now.Ticks); // This serves no purpose. } InternalMessageHandler.Send(clientId, MLAPIConstants.MLAPI_GREETINGS, "MLAPI_INTERNAL", outStream, SecuritySendFlags.None, true); } }