using System; namespace Intelix.Helper.Encrypted; public class AesGcm256 { private static readonly byte[] SBox = new byte[256] { 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22 }; private static readonly byte[] Rcon = new byte[256] { 0, 1, 2, 4, 8, 16, 32, 64, 128, 27, 54, 108, 216, 171, 77, 154, 47, 94, 188, 99, 198, 151, 53, 106, 212, 179, 125, 250, 239, 197, 145, 57, 114, 228, 211, 189, 97, 194, 159, 37, 74, 148, 51, 102, 204, 131, 29, 58, 116, 232, 203, 141, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; private byte[] Key; private byte[,] RoundKeys; public AesGcm256(byte[] key) { if (key.Length != 32) { throw new ArgumentException("Key length must be 256 bits."); } Key = new byte[32]; Array.Copy(key, Key, 32); KeyExpansion(); } public static byte[] Decrypt(byte[] key, byte[] iv, byte[] aad, byte[] cipherText, byte[] authTag) { return new AesGcm256(key).Decrypt(cipherText, authTag, iv, aad); } private void KeyExpansion() { int num = 8; int num2 = 4; int num3 = 14; RoundKeys = new byte[num2 * (num3 + 1), 4]; for (int i = 0; i < num; i++) { RoundKeys[i, 0] = Key[4 * i]; RoundKeys[i, 1] = Key[4 * i + 1]; RoundKeys[i, 2] = Key[4 * i + 2]; RoundKeys[i, 3] = Key[4 * i + 3]; } byte[] array = new byte[4]; for (int j = num; j < num2 * (num3 + 1); j++) { array[0] = RoundKeys[j - 1, 0]; array[1] = RoundKeys[j - 1, 1]; array[2] = RoundKeys[j - 1, 2]; array[3] = RoundKeys[j - 1, 3]; if (j % num == 0) { byte b = array[0]; array[0] = array[1]; array[1] = array[2]; array[2] = array[3]; array[3] = b; array[0] = SBox[array[0]]; array[1] = SBox[array[1]]; array[2] = SBox[array[2]]; array[3] = SBox[array[3]]; array[0] ^= Rcon[j / num]; } else if (num > 6 && j % num == 4) { array[0] = SBox[array[0]]; array[1] = SBox[array[1]]; array[2] = SBox[array[2]]; array[3] = SBox[array[3]]; } RoundKeys[j, 0] = (byte)(RoundKeys[j - num, 0] ^ array[0]); RoundKeys[j, 1] = (byte)(RoundKeys[j - num, 1] ^ array[1]); RoundKeys[j, 2] = (byte)(RoundKeys[j - num, 2] ^ array[2]); RoundKeys[j, 3] = (byte)(RoundKeys[j - num, 3] ^ array[3]); } } private void AddRoundKey(byte[,] state, int round) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { state[j, i] ^= RoundKeys[round * 4 + i, j]; } } } private void SubBytes(byte[,] state) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { state[i, j] = SBox[state[i, j]]; } } } private void ShiftRows(byte[,] state) { byte b = state[1, 0]; state[1, 0] = state[1, 1]; state[1, 1] = state[1, 2]; state[1, 2] = state[1, 3]; state[1, 3] = b; b = state[2, 0]; state[2, 0] = state[2, 2]; state[2, 2] = b; b = state[2, 1]; state[2, 1] = state[2, 3]; state[2, 3] = b; b = state[3, 3]; state[3, 3] = state[3, 2]; state[3, 2] = state[3, 1]; state[3, 1] = state[3, 0]; state[3, 0] = b; } private void MixColumns(byte[,] state) { byte[] array = new byte[4]; for (int i = 0; i < 4; i++) { array[0] = (byte)(GFMultiply(2, state[0, i]) ^ GFMultiply(3, state[1, i]) ^ state[2, i] ^ state[3, i]); array[1] = (byte)(state[0, i] ^ GFMultiply(2, state[1, i]) ^ GFMultiply(3, state[2, i]) ^ state[3, i]); array[2] = (byte)(state[0, i] ^ state[1, i] ^ GFMultiply(2, state[2, i]) ^ GFMultiply(3, state[3, i])); array[3] = (byte)(GFMultiply(3, state[0, i]) ^ state[1, i] ^ state[2, i] ^ GFMultiply(2, state[3, i])); state[0, i] = array[0]; state[1, i] = array[1]; state[2, i] = array[2]; state[3, i] = array[3]; } } private byte GFMultiply(byte a, byte b) { byte b2 = 0; for (int i = 0; i < 8; i++) { if ((b & 1) != 0) { b2 ^= a; } bool num = (a & 0x80) != 0; a <<= 1; if (num) { a ^= 0x1B; } b >>= 1; } return b2; } private void EncryptBlock(byte[] input, byte[] output) { int num = 4; int num2 = 14; byte[,] array = new byte[4, num]; for (int i = 0; i < 16; i++) { array[i % 4, i / 4] = input[i]; } AddRoundKey(array, 0); for (int j = 1; j <= num2 - 1; j++) { SubBytes(array); ShiftRows(array); MixColumns(array); AddRoundKey(array, j); } SubBytes(array); ShiftRows(array); AddRoundKey(array, num2); for (int k = 0; k < 16; k++) { output[k] = array[k % 4, k / 4]; } } private byte[] GF128Multiply(byte[] X, byte[] Y) { byte[] array = new byte[16]; byte[] array2 = new byte[16]; Array.Copy(Y, array2, 16); for (int i = 0; i < 128; i++) { if (((X[i / 8] >> 7 - i % 8) & 1) == 1) { for (int j = 0; j < 16; j++) { array[j] ^= array2[j]; } } bool flag = (array2[15] & 1) == 1; for (int num = 15; num >= 0; num--) { array2[num] = (byte)((array2[num] >> 1) | (((num > 0) ? array2[num - 1] : 0) << 7)); } if (flag) { array2[0] ^= 225; } } return array; } private byte[] GHASH(byte[] H, byte[] A, byte[] C) { _ = (A.Length + 15) / 16; _ = (C.Length + 15) / 16; byte[] array = new byte[16]; byte[] array2 = new byte[16]; byte[] array3 = new byte[16]; int num; for (int i = 0; i < A.Length; i += num) { Array.Clear(array3, 0, 16); num = Math.Min(16, A.Length - i); Array.Copy(A, i, array3, 0, num); for (int j = 0; j < 16; j++) { array2[j] = (byte)(array[j] ^ array3[j]); } array = GF128Multiply(array2, H); } int num2; for (int i = 0; i < C.Length; i += num2) { Array.Clear(array3, 0, 16); num2 = Math.Min(16, C.Length - i); Array.Copy(C, i, array3, 0, num2); for (int k = 0; k < 16; k++) { array2[k] = (byte)(array[k] ^ array3[k]); } array = GF128Multiply(array2, H); } byte[] array4 = new byte[16]; ulong num3 = (ulong)A.Length * 8uL; ulong num4 = (ulong)C.Length * 8uL; for (int l = 0; l < 8; l++) { array4[7 - l] = (byte)(num3 >> l * 8); array4[15 - l] = (byte)(num4 >> l * 8); } for (int m = 0; m < 16; m++) { array2[m] = (byte)(array[m] ^ array4[m]); } return GF128Multiply(array2, H); } private void IncrementCounter(byte[] counterBlock) { int num = 15; while (num >= 12 && ++counterBlock[num] == 0) { num--; } } public byte[] Decrypt(byte[] ciphertext, byte[] tag, byte[] iv, byte[] aad) { if (aad == null) { aad = new byte[0]; } byte[] array = new byte[16]; EncryptBlock(new byte[16], array); byte[] array2 = new byte[16]; if (iv.Length == 12) { Array.Copy(iv, 0, array2, 0, 12); array2[15] = 1; } else { array2 = GHASH(array, null, iv); } byte[] array3 = new byte[ciphertext.Length]; byte[] array4 = new byte[16]; Array.Copy(array2, array4, 16); int num = ciphertext.Length / 16; int num2 = ciphertext.Length % 16; int num3 = ((num2 == 0) ? num : (num + 1)); for (int i = 0; i < num3; i++) { IncrementCounter(array4); byte[] array5 = new byte[16]; EncryptBlock(array4, array5); int num4 = ((i < num) ? 16 : num2); for (int j = 0; j < num4; j++) { array3[i * 16 + j] = (byte)(ciphertext[i * 16 + j] ^ array5[j]); } } byte[] array6 = GHASH(array, aad, ciphertext); byte[] array7 = new byte[16]; EncryptBlock(array2, array7); byte[] array8 = new byte[16]; for (int k = 0; k < 16; k++) { array8[k] = (byte)(array7[k] ^ array6[k]); } if (!VerifyTag(tag, array8)) { throw new Exception("Authentication tag does not match. Decryption failed."); } return array3; } private bool VerifyTag(byte[] tag1, byte[] tag2) { if (tag1.Length != tag2.Length) { return false; } int num = 0; for (int i = 0; i < tag1.Length; i++) { num |= tag1[i] ^ tag2[i]; } return num == 0; } }