/// <summary>
        /// Symmetrically encrypts the specified buffer using a randomly generated key.
        /// </summary>
        /// <param name="data">The data to encrypt.</param>
        /// <param name="encryptionVariables">Optional encryption variables to use; or <c>null</c> to use randomly generated ones.</param>
        /// <returns>
        /// The result of the encryption.
        /// </returns>
        public override SymmetricEncryptionResult Encrypt(byte[] data, SymmetricEncryptionVariables encryptionVariables)
        {
            Requires.NotNull(data, "data");

            IBuffer plainTextBuffer = CryptographicBuffer.CreateFromByteArray(data);
            IBuffer symmetricKeyMaterial, ivBuffer;

            if (encryptionVariables == null)
            {
                symmetricKeyMaterial = CryptographicBuffer.GenerateRandom((uint)this.SymmetricEncryptionKeySize / 8);
                ivBuffer             = CryptographicBuffer.GenerateRandom(SymmetricAlgorithm.BlockLength);
            }
            else
            {
                Requires.Argument(encryptionVariables.Key.Length == this.SymmetricEncryptionKeySize / 8, "key", "Incorrect length.");
                Requires.Argument(encryptionVariables.IV.Length == this.SymmetricEncryptionBlockSize / 8, "iv", "Incorrect length.");
                symmetricKeyMaterial = CryptographicBuffer.CreateFromByteArray(encryptionVariables.Key);
                ivBuffer             = CryptographicBuffer.CreateFromByteArray(encryptionVariables.IV);
            }

            var symmetricKey     = SymmetricAlgorithm.CreateSymmetricKey(symmetricKeyMaterial);
            var cipherTextBuffer = CryptographicEngine.Encrypt(symmetricKey, plainTextBuffer, ivBuffer);

            return(new SymmetricEncryptionResult(
                       symmetricKeyMaterial.ToArray(),
                       ivBuffer.ToArray(),
                       cipherTextBuffer.ToArray()));
        }
        /// <summary>
        /// Symmetrically encrypts a stream.
        /// </summary>
        /// <param name="plaintext">The stream of plaintext to encrypt.</param>
        /// <param name="ciphertext">The stream to receive the ciphertext.</param>
        /// <param name="encryptionVariables">An optional key and IV to use. May be <c>null</c> to use randomly generated values.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that completes when encryption has completed, whose result is the key and IV to use to decrypt the ciphertext.</returns>
        public override async Task <SymmetricEncryptionVariables> EncryptAsync(Stream plaintext, Stream ciphertext, SymmetricEncryptionVariables encryptionVariables, CancellationToken cancellationToken)
        {
            Requires.NotNull(plaintext, "plaintext");
            Requires.NotNull(ciphertext, "ciphertext");

            using (var alg = SymmetricAlgorithm.Create(this.SymmetricEncryptionConfiguration.AlgorithmName)) {
                alg.Mode    = (CipherMode)Enum.Parse(typeof(CipherMode), this.SymmetricEncryptionConfiguration.BlockMode);
                alg.Padding = (PaddingMode)Enum.Parse(typeof(PaddingMode), this.SymmetricEncryptionConfiguration.Padding);
                alg.KeySize = this.SymmetricEncryptionKeySize;

                if (encryptionVariables != null)
                {
                    Requires.Argument(encryptionVariables.Key.Length == this.SymmetricEncryptionKeySize / 8, "key", "Incorrect length.");
                    Requires.Argument(encryptionVariables.IV.Length == this.SymmetricEncryptionBlockSize / 8, "iv", "Incorrect length.");
                    alg.Key = encryptionVariables.Key;
                    alg.IV  = encryptionVariables.IV;
                }
                else
                {
                    encryptionVariables = new SymmetricEncryptionVariables(alg.Key, alg.IV);
                }

                using (var encryptor = alg.CreateEncryptor()) {
                    var cryptoStream = new CryptoStream(ciphertext, encryptor, CryptoStreamMode.Write);                     // DON'T dispose this, or it disposes of the ciphertext stream.
                    await plaintext.CopyToAsync(cryptoStream, alg.BlockSize, cancellationToken);

                    cryptoStream.FlushFinalBlock();
                    return(encryptionVariables);
                }
            }
        }
        public SymmetricEncryptionResult Encrypt(byte[] data, SymmetricEncryptionVariables encryptionVariables)
        {
            var rng = new Random();

            byte[] key, iv;
            if (encryptionVariables != null)
            {
                key = encryptionVariables.Key;
                iv  = encryptionVariables.IV;
            }
            else
            {
                key = new byte[KeyLengthInBytes];
                rng.NextBytes(key);

                iv = new byte[KeyLengthInBytes];
                rng.NextBytes(iv);
            }

            var ciphertext = new byte[key.Length + iv.Length + data.Length];

            Array.Copy(key, ciphertext, key.Length);
            Array.Copy(iv, 0, ciphertext, key.Length, iv.Length);
            Array.Copy(data, 0, ciphertext, key.Length + iv.Length, data.Length);
            return(new SymmetricEncryptionResult(key, iv, ciphertext));
        }
        /// <summary>
        /// Symmetrically encrypts a stream.
        /// </summary>
        /// <param name="plaintext">The stream of plaintext to encrypt.</param>
        /// <param name="ciphertext">The stream to receive the ciphertext.</param>
        /// <param name="encryptionVariables">An optional key and IV to use. May be <c>null</c> to use randomly generated values.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that completes when encryption has completed, whose result is the key and IV to use to decrypt the ciphertext.</returns>
        public override async Task <SymmetricEncryptionVariables> EncryptAsync(Stream plaintext, Stream ciphertext, SymmetricEncryptionVariables encryptionVariables, CancellationToken cancellationToken)
        {
            var encryptor = this.GetCipher();

            if (encryptionVariables == null)
            {
                var    secureRandom = new SecureRandom();
                byte[] key          = new byte[this.SymmetricEncryptionKeySize / 8];
                secureRandom.NextBytes(key);

                var    random = new Random();
                byte[] iv     = new byte[encryptor.GetBlockSize()];
                random.NextBytes(iv);

                encryptionVariables = new SymmetricEncryptionVariables(key, iv);
            }
            else
            {
                Requires.Argument(encryptionVariables.Key.Length == this.SymmetricEncryptionKeySize / 8, "key", "Incorrect length.");
                Requires.Argument(encryptionVariables.IV.Length == encryptor.GetBlockSize(), "iv", "Incorrect length.");
            }

            var parameters = new ParametersWithIV(new KeyParameter(encryptionVariables.Key), encryptionVariables.IV);

            encryptor.Init(true, parameters);
            await CipherStreamCopyAsync(plaintext, ciphertext, encryptor, cancellationToken);

            return(encryptionVariables);
        }
        public async Task DecryptAsync(Stream ciphertext, Stream plaintext, SymmetricEncryptionVariables encryptionVariables, CancellationToken cancellationToken = default(CancellationToken))
        {
            var buffer = new byte[ciphertext.Length - ciphertext.Position];
            await ciphertext.ReadAsync(buffer, 0, buffer.Length);

            var oldResult       = new SymmetricEncryptionResult(encryptionVariables, buffer);
            var plaintextBuffer = this.Decrypt(oldResult);
            await plaintext.WriteAsync(plaintextBuffer, 0, plaintextBuffer.Length);
        }
        /// <summary>
        /// Symmetrically decrypts a stream.
        /// </summary>
        /// <param name="ciphertext">The stream of ciphertext to decrypt.</param>
        /// <param name="plaintext">The stream to receive the plaintext.</param>
        /// <param name="encryptionVariables">The key and IV to use.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that represents the asynchronous operation.</returns>
        public override async Task DecryptAsync(Stream ciphertext, Stream plaintext, SymmetricEncryptionVariables encryptionVariables, CancellationToken cancellationToken)
        {
            Requires.NotNull(ciphertext, "ciphertext");
            Requires.NotNull(plaintext, "plaintext");
            Requires.NotNull(encryptionVariables, "encryptionVariables");

            var parameters = new ParametersWithIV(new KeyParameter(encryptionVariables.Key), encryptionVariables.IV);
            var decryptor  = this.GetCipher();

            decryptor.Init(false, parameters);
            await CipherStreamCopyAsync(ciphertext, plaintext, decryptor, cancellationToken);
        }
        /// <summary>
        /// Symmetrically decrypts a stream.
        /// </summary>
        /// <param name="ciphertext">The stream of ciphertext to decrypt.</param>
        /// <param name="plaintext">The stream to receive the plaintext.</param>
        /// <param name="encryptionVariables">The key and IV to use.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that represents the asynchronous operation.</returns>
        public override async Task DecryptAsync(Stream ciphertext, Stream plaintext, SymmetricEncryptionVariables encryptionVariables, CancellationToken cancellationToken)
        {
            Requires.NotNull(ciphertext, "ciphertext");
            Requires.NotNull(plaintext, "plaintext");
            Requires.NotNull(encryptionVariables, "encryptionVariables");

            var ciphertextMemoryStream = new MemoryStream();
            await ciphertext.CopyToAsync(ciphertextMemoryStream, 4096, cancellationToken);

            cancellationToken.ThrowIfCancellationRequested();
            byte[] plaintextBytes = this.Decrypt(new SymmetricEncryptionResult(encryptionVariables, ciphertextMemoryStream.ToArray()));
            await plaintext.WriteAsync(plaintextBytes, 0, plaintextBytes.Length, cancellationToken);
        }
        /// <summary>
        /// Symmetrically decrypts a stream.
        /// </summary>
        /// <param name="ciphertext">The stream of ciphertext to decrypt.</param>
        /// <param name="plaintext">The stream to receive the plaintext.</param>
        /// <param name="encryptionVariables">The key and IV to use.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that represents the asynchronous operation.</returns>
        public override async Task DecryptAsync(Stream ciphertext, Stream plaintext, SymmetricEncryptionVariables encryptionVariables, CancellationToken cancellationToken)
        {
            Requires.NotNull(ciphertext, "ciphertext");
            Requires.NotNull(plaintext, "plaintext");
            Requires.NotNull(encryptionVariables, "encryptionVariables");

            using (var alg = SymmetricAlgorithm.Create(this.SymmetricEncryptionConfiguration.AlgorithmName)) {
                alg.Mode    = (CipherMode)Enum.Parse(typeof(CipherMode), this.SymmetricEncryptionConfiguration.BlockMode);
                alg.Padding = (PaddingMode)Enum.Parse(typeof(PaddingMode), this.SymmetricEncryptionConfiguration.Padding);
                using (var decryptor = alg.CreateDecryptor(encryptionVariables.Key, encryptionVariables.IV)) {
                    var cryptoStream = new CryptoStream(plaintext, decryptor, CryptoStreamMode.Write);                     // don't dispose this or it disposes the target stream.
                    await ciphertext.CopyToAsync(cryptoStream, 4096, cancellationToken);

                    cryptoStream.FlushFinalBlock();
                }
            }
        }
        /// <summary>
        /// Symmetrically encrypts a stream.
        /// </summary>
        /// <param name="plaintext">The stream of plaintext to encrypt.</param>
        /// <param name="ciphertext">The stream to receive the ciphertext.</param>
        /// <param name="encryptionVariables">An optional key and IV to use. May be <c>null</c> to use randomly generated values.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that completes when encryption has completed, whose result is the key and IV to use to decrypt the ciphertext.</returns>
        public override async Task <SymmetricEncryptionVariables> EncryptAsync(Stream plaintext, Stream ciphertext, SymmetricEncryptionVariables encryptionVariables, CancellationToken cancellationToken)
        {
            Requires.NotNull(plaintext, "plaintext");
            Requires.NotNull(ciphertext, "ciphertext");

            var plaintextMemoryStream = new MemoryStream();
            await plaintext.CopyToAsync(plaintextMemoryStream, 4096, cancellationToken);

            cancellationToken.ThrowIfCancellationRequested();
            var result = this.Encrypt(plaintextMemoryStream.ToArray(), encryptionVariables);
            await ciphertext.WriteAsync(result.Ciphertext, 0, result.Ciphertext.Length, cancellationToken);

            return(result);
        }
        public async Task <SymmetricEncryptionVariables> EncryptAsync(Stream plaintext, Stream ciphertext, SymmetricEncryptionVariables encryptionVariables = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            var plaintextBuffer = new byte[plaintext.Length - plaintext.Position];
            await plaintext.ReadAsync(plaintextBuffer, 0, plaintextBuffer.Length);

            var result = this.Encrypt(plaintextBuffer, encryptionVariables);
            await ciphertext.WriteAsync(result.Ciphertext, 0, result.Ciphertext.Length);

            return(result);
        }