예제 #1
0
        // 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);
                }
            }
        }