Files

384 lines
9.7 KiB
C#
Raw Permalink Normal View History

2026-08-27 10:55:23 -06:00
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;
}
}