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

namespace Server.Algorithm;

public class Aes256
{
	private const int KeyLength = 32;

	private const int AuthKeyLength = 64;

	private const int IvLength = 16;

	private const int HmacSha256Length = 32;

	private readonly byte[] _key;

	private readonly byte[] _authKey;

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

	public Aes256(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 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;
	}
}
