// Ran on server internal static void HandleHailResponse(ulong clientId, Stream stream) { if (!NetworkingManager.Singleton.PendingClients.ContainsKey(clientId) || NetworkingManager.Singleton.PendingClients[clientId].ConnectionState != PendingClient.State.PendingHail) { return; } if (!NetworkingManager.Singleton.NetworkConfig.EnableEncryption) { return; } using (PooledBitReader reader = PooledBitReader.Get(stream)) { if (NetworkingManager.Singleton.PendingClients[clientId].KeyExchange != null) { byte[] diffieHellmanPublic = reader.ReadByteArray(); NetworkingManager.Singleton.PendingClients[clientId].AesKey = NetworkingManager.Singleton.PendingClients[clientId].KeyExchange.GetSharedSecret(diffieHellmanPublic); if (NetworkingManager.Singleton.NetworkConfig.SignKeyExchange) { byte[] diffieHellmanPublicSignature = reader.ReadByteArray(); X509Certificate2 certificate = NetworkingManager.Singleton.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 (!CryptographyHelper.ConstTimeArrayEqual(clientHash, serverHash)) { //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"); } } NetworkingManager.Singleton.DisconnectClient(clientId); return; } } } else { throw new CryptographicException("[MLAPI] Only RSA certificates are supported. No valid RSA key was found"); } } } } NetworkingManager.Singleton.PendingClients[clientId].ConnectionState = PendingClient.State.PendingConnection; NetworkingManager.Singleton.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. } InternalMessageSender.Send(clientId, MLAPIConstants.MLAPI_GREETINGS, "MLAPI_INTERNAL", outStream, SecuritySendFlags.None, null, true); } }
// This method is responsible for unwrapping a message, that is extracting the messagebody. // Could include decrypting and/or authentication. internal static BitStream UnwrapMessage(BitStream inputStream, ulong clientId, out byte messageType, out SecuritySendFlags security) { using (PooledBitReader inputHeaderReader = PooledBitReader.Get(inputStream)) { try { if (inputStream.Length < 1) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogError("The incoming message was too small"); } messageType = MLAPIConstants.INVALID; security = SecuritySendFlags.None; return(null); } bool isEncrypted = inputHeaderReader.ReadBit(); bool isAuthenticated = inputHeaderReader.ReadBit(); if (isEncrypted && isAuthenticated) { security = SecuritySendFlags.Encrypted | SecuritySendFlags.Authenticated; } else if (isEncrypted) { security = SecuritySendFlags.Encrypted; } else if (isAuthenticated) { security = SecuritySendFlags.Authenticated; } else { security = SecuritySendFlags.None; } #if !DISABLE_CRYPTOGRAPHY if (isEncrypted || isAuthenticated) { if (!NetworkingManager.Singleton.NetworkConfig.EnableEncryption) { if (LogHelper.CurrentLogLevel <= LogLevel.Error) { LogHelper.LogError("Got a encrypted and/or authenticated message but key exchange (\"encryption\") was not enabled"); } messageType = MLAPIConstants.INVALID; return(null); } // Skip last bits in first byte inputHeaderReader.SkipPadBits(); if (isAuthenticated) { long hmacStartPos = inputStream.Position; int readHmacLength = inputStream.Read(HMAC_BUFFER, 0, HMAC_BUFFER.Length); if (readHmacLength != HMAC_BUFFER.Length) { if (LogHelper.CurrentLogLevel <= LogLevel.Error) { LogHelper.LogError("HMAC length was invalid"); } messageType = MLAPIConstants.INVALID; return(null); } // Now we have read the HMAC, we need to set the hmac in the input to 0s to perform the HMAC. inputStream.Position = hmacStartPos; inputStream.Write(HMAC_PLACEHOLDER, 0, HMAC_PLACEHOLDER.Length); byte[] key = NetworkingManager.Singleton.IsServer ? CryptographyHelper.GetClientKey(clientId) : CryptographyHelper.GetServerKey(); if (key == null) { if (LogHelper.CurrentLogLevel <= LogLevel.Error) { LogHelper.LogError("Failed to grab key"); } messageType = MLAPIConstants.INVALID; return(null); } using (HMACSHA256 hmac = new HMACSHA256(key)) { byte[] computedHmac = hmac.ComputeHash(inputStream.GetBuffer(), 0, (int)inputStream.Length); if (!CryptographyHelper.ConstTimeArrayEqual(computedHmac, HMAC_BUFFER)) { if (LogHelper.CurrentLogLevel <= LogLevel.Error) { LogHelper.LogError("Received HMAC did not match the computed HMAC"); } messageType = MLAPIConstants.INVALID; return(null); } } } if (isEncrypted) { int ivRead = inputStream.Read(IV_BUFFER, 0, IV_BUFFER.Length); if (ivRead != IV_BUFFER.Length) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogError("Invalid IV size"); } messageType = MLAPIConstants.INVALID; return(null); } PooledBitStream outputStream = PooledBitStream.Get(); using (RijndaelManaged rijndael = new RijndaelManaged()) { rijndael.IV = IV_BUFFER; rijndael.Padding = PaddingMode.PKCS7; byte[] key = NetworkingManager.Singleton.IsServer ? CryptographyHelper.GetClientKey(clientId) : CryptographyHelper.GetServerKey(); if (key == null) { if (LogHelper.CurrentLogLevel <= LogLevel.Error) { LogHelper.LogError("Failed to grab key"); } messageType = MLAPIConstants.INVALID; return(null); } rijndael.Key = key; using (CryptoStream cryptoStream = new CryptoStream(outputStream, rijndael.CreateDecryptor(), CryptoStreamMode.Write)) { cryptoStream.Write(inputStream.GetBuffer(), (int)inputStream.Position, (int)(inputStream.Length - inputStream.Position)); } outputStream.Position = 0; if (outputStream.Length == 0) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogError("The incoming message was too small"); } messageType = MLAPIConstants.INVALID; return(null); } int msgType = outputStream.ReadByte(); messageType = msgType == -1 ? MLAPIConstants.INVALID : (byte)msgType; } return(outputStream); } else { if (inputStream.Length - inputStream.Position <= 0) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogError("The incoming message was too small"); } messageType = MLAPIConstants.INVALID; return(null); } int msgType = inputStream.ReadByte(); messageType = msgType == -1 ? MLAPIConstants.INVALID : (byte)msgType; return(inputStream); } } else { #endif messageType = inputHeaderReader.ReadByteBits(6); // The input stream is now ready to be read from. It's "safe" and has the correct position return(inputStream); #if !DISABLE_CRYPTOGRAPHY } #endif } catch (Exception e) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogError("Error while unwrapping headers"); } if (LogHelper.CurrentLogLevel <= LogLevel.Error) { LogHelper.LogError(e.ToString()); } security = SecuritySendFlags.None; messageType = MLAPIConstants.INVALID; return(null); } } }