mirror of
https://github.com/EggLinks/DanhengServer-OpenSource.git
synced 2026-01-03 04:36:03 +08:00
187 lines
5.8 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|