/// <summary>
        /// Creates and returns a stream containing decrypted data.
        /// </summary>
        /// <param name="input">A stream containing encrypted data.</param>
        /// <returns>A stream with decrypted data.</returns>
        public override Stream Decrypt(Stream input)
        {
            // check on view whether it's session's packet
            if (input.ReadByte() == 0)
            {
                // continue the Security Session establishing
                Stream outputStream = this.EstablishSession(input, false);

                if (outputStream != null)
                {
                    GenuineThreadPool.QueueUserWorkItem(new WaitCallback(this.SendMessage), outputStream, false);
                }
                return(null);
            }

            lock (this)
            {
                GenuineChunkedStream output = new GenuineChunkedStream(false);
                byte[] sign = null;

                int signSize = 0;
                if (this.KeyedHashAlgorithm != null)
                {
                    signSize = this.KeyedHashAlgorithm.HashSize / 8;
                }

                // decrypt the content and fetch the sign
                if (this._decryptor != null)
                {
                    CryptoStream cryptoStream = new CryptoStream(new FinishReadingStream(output), this._decryptor, CryptoStreamMode.Write);
                    sign = GenuineUtility.CopyStreamToStreamExceptSign(input, cryptoStream, signSize);
                    cryptoStream.FlushFinalBlock();
                }
                else
                {
                    sign = GenuineUtility.CopyStreamToStreamExceptSign(input, output, signSize);
                }

                // check the sign
                if (this.KeyedHashAlgorithm != null)
                {
                    if (!ZeroProofAuthorizationUtility.CompareBuffers(sign, this.KeyedHashAlgorithm.ComputeHash(output), sign.Length))
                    {
                        throw GenuineExceptions.Get_Security_WrongSignature();
                    }
                    output.Position = 0;
                }

                output.ReleaseOnReadMode = true;
                return(output);
            }
        }