using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;

namespace HardcodedSymmetricEncryptionKey
{
    class Program
    {
        static void Main(string[] args)
        {
            var a = new AesCryptoServiceProvider();

            // BAD: explicit key assignment, hard-coded value
            a.Key = new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 };

            var b = new AesCryptoServiceProvider()
            {
                // BAD: explicit key assignment, hard-coded value
                Key = new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 }
            };

            var c = new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 };
            var d = c;

            var byteArrayFromString = Encoding.UTF8.GetBytes("Hello, world: here is a very bad way to create a key");

            // BAD: key assignment via variable, from hard-coded value
            a.Key = d;

            // GOOD (not really, but better than hard coding)
            a.Key = File.ReadAllBytes("secret.key");

            var cp = CreateProvider(d);

            var iv = new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 };

            // BAD: hard-coded key passed to Encrypt [NOT DETECTED]
            var ct = Encrypt("Test string here", c, iv);

            // BAD: hard-coded key converted from string and passed to Encrypt [NOT DETECTED]
            var ct1 = Encrypt("Test string here", byteArrayFromString, iv);

            // GOOD (this function hashes password)
            var de = DecryptWithPassword(ct, c, iv);

            // BAD: hard-coded password passed to Decrypt
            var de1 = Decrypt(ct, c, iv);

            // BAD [NOT DETECTED]
            CreateCryptographicKey(null, byteArrayFromString);

            // GOOD
            CreateCryptographicKey(null, File.ReadAllBytes("secret.key"));
        }

        public static string Decrypt(byte[] cipherText, byte[] password, byte[] IV)
        {
            byte[] rawPlaintext;
            var salt = new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 };

            using (Aes aes = new AesManaged())
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(password, IV), CryptoStreamMode.Write))
                    {
                        cs.Write(cipherText, 0, cipherText.Length);
                    }
                    rawPlaintext = ms.ToArray();
                }

                return Encoding.Unicode.GetString(rawPlaintext);
            }
        }

        public static string DecryptWithPassword(byte[] cipherText, byte[] password, byte[] IV)
        {
            byte[] rawPlaintext;
            var salt = new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 };
            using (Rfc2898DeriveBytes k1 = new Rfc2898DeriveBytes(password, salt, 100))
            {
                using (Aes aes = new AesManaged())
                {
                    using (MemoryStream ms = new MemoryStream())
                    {
                        // GOOD: for this test, the flow through Rfc2898DeriveBytes should be considered GOOD.
                        // GOOD: Use of hard-coded password for Rfc2898DeriveBytes is found by Semmle CWE-798: HardcodedCredentials.ql
                        using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(k1.GetBytes(16), IV), CryptoStreamMode.Write))
                        {
                            cs.Write(cipherText, 0, cipherText.Length);
                        }
                        rawPlaintext = ms.ToArray();
                    }

                    return Encoding.Unicode.GetString(rawPlaintext);
                }
            }
        }

        static SymmetricAlgorithm CreateProvider(byte[] key)
        {
            return new AesManaged()
            {
                // BAD: assignment from parameter
                Key = key
            };
        }

        public static byte[] Encrypt(string plaintext, byte[] key, byte[] IV)
        {
            byte[] rawPlaintext = Encoding.Unicode.GetBytes("Test string here");
            byte[] cipherText = null;
            using (Aes aes = new AesManaged())
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    // BAD: flow of hardcoded key to CreateEncryptor constructor
                    using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(key, IV), CryptoStreamMode.Write))
                    {
                        cs.Write(rawPlaintext, 0, rawPlaintext.Length);
                    }

                    cipherText = ms.ToArray();
                }

                return cipherText;
            }
        }

        static CryptographicKey CreateCryptographicKey(SymmetricKeyAlgorithmProvider p, byte[] bytes)
        {
            var buffer = CryptographicBuffer.CreateFromByteArray(bytes);
            return p.CreateSymmetricKey(buffer);
        }
    }
}
