Files
DanhengServer-OpenSource/Common/Util/Security/Ec2b.cs

187 lines
5.8 KiB
C#

using System;
using System.Buffers.Binary;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace EggLink.DanhengServer.Util.Security
{
public class Ec2b
{
private const int HEAD_MAGIC = 1647469381; // "Ec2b"
private const int KEY_SIZE = 16;
private const int DATA_SIZE = 2048;
private byte[] Key;
private byte[] Data;
private ulong Seed;
private byte[] XorKey;
#pragma warning disable CS8618 // CS8618 - Non-nullable variable must contain a non-null value when exiting constructor.
private Ec2b(byte[] key, byte[] data)
#pragma warning restore CS8618 // CS8618 - Non-nullable variable must contain a non-null value when exiting constructor.
{
Key = key;
Data = data;
byte[] scrambledKey = new byte[KEY_SIZE];
Array.Copy(Key, scrambledKey, KEY_SIZE);
KeyScramble(scrambledKey);
GenerateDecryptionVector(scrambledKey, Data);
}
public static Ec2b Read(byte[] input)
{
if (input == null || input.Length < 2076)
{
throw new Exception("Input data is too short.");
}
int offset = 0;
int magic = BinaryPrimitives.ReadInt32LittleEndian(input.AsSpan(offset, 4));
if (magic != HEAD_MAGIC)
{
throw new Exception($"Magic mismatch, expected: {HEAD_MAGIC}, got: {magic}");
}
offset += 4;
int keySize = BinaryPrimitives.ReadInt32LittleEndian(input.AsSpan(offset, 4));
offset += 4;
if (keySize != KEY_SIZE)
{
throw new Exception($"Invalid key size, expected: {KEY_SIZE}, got: {keySize}");
}
if (input.Length < offset + KEY_SIZE)
{
throw new Exception("Input data is too short for key.");
}
byte[] key = new byte[KEY_SIZE];
Array.Copy(input, offset, key, 0, KEY_SIZE);
offset += KEY_SIZE;
if (input.Length < offset + 4)
{
throw new Exception("Input data is too short for data size.");
}
int dataSize = BinaryPrimitives.ReadInt32LittleEndian(input.AsSpan(offset, 4));
offset += 4;
if (dataSize != DATA_SIZE)
{
throw new Exception($"Invalid data size, expected: {DATA_SIZE}, got: {dataSize}");
}
if (input.Length < offset + DATA_SIZE)
{
throw new Exception("Input data is too short for data.");
}
byte[] data = new byte[DATA_SIZE];
Array.Copy(input, offset, data, 0, DATA_SIZE);
return new Ec2b(key, data);
}
public static Ec2b GenerateEc2b()
{
byte[] key = new byte[KEY_SIZE];
byte[] data = new byte[DATA_SIZE];
MT19937 random = new MT19937((ulong)DateTimeOffset.UtcNow.ToUnixTimeSeconds());
for (int i = 0; i < KEY_SIZE; i++)
key[i] = (byte)(random.NextUInt64() % 256);
for (int i = 0; i < DATA_SIZE; i++)
data[i] = (byte)(random.NextUInt64() % 256);
return new Ec2b(key, data);
}
public byte[] GetBytes()
{
byte[] output = new byte[4 + 4 + KEY_SIZE + 4 + DATA_SIZE];
int offset = 0;
BinaryPrimitives.WriteInt32LittleEndian(output.AsSpan(offset, 4), HEAD_MAGIC);
offset += 4;
BinaryPrimitives.WriteInt32LittleEndian(output.AsSpan(offset, 4), KEY_SIZE);
offset += 4;
Array.Copy(Key, 0, output, offset, KEY_SIZE);
offset += KEY_SIZE;
BinaryPrimitives.WriteInt32LittleEndian(output.AsSpan(offset, 4), DATA_SIZE);
offset += 4;
Array.Copy(Data, 0, output, offset, DATA_SIZE);
return output;
}
public ulong GetSeed()
{
return Seed;
}
public byte[] GetXorKey()
{
return XorKey;
}
private void GenerateDecryptionVector(byte[] key, byte[] crypt)
{
List<byte> output = new List<byte>(4096);
ulong val = 18446744073709551615;
for (int i = 0; i < crypt.Length; i += 8)
{
ulong chunk = BitConverter.ToUInt64(crypt, i);
val ^= chunk;
}
ulong key_qword_0 = BitConverter.ToUInt64(key, 0);
ulong key_qword_1 = BitConverter.ToUInt64(key, 8);
Seed = key_qword_1 ^ 0b1100111010101100001110110101101010000110011110000011011110101100 ^ val ^ key_qword_0;
MT19937 mt = new MT19937(Seed);
for (int i = 0; i < 512; i++)
{
ulong next = mt.NextUInt64();
byte[] bytes = BitConverter.GetBytes(next);
output.AddRange(bytes);
}
XorKey = output.ToArray();
}
private static void KeyScramble(byte[] key)
{
byte[] roundKeys = new byte[176];
for (int round = 0; round < 11; round++)
{
for (int i = 0; i < 16; i++)
{
for (int j = 0; j < 16; j++)
{
int idx = (round << 8) + (i * 16) + j;
roundKeys[round * 16 + i] ^= (byte)(Magic.aesXorTable[1, idx] ^ Magic.aesXorTable[0, idx]);
}
}
}
byte[] chip = new byte[16];
AES128.RevAes128Enc(key, roundKeys, chip);
for (int i = 0; i < KEY_SIZE; i++)
{
chip[i] ^= Magic.keyXorTable[i];
}
Array.Copy(chip, key, KEY_SIZE);
}
}
}