// 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, uint clientId, out byte messageType) { using (PooledBitReader inputHeaderReader = PooledBitReader.Get(inputStream)) { try { if (inputStream.Length < 1) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogError("The incomming message was too small"); } messageType = MLAPIConstants.INVALID; return(null); } bool isEncrypted = inputHeaderReader.ReadBit(); bool isAuthenticated = inputHeaderReader.ReadBit(); #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); for (int i = 0; i < computedHmac.Length; i++) { if (computedHmac[i] != HMAC_BUFFER[i]) { if (LogHelper.CurrentLogLevel <= LogLevel.Error) { LogHelper.LogError("Received HMAC at position [" + i + "] 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 incomming 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 incomming 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()); } messageType = MLAPIConstants.INVALID; return(null); } } }