/// <summary>
        ///     Connects the <see cref="Client"/> asynchronously to the specified TCP port on the specified host.
        /// </summary>
        /// <param name="host">The name or IP address of the host.</param>
        /// <param name="port">The port to connect to.</param>
        /// <param name="submitCryptographicData">Set to "true" to use <see cref="Aes"/> encryption for all communications.</param>
        /// <remarks>Using <paramref name="submitCryptographicData" /> requires the <see cref="Client"/> to have valid <see cref="CryptographicData"/> set.</remarks>
        /// <returns>Returns a <see cref="Task"/> that represents the asynchronous connect operation.</returns>
        public async Task ConnectAsync(string host, int port, bool submitCryptographicData)
        {
            await client.ConnectAsync(host, port);

            reader = new StreamReader(client.GetStream());
            writer = new StreamWriter(client.GetStream());
            if (submitCryptographicData && CryptographicData.IsValid())
            {
                var @params = await ReadAsync <RSAParameters>();

                var encrypted = CryptographyProvider.Instance.RSAEncrypt(JsonConvert.SerializeObject(CryptographicData), @params);
                await WriteRawAsync(encrypted);

                SessionId = Guid.Parse(await ReadAsync());
            }
            OnConnected(this, new ConnectedEventArgs());
        }
        /// <summary>
        ///     Writes a line of characters asynchronously to the stream.
        /// </summary>
        /// <param name="s">The string to write to the stream.</param>
        /// <returns>Returns a <see cref="Task"/> that represents the asynchronous write operation.</returns>
        /// <remarks>The written string will be encrypted if the <see cref="Client"/> has valid <see cref="CryptographicData"/>.<para>Use the <see cref="WriteRawAsync"/> function to always write data unhandled.</para></remarks>
        public async Task WriteAsync(string s)
        {
            if (CryptographicData == null || !CryptographicData.IsValid())
            {
                await WriteRawAsync(s);
            }
            else
            {
                var encrypted = await CryptographyProvider.Instance.AesEncryptAsync(s, CryptographicData);

                var signature = CryptographyProvider.Instance.HmacCreateSignature(encrypted, CryptographicData);

                await writer.WriteLineAsync($"{encrypted}|{signature}");

                await writer.FlushAsync();
            }
        }
        /// <summary>
        ///     Reads a line of characters asynchronously from the stream and returns the data as a string.
        /// </summary>
        /// <returns>Returns a <see cref="Task"/> that represents the asynchronous read operation. The value of the TResult parameter contains the next line from the stream.</returns>
        /// <remarks>The returned string will be decrypted if the <see cref="Client"/> has valid <see cref="CryptographicData"/> and the data seems encrypted (contains a "|").<para>Use the <see cref="ReadRawAsync"/> function to always get unhandled data.</para></remarks>
        /// <exception cref="ClientDisconnectedException">The <see cref="Client"/> isn't connected.</exception>
        /// <exception cref="HmacSignatureInvalidException">The signature is invalid or the data isn't encrypted but contains a "|".</exception>
        public async Task <string> ReadAsync()
        {
            if (CryptographicData == null || !CryptographicData.IsValid())
            {
                return(await ReadRawAsync());
            }

            var data = await ReadRawAsync();

            if (!data.Contains("|"))
            {
                return(data);
            }

            var encrypted = data.Split('|')[0];
            var signature = data.Split('|')[1];

            if (!CryptographyProvider.Instance.HmacValidateSignature(encrypted, signature, CryptographicData))
            {
                throw new HmacSignatureInvalidException();
            }

            return(await CryptographyProvider.Instance.AesDecryptAsync(encrypted, CryptographicData));
        }